mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2025-01-30 14:58:38 +00:00
web: add basic edit capability for first line
This commit is contained in:
parent
2acd77dea0
commit
968c7021df
@ -25,6 +25,12 @@ class RequestHandler(tornado.web.RequestHandler):
|
||||
"style-src 'self' 'unsafe-inline'"
|
||||
)
|
||||
|
||||
@property
|
||||
def json(self):
|
||||
if not self.request.headers.get("Content-Type").startswith("application/json"):
|
||||
return None
|
||||
return json.loads(self.request.body)
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
return self.application.master.state
|
||||
@ -111,6 +117,35 @@ class FlowHandler(RequestHandler):
|
||||
self.flow.kill(self.master)
|
||||
self.state.delete_flow(self.flow)
|
||||
|
||||
def put(self, flow_id):
|
||||
flow = self.flow
|
||||
for a, b in self.json.iteritems():
|
||||
|
||||
if a == "request":
|
||||
request = flow.request
|
||||
for k, v in b.iteritems():
|
||||
if k in ["method", "scheme", "host", "path"]:
|
||||
setattr(request, k, str(v))
|
||||
elif k == "port":
|
||||
request.port = int(v)
|
||||
elif k == "httpversion":
|
||||
request.httpversion = tuple(int(x) for x in v)
|
||||
else:
|
||||
print "Warning: Unknown update {}.{}: {}".format(a, k, v)
|
||||
|
||||
elif a == "response":
|
||||
response = flow.response
|
||||
for k, v in b.iteritems():
|
||||
if k == "msg":
|
||||
response.msg = str(v)
|
||||
elif k == "code":
|
||||
response.code = int(v)
|
||||
elif k == "httpversion":
|
||||
response.httpversion = tuple(int(x) for x in v)
|
||||
else:
|
||||
print "Warning: Unknown update {}: {}".format(a, b)
|
||||
self.state.update_flow(flow)
|
||||
|
||||
|
||||
class DuplicateFlow(RequestHandler):
|
||||
def post(self, flow_id):
|
||||
@ -176,18 +211,12 @@ class Settings(RequestHandler):
|
||||
)
|
||||
))
|
||||
|
||||
def put(self, *update, **kwargs):
|
||||
def put(self):
|
||||
update = {}
|
||||
for k, v in self.request.arguments.iteritems():
|
||||
if len(v) != 1:
|
||||
print "Warning: Unknown length for setting {}: {}".format(k, v)
|
||||
continue
|
||||
|
||||
if k == "_xsrf":
|
||||
continue
|
||||
elif k == "intercept":
|
||||
self.state.set_intercept(v[0])
|
||||
update[k] = v[0]
|
||||
for k, v in self.json.iteritems():
|
||||
if k == "intercept":
|
||||
self.state.set_intercept(v)
|
||||
update[k] = v
|
||||
else:
|
||||
print "Warning: Unknown setting {}: {}".format(k, v)
|
||||
|
||||
|
@ -273,6 +273,10 @@ header .menu {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
overflow-y: scroll;
|
||||
/*.request .response-line,
|
||||
.response .request-line {
|
||||
opacity: 0.7;
|
||||
}*/
|
||||
}
|
||||
.flow-detail nav {
|
||||
background-color: #F2F2F2;
|
||||
@ -291,9 +295,22 @@ header .menu {
|
||||
max-height: 100px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.flow-detail .request-line {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.flow-detail hr {
|
||||
margin: 0 0 5px;
|
||||
}
|
||||
.inline-input {
|
||||
margin: 0 -5px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
.inline-input[contenteditable] {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
.inline-input[contenteditable].has-warning {
|
||||
color: #ffb8b8;
|
||||
}
|
||||
.view-options {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
@ -303,6 +303,7 @@ function isUndefined(arg) {
|
||||
|
||||
},{}],2:[function(require,module,exports){
|
||||
var $ = require("jquery");
|
||||
var _ = require("lodash");
|
||||
var AppDispatcher = require("./dispatcher.js").AppDispatcher;
|
||||
|
||||
var ActionTypes = {
|
||||
@ -348,7 +349,8 @@ var SettingsActions = {
|
||||
$.ajax({
|
||||
type: "PUT",
|
||||
url: "/settings",
|
||||
data: settings
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(settings)
|
||||
});
|
||||
|
||||
/*
|
||||
@ -399,15 +401,26 @@ var FlowActions = {
|
||||
revert: function(flow){
|
||||
$.post("/flows/" + flow.id + "/revert");
|
||||
},
|
||||
update: function (flow) {
|
||||
update: function (flow, nextProps) {
|
||||
/*
|
||||
//Facebook Flux: We do an optimistic update on the client already.
|
||||
var nextFlow = _.cloneDeep(flow);
|
||||
_.merge(nextFlow, nextProps);
|
||||
AppDispatcher.dispatchViewAction({
|
||||
type: ActionTypes.FLOW_STORE,
|
||||
cmd: StoreCmds.UPDATE,
|
||||
data: flow
|
||||
data: nextFlow
|
||||
});
|
||||
*/
|
||||
$.ajax({
|
||||
type: "PUT",
|
||||
url: "/flows/" + flow.id,
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(nextProps)
|
||||
});
|
||||
},
|
||||
clear: function(){
|
||||
$.post("/clear");
|
||||
$.post("/flows/" + flow.id);
|
||||
}
|
||||
};
|
||||
|
||||
@ -427,7 +440,7 @@ module.exports = {
|
||||
Query: Query
|
||||
};
|
||||
|
||||
},{"./dispatcher.js":19,"jquery":"jquery"}],3:[function(require,module,exports){
|
||||
},{"./dispatcher.js":19,"jquery":"jquery","lodash":"lodash"}],3:[function(require,module,exports){
|
||||
|
||||
var React = require("react");
|
||||
var ReactRouter = require("react-router");
|
||||
@ -478,6 +491,13 @@ var StickyHeadMixin = {
|
||||
};
|
||||
|
||||
|
||||
var ChildFocus = {
|
||||
contextTypes: {
|
||||
returnFocus: React.PropTypes.func
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var Navigation = _.extend({}, ReactRouter.Navigation, {
|
||||
setQuery: function (dict) {
|
||||
var q = this.context.router.getCurrentQuery();
|
||||
@ -624,6 +644,7 @@ var Splitter = React.createClass({displayName: "Splitter",
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
ChildFocus: ChildFocus,
|
||||
State: State,
|
||||
Navigation: Navigation,
|
||||
StickyHeadMixin: StickyHeadMixin,
|
||||
@ -810,7 +831,7 @@ var TLSColumn = React.createClass({displayName: "TLSColumn",
|
||||
},
|
||||
render: function () {
|
||||
var flow = this.props.flow;
|
||||
var ssl = (flow.request.scheme == "https");
|
||||
var ssl = (flow.request.scheme === "https");
|
||||
var classes;
|
||||
if (ssl) {
|
||||
classes = "col-tls col-tls-https";
|
||||
@ -838,7 +859,7 @@ var IconColumn = React.createClass({displayName: "IconColumn",
|
||||
var contentType = ResponseUtils.getContentType(flow.response);
|
||||
|
||||
//TODO: We should assign a type to the flow somewhere else.
|
||||
if (flow.response.code == 304) {
|
||||
if (flow.response.code === 304) {
|
||||
icon = "resource-icon-not-modified";
|
||||
} else if (300 <= flow.response.code && flow.response.code < 400) {
|
||||
icon = "resource-icon-redirect";
|
||||
@ -1224,7 +1245,7 @@ var RawMixin = {
|
||||
}
|
||||
},
|
||||
requestContent: function (nextProps) {
|
||||
if(this.state.request){
|
||||
if (this.state.request) {
|
||||
this.state.request.abort();
|
||||
}
|
||||
var request = MessageUtils.getContent(nextProps.flow, nextProps.message);
|
||||
@ -1235,11 +1256,11 @@ var RawMixin = {
|
||||
request.done(function (data) {
|
||||
this.setState({content: data});
|
||||
}.bind(this)).fail(function (jqXHR, textStatus, errorThrown) {
|
||||
if(textStatus === "abort"){
|
||||
if (textStatus === "abort") {
|
||||
return;
|
||||
}
|
||||
this.setState({content: "AJAX Error: " + textStatus + "\r\n" + errorThrown});
|
||||
}.bind(this)).always(function(){
|
||||
}.bind(this)).always(function () {
|
||||
this.setState({request: undefined});
|
||||
}.bind(this));
|
||||
|
||||
@ -1252,8 +1273,8 @@ var RawMixin = {
|
||||
this.requestContent(nextProps);
|
||||
}
|
||||
},
|
||||
componentWillUnmount: function(){
|
||||
if(this.state.request){
|
||||
componentWillUnmount: function () {
|
||||
if (this.state.request) {
|
||||
this.state.request.abort();
|
||||
}
|
||||
},
|
||||
@ -1291,7 +1312,7 @@ var ViewJSON = React.createClass({displayName: "ViewJSON",
|
||||
var json = this.state.content;
|
||||
try {
|
||||
json = JSON.stringify(JSON.parse(json), null, 2);
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
}
|
||||
return React.createElement("pre", null, json);
|
||||
}
|
||||
@ -1336,7 +1357,7 @@ var ContentMissing = React.createClass({displayName: "ContentMissing",
|
||||
|
||||
var TooLarge = React.createClass({displayName: "TooLarge",
|
||||
statics: {
|
||||
isTooLarge: function(message){
|
||||
isTooLarge: function (message) {
|
||||
var max_mb = ViewImage.matches(message) ? 10 : 0.2;
|
||||
return message.contentLength > 1024 * 1024 * max_mb;
|
||||
}
|
||||
@ -1422,8 +1443,10 @@ var ContentView = React.createClass({displayName: "ContentView",
|
||||
React.createElement(this.state.View, React.__spread({}, this.props)),
|
||||
React.createElement("div", {className: "view-options text-center"},
|
||||
React.createElement(ViewSelector, {selectView: this.selectView, active: this.state.View, message: message}),
|
||||
" ",
|
||||
React.createElement("a", {className: "btn btn-default btn-xs", href: downloadUrl}, React.createElement("i", {className: "fa fa-download"}))
|
||||
" ",
|
||||
React.createElement("a", {className: "btn btn-default btn-xs", href: downloadUrl},
|
||||
React.createElement("i", {className: "fa fa-download"})
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -1692,7 +1715,10 @@ module.exports = FlowView;
|
||||
|
||||
},{"../common.js":4,"./details.js":9,"./messages.js":11,"./nav.js":12,"lodash":"lodash","react":"react"}],11:[function(require,module,exports){
|
||||
var React = require("react");
|
||||
var _ = require("lodash");
|
||||
|
||||
var common = require("../common.js");
|
||||
var actions = require("../../actions.js");
|
||||
var flowutils = require("../../flow/utils.js");
|
||||
var utils = require("../../utils.js");
|
||||
var ContentView = require("./contentview.js");
|
||||
@ -1717,20 +1743,217 @@ var Headers = React.createClass({displayName: "Headers",
|
||||
}
|
||||
});
|
||||
|
||||
var InlineInput = React.createClass({displayName: "InlineInput",
|
||||
mixins: [common.ChildFocus],
|
||||
getInitialState: function () {
|
||||
return {
|
||||
editable: false
|
||||
};
|
||||
},
|
||||
render: function () {
|
||||
var Tag = this.props.tag || "span";
|
||||
var className = "inline-input " + (this.props.className || "");
|
||||
var html = {__html: _.escape(this.props.content)};
|
||||
return React.createElement(Tag, React.__spread({},
|
||||
this.props,
|
||||
{tabIndex: "0",
|
||||
className: className,
|
||||
contentEditable: this.state.editable || undefined,
|
||||
onInput: this.onInput,
|
||||
onFocus: this.onFocus,
|
||||
onBlur: this.onBlur,
|
||||
onKeyDown: this.onKeyDown,
|
||||
dangerouslySetInnerHTML: html})
|
||||
);
|
||||
},
|
||||
onKeyDown: function (e) {
|
||||
e.stopPropagation();
|
||||
switch (e.keyCode) {
|
||||
case utils.Key.ESC:
|
||||
this.blur();
|
||||
break;
|
||||
case utils.Key.ENTER:
|
||||
e.preventDefault();
|
||||
if (!e.ctrlKey) {
|
||||
this.blur();
|
||||
} else {
|
||||
this.props.onDone && this.props.onDone();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
blur: function(){
|
||||
this.getDOMNode().blur();
|
||||
this.context.returnFocus && this.context.returnFocus();
|
||||
},
|
||||
selectContents: function () {
|
||||
var range = document.createRange();
|
||||
range.selectNodeContents(this.getDOMNode());
|
||||
var sel = window.getSelection();
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
},
|
||||
onFocus: function () {
|
||||
this.setState({editable: true}, this.selectContents);
|
||||
},
|
||||
onBlur: function (e) {
|
||||
this.setState({editable: false});
|
||||
this.handleChange();
|
||||
this.props.onDone && this.props.onDone();
|
||||
},
|
||||
onInput: function () {
|
||||
this.handleChange();
|
||||
},
|
||||
handleChange: function () {
|
||||
var content = this.getDOMNode().textContent;
|
||||
if (content !== this.props.content) {
|
||||
this.props.onChange(content);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var ValidateInlineInput = React.createClass({displayName: "ValidateInlineInput",
|
||||
getInitialState: function () {
|
||||
return {
|
||||
content: ""+this.props.content,
|
||||
originalContent: ""+this.props.content
|
||||
};
|
||||
},
|
||||
onChange: function (val) {
|
||||
this.setState({
|
||||
content: val
|
||||
});
|
||||
},
|
||||
onDone: function () {
|
||||
if (this.state.content === this.state.originalContent) {
|
||||
return true;
|
||||
}
|
||||
if (this.props.isValid(this.state.content)) {
|
||||
this.props.onChange(this.state.content);
|
||||
} else {
|
||||
this.setState({
|
||||
content: this.state.originalContent
|
||||
});
|
||||
}
|
||||
},
|
||||
componentWillReceiveProps: function (nextProps) {
|
||||
if (nextProps.content !== this.state.content) {
|
||||
this.setState({
|
||||
content: ""+nextProps.content,
|
||||
originalContent: ""+nextProps.content
|
||||
})
|
||||
}
|
||||
},
|
||||
render: function () {
|
||||
var className = this.props.className || "";
|
||||
if (this.props.isValid(this.state.content)) {
|
||||
className += " has-success";
|
||||
} else {
|
||||
className += " has-warning"
|
||||
}
|
||||
return React.createElement(InlineInput, React.__spread({}, this.props,
|
||||
{className: className,
|
||||
content: this.state.content,
|
||||
onChange: this.onChange,
|
||||
onDone: this.onDone})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var RequestLine = React.createClass({displayName: "RequestLine",
|
||||
render: function () {
|
||||
var flow = this.props.flow;
|
||||
var url = flowutils.RequestUtils.pretty_url(flow.request);
|
||||
var httpver = "HTTP/" + flow.request.httpversion.join(".");
|
||||
|
||||
return React.createElement("div", {className: "first-line request-line"},
|
||||
React.createElement(ValidateInlineInput, {content: flow.request.method, onChange: this.onMethodChange, isValid: this.isValidMethod}),
|
||||
" ",
|
||||
React.createElement(ValidateInlineInput, {content: url, onChange: this.onUrlChange, isValid: this.isValidUrl}),
|
||||
" ",
|
||||
React.createElement(ValidateInlineInput, {content: httpver, onChange: this.onHttpVersionChange, isValid: flowutils.isValidHttpVersion})
|
||||
)
|
||||
},
|
||||
isValidMethod: function (method) {
|
||||
return true;
|
||||
},
|
||||
isValidUrl: function (url) {
|
||||
var u = flowutils.parseUrl(url);
|
||||
return !!u.host;
|
||||
},
|
||||
onMethodChange: function (nextMethod) {
|
||||
actions.FlowActions.update(
|
||||
this.props.flow,
|
||||
{request: {method: nextMethod}}
|
||||
);
|
||||
},
|
||||
onUrlChange: function (nextUrl) {
|
||||
var props = flowutils.parseUrl(nextUrl);
|
||||
props.path = props.path || "";
|
||||
actions.FlowActions.update(
|
||||
this.props.flow,
|
||||
{request: props}
|
||||
);
|
||||
},
|
||||
onHttpVersionChange: function (nextVer) {
|
||||
var ver = flowutils.parseHttpVersion(nextVer);
|
||||
actions.FlowActions.update(
|
||||
this.props.flow,
|
||||
{request: {httpversion: ver}}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var ResponseLine = React.createClass({displayName: "ResponseLine",
|
||||
render: function () {
|
||||
var flow = this.props.flow;
|
||||
var httpver = "HTTP/" + flow.response.httpversion.join(".");
|
||||
return React.createElement("div", {className: "first-line response-line"},
|
||||
React.createElement(ValidateInlineInput, {content: httpver, onChange: this.onHttpVersionChange, isValid: flowutils.isValidHttpVersion}),
|
||||
" ",
|
||||
React.createElement(ValidateInlineInput, {content: flow.response.code, onChange: this.onCodeChange, isValid: this.isValidCode}),
|
||||
" ",
|
||||
React.createElement(ValidateInlineInput, {content: flow.response.msg, onChange: this.onMsgChange, isValid: this.isValidMsg})
|
||||
|
||||
);
|
||||
},
|
||||
isValidCode: function (code) {
|
||||
return /^\d+$/.test(code);
|
||||
},
|
||||
isValidMsg: function () {
|
||||
return true;
|
||||
},
|
||||
onHttpVersionChange: function (nextVer) {
|
||||
var ver = flowutils.parseHttpVersion(nextVer);
|
||||
actions.FlowActions.update(
|
||||
this.props.flow,
|
||||
{response: {httpversion: ver}}
|
||||
);
|
||||
},
|
||||
onMsgChange: function(nextMsg){
|
||||
actions.FlowActions.update(
|
||||
this.props.flow,
|
||||
{response: {msg: nextMsg}}
|
||||
);
|
||||
},
|
||||
onCodeChange: function(nextCode){
|
||||
nextCode = parseInt(nextCode);
|
||||
actions.FlowActions.update(
|
||||
this.props.flow,
|
||||
{response: {code: nextCode}}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var Request = React.createClass({displayName: "Request",
|
||||
render: function () {
|
||||
var flow = this.props.flow;
|
||||
var first_line = [
|
||||
flow.request.method,
|
||||
flowutils.RequestUtils.pretty_url(flow.request),
|
||||
"HTTP/" + flow.request.httpversion.join(".")
|
||||
].join(" ");
|
||||
|
||||
//TODO: Styling
|
||||
|
||||
return (
|
||||
React.createElement("section", null,
|
||||
React.createElement("div", {className: "first-line"}, first_line ),
|
||||
React.createElement("section", {className: "request"},
|
||||
React.createElement(RequestLine, {flow: flow}),
|
||||
/*<ResponseLine flow={flow}/>*/
|
||||
React.createElement(Headers, {message: flow.request}),
|
||||
React.createElement("hr", null),
|
||||
React.createElement(ContentView, {flow: flow, message: flow.request})
|
||||
@ -1742,17 +1965,10 @@ var Request = React.createClass({displayName: "Request",
|
||||
var Response = React.createClass({displayName: "Response",
|
||||
render: function () {
|
||||
var flow = this.props.flow;
|
||||
var first_line = [
|
||||
"HTTP/" + flow.response.httpversion.join("."),
|
||||
flow.response.code,
|
||||
flow.response.msg
|
||||
].join(" ");
|
||||
|
||||
//TODO: Styling
|
||||
|
||||
return (
|
||||
React.createElement("section", null,
|
||||
React.createElement("div", {className: "first-line"}, first_line ),
|
||||
React.createElement("section", {className: "response"},
|
||||
/*<RequestLine flow={flow}/>*/
|
||||
React.createElement(ResponseLine, {flow: flow}),
|
||||
React.createElement(Headers, {message: flow.response}),
|
||||
React.createElement("hr", null),
|
||||
React.createElement(ContentView, {flow: flow, message: flow.response})
|
||||
@ -1783,7 +1999,7 @@ module.exports = {
|
||||
Error: Error
|
||||
};
|
||||
|
||||
},{"../../flow/utils.js":21,"../../utils.js":24,"./contentview.js":8,"react":"react"}],12:[function(require,module,exports){
|
||||
},{"../../actions.js":2,"../../flow/utils.js":21,"../../utils.js":24,"../common.js":4,"./contentview.js":8,"lodash":"lodash","react":"react"}],12:[function(require,module,exports){
|
||||
var React = require("react");
|
||||
|
||||
var actions = require("../../actions.js");
|
||||
@ -2273,6 +2489,15 @@ var FlowView = require("./flowview/index.js");
|
||||
|
||||
var MainView = React.createClass({displayName: "MainView",
|
||||
mixins: [common.Navigation, common.State],
|
||||
childContextTypes: {
|
||||
returnFocus: React.PropTypes.func.isRequired
|
||||
},
|
||||
getChildContext: function() {
|
||||
return { returnFocus: this.returnFocus };
|
||||
},
|
||||
returnFocus: function(){
|
||||
this.getDOMNode().focus();
|
||||
},
|
||||
getInitialState: function () {
|
||||
return {
|
||||
flows: [],
|
||||
@ -4522,9 +4747,17 @@ module.exports = (function() {
|
||||
var _ = require("lodash");
|
||||
var $ = require("jquery");
|
||||
|
||||
var defaultPorts = {
|
||||
"http": 80,
|
||||
"https": 443
|
||||
};
|
||||
|
||||
var MessageUtils = {
|
||||
getContentType: function (message) {
|
||||
return this.get_first_header(message, /^Content-Type$/i).split(";")[0].trim();
|
||||
var ct = this.get_first_header(message, /^Content-Type$/i);
|
||||
if(ct){
|
||||
return ct.split(";")[0].trim();
|
||||
}
|
||||
},
|
||||
get_first_header: function (message, regex) {
|
||||
//FIXME: Cache Invalidation.
|
||||
@ -4557,25 +4790,20 @@ var MessageUtils = {
|
||||
}
|
||||
return false;
|
||||
},
|
||||
getContentURL: function(flow, message){
|
||||
if(message === flow.request){
|
||||
getContentURL: function (flow, message) {
|
||||
if (message === flow.request) {
|
||||
message = "request";
|
||||
} else if (message === flow.response){
|
||||
} else if (message === flow.response) {
|
||||
message = "response";
|
||||
}
|
||||
return "/flows/" + flow.id + "/" + message + "/content";
|
||||
},
|
||||
getContent: function(flow, message){
|
||||
getContent: function (flow, message) {
|
||||
var url = MessageUtils.getContentURL(flow, message);
|
||||
return $.get(url);
|
||||
}
|
||||
};
|
||||
|
||||
var defaultPorts = {
|
||||
"http": 80,
|
||||
"https": 443
|
||||
};
|
||||
|
||||
var RequestUtils = _.extend(MessageUtils, {
|
||||
pretty_host: function (request) {
|
||||
//FIXME: Add hostheader
|
||||
@ -4593,10 +4821,55 @@ var RequestUtils = _.extend(MessageUtils, {
|
||||
var ResponseUtils = _.extend(MessageUtils, {});
|
||||
|
||||
|
||||
var parseUrl_regex = /^(?:(https?):\/\/)?([^\/:]+)?(?::(\d+))?(\/.*)?$/i;
|
||||
var parseUrl = function (url) {
|
||||
//there are many correct ways to parse a URL,
|
||||
//however, a mitmproxy user may also wish to generate a not-so-correct URL. ;-)
|
||||
var parts = parseUrl_regex.exec(url);
|
||||
|
||||
var scheme = parts[1],
|
||||
host = parts[2],
|
||||
port = parseInt(parts[3]),
|
||||
path = parts[4];
|
||||
if (scheme) {
|
||||
port = port || defaultPorts[scheme];
|
||||
}
|
||||
var ret = {};
|
||||
if (scheme) {
|
||||
ret.scheme = scheme;
|
||||
}
|
||||
if (host) {
|
||||
ret.host = host;
|
||||
}
|
||||
if (port) {
|
||||
ret.port = port;
|
||||
}
|
||||
if (path) {
|
||||
ret.path = path;
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
|
||||
var isValidHttpVersion_regex = /^HTTP\/\d+(\.\d+)*$/i;
|
||||
var isValidHttpVersion = function (httpVersion) {
|
||||
return isValidHttpVersion_regex.test(httpVersion);
|
||||
};
|
||||
|
||||
var parseHttpVersion = function (httpVersion) {
|
||||
httpVersion = httpVersion.replace("HTTP/", "").split(".");
|
||||
return _.map(httpVersion, function (x) {
|
||||
return parseInt(x);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
ResponseUtils: ResponseUtils,
|
||||
RequestUtils: RequestUtils,
|
||||
MessageUtils: MessageUtils
|
||||
MessageUtils: MessageUtils,
|
||||
parseUrl: parseUrl,
|
||||
parseHttpVersion: parseHttpVersion,
|
||||
isValidHttpVersion: isValidHttpVersion
|
||||
};
|
||||
|
||||
},{"jquery":"jquery","lodash":"lodash"}],22:[function(require,module,exports){
|
||||
@ -4903,6 +5176,10 @@ var $ = require("jquery");
|
||||
var _ = require("lodash");
|
||||
var actions = require("./actions.js");
|
||||
|
||||
//debug
|
||||
window.$ = $;
|
||||
window._ = _;
|
||||
|
||||
var Key = {
|
||||
UP: 38,
|
||||
DOWN: 40,
|
||||
@ -4929,17 +5206,17 @@ var formatSize = function (bytes) {
|
||||
if (bytes === 0)
|
||||
return "0";
|
||||
var prefix = ["b", "kb", "mb", "gb", "tb"];
|
||||
for (var i = 0; i < prefix.length; i++){
|
||||
if (Math.pow(1024, i + 1) > bytes){
|
||||
for (var i = 0; i < prefix.length; i++) {
|
||||
if (Math.pow(1024, i + 1) > bytes) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
var precision;
|
||||
if (bytes%Math.pow(1024, i) === 0)
|
||||
if (bytes % Math.pow(1024, i) === 0)
|
||||
precision = 0;
|
||||
else
|
||||
precision = 1;
|
||||
return (bytes/Math.pow(1024, i)).toFixed(precision) + prefix[i];
|
||||
return (bytes / Math.pow(1024, i)).toFixed(precision) + prefix[i];
|
||||
};
|
||||
|
||||
|
||||
@ -4961,17 +5238,16 @@ var formatTimeStamp = function (seconds) {
|
||||
return ts.replace("T", " ").replace("Z", "");
|
||||
};
|
||||
|
||||
|
||||
// At some places, we need to sort strings alphabetically descending,
|
||||
// but we can only provide a key function.
|
||||
// This beauty "reverses" a JS string.
|
||||
var end = String.fromCharCode(0xffff);
|
||||
function reverseString(s){
|
||||
function reverseString(s) {
|
||||
return String.fromCharCode.apply(String,
|
||||
_.map(s.split(""), function (c) {
|
||||
return 0xffff - c.charCodeAt(0);
|
||||
})
|
||||
) + end;
|
||||
_.map(s.split(""), function (c) {
|
||||
return 0xffff - c.charCodeAt(0);
|
||||
})
|
||||
) + end;
|
||||
}
|
||||
|
||||
function getCookie(name) {
|
||||
@ -4983,21 +5259,22 @@ var xsrf = $.param({_xsrf: getCookie("_xsrf")});
|
||||
//Tornado XSRF Protection.
|
||||
$.ajaxPrefilter(function (options) {
|
||||
if (["post", "put", "delete"].indexOf(options.type.toLowerCase()) >= 0 && options.url[0] === "/") {
|
||||
if (options.data) {
|
||||
options.data += ("&" + xsrf);
|
||||
if(options.url.indexOf("?") === -1){
|
||||
options.url += "?" + xsrf;
|
||||
} else {
|
||||
options.data = xsrf;
|
||||
options.url += "&" + xsrf;
|
||||
}
|
||||
}
|
||||
});
|
||||
// Log AJAX Errors
|
||||
$(document).ajaxError(function (event, jqXHR, ajaxSettings, thrownError) {
|
||||
if(thrownError === "abort"){
|
||||
if (thrownError === "abort") {
|
||||
return;
|
||||
}
|
||||
var message = jqXHR.responseText;
|
||||
console.error(thrownError, message, arguments);
|
||||
actions.EventLogActions.add_event(thrownError + ": " + message);
|
||||
alert(message);
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
@ -5005,7 +5282,7 @@ module.exports = {
|
||||
formatTimeDelta: formatTimeDelta,
|
||||
formatTimeStamp: formatTimeStamp,
|
||||
reverseString: reverseString,
|
||||
Key: Key
|
||||
Key: Key,
|
||||
};
|
||||
|
||||
},{"./actions.js":2,"jquery":"jquery","lodash":"lodash"}]},{},[3])
|
||||
|
@ -27,6 +27,13 @@
|
||||
max-height: 100px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.request-line {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
/*.request .response-line,
|
||||
.response .request-line {
|
||||
opacity: 0.7;
|
||||
}*/
|
||||
|
||||
hr {
|
||||
margin: 0 0 5px;
|
||||
@ -34,6 +41,23 @@
|
||||
|
||||
}
|
||||
|
||||
.inline-input {
|
||||
margin: 0 -5px;
|
||||
padding: 0 5px;
|
||||
|
||||
&[contenteditable] {
|
||||
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
|
||||
&.has-warning {
|
||||
color: rgb(255, 184, 184);
|
||||
}
|
||||
&.has-success {
|
||||
//color: green;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.view-options {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
var $ = require("jquery");
|
||||
var _ = require("lodash");
|
||||
var AppDispatcher = require("./dispatcher.js").AppDispatcher;
|
||||
|
||||
var ActionTypes = {
|
||||
@ -44,7 +45,8 @@ var SettingsActions = {
|
||||
$.ajax({
|
||||
type: "PUT",
|
||||
url: "/settings",
|
||||
data: settings
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(settings)
|
||||
});
|
||||
|
||||
/*
|
||||
@ -95,15 +97,26 @@ var FlowActions = {
|
||||
revert: function(flow){
|
||||
$.post("/flows/" + flow.id + "/revert");
|
||||
},
|
||||
update: function (flow) {
|
||||
update: function (flow, nextProps) {
|
||||
/*
|
||||
//Facebook Flux: We do an optimistic update on the client already.
|
||||
var nextFlow = _.cloneDeep(flow);
|
||||
_.merge(nextFlow, nextProps);
|
||||
AppDispatcher.dispatchViewAction({
|
||||
type: ActionTypes.FLOW_STORE,
|
||||
cmd: StoreCmds.UPDATE,
|
||||
data: flow
|
||||
data: nextFlow
|
||||
});
|
||||
*/
|
||||
$.ajax({
|
||||
type: "PUT",
|
||||
url: "/flows/" + flow.id,
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(nextProps)
|
||||
});
|
||||
},
|
||||
clear: function(){
|
||||
$.post("/clear");
|
||||
$.post("/flows/" + flow.id);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -30,6 +30,13 @@ var StickyHeadMixin = {
|
||||
};
|
||||
|
||||
|
||||
var ChildFocus = {
|
||||
contextTypes: {
|
||||
returnFocus: React.PropTypes.func
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var Navigation = _.extend({}, ReactRouter.Navigation, {
|
||||
setQuery: function (dict) {
|
||||
var q = this.context.router.getCurrentQuery();
|
||||
@ -176,6 +183,7 @@ var Splitter = React.createClass({
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
ChildFocus: ChildFocus,
|
||||
State: State,
|
||||
Navigation: Navigation,
|
||||
StickyHeadMixin: StickyHeadMixin,
|
||||
|
@ -16,7 +16,7 @@ var TLSColumn = React.createClass({
|
||||
},
|
||||
render: function () {
|
||||
var flow = this.props.flow;
|
||||
var ssl = (flow.request.scheme == "https");
|
||||
var ssl = (flow.request.scheme === "https");
|
||||
var classes;
|
||||
if (ssl) {
|
||||
classes = "col-tls col-tls-https";
|
||||
@ -44,7 +44,7 @@ var IconColumn = React.createClass({
|
||||
var contentType = ResponseUtils.getContentType(flow.response);
|
||||
|
||||
//TODO: We should assign a type to the flow somewhere else.
|
||||
if (flow.response.code == 304) {
|
||||
if (flow.response.code === 304) {
|
||||
icon = "resource-icon-not-modified";
|
||||
} else if (300 <= flow.response.code && flow.response.code < 400) {
|
||||
icon = "resource-icon-redirect";
|
||||
|
@ -27,7 +27,7 @@ var RawMixin = {
|
||||
}
|
||||
},
|
||||
requestContent: function (nextProps) {
|
||||
if(this.state.request){
|
||||
if (this.state.request) {
|
||||
this.state.request.abort();
|
||||
}
|
||||
var request = MessageUtils.getContent(nextProps.flow, nextProps.message);
|
||||
@ -38,11 +38,11 @@ var RawMixin = {
|
||||
request.done(function (data) {
|
||||
this.setState({content: data});
|
||||
}.bind(this)).fail(function (jqXHR, textStatus, errorThrown) {
|
||||
if(textStatus === "abort"){
|
||||
if (textStatus === "abort") {
|
||||
return;
|
||||
}
|
||||
this.setState({content: "AJAX Error: " + textStatus + "\r\n" + errorThrown});
|
||||
}.bind(this)).always(function(){
|
||||
}.bind(this)).always(function () {
|
||||
this.setState({request: undefined});
|
||||
}.bind(this));
|
||||
|
||||
@ -55,8 +55,8 @@ var RawMixin = {
|
||||
this.requestContent(nextProps);
|
||||
}
|
||||
},
|
||||
componentWillUnmount: function(){
|
||||
if(this.state.request){
|
||||
componentWillUnmount: function () {
|
||||
if (this.state.request) {
|
||||
this.state.request.abort();
|
||||
}
|
||||
},
|
||||
@ -94,7 +94,7 @@ var ViewJSON = React.createClass({
|
||||
var json = this.state.content;
|
||||
try {
|
||||
json = JSON.stringify(JSON.parse(json), null, 2);
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
}
|
||||
return <pre>{json}</pre>;
|
||||
}
|
||||
@ -139,7 +139,7 @@ var ContentMissing = React.createClass({
|
||||
|
||||
var TooLarge = React.createClass({
|
||||
statics: {
|
||||
isTooLarge: function(message){
|
||||
isTooLarge: function (message) {
|
||||
var max_mb = ViewImage.matches(message) ? 10 : 0.2;
|
||||
return message.contentLength > 1024 * 1024 * max_mb;
|
||||
}
|
||||
@ -225,8 +225,10 @@ var ContentView = React.createClass({
|
||||
<this.state.View {...this.props} />
|
||||
<div className="view-options text-center">
|
||||
<ViewSelector selectView={this.selectView} active={this.state.View} message={message}/>
|
||||
|
||||
<a className="btn btn-default btn-xs" href={downloadUrl}><i className="fa fa-download"/></a>
|
||||
|
||||
<a className="btn btn-default btn-xs" href={downloadUrl}>
|
||||
<i className="fa fa-download"/>
|
||||
</a>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
var React = require("react");
|
||||
var _ = require("lodash");
|
||||
|
||||
var common = require("../common.js");
|
||||
var actions = require("../../actions.js");
|
||||
var flowutils = require("../../flow/utils.js");
|
||||
var utils = require("../../utils.js");
|
||||
var ContentView = require("./contentview.js");
|
||||
@ -24,20 +27,217 @@ var Headers = React.createClass({
|
||||
}
|
||||
});
|
||||
|
||||
var InlineInput = React.createClass({
|
||||
mixins: [common.ChildFocus],
|
||||
getInitialState: function () {
|
||||
return {
|
||||
editable: false
|
||||
};
|
||||
},
|
||||
render: function () {
|
||||
var Tag = this.props.tag || "span";
|
||||
var className = "inline-input " + (this.props.className || "");
|
||||
var html = {__html: _.escape(this.props.content)};
|
||||
return <Tag
|
||||
{...this.props}
|
||||
tabIndex="0"
|
||||
className={className}
|
||||
contentEditable={this.state.editable || undefined}
|
||||
onInput={this.onInput}
|
||||
onFocus={this.onFocus}
|
||||
onBlur={this.onBlur}
|
||||
onKeyDown={this.onKeyDown}
|
||||
dangerouslySetInnerHTML={html}
|
||||
/>;
|
||||
},
|
||||
onKeyDown: function (e) {
|
||||
e.stopPropagation();
|
||||
switch (e.keyCode) {
|
||||
case utils.Key.ESC:
|
||||
this.blur();
|
||||
break;
|
||||
case utils.Key.ENTER:
|
||||
e.preventDefault();
|
||||
if (!e.ctrlKey) {
|
||||
this.blur();
|
||||
} else {
|
||||
this.props.onDone && this.props.onDone();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
blur: function(){
|
||||
this.getDOMNode().blur();
|
||||
this.context.returnFocus && this.context.returnFocus();
|
||||
},
|
||||
selectContents: function () {
|
||||
var range = document.createRange();
|
||||
range.selectNodeContents(this.getDOMNode());
|
||||
var sel = window.getSelection();
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
},
|
||||
onFocus: function () {
|
||||
this.setState({editable: true}, this.selectContents);
|
||||
},
|
||||
onBlur: function (e) {
|
||||
this.setState({editable: false});
|
||||
this.handleChange();
|
||||
this.props.onDone && this.props.onDone();
|
||||
},
|
||||
onInput: function () {
|
||||
this.handleChange();
|
||||
},
|
||||
handleChange: function () {
|
||||
var content = this.getDOMNode().textContent;
|
||||
if (content !== this.props.content) {
|
||||
this.props.onChange(content);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var ValidateInlineInput = React.createClass({
|
||||
getInitialState: function () {
|
||||
return {
|
||||
content: ""+this.props.content,
|
||||
originalContent: ""+this.props.content
|
||||
};
|
||||
},
|
||||
onChange: function (val) {
|
||||
this.setState({
|
||||
content: val
|
||||
});
|
||||
},
|
||||
onDone: function () {
|
||||
if (this.state.content === this.state.originalContent) {
|
||||
return true;
|
||||
}
|
||||
if (this.props.isValid(this.state.content)) {
|
||||
this.props.onChange(this.state.content);
|
||||
} else {
|
||||
this.setState({
|
||||
content: this.state.originalContent
|
||||
});
|
||||
}
|
||||
},
|
||||
componentWillReceiveProps: function (nextProps) {
|
||||
if (nextProps.content !== this.state.content) {
|
||||
this.setState({
|
||||
content: ""+nextProps.content,
|
||||
originalContent: ""+nextProps.content
|
||||
})
|
||||
}
|
||||
},
|
||||
render: function () {
|
||||
var className = this.props.className || "";
|
||||
if (this.props.isValid(this.state.content)) {
|
||||
className += " has-success";
|
||||
} else {
|
||||
className += " has-warning"
|
||||
}
|
||||
return <InlineInput {...this.props}
|
||||
className={className}
|
||||
content={this.state.content}
|
||||
onChange={this.onChange}
|
||||
onDone={this.onDone}
|
||||
/>;
|
||||
}
|
||||
});
|
||||
|
||||
var RequestLine = React.createClass({
|
||||
render: function () {
|
||||
var flow = this.props.flow;
|
||||
var url = flowutils.RequestUtils.pretty_url(flow.request);
|
||||
var httpver = "HTTP/" + flow.request.httpversion.join(".");
|
||||
|
||||
return <div className="first-line request-line">
|
||||
<ValidateInlineInput content={flow.request.method} onChange={this.onMethodChange} isValid={this.isValidMethod}/>
|
||||
|
||||
<ValidateInlineInput content={url} onChange={this.onUrlChange} isValid={this.isValidUrl} />
|
||||
|
||||
<ValidateInlineInput content={httpver} onChange={this.onHttpVersionChange} isValid={flowutils.isValidHttpVersion} />
|
||||
</div>
|
||||
},
|
||||
isValidMethod: function (method) {
|
||||
return true;
|
||||
},
|
||||
isValidUrl: function (url) {
|
||||
var u = flowutils.parseUrl(url);
|
||||
return !!u.host;
|
||||
},
|
||||
onMethodChange: function (nextMethod) {
|
||||
actions.FlowActions.update(
|
||||
this.props.flow,
|
||||
{request: {method: nextMethod}}
|
||||
);
|
||||
},
|
||||
onUrlChange: function (nextUrl) {
|
||||
var props = flowutils.parseUrl(nextUrl);
|
||||
props.path = props.path || "";
|
||||
actions.FlowActions.update(
|
||||
this.props.flow,
|
||||
{request: props}
|
||||
);
|
||||
},
|
||||
onHttpVersionChange: function (nextVer) {
|
||||
var ver = flowutils.parseHttpVersion(nextVer);
|
||||
actions.FlowActions.update(
|
||||
this.props.flow,
|
||||
{request: {httpversion: ver}}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var ResponseLine = React.createClass({
|
||||
render: function () {
|
||||
var flow = this.props.flow;
|
||||
var httpver = "HTTP/" + flow.response.httpversion.join(".");
|
||||
return <div className="first-line response-line">
|
||||
<ValidateInlineInput content={httpver} onChange={this.onHttpVersionChange} isValid={flowutils.isValidHttpVersion} />
|
||||
|
||||
<ValidateInlineInput content={flow.response.code} onChange={this.onCodeChange} isValid={this.isValidCode} />
|
||||
|
||||
<ValidateInlineInput content={flow.response.msg} onChange={this.onMsgChange} isValid={this.isValidMsg} />
|
||||
|
||||
</div>;
|
||||
},
|
||||
isValidCode: function (code) {
|
||||
return /^\d+$/.test(code);
|
||||
},
|
||||
isValidMsg: function () {
|
||||
return true;
|
||||
},
|
||||
onHttpVersionChange: function (nextVer) {
|
||||
var ver = flowutils.parseHttpVersion(nextVer);
|
||||
actions.FlowActions.update(
|
||||
this.props.flow,
|
||||
{response: {httpversion: ver}}
|
||||
);
|
||||
},
|
||||
onMsgChange: function(nextMsg){
|
||||
actions.FlowActions.update(
|
||||
this.props.flow,
|
||||
{response: {msg: nextMsg}}
|
||||
);
|
||||
},
|
||||
onCodeChange: function(nextCode){
|
||||
nextCode = parseInt(nextCode);
|
||||
actions.FlowActions.update(
|
||||
this.props.flow,
|
||||
{response: {code: nextCode}}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var Request = React.createClass({
|
||||
render: function () {
|
||||
var flow = this.props.flow;
|
||||
var first_line = [
|
||||
flow.request.method,
|
||||
flowutils.RequestUtils.pretty_url(flow.request),
|
||||
"HTTP/" + flow.request.httpversion.join(".")
|
||||
].join(" ");
|
||||
|
||||
//TODO: Styling
|
||||
|
||||
return (
|
||||
<section>
|
||||
<div className="first-line">{ first_line }</div>
|
||||
<section className="request">
|
||||
<RequestLine flow={flow}/>
|
||||
{/*<ResponseLine flow={flow}/>*/}
|
||||
<Headers message={flow.request}/>
|
||||
<hr/>
|
||||
<ContentView flow={flow} message={flow.request}/>
|
||||
@ -49,17 +249,10 @@ var Request = React.createClass({
|
||||
var Response = React.createClass({
|
||||
render: function () {
|
||||
var flow = this.props.flow;
|
||||
var first_line = [
|
||||
"HTTP/" + flow.response.httpversion.join("."),
|
||||
flow.response.code,
|
||||
flow.response.msg
|
||||
].join(" ");
|
||||
|
||||
//TODO: Styling
|
||||
|
||||
return (
|
||||
<section>
|
||||
<div className="first-line">{ first_line }</div>
|
||||
<section className="response">
|
||||
{/*<RequestLine flow={flow}/>*/}
|
||||
<ResponseLine flow={flow}/>
|
||||
<Headers message={flow.response}/>
|
||||
<hr/>
|
||||
<ContentView flow={flow} message={flow.response}/>
|
||||
|
@ -11,6 +11,15 @@ var FlowView = require("./flowview/index.js");
|
||||
|
||||
var MainView = React.createClass({
|
||||
mixins: [common.Navigation, common.State],
|
||||
childContextTypes: {
|
||||
returnFocus: React.PropTypes.func.isRequired
|
||||
},
|
||||
getChildContext: function() {
|
||||
return { returnFocus: this.returnFocus };
|
||||
},
|
||||
returnFocus: function(){
|
||||
this.getDOMNode().focus();
|
||||
},
|
||||
getInitialState: function () {
|
||||
return {
|
||||
flows: [],
|
||||
|
@ -1,9 +1,17 @@
|
||||
var _ = require("lodash");
|
||||
var $ = require("jquery");
|
||||
|
||||
var defaultPorts = {
|
||||
"http": 80,
|
||||
"https": 443
|
||||
};
|
||||
|
||||
var MessageUtils = {
|
||||
getContentType: function (message) {
|
||||
return this.get_first_header(message, /^Content-Type$/i).split(";")[0].trim();
|
||||
var ct = this.get_first_header(message, /^Content-Type$/i);
|
||||
if(ct){
|
||||
return ct.split(";")[0].trim();
|
||||
}
|
||||
},
|
||||
get_first_header: function (message, regex) {
|
||||
//FIXME: Cache Invalidation.
|
||||
@ -36,25 +44,20 @@ var MessageUtils = {
|
||||
}
|
||||
return false;
|
||||
},
|
||||
getContentURL: function(flow, message){
|
||||
if(message === flow.request){
|
||||
getContentURL: function (flow, message) {
|
||||
if (message === flow.request) {
|
||||
message = "request";
|
||||
} else if (message === flow.response){
|
||||
} else if (message === flow.response) {
|
||||
message = "response";
|
||||
}
|
||||
return "/flows/" + flow.id + "/" + message + "/content";
|
||||
},
|
||||
getContent: function(flow, message){
|
||||
getContent: function (flow, message) {
|
||||
var url = MessageUtils.getContentURL(flow, message);
|
||||
return $.get(url);
|
||||
}
|
||||
};
|
||||
|
||||
var defaultPorts = {
|
||||
"http": 80,
|
||||
"https": 443
|
||||
};
|
||||
|
||||
var RequestUtils = _.extend(MessageUtils, {
|
||||
pretty_host: function (request) {
|
||||
//FIXME: Add hostheader
|
||||
@ -72,8 +75,53 @@ var RequestUtils = _.extend(MessageUtils, {
|
||||
var ResponseUtils = _.extend(MessageUtils, {});
|
||||
|
||||
|
||||
var parseUrl_regex = /^(?:(https?):\/\/)?([^\/:]+)?(?::(\d+))?(\/.*)?$/i;
|
||||
var parseUrl = function (url) {
|
||||
//there are many correct ways to parse a URL,
|
||||
//however, a mitmproxy user may also wish to generate a not-so-correct URL. ;-)
|
||||
var parts = parseUrl_regex.exec(url);
|
||||
|
||||
var scheme = parts[1],
|
||||
host = parts[2],
|
||||
port = parseInt(parts[3]),
|
||||
path = parts[4];
|
||||
if (scheme) {
|
||||
port = port || defaultPorts[scheme];
|
||||
}
|
||||
var ret = {};
|
||||
if (scheme) {
|
||||
ret.scheme = scheme;
|
||||
}
|
||||
if (host) {
|
||||
ret.host = host;
|
||||
}
|
||||
if (port) {
|
||||
ret.port = port;
|
||||
}
|
||||
if (path) {
|
||||
ret.path = path;
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
|
||||
var isValidHttpVersion_regex = /^HTTP\/\d+(\.\d+)*$/i;
|
||||
var isValidHttpVersion = function (httpVersion) {
|
||||
return isValidHttpVersion_regex.test(httpVersion);
|
||||
};
|
||||
|
||||
var parseHttpVersion = function (httpVersion) {
|
||||
httpVersion = httpVersion.replace("HTTP/", "").split(".");
|
||||
return _.map(httpVersion, function (x) {
|
||||
return parseInt(x);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
ResponseUtils: ResponseUtils,
|
||||
RequestUtils: RequestUtils,
|
||||
MessageUtils: MessageUtils
|
||||
MessageUtils: MessageUtils,
|
||||
parseUrl: parseUrl,
|
||||
parseHttpVersion: parseHttpVersion,
|
||||
isValidHttpVersion: isValidHttpVersion
|
||||
};
|
@ -2,6 +2,10 @@ var $ = require("jquery");
|
||||
var _ = require("lodash");
|
||||
var actions = require("./actions.js");
|
||||
|
||||
//debug
|
||||
window.$ = $;
|
||||
window._ = _;
|
||||
|
||||
var Key = {
|
||||
UP: 38,
|
||||
DOWN: 40,
|
||||
@ -28,17 +32,17 @@ var formatSize = function (bytes) {
|
||||
if (bytes === 0)
|
||||
return "0";
|
||||
var prefix = ["b", "kb", "mb", "gb", "tb"];
|
||||
for (var i = 0; i < prefix.length; i++){
|
||||
if (Math.pow(1024, i + 1) > bytes){
|
||||
for (var i = 0; i < prefix.length; i++) {
|
||||
if (Math.pow(1024, i + 1) > bytes) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
var precision;
|
||||
if (bytes%Math.pow(1024, i) === 0)
|
||||
if (bytes % Math.pow(1024, i) === 0)
|
||||
precision = 0;
|
||||
else
|
||||
precision = 1;
|
||||
return (bytes/Math.pow(1024, i)).toFixed(precision) + prefix[i];
|
||||
return (bytes / Math.pow(1024, i)).toFixed(precision) + prefix[i];
|
||||
};
|
||||
|
||||
|
||||
@ -60,17 +64,16 @@ var formatTimeStamp = function (seconds) {
|
||||
return ts.replace("T", " ").replace("Z", "");
|
||||
};
|
||||
|
||||
|
||||
// At some places, we need to sort strings alphabetically descending,
|
||||
// but we can only provide a key function.
|
||||
// This beauty "reverses" a JS string.
|
||||
var end = String.fromCharCode(0xffff);
|
||||
function reverseString(s){
|
||||
function reverseString(s) {
|
||||
return String.fromCharCode.apply(String,
|
||||
_.map(s.split(""), function (c) {
|
||||
return 0xffff - c.charCodeAt(0);
|
||||
})
|
||||
) + end;
|
||||
_.map(s.split(""), function (c) {
|
||||
return 0xffff - c.charCodeAt(0);
|
||||
})
|
||||
) + end;
|
||||
}
|
||||
|
||||
function getCookie(name) {
|
||||
@ -82,21 +85,22 @@ var xsrf = $.param({_xsrf: getCookie("_xsrf")});
|
||||
//Tornado XSRF Protection.
|
||||
$.ajaxPrefilter(function (options) {
|
||||
if (["post", "put", "delete"].indexOf(options.type.toLowerCase()) >= 0 && options.url[0] === "/") {
|
||||
if (options.data) {
|
||||
options.data += ("&" + xsrf);
|
||||
if(options.url.indexOf("?") === -1){
|
||||
options.url += "?" + xsrf;
|
||||
} else {
|
||||
options.data = xsrf;
|
||||
options.url += "&" + xsrf;
|
||||
}
|
||||
}
|
||||
});
|
||||
// Log AJAX Errors
|
||||
$(document).ajaxError(function (event, jqXHR, ajaxSettings, thrownError) {
|
||||
if(thrownError === "abort"){
|
||||
if (thrownError === "abort") {
|
||||
return;
|
||||
}
|
||||
var message = jqXHR.responseText;
|
||||
console.error(thrownError, message, arguments);
|
||||
actions.EventLogActions.add_event(thrownError + ": " + message);
|
||||
alert(message);
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
@ -104,5 +108,5 @@ module.exports = {
|
||||
formatTimeDelta: formatTimeDelta,
|
||||
formatTimeStamp: formatTimeStamp,
|
||||
reverseString: reverseString,
|
||||
Key: Key
|
||||
Key: Key,
|
||||
};
|
Loading…
Reference in New Issue
Block a user