From e5bf1e930a5b6ba0b3300b02daf792d65d795202 Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 14 Jun 2016 23:52:00 +0800 Subject: [web] FlowView and ContentView --- web/src/js/components/flowview/contentview.js | 267 --------------------- web/src/js/components/flowview/details.js | 181 --------------- web/src/js/components/flowview/index.js | 114 --------- web/src/js/components/flowview/messages.js | 320 -------------------------- web/src/js/components/flowview/nav.js | 61 ----- 5 files changed, 943 deletions(-) delete mode 100644 web/src/js/components/flowview/contentview.js delete mode 100644 web/src/js/components/flowview/details.js delete mode 100644 web/src/js/components/flowview/index.js delete mode 100644 web/src/js/components/flowview/messages.js delete mode 100644 web/src/js/components/flowview/nav.js (limited to 'web/src/js/components/flowview') diff --git a/web/src/js/components/flowview/contentview.js b/web/src/js/components/flowview/contentview.js deleted file mode 100644 index cbac9a75..00000000 --- a/web/src/js/components/flowview/contentview.js +++ /dev/null @@ -1,267 +0,0 @@ -import React from "react"; -import _ from "lodash"; - -import {MessageUtils} from "../../flow/utils.js"; -import {formatSize} from "../../utils.js"; - -var ViewImage = React.createClass({ - propTypes: { - flow: React.PropTypes.object.isRequired, - message: React.PropTypes.object.isRequired, - }, - statics: { - regex: /^image\/(png|jpe?g|gif|vnc.microsoft.icon|x-icon)$/i, - matches: function (message) { - return ViewImage.regex.test(MessageUtils.getContentType(message)); - } - }, - render: function () { - var url = MessageUtils.getContentURL(this.props.flow, this.props.message); - return
- preview -
; - } -}); - -var ContentLoader = React.createClass({ - propTypes: { - flow: React.PropTypes.object.isRequired, - message: React.PropTypes.object.isRequired, - }, - getInitialState: function () { - return { - content: undefined, - request: undefined - } - }, - requestContent: function (nextProps) { - if (this.state.request) { - this.state.request.abort(); - } - var request = MessageUtils.getContent(nextProps.flow, nextProps.message); - this.setState({ - content: undefined, - request: request - }); - request.done(function (data) { - this.setState({content: data}); - }.bind(this)).fail(function (jqXHR, textStatus, errorThrown) { - if (textStatus === "abort") { - return; - } - this.setState({content: "AJAX Error: " + textStatus + "\r\n" + errorThrown}); - }.bind(this)).always(function () { - this.setState({request: undefined}); - }.bind(this)); - - }, - componentWillMount: function () { - this.requestContent(this.props); - }, - componentWillReceiveProps: function (nextProps) { - if (nextProps.message !== this.props.message) { - this.requestContent(nextProps); - } - }, - componentWillUnmount: function () { - if (this.state.request) { - this.state.request.abort(); - } - }, - render: function () { - if (!this.state.content) { - return
- -
; - } - return React.cloneElement(this.props.children, { - content: this.state.content - }) - } -}); - -var ViewRaw = React.createClass({ - propTypes: { - content: React.PropTypes.string.isRequired, - }, - statics: { - textView: true, - matches: function (message) { - return true; - } - }, - render: function () { - return
{this.props.content}
; - } -}); - -var ViewJSON = React.createClass({ - propTypes: { - content: React.PropTypes.string.isRequired, - }, - statics: { - textView: true, - regex: /^application\/json$/i, - matches: function (message) { - return ViewJSON.regex.test(MessageUtils.getContentType(message)); - } - }, - render: function () { - var json = this.props.content; - try { - json = JSON.stringify(JSON.parse(json), null, 2); - } catch (e) { - // @noop - } - return
{json}
; - } -}); - -var ViewAuto = React.createClass({ - propTypes: { - message: React.PropTypes.object.isRequired, - flow: React.PropTypes.object.isRequired, - }, - statics: { - matches: function () { - return false; // don't match itself - }, - findView: function (message) { - for (var i = 0; i < all.length; i++) { - if (all[i].matches(message)) { - return all[i]; - } - } - return all[all.length - 1]; - } - }, - render: function () { - var { message, flow } = this.props - var View = ViewAuto.findView(this.props.message); - if (View.textView) { - return - } else { - return - } - } -}); - -var all = [ViewAuto, ViewImage, ViewJSON, ViewRaw]; - -var ContentEmpty = React.createClass({ - render: function () { - var message_name = this.props.flow.request === this.props.message ? "request" : "response"; - return
No {message_name} content.
; - } -}); - -var ContentMissing = React.createClass({ - render: function () { - var message_name = this.props.flow.request === this.props.message ? "Request" : "Response"; - return
{message_name} content missing.
; - } -}); - -var TooLarge = React.createClass({ - statics: { - isTooLarge: function (message) { - var max_mb = ViewImage.matches(message) ? 10 : 0.2; - return message.contentLength > 1024 * 1024 * max_mb; - } - }, - render: function () { - var size = formatSize(this.props.message.contentLength); - return
- - {size} content size. -
; - } -}); - -var ViewSelector = React.createClass({ - render: function () { - var views = []; - for (var i = 0; i < all.length; i++) { - var view = all[i]; - var className = "btn btn-default"; - if (view === this.props.active) { - className += " active"; - } - var text; - if (view === ViewAuto) { - text = "auto: " + ViewAuto.findView(this.props.message).displayName.toLowerCase().replace("view", ""); - } else { - text = view.displayName.toLowerCase().replace("view", ""); - } - views.push( - - ); - } - - return
{views}
; - } -}); - -var ContentView = React.createClass({ - getInitialState: function () { - return { - displayLarge: false, - View: ViewAuto - }; - }, - propTypes: { - // It may seem a bit weird at the first glance: - // Every view takes the flow and the message as props, e.g. - // - flow: React.PropTypes.object.isRequired, - message: React.PropTypes.object.isRequired, - }, - selectView: function (view) { - this.setState({ - View: view - }); - }, - displayLarge: function () { - this.setState({displayLarge: true}); - }, - componentWillReceiveProps: function (nextProps) { - if (nextProps.message !== this.props.message) { - this.setState(this.getInitialState()); - } - }, - render: function () { - var { flow, message } = this.props - var message = this.props.message; - if (message.contentLength === 0) { - return ; - } else if (message.contentLength === null) { - return ; - } else if (!this.state.displayLarge && TooLarge.isTooLarge(message)) { - return ; - } - - var downloadUrl = MessageUtils.getContentURL(this.props.flow, message); - - return
- {this.state.View.textView ? ( - - ) : ( - - )} -
- -   - - - -
-
; - } -}); - -export default ContentView; diff --git a/web/src/js/components/flowview/details.js b/web/src/js/components/flowview/details.js deleted file mode 100644 index 45fe1292..00000000 --- a/web/src/js/components/flowview/details.js +++ /dev/null @@ -1,181 +0,0 @@ -import React from "react"; -import _ from "lodash"; - -import {formatTimeStamp, formatTimeDelta} from "../../utils.js"; - -var TimeStamp = React.createClass({ - render: function () { - - if (!this.props.t) { - //should be return null, but that triggers a React bug. - return ; - } - - var ts = formatTimeStamp(this.props.t); - - var delta; - if (this.props.deltaTo) { - delta = formatTimeDelta(1000 * (this.props.t - this.props.deltaTo)); - delta = {"(" + delta + ")"}; - } else { - delta = null; - } - - return - {this.props.title + ":"} - {ts} {delta} - ; - } -}); - -var ConnectionInfo = React.createClass({ - - render: function () { - var conn = this.props.conn; - var address = conn.address.address.join(":"); - - var sni = ; //should be null, but that triggers a React bug. - if (conn.sni) { - sni = - - TLS SNI: - - {conn.sni} - ; - } - return ( - - - - - - - {sni} - -
Address:{address}
- ); - } -}); - -var CertificateInfo = React.createClass({ - render: function () { - //TODO: We should fetch human-readable certificate representation - // from the server - var flow = this.props.flow; - var client_conn = flow.client_conn; - var server_conn = flow.server_conn; - - var preStyle = {maxHeight: 100}; - return ( -
- {client_conn.cert ?

Client Certificate

: null} - {client_conn.cert ?
{client_conn.cert}
: null} - - {server_conn.cert ?

Server Certificate

: null} - {server_conn.cert ?
{server_conn.cert}
: null} -
- ); - } -}); - -var Timing = React.createClass({ - render: function () { - var flow = this.props.flow; - var sc = flow.server_conn; - var cc = flow.client_conn; - var req = flow.request; - var resp = flow.response; - - var timestamps = [ - { - title: "Server conn. initiated", - t: sc.timestamp_start, - deltaTo: req.timestamp_start - }, { - title: "Server conn. TCP handshake", - t: sc.timestamp_tcp_setup, - deltaTo: req.timestamp_start - }, { - title: "Server conn. SSL handshake", - t: sc.timestamp_ssl_setup, - deltaTo: req.timestamp_start - }, { - title: "Client conn. established", - t: cc.timestamp_start, - deltaTo: req.timestamp_start - }, { - title: "Client conn. SSL handshake", - t: cc.timestamp_ssl_setup, - deltaTo: req.timestamp_start - }, { - title: "First request byte", - t: req.timestamp_start, - }, { - title: "Request complete", - t: req.timestamp_end, - deltaTo: req.timestamp_start - } - ]; - - if (flow.response) { - timestamps.push( - { - title: "First response byte", - t: resp.timestamp_start, - deltaTo: req.timestamp_start - }, { - title: "Response complete", - t: resp.timestamp_end, - deltaTo: req.timestamp_start - } - ); - } - - //Add unique key for each row. - timestamps.forEach(function (e) { - e.key = e.title; - }); - - timestamps = _.sortBy(timestamps, 't'); - - var rows = timestamps.map(function (e) { - return ; - }); - - return ( -
-

Timing

- - - {rows} - -
-
- ); - } -}); - -var Details = React.createClass({ - render: function () { - var flow = this.props.flow; - var client_conn = flow.client_conn; - var server_conn = flow.server_conn; - return ( -
- -

Client Connection

- - -

Server Connection

- - - - - - -
- ); - } -}); - -export default Details; \ No newline at end of file diff --git a/web/src/js/components/flowview/index.js b/web/src/js/components/flowview/index.js deleted file mode 100644 index 7f5e9768..00000000 --- a/web/src/js/components/flowview/index.js +++ /dev/null @@ -1,114 +0,0 @@ -import React from "react"; - -import Nav from "./nav.js"; -import {Request, Response, Error} from "./messages.js"; -import Details from "./details.js"; -import Prompt from "../prompt.js"; - - -var allTabs = { - request: Request, - response: Response, - error: Error, - details: Details -}; - -var FlowView = React.createClass({ - getInitialState: function () { - return { - prompt: false - }; - }, - getTabs: function (flow) { - var tabs = []; - ["request", "response", "error"].forEach(function (e) { - if (flow[e]) { - tabs.push(e); - } - }); - tabs.push("details"); - return tabs; - }, - nextTab: function (i) { - var tabs = this.getTabs(this.props.flow); - var currentIndex = tabs.indexOf(this.props.tab); - // JS modulo operator doesn't correct negative numbers, make sure that we are positive. - var nextIndex = (currentIndex + i + tabs.length) % tabs.length; - this.selectTab(tabs[nextIndex]); - }, - selectTab: function (panel) { - this.props.updateLocation(`/flows/${this.props.flow.id}/${panel}`); - }, - promptEdit: function () { - var options; - switch (this.props.tab) { - case "request": - options = [ - "method", - "url", - {text: "http version", key: "v"}, - "header" - /*, "content"*/]; - break; - case "response": - options = [ - {text: "http version", key: "v"}, - "code", - "message", - "header" - /*, "content"*/]; - break; - case "details": - return; - default: - throw "Unknown tab for edit: " + this.props.tab; - } - - this.setState({ - prompt: { - done: function (k) { - this.setState({prompt: false}); - if (k) { - this.refs.tab.edit(k); - } - }.bind(this), - options: options - } - }); - }, - render: function () { - var flow = this.props.flow; - var tabs = this.getTabs(flow); - var active = this.props.tab; - - if (tabs.indexOf(active) < 0) { - if (active === "response" && flow.error) { - active = "error"; - } else if (active === "error" && flow.response) { - active = "response"; - } else { - active = tabs[0]; - } - } - - var prompt = null; - if (this.state.prompt) { - prompt = ; - } - - var Tab = allTabs[active]; - return ( -
-
- ); - } -}); - -export default FlowView; diff --git a/web/src/js/components/flowview/messages.js b/web/src/js/components/flowview/messages.js deleted file mode 100644 index 2885b3b1..00000000 --- a/web/src/js/components/flowview/messages.js +++ /dev/null @@ -1,320 +0,0 @@ -import React from "react"; -import ReactDOM from 'react-dom'; -import _ from "lodash"; - -import {FlowActions} from "../../actions.js"; -import {RequestUtils, isValidHttpVersion, parseUrl, parseHttpVersion} from "../../flow/utils.js"; -import {Key, formatTimeStamp} from "../../utils.js"; -import ContentView from "./contentview.js"; -import {ValueEditor} from "../editor.js"; - -var Headers = React.createClass({ - propTypes: { - onChange: React.PropTypes.func.isRequired, - message: React.PropTypes.object.isRequired - }, - onChange: function (row, col, val) { - var nextHeaders = _.cloneDeep(this.props.message.headers); - nextHeaders[row][col] = val; - if (!nextHeaders[row][0] && !nextHeaders[row][1]) { - // do not delete last row - if (nextHeaders.length === 1) { - nextHeaders[0][0] = "Name"; - nextHeaders[0][1] = "Value"; - } else { - nextHeaders.splice(row, 1); - // manually move selection target if this has been the last row. - if (row === nextHeaders.length) { - this._nextSel = (row - 1) + "-value"; - } - } - } - this.props.onChange(nextHeaders); - }, - edit: function () { - this.refs["0-key"].focus(); - }, - onTab: function (row, col, e) { - var headers = this.props.message.headers; - if (row === headers.length - 1 && col === 1) { - e.preventDefault(); - - var nextHeaders = _.cloneDeep(this.props.message.headers); - nextHeaders.push(["Name", "Value"]); - this.props.onChange(nextHeaders); - this._nextSel = (row + 1) + "-key"; - } - }, - componentDidUpdate: function () { - if (this._nextSel && this.refs[this._nextSel]) { - this.refs[this._nextSel].focus(); - this._nextSel = undefined; - } - }, - onRemove: function (row, col, e) { - if (col === 1) { - e.preventDefault(); - this.refs[row + "-key"].focus(); - } else if (row > 0) { - e.preventDefault(); - this.refs[(row - 1) + "-value"].focus(); - } - }, - render: function () { - - var rows = this.props.message.headers.map(function (header, i) { - - var kEdit = ; - var vEdit = ; - return ( - - {kEdit}: - {vEdit} - - ); - }.bind(this)); - return ( - - - {rows} - -
- ); - } -}); - -var HeaderEditor = React.createClass({ - render: function () { - return ; - }, - focus: function () { - ReactDOM.findDOMNode(this).focus(); - }, - onKeyDown: function (e) { - switch (e.keyCode) { - case Key.BACKSPACE: - var s = window.getSelection().getRangeAt(0); - if (s.startOffset === 0 && s.endOffset === 0) { - this.props.onRemove(e); - } - break; - case Key.TAB: - if (!e.shiftKey) { - this.props.onTab(e); - } - break; - } - } -}); - -var RequestLine = React.createClass({ - render: function () { - var flow = this.props.flow; - var url = RequestUtils.pretty_url(flow.request); - var httpver = flow.request.http_version; - - return
- -   - -   - -
- }, - isValidUrl: function (url) { - var u = parseUrl(url); - return !!u.host; - }, - onMethodChange: function (nextMethod) { - FlowActions.update( - this.props.flow, - {request: {method: nextMethod}} - ); - }, - onUrlChange: function (nextUrl) { - var props = parseUrl(nextUrl); - props.path = props.path || ""; - FlowActions.update( - this.props.flow, - {request: props} - ); - }, - onHttpVersionChange: function (nextVer) { - var ver = parseHttpVersion(nextVer); - FlowActions.update( - this.props.flow, - {request: {http_version: ver}} - ); - } -}); - -var ResponseLine = React.createClass({ - render: function () { - var flow = this.props.flow; - var httpver = flow.response.http_version; - return
- -   - -   - -
; - }, - isValidCode: function (code) { - return /^\d+$/.test(code); - }, - onHttpVersionChange: function (nextVer) { - var ver = parseHttpVersion(nextVer); - FlowActions.update( - this.props.flow, - {response: {http_version: ver}} - ); - }, - onMsgChange: function (nextMsg) { - FlowActions.update( - this.props.flow, - {response: {msg: nextMsg}} - ); - }, - onCodeChange: function (nextCode) { - nextCode = parseInt(nextCode); - FlowActions.update( - this.props.flow, - {response: {code: nextCode}} - ); - } -}); - -export var Request = React.createClass({ - render: function () { - var flow = this.props.flow; - return ( -
- - {/**/} - -
- -
- ); - }, - edit: function (k) { - switch (k) { - case "m": - this.refs.requestLine.refs.method.focus(); - break; - case "u": - this.refs.requestLine.refs.url.focus(); - break; - case "v": - this.refs.requestLine.refs.httpVersion.focus(); - break; - case "h": - this.refs.headers.edit(); - break; - default: - throw "Unimplemented: " + k; - } - }, - onHeaderChange: function (nextHeaders) { - FlowActions.update(this.props.flow, { - request: { - headers: nextHeaders - } - }); - } -}); - -export var Response = React.createClass({ - render: function () { - var flow = this.props.flow; - return ( -
- {/**/} - - -
- -
- ); - }, - edit: function (k) { - switch (k) { - case "c": - this.refs.responseLine.refs.status_code.focus(); - break; - case "m": - this.refs.responseLine.refs.msg.focus(); - break; - case "v": - this.refs.responseLine.refs.httpVersion.focus(); - break; - case "h": - this.refs.headers.edit(); - break; - default: - throw "Unimplemented: " + k; - } - }, - onHeaderChange: function (nextHeaders) { - FlowActions.update(this.props.flow, { - response: { - headers: nextHeaders - } - }); - } -}); - -export var Error = React.createClass({ - render: function () { - var flow = this.props.flow; - return ( -
-
- {flow.error.msg} -
- { formatTimeStamp(flow.error.timestamp) } -
-
-
- ); - } -}); diff --git a/web/src/js/components/flowview/nav.js b/web/src/js/components/flowview/nav.js deleted file mode 100644 index a12fd1fd..00000000 --- a/web/src/js/components/flowview/nav.js +++ /dev/null @@ -1,61 +0,0 @@ -import React from "react"; - -import {FlowActions} from "../../actions.js"; - -var NavAction = React.createClass({ - onClick: function (e) { - e.preventDefault(); - this.props.onClick(); - }, - render: function () { - return ( - - - - ); - } -}); - -var Nav = React.createClass({ - render: function () { - var flow = this.props.flow; - - var tabs = this.props.tabs.map(function (e) { - var str = e.charAt(0).toUpperCase() + e.slice(1); - var className = this.props.active === e ? "active" : ""; - var onClick = function (event) { - this.props.selectTab(e); - event.preventDefault(); - }.bind(this); - return {str}; - }.bind(this)); - - var acceptButton = null; - if(flow.intercepted){ - acceptButton = ; - } - var revertButton = null; - if(flow.modified){ - revertButton = ; - } - - return ( - - ); - } -}); - -export default Nav; \ No newline at end of file -- cgit v1.2.3