var FlowDetailNav = React.createClass({ render: function () { var items = 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)); return ( ); } }); var Headers = React.createClass({ render: function () { var rows = this.props.message.headers.map(function (header, i) { return ( {header[0] + ":"} {header[1]} ); }); return ( {rows}
); } }); var FlowDetailRequest = React.createClass({ render: function () { var flow = this.props.flow; var first_line = [ flow.request.method, RequestUtils.pretty_url(flow.request), "HTTP/" + flow.request.httpversion.join(".") ].join(" "); var content = null; if (flow.request.contentLength > 0) { content = "Request Content Size: " + formatSize(flow.request.contentLength); } else { content =
No Content
; } //TODO: Styling return (
{ first_line }

{content}
); } }); var FlowDetailResponse = React.createClass({ render: function () { var flow = this.props.flow; var first_line = [ "HTTP/" + flow.response.httpversion.join("."), flow.response.code, flow.response.msg ].join(" "); var content = null; if (flow.response.contentLength > 0) { content = "Response Content Size: " + formatSize(flow.response.contentLength); } else { content =
No Content
; } //TODO: Styling return (
{ first_line }

{content}
); } }); var FlowDetailError = React.createClass({ render: function () { var flow = this.props.flow; return (
{flow.error.msg}
{ formatTimeStamp(flow.error.timestamp) }
); } }); 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 FlowDetailConnectionInfo = 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

); } }); var allTabs = { request: FlowDetailRequest, response: FlowDetailResponse, error: FlowDetailError, details: FlowDetailConnectionInfo }; var FlowDetail = React.createClass({ mixins: [StickyHeadMixin, ReactRouter.Navigation, ReactRouter.State], 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.getParams().detailTab); // 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.replaceWith( "flow", { flowId: this.getParams().flowId, detailTab: panel } ); }, render: function () { var flow = this.props.flow; var tabs = this.getTabs(flow); var active = this.getParams().detailTab; if (!_.contains(tabs, active)) { if (active === "response" && flow.error) { active = "error"; } else if (active === "error" && flow.response) { active = "response"; } else { active = tabs[0]; } this.selectTab(active); } var Tab = allTabs[active]; return (
); } });