mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-23 00:01:36 +00:00
mitmproxy.contentviews added
This commit is contained in:
parent
3e6c284757
commit
ad5bebeda0
@ -14,6 +14,7 @@ import tornado.web
|
|||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from mitmproxy.flow import FlowWriter, FlowReader
|
from mitmproxy.flow import FlowWriter, FlowReader
|
||||||
|
|
||||||
|
from mitmproxy import exceptions
|
||||||
from mitmproxy import filt
|
from mitmproxy import filt
|
||||||
from mitmproxy import models
|
from mitmproxy import models
|
||||||
from mitmproxy import contentviews
|
from mitmproxy import contentviews
|
||||||
@ -305,6 +306,36 @@ class ReplayFlow(RequestHandler):
|
|||||||
|
|
||||||
|
|
||||||
class FlowContent(RequestHandler):
|
class FlowContent(RequestHandler):
|
||||||
|
def _get_content_view(self, message, viewmode):
|
||||||
|
|
||||||
|
try:
|
||||||
|
content = message.content
|
||||||
|
if content != message.raw_content:
|
||||||
|
enc = "[decoded {}]".format(
|
||||||
|
message.headers.get("content-encoding")
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
enc = None
|
||||||
|
except ValueError:
|
||||||
|
content = message.raw_content
|
||||||
|
enc = "[cannot decode]"
|
||||||
|
try:
|
||||||
|
query = None
|
||||||
|
if isinstance(message, models.HTTPRequest):
|
||||||
|
query = message.query
|
||||||
|
description, lines = contentviews.get_content_view(
|
||||||
|
viewmode, content, headers=message.headers, query=query
|
||||||
|
)
|
||||||
|
except exceptions.ContentViewException:
|
||||||
|
description, lines = contentviews.get_content_view(
|
||||||
|
contentviews.get("Raw"), content, headers=message.headers
|
||||||
|
)
|
||||||
|
description = description.replace("Raw", "Couldn't parse: falling back to Raw")
|
||||||
|
|
||||||
|
if enc:
|
||||||
|
description = " ".join([enc, description])
|
||||||
|
|
||||||
|
return description, lines
|
||||||
|
|
||||||
def post(self, flow_id, message):
|
def post(self, flow_id, message):
|
||||||
self.flow.backup()
|
self.flow.backup()
|
||||||
@ -314,7 +345,6 @@ class FlowContent(RequestHandler):
|
|||||||
|
|
||||||
def get(self, flow_id, message):
|
def get(self, flow_id, message):
|
||||||
message = getattr(self.flow, message)
|
message = getattr(self.flow, message)
|
||||||
contentview = self.get_argument("content_view", "raw", True)
|
|
||||||
|
|
||||||
if not message.raw_content:
|
if not message.raw_content:
|
||||||
raise APIError(400, "No content.")
|
raise APIError(400, "No content.")
|
||||||
@ -339,7 +369,21 @@ class FlowContent(RequestHandler):
|
|||||||
self.set_header("Content-Type", "application/text")
|
self.set_header("Content-Type", "application/text")
|
||||||
self.set_header("X-Content-Type-Options", "nosniff")
|
self.set_header("X-Content-Type-Options", "nosniff")
|
||||||
self.set_header("X-Frame-Options", "DENY")
|
self.set_header("X-Frame-Options", "DENY")
|
||||||
self.write(message.raw_content)
|
|
||||||
|
cv = self.get_argument("cv", None)
|
||||||
|
if cv:
|
||||||
|
self.set_header("Content-Encoding", "")
|
||||||
|
viewmode = next(v for v in contentviews.views if v.name == cv)
|
||||||
|
description, lines = self._get_content_view(
|
||||||
|
message, viewmode
|
||||||
|
)
|
||||||
|
|
||||||
|
self.write(dict(
|
||||||
|
lines=list(lines),
|
||||||
|
description=description
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
self.write(message.raw_content)
|
||||||
|
|
||||||
|
|
||||||
class Events(RequestHandler):
|
class Events(RequestHandler):
|
||||||
@ -368,7 +412,7 @@ class Settings(RequestHandler):
|
|||||||
stickyauth=self.master.options.stickyauth,
|
stickyauth=self.master.options.stickyauth,
|
||||||
stickycookie=self.master.options.stickycookie,
|
stickycookie=self.master.options.stickycookie,
|
||||||
stream= self.master.options.stream_large_bodies,
|
stream= self.master.options.stream_large_bodies,
|
||||||
contentViews= map(lambda v : v.name, contentviews.views)
|
contentViews= [v.name for v in contentviews.views]
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
|
|
||||||
|
@ -18,3 +18,4 @@ html {
|
|||||||
@import (less) "eventlog.less";
|
@import (less) "eventlog.less";
|
||||||
@import (less) "footer.less";
|
@import (less) "footer.less";
|
||||||
@import (less) "codemirror.less";
|
@import (less) "codemirror.less";
|
||||||
|
@import (less) "contentview.less";
|
||||||
|
10
web/src/css/contentview.less
Normal file
10
web/src/css/contentview.less
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
.contentview {
|
||||||
|
.header {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.highlight{
|
||||||
|
color: pink;
|
||||||
|
}
|
||||||
|
.offset{ }
|
||||||
|
.text{ }
|
||||||
|
}
|
@ -35,7 +35,7 @@ function ContentView(props) {
|
|||||||
|
|
||||||
const View = ContentViews[contentView] || ContentViews['ViewServer']
|
const View = ContentViews[contentView] || ContentViews['ViewServer']
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="contentview">
|
||||||
<View flow={flow} message={message} contentView={contentView} readonly={readonly} onChange={onContentChange}/>
|
<View flow={flow} message={message} contentView={contentView} readonly={readonly} onChange={onContentChange}/>
|
||||||
|
|
||||||
<div className="view-options text-center">
|
<div className="view-options text-center">
|
||||||
|
@ -53,7 +53,7 @@ export default View => class extends React.Component {
|
|||||||
return this.setState({request: undefined, content: ""})
|
return this.setState({request: undefined, content: ""})
|
||||||
}
|
}
|
||||||
|
|
||||||
let requestUrl = MessageUtils.getContentURL(props.flow, props.message, props.contentView)
|
let requestUrl = MessageUtils.getContentURL(props.flow, props.message, (View.name == 'ViewServer' ? props.contentView : undefined))
|
||||||
|
|
||||||
// We use XMLHttpRequest instead of fetch() because fetch() is not (yet) abortable.
|
// We use XMLHttpRequest instead of fetch() because fetch() is not (yet) abortable.
|
||||||
let request = new XMLHttpRequest();
|
let request = new XMLHttpRequest();
|
||||||
|
@ -28,26 +28,8 @@ function ViewRaw({ content, readonly, onChange }) {
|
|||||||
}
|
}
|
||||||
ViewRaw = ContentLoader(ViewRaw)
|
ViewRaw = ContentLoader(ViewRaw)
|
||||||
|
|
||||||
|
|
||||||
const isJSON = /^application\/json$/i
|
|
||||||
ViewJSON.matches = msg => isJSON.test(MessageUtils.getContentType(msg))
|
|
||||||
ViewJSON.propTypes = {
|
|
||||||
content: React.PropTypes.string.isRequired,
|
|
||||||
}
|
|
||||||
function ViewJSON({ content }) {
|
|
||||||
let json = content
|
|
||||||
try {
|
|
||||||
json = JSON.stringify(JSON.parse(content), null, 2);
|
|
||||||
} catch (e) {
|
|
||||||
// @noop
|
|
||||||
}
|
|
||||||
return <pre>{json}</pre>
|
|
||||||
}
|
|
||||||
ViewJSON = ContentLoader(ViewJSON)
|
|
||||||
|
|
||||||
|
|
||||||
ViewAuto.matches = () => false
|
ViewAuto.matches = () => false
|
||||||
ViewAuto.findView = msg => [ViewImage, ViewJSON, ViewRaw].find(v => v.matches(msg)) || ViewRaw
|
ViewAuto.findView = msg => [ViewImage, ViewRaw].find(v => v.matches(msg)) || ViewRaw
|
||||||
ViewAuto.propTypes = {
|
ViewAuto.propTypes = {
|
||||||
message: React.PropTypes.object.isRequired,
|
message: React.PropTypes.object.isRequired,
|
||||||
flow: React.PropTypes.object.isRequired,
|
flow: React.PropTypes.object.isRequired,
|
||||||
@ -57,14 +39,26 @@ function ViewAuto({ message, flow, readonly, onChange }) {
|
|||||||
return <View message={message} flow={flow} readonly={readonly} onChange={onChange}/>
|
return <View message={message} flow={flow} readonly={readonly} onChange={onChange}/>
|
||||||
}
|
}
|
||||||
|
|
||||||
function ViewServer({contentView, content}){
|
function ViewServer({content, contentView}){
|
||||||
|
let data = JSON.parse(content)
|
||||||
return <div>
|
return <div>
|
||||||
<pre>load from server this view: {contentView}</pre>
|
{contentView != data.description &&
|
||||||
<pre>{content}</pre>
|
<div className="alert alert-warning">{data.description}</div>
|
||||||
|
}
|
||||||
|
<pre>
|
||||||
|
{data.lines.map((line, i) =>
|
||||||
|
<div key={`line${i}`}>
|
||||||
|
{line.map((tuple, j) =>
|
||||||
|
<span key={`tuple${j}`} className={tuple[0]}>
|
||||||
|
{tuple[1]}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewServer = ContentLoader(ViewServer)
|
ViewServer = ContentLoader(ViewServer)
|
||||||
|
|
||||||
export { ViewImage, ViewRaw, ViewAuto, ViewJSON, ViewServer }
|
export { ViewImage, ViewRaw, ViewAuto, ViewServer }
|
||||||
|
@ -38,7 +38,7 @@ function ViewSelector({ message, contentViews }) {
|
|||||||
<ViewButton name="ViewAuto">auto: {autoViewName}</ViewButton>
|
<ViewButton name="ViewAuto">auto: {autoViewName}</ViewButton>
|
||||||
|
|
||||||
{Object.keys(ContentViews).map(name =>
|
{Object.keys(ContentViews).map(name =>
|
||||||
name !== "ViewAuto" &&
|
name !== "ViewAuto" && name !== "ViewServer" &&
|
||||||
<ViewButton key={name} name={name}>{name.toLowerCase().replace('view', '')}</ViewButton>
|
<ViewButton key={name} name={name}>{name.toLowerCase().replace('view', '')}</ViewButton>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -49,8 +49,8 @@ export var MessageUtils = {
|
|||||||
} else if (message === flow.response) {
|
} else if (message === flow.response) {
|
||||||
message = "response";
|
message = "response";
|
||||||
}
|
}
|
||||||
return "/flows/" + flow.id + "/" + message + "/content" + (view ? "?content_view="+view : "");
|
return `/flows/${flow.id}/${message}/content` + (view ? `?cv=${view}` : '');
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export var RequestUtils = _.extend(MessageUtils, {
|
export var RequestUtils = _.extend(MessageUtils, {
|
||||||
|
Loading…
Reference in New Issue
Block a user