aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClemens <cle1000.cb@gmail.com>2016-07-13 18:16:31 +0200
committerClemens <cle1000.cb@gmail.com>2016-07-13 18:16:31 +0200
commitf84098554a3d50ec02255603bb0a145b3aa0c6b2 (patch)
treee778b465346d9c96c27cd7663c2a95451710c262
parent544b1e32389d28df4a40013ea6886f1f9d61e37d (diff)
downloadmitmproxy-f84098554a3d50ec02255603bb0a145b3aa0c6b2.tar.gz
mitmproxy-f84098554a3d50ec02255603bb0a145b3aa0c6b2.tar.bz2
mitmproxy-f84098554a3d50ec02255603bb0a145b3aa0c6b2.zip
added code editor and file upload
-rw-r--r--mitmproxy/web/app.py22
-rw-r--r--web/package.json4
-rw-r--r--web/src/js/app.jsx2
-rw-r--r--web/src/js/components/ContentView.jsx24
-rw-r--r--web/src/js/components/ContentView/ContentViews.jsx30
-rw-r--r--web/src/js/components/common/Button.jsx6
-rw-r--r--web/src/js/components/common/CodeEditor.jsx41
-rw-r--r--web/src/js/components/common/MonacoEditor.jsx34
-rw-r--r--web/src/js/ducks/flows.js8
9 files changed, 115 insertions, 56 deletions
diff --git a/mitmproxy/web/app.py b/mitmproxy/web/app.py
index 8434818d..c2d46d30 100644
--- a/mitmproxy/web/app.py
+++ b/mitmproxy/web/app.py
@@ -245,9 +245,6 @@ class FlowHandler(RequestHandler):
request.port = int(v)
elif k == "headers":
request.headers.set_state(v)
- elif k == "content":
- print(v)
- response.content = str(v)
else:
print("Warning: Unknown update {}.{}: {}".format(a, k, v))
@@ -262,9 +259,6 @@ class FlowHandler(RequestHandler):
response.http_version = str(v)
elif k == "headers":
response.headers.set_state(v)
- elif k == "content":
- print(v)
- response.content = str(v)
else:
print("Warning: Unknown update {}.{}: {}".format(a, k, v))
else:
@@ -298,6 +292,22 @@ class ReplayFlow(RequestHandler):
class FlowContent(RequestHandler):
+ def post (self, flow_id, message):
+ # handle request later now just change response content
+
+ flow = self.flow
+ flow.backup()
+ content = ''
+ if (len(self.request.files.values()) > 0):
+ content = self.request.files.values()[0][0]["body"]
+ elif (len(self.request.arguments) > 0):
+ content = self.request.arguments['file']
+
+ flow.response.content = str(content)
+ self.state.update_flow(flow)
+
+
+
def get(self, flow_id, message):
message = getattr(self.flow, message)
diff --git a/web/package.json b/web/package.json
index 1484961d..7cfd3d90 100644
--- a/web/package.json
+++ b/web/package.json
@@ -3,7 +3,7 @@
"private": true,
"scripts": {
"test": "jest",
- "build": "gulp dev",
+ "build": "gulp prod",
"start": "gulp"
},
"jest": {
@@ -29,7 +29,7 @@
"redux-logger": "^2.6.1",
"redux-thunk": "^2.1.0",
"shallowequal": "^0.2.2",
- "monaco-editor": "^0.5.1"
+ "react-ace": "^3.5.0"
},
"devDependencies": {
"babel-core": "^6.7.7",
diff --git a/web/src/js/app.jsx b/web/src/js/app.jsx
index 51b2b639..5acf5dd4 100644
--- a/web/src/js/app.jsx
+++ b/web/src/js/app.jsx
@@ -12,7 +12,7 @@ import { add as addLog } from './ducks/eventLog'
const middlewares = [thunk];
-if (process.env.NODE_ENV === 'development' || true) {
+if (process.env.NODE_ENV === 'development' ) {
const createLogger = require('redux-logger');
middlewares.push(createLogger());
}
diff --git a/web/src/js/components/ContentView.jsx b/web/src/js/components/ContentView.jsx
index f6dbe90a..6c9d9b26 100644
--- a/web/src/js/components/ContentView.jsx
+++ b/web/src/js/components/ContentView.jsx
@@ -4,6 +4,7 @@ import { ViewAuto, ViewImage } from './ContentView/ContentViews'
import * as MetaViews from './ContentView/MetaViews'
import ContentLoader from './ContentView/ContentLoader'
import ViewSelector from './ContentView/ViewSelector'
+import * as flowsActions from '../ducks/flows'
export default class ContentView extends Component {
@@ -40,6 +41,15 @@ export default class ContentView extends Component {
return msg.contentLength > 1024 * 1024 * (ViewImage.matches(msg) ? 10 : 0.2)
}
+ onOpenFile(e) {
+ if (e.target.files.length > 0) {
+ //alert(e.target.files[0])
+ flowsActions.update_content(this.props.flow, e.target.files[0])
+ //this.fileInput.value = ''
+ }
+ e.preventDefault()
+ }
+
render() {
const { flow, message } = this.props
const { displayLarge, View } = this.state
@@ -60,10 +70,10 @@ export default class ContentView extends Component {
<div>
{View.textView ? (
<ContentLoader flow={flow} message={message}>
- <this.state.View onChange={this.props.onChange} content="" />
+ <this.state.View update_content={content => flowsActions.update_content(this.props.flow, content)} content="" />
</ContentLoader>
) : (
- <View flow={flow} onChange={this.props.onChange} message={message} />
+ <View flow={flow} update_content={content => flowsActions.update_content(this.props.flow, content)} message={message} />
)}
<div className="view-options text-center">
<ViewSelector onSelectView={this.selectView} active={View} message={message}/>
@@ -71,6 +81,16 @@ export default class ContentView extends Component {
<a className="btn btn-default btn-xs" href={MessageUtils.getContentURL(flow, message)}>
<i className="fa fa-download"/>
</a>
+ &nbsp;
+ <a className="btn btn-default btn-xs" href="#" onClick={e => {this.fileInput.click(); e.preventDefault();}}>
+ <i className="fa fa-upload"/>
+ </a>
+ <input
+ ref={ref => this.fileInput = ref}
+ className="hidden"
+ type="file"
+ onChange={e => this.onOpenFile(e)}
+ />
</div>
</div>
)
diff --git a/web/src/js/components/ContentView/ContentViews.jsx b/web/src/js/components/ContentView/ContentViews.jsx
index e5a864bf..617ed242 100644
--- a/web/src/js/components/ContentView/ContentViews.jsx
+++ b/web/src/js/components/ContentView/ContentViews.jsx
@@ -1,10 +1,12 @@
import React, { PropTypes } from 'react'
import ContentLoader from './ContentLoader'
import { MessageUtils } from '../../flow/utils.js'
-import Button from '../common/Button'
+import CodeEditor from '../common/CodeEditor'
+import {formatSize} from '../../utils.js'
-const views = [ViewAuto, ViewImage, ViewJSON, ViewRaw]
+
+const views = [ViewAuto, ViewImage, ViewJSON, ViewRaw, ViewFile]
ViewImage.regex = /^image\/(png|jpe?g|gif|vnc.microsoft.icon|x-icon)$/i
ViewImage.matches = msg => ViewImage.regex.test(MessageUtils.getContentType(msg))
@@ -30,12 +32,9 @@ ViewRaw.propTypes = {
content: React.PropTypes.string.isRequired,
}
-export function ViewRaw({ content, onChange }) {
+export function ViewRaw({ content, update_content }) {
return (
- <div>
- <textarea onKeyDown={e => e.stopPropagation()} ref={ref => ViewRaw.input = ref}>{content}</textarea>
- <Button onClick={(e) => onChange(ViewRaw.input.value)} text="Update"/>
- </div>
+ <CodeEditor value={content} onSave={update_content}/>
)
}
@@ -66,13 +65,26 @@ ViewAuto.propTypes = {
flow: React.PropTypes.object.isRequired,
}
-export function ViewAuto({ message, flow, onChange }) {
+export function ViewAuto({ message, flow, update_content }) {
const View = ViewAuto.findView(message)
if (View.textView) {
- return <ContentLoader message={message} flow={flow}><View onChange={onChange} content="" /></ContentLoader>
+ return <ContentLoader message={message} flow={flow}><View update_content={update_content} content="" /></ContentLoader>
} else {
return <View message={message} flow={flow} />
}
}
+ViewFile.matches = () => false
+
+ViewFile.propTypes = {
+ message: React.PropTypes.object.isRequired,
+ flow: React.PropTypes.object.isRequired,
+}
+
+export function ViewFile({ message, flow }) {
+ return <div className="alert alert-info">
+ {formatSize(message.contentLength)} content size.
+ </div>
+}
+
export default views
diff --git a/web/src/js/components/common/Button.jsx b/web/src/js/components/common/Button.jsx
index 574288df..221c6ace 100644
--- a/web/src/js/components/common/Button.jsx
+++ b/web/src/js/components/common/Button.jsx
@@ -10,8 +10,10 @@ export default function Button({ onClick, text, icon, disabled }) {
<div className={"btn btn-default"}
onClick={onClick}
disabled={disabled}>
- <i className={"fa fa-fw " + icon}/>
- &nbsp;
+ <span hidden={!icon}>
+ <i className={"fa fa-fw " + icon}/>
+ &nbsp;
+ </span>
{text}
</div>
)
diff --git a/web/src/js/components/common/CodeEditor.jsx b/web/src/js/components/common/CodeEditor.jsx
new file mode 100644
index 00000000..d7e6aabb
--- /dev/null
+++ b/web/src/js/components/common/CodeEditor.jsx
@@ -0,0 +1,41 @@
+import React, { Component, PropTypes } from 'react'
+import { render } from 'react-dom';
+import brace from 'brace';
+import AceEditor from 'react-ace';
+import Button from './Button'
+
+
+import 'brace/mode/javascript';
+import 'brace/mode/json';
+import 'brace/theme/monokai';
+
+
+
+
+export default class CodeEditor extends Component{
+ constructor( props ) {
+ super(props)
+ this.state = {value: this.props.value}
+ }
+
+ onChange(newValue) {
+ this.setState({value: newValue})
+ }
+
+ render() {
+ return (
+ <div onKeyDown={e => e.stopPropagation()}>
+ <AceEditor
+ onChange={e => this.onChange(e)}
+ mode="javascript"
+ theme="monokai"
+ value={this.state.value}
+ width="100%"
+ name="codeEditor"
+ editorProps={{$blockScrolling: Infinity}}
+ />
+ <Button onClick={(e) => this.props.onSave(this.state.value)} text="Update"/>
+ </div>
+ )
+ }
+}
diff --git a/web/src/js/components/common/MonacoEditor.jsx b/web/src/js/components/common/MonacoEditor.jsx
deleted file mode 100644
index a0e8d58c..00000000
--- a/web/src/js/components/common/MonacoEditor.jsx
+++ /dev/null
@@ -1,34 +0,0 @@
-//not working
-import React, { Component, PropTypes } from 'react'
-
-export default class MonacoEditor extends Component {
-
- constructor(props) {
- super(props)
- }
-
- onLoad(){
- window.MonacoEnvironment = {
- getWorkerUrl: function(workerId, label) {
- return 'worker-loader-proxy.js';
- }
- };
- require.config({
- paths: {
- vs: '../release/min/vs'
- }
- });
-
- }
-
-
- render() {
- return (
- <div id="container"
- ref={ref => this.editor = ref}
- style="width:800px;height:600px;border:1px solid grey"
- onLoad={this.onLoad()}>
- </div>
- )
- }
-}
diff --git a/web/src/js/ducks/flows.js b/web/src/js/ducks/flows.js
index f0b09530..36274f39 100644
--- a/web/src/js/ducks/flows.js
+++ b/web/src/js/ducks/flows.js
@@ -117,6 +117,14 @@ export function update(flow, data) {
return { type: REQUEST_ACTION }
}
+export function update_content(flow, file) {
+ const body = new FormData()
+ body.append('file', file)
+ fetchApi(`/flows/${flow.id}/response/content`, {method: 'post', body} )
+ return { type: REQUEST_ACTION }
+}
+
+
/**
* @public
*/