aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--mitmproxy/web/app.py79
-rw-r--r--web/src/css/contentview.less12
-rw-r--r--web/src/js/components/ContentView/ContentViews.jsx32
-rw-r--r--web/src/js/components/ContentView/ViewSelector.jsx24
-rw-r--r--web/src/js/ducks/ui/flow.js4
-rw-r--r--web/src/js/flow/utils.js2
6 files changed, 80 insertions, 73 deletions
diff --git a/mitmproxy/web/app.py b/mitmproxy/web/app.py
index 6abd672d..eabdb147 100644
--- a/mitmproxy/web/app.py
+++ b/mitmproxy/web/app.py
@@ -306,6 +306,42 @@ class ReplayFlow(RequestHandler):
class FlowContent(RequestHandler):
+
+ def post(self, flow_id, message):
+ self.flow.backup()
+ message = getattr(self.flow, message)
+ message.content = self.request.files.values()[0][0].body
+ self.state.update_flow(self.flow)
+
+ def get(self, flow_id, message):
+ message = getattr(self.flow, message)
+
+ if not message.raw_content:
+ raise APIError(400, "No content.")
+
+ content_encoding = message.headers.get("Content-Encoding", None)
+ if content_encoding:
+ content_encoding = re.sub(r"[^\w]", "", content_encoding)
+ self.set_header("Content-Encoding", content_encoding)
+
+ original_cd = message.headers.get("Content-Disposition", None)
+ filename = None
+ if original_cd:
+ filename = re.search("filename=([\w\" \.\-\(\)]+)", original_cd)
+ if filename:
+ filename = filename.group(1)
+ if not filename:
+ filename = self.flow.request.path.split("?")[0].split("/")[-1]
+
+ filename = re.sub(r"[^\w\" \.\-\(\)]", "", filename)
+ cd = "attachment; filename={}".format(filename)
+ self.set_header("Content-Disposition", cd)
+ self.set_header("Content-Type", "application/text")
+ self.set_header("X-Content-Type-Options", "nosniff")
+ self.set_header("X-Frame-Options", "DENY")
+ self.write(message.raw_content)
+
+class FlowContentView(RequestHandler):
def _get_content_view(self, message, viewmode):
try:
@@ -337,23 +373,9 @@ class FlowContent(RequestHandler):
return description, lines
- def post(self, flow_id, message):
- self.flow.backup()
- message = getattr(self.flow, message)
- message.content = self.request.files.values()[0][0].body
- self.state.update_flow(self.flow)
-
- def get(self, flow_id, message):
+ def get(self, flow_id, message, content_view):
message = getattr(self.flow, message)
- if not message.raw_content:
- raise APIError(400, "No content.")
-
- content_encoding = message.headers.get("Content-Encoding", None)
- if content_encoding:
- content_encoding = re.sub(r"[^\w]", "", content_encoding)
- self.set_header("Content-Encoding", content_encoding)
-
original_cd = message.headers.get("Content-Disposition", None)
filename = None
if original_cd:
@@ -366,24 +388,20 @@ class FlowContent(RequestHandler):
filename = re.sub(r"[^\w\" \.\-\(\)]", "", filename)
cd = "attachment; filename={}".format(filename)
self.set_header("Content-Disposition", cd)
- self.set_header("Content-Type", "application/text")
+ self.set_header("Content-Type", "application/json")
self.set_header("X-Content-Type-Options", "nosniff")
self.set_header("X-Frame-Options", "DENY")
- 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.set_header("Content-Encoding", "")
- self.write(dict(
- lines=list(lines),
- description=description
- ))
- else:
- self.write(message.raw_content)
+ description, lines = self._get_content_view(
+ message, contentviews.get(content_view.replace('_', ' '))
+ )
+
+ self.write(dict(
+ lines=list(lines),
+ description=description
+ ))
class Events(RequestHandler):
@@ -412,7 +430,7 @@ class Settings(RequestHandler):
stickyauth=self.master.options.stickyauth,
stickycookie=self.master.options.stickycookie,
stream= self.master.options.stream_large_bodies,
- contentViews= [v.name for v in contentviews.views]
+ contentViews= [v.name.replace(' ', '_') for v in contentviews.views]
)
))
@@ -477,6 +495,7 @@ class Application(tornado.web.Application):
(r"/flows/(?P<flow_id>[0-9a-f\-]+)/replay", ReplayFlow),
(r"/flows/(?P<flow_id>[0-9a-f\-]+)/revert", RevertFlow),
(r"/flows/(?P<flow_id>[0-9a-f\-]+)/(?P<message>request|response)/content", FlowContent),
+ (r"/flows/(?P<flow_id>[0-9a-f\-]+)/(?P<message>request|response)/content/(?P<content_view>[0-9a-zA-Z\-\_]+)", FlowContentView),
(r"/settings", Settings),
(r"/clear", ClearAll),
]
diff --git a/web/src/css/contentview.less b/web/src/css/contentview.less
index 327dd689..becac9a2 100644
--- a/web/src/css/contentview.less
+++ b/web/src/css/contentview.less
@@ -1,10 +1,14 @@
.contentview {
.header {
- font-weight: bold;
+ font-weight: bold;
}
.highlight{
- color: pink;
+ font-weight: bold;
+ }
+ .offset{
+ color: blue
+ }
+ .text{
+
}
- .offset{ }
- .text{ }
}
diff --git a/web/src/js/components/ContentView/ContentViews.jsx b/web/src/js/components/ContentView/ContentViews.jsx
index a1bee54e..3b2af0a9 100644
--- a/web/src/js/components/ContentView/ContentViews.jsx
+++ b/web/src/js/components/ContentView/ContentViews.jsx
@@ -18,31 +18,19 @@ function ViewImage({ flow, message }) {
)
}
-
-ViewRaw.matches = () => true
-ViewRaw.propTypes = {
+Edit.propTypes = {
content: React.PropTypes.string.isRequired,
}
-function ViewRaw({ content, readonly, onChange }) {
- return readonly ? <pre>{content}</pre> : <CodeEditor content={content} onChange={onChange}/>
-}
-ViewRaw = ContentLoader(ViewRaw)
-ViewAuto.matches = () => false
-ViewAuto.findView = msg => [ViewImage, ViewRaw].find(v => v.matches(msg)) || ViewRaw
-ViewAuto.propTypes = {
- message: React.PropTypes.object.isRequired,
- flow: React.PropTypes.object.isRequired,
-}
-function ViewAuto({ message, flow, readonly, onChange }) {
- const View = ViewAuto.findView(message)
- return <View message={message} flow={flow} readonly={readonly} onChange={onChange}/>
+function Edit({ content, onChange }) {
+ return <CodeEditor content={content} onChange={onChange}/>
}
+Edit = ContentLoader(Edit)
-function ViewServer({content, contentView, message, flow}){
+function ViewServer(props){
+ const {content, contentView, message} = props
let data = JSON.parse(content)
- let showImage = isImage.test(MessageUtils.getContentType(message))
return <div>
{contentView != data.description &&
@@ -59,14 +47,12 @@ function ViewServer({content, contentView, message, flow}){
</div>
)}
</pre>
- {showImage &&
- <div className="flowview-image">
- <img src={MessageUtils.getContentURL(flow, message)} alt="preview" className="img-thumbnail"/>
- </div>
+ {ViewImage.matches(message) &&
+ <ViewImage {...props} />
}
</div>
}
ViewServer = ContentLoader(ViewServer)
-export { ViewImage, ViewRaw, ViewAuto, ViewServer }
+export { Edit, ViewServer, ViewImage }
diff --git a/web/src/js/components/ContentView/ViewSelector.jsx b/web/src/js/components/ContentView/ViewSelector.jsx
index e031b51f..c5670328 100644
--- a/web/src/js/components/ContentView/ViewSelector.jsx
+++ b/web/src/js/components/ContentView/ViewSelector.jsx
@@ -24,31 +24,25 @@ ViewButton = connect(state => ({
ViewSelector.propTypes = {
message: PropTypes.object.isRequired,
}
-function ViewSelector({ message, contentViews }) {
-
- let autoView = ContentViews.ViewAuto.findView(message)
- let autoViewName = (autoView.displayName || autoView.name)
- .toLowerCase()
- .replace('view', '')
- .replace(/ContentLoader\((.+)\)/,"$1")
-
+function ViewSelector({contentViews, isEdit }) {
+ let edit = ContentViews.Edit.displayName
return (
<div className="view-selector btn-group btn-group-xs">
- {Object.keys(ContentViews).map(name =>
- name === "ViewRaw" &&
- <ViewButton key={name} name={name}>{name.toLowerCase().replace('view', '')}</ViewButton>
- )}
-
{contentViews.map(name =>
- <ViewButton key={name} name={name}>{name.toLowerCase().replace('view', '')}</ViewButton>
+ <ViewButton key={name} name={name}>{name.toLowerCase().replace('_', ' ')}</ViewButton>
)}
+ {isEdit &&
+ <ViewButton key={edit} name={edit}>{edit.toLowerCase()}</ViewButton>
+ }
+
</div>
)
}
export default connect (
state => ({
- contentViews: state.settings.contentViews
+ contentViews: state.settings.contentViews,
+ isEdit: !!state.ui.flow.modifiedFlow,
}))(ViewSelector)
diff --git a/web/src/js/ducks/ui/flow.js b/web/src/js/ducks/ui/flow.js
index 549efb1d..d9811a33 100644
--- a/web/src/js/ducks/ui/flow.js
+++ b/web/src/js/ducks/ui/flow.js
@@ -19,12 +19,14 @@ const defaultState = {
}
export default function reducer(state = defaultState, action) {
+ let wasInEditMode = !!(state.modifiedFlow)
switch (action.type) {
case START_EDIT:
return {
...state,
modifiedFlow: action.flow,
+ contentView: 'Edit'
}
case UPDATE_EDIT:
@@ -38,6 +40,7 @@ export default function reducer(state = defaultState, action) {
...state,
modifiedFlow: false,
displayLarge: false,
+ contentView: (wasInEditMode ? 'Auto' : state.contentView)
}
case flowsActions.UPDATE:
@@ -49,6 +52,7 @@ export default function reducer(state = defaultState, action) {
...state,
modifiedFlow: false,
displayLarge: false,
+ contentView: (wasInEditMode ? 'Auto' : state.contentView)
}
} else {
return state
diff --git a/web/src/js/flow/utils.js b/web/src/js/flow/utils.js
index b8435aa0..cd174069 100644
--- a/web/src/js/flow/utils.js
+++ b/web/src/js/flow/utils.js
@@ -49,7 +49,7 @@ export var MessageUtils = {
} else if (message === flow.response) {
message = "response";
}
- return `/flows/${flow.id}/${message}/content` + (view ? `?cv=${view}` : '');
+ return `/flows/${flow.id}/${message}/content` + (view ? `/${view}` : '');
}
};