web: various improvements

This commit is contained in:
Maximilian Hils 2014-09-21 23:43:27 +02:00
parent 60cec1f9b6
commit 9cda2eb3a3
8 changed files with 234 additions and 106 deletions

View File

@ -148,9 +148,6 @@ header .menu {
font-weight: normal;
box-shadow: 0 1px 0 #a6a6a6;
}
.flow-table tbody {
outline: 0;
}
.flow-table tr {
cursor: pointer;
}
@ -198,11 +195,16 @@ header .menu {
background-color: #F2F2F2;
}
.flow-detail section {
padding: 5px;
padding: 5px 12px;
}
.flow-detail code {
.flow-detail .first-line {
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
background-color: #428bca;
color: white;
margin: 0 -8px;
padding: 4px 8px;
border-radius: 5px;
word-break: break-all;
padding-left: 0;
}
.flow-detail table {
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;

View File

@ -41,7 +41,7 @@ var Key = {
var formatSize = function (bytes) {
var size = bytes;
var prefix = ["B", "KB", "MB", "GB", "TB"];
while (size >= 1024 && prefix.length > 1) {
while (Math.abs(size) >= 1024 && prefix.length > 1) {
prefix.shift();
size = size / 1024;
}
@ -50,9 +50,9 @@ var formatSize = function (bytes) {
var formatTimeDelta = function (milliseconds) {
var time = milliseconds;
var prefix = ["ms", "s", "m", "h"];
var prefix = ["ms", "s", "min", "h"];
var div = [1000, 60, 60];
while (time >= div[0] && prefix.length > 1) {
while (Math.abs(time) >= div[0] && prefix.length > 1) {
prefix.shift();
time = time / div.shift();
}
@ -617,12 +617,12 @@ var Header = React.createClass({displayName: 'Header',
console.log("File click");
},
render: function () {
var header = header_entries.map(function(entry){
var header = header_entries.map(function(entry, i){
var classes = React.addons.classSet({
active: entry == this.state.active
});
return (
React.DOM.a({key: entry.title,
React.DOM.a({key: i,
href: "#",
className: classes,
onClick: this.handleClick.bind(this, entry)
@ -685,7 +685,6 @@ var IconColumn = React.createClass({displayName: 'IconColumn',
var contentType = ResponseUtils.getContentType(flow.response);
//TODO: We should assign a type to the flow somewhere else.
var icon;
if(flow.response.code == 304) {
icon = "resource-icon-not-modified";
} else if(300 <= flow.response.code && flow.response.code < 400) {
@ -763,9 +762,10 @@ var SizeColumn = React.createClass({displayName: 'SizeColumn',
},
render: function(){
var flow = this.props.flow;
var total = flow.request.contentLength;
if(flow.response){
total += flow.response.contentLength;
total += flow.response.contentLength || 0;
}
var size = formatSize(total);
return React.DOM.td({className: "col-size"}, size);
@ -917,9 +917,9 @@ var FlowDetailNav = React.createClass({displayName: 'FlowDetailNav',
var Headers = React.createClass({displayName: 'Headers',
render: function(){
var rows = this.props.message.headers.map(function(header){
var rows = this.props.message.headers.map(function(header, i){
return (
React.DOM.tr(null,
React.DOM.tr({key: i},
React.DOM.td({className: "header-name"}, header[0]+":"),
React.DOM.td({className: "header-value"}, header[1])
)
@ -954,7 +954,7 @@ var FlowDetailRequest = React.createClass({displayName: 'FlowDetailRequest',
return (
React.DOM.section(null,
React.DOM.code(null, first_line ),
React.DOM.div({className: "first-line"}, first_line ),
Headers({message: flow.request}),
React.DOM.hr(null),
content
@ -982,7 +982,7 @@ var FlowDetailResponse = React.createClass({displayName: 'FlowDetailResponse',
return (
React.DOM.section(null,
React.DOM.code(null, first_line ),
React.DOM.div({className: "first-line"}, first_line ),
Headers({message: flow.response}),
React.DOM.hr(null),
content
@ -993,24 +993,22 @@ var FlowDetailResponse = React.createClass({displayName: 'FlowDetailResponse',
var TimeStamp = React.createClass({displayName: 'TimeStamp',
render: function() {
var ts, delta;
if(!this.props.t && this.props.optional){
if(!this.props.t){
//should be return null, but that triggers a React bug.
return React.DOM.tr(null);
} else if (!this.props.t){
ts = "active";
} else {
ts = (new Date(this.props.t * 1000)).toISOString();
}
var ts = (new Date(this.props.t * 1000)).toISOString();
ts = ts.replace("T", " ").replace("Z","");
var delta;
if(this.props.deltaTo){
delta = Math.round((this.props.t-this.props.deltaTo)*1000) + "ms";
delta = formatTimeDelta(1000 * (this.props.t-this.props.deltaTo));
delta = React.DOM.span({className: "text-muted"}, "(" + delta + ")");
} else {
delta = null;
}
}
return React.DOM.tr(null, React.DOM.td(null, this.props.title + ":"), React.DOM.td(null, ts, " ", delta));
}
@ -1030,24 +1028,7 @@ var ConnectionInfo = React.createClass({displayName: 'ConnectionInfo',
React.DOM.table({className: "connection-table"},
React.DOM.tbody(null,
React.DOM.tr({key: "address"}, React.DOM.td(null, "Address:"), React.DOM.td(null, address)),
sni,
TimeStamp({title: "Start time",
key: "start",
t: conn.timestamp_start}),
TimeStamp({title: "TCP Setup",
key: "tcpsetup",
t: conn.timestamp_tcp_setup,
deltaTo: conn.timestamp_start,
optional: true}),
TimeStamp({title: "SSL handshake",
key: "sslsetup",
t: conn.timestamp_ssl_setup,
deltaTo: conn.timestamp_start,
optional: true}),
TimeStamp({title: "End time",
key: "end",
t: conn.timestamp_end,
deltaTo: conn.timestamp_start})
sni
)
)
);
@ -1061,13 +1042,92 @@ var CertificateInfo = React.createClass({displayName: 'CertificateInfo',
var flow = this.props.flow;
var client_conn = flow.client_conn;
var server_conn = flow.server_conn;
var preStyle = {maxHeight: 100};
return (
React.DOM.div(null,
client_conn.cert ? React.DOM.h4(null, "Client Certificate") : null,
client_conn.cert ? React.DOM.pre(null, client_conn.cert) : null,
client_conn.cert ? React.DOM.pre({style: preStyle}, client_conn.cert) : null,
server_conn.cert ? React.DOM.h4(null, "Server Certificate") : null,
server_conn.cert ? React.DOM.pre(null, server_conn.cert) : null
server_conn.cert ? React.DOM.pre({style: preStyle}, server_conn.cert) : null
)
);
}
});
var Timing = React.createClass({displayName: 'Timing',
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 TimeStamp(e);
});
return (
React.DOM.div(null,
React.DOM.h4(null, "Timing"),
React.DOM.table(null,
React.DOM.tbody(null,
rows
)
)
)
);
}
@ -1087,7 +1147,9 @@ var FlowDetailConnectionInfo = React.createClass({displayName: 'FlowDetailConnec
React.DOM.h4(null, "Server Connection"),
ConnectionInfo({conn: server_conn}),
CertificateInfo({flow: flow})
CertificateInfo({flow: flow}),
Timing({flow: flow})
)
);

View File

@ -1,3 +1,9 @@
//TODO: Move into some utils
.monospace(){
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
}
.flow-detail {
width: 100%;
overflow: auto;
@ -7,20 +13,20 @@
}
section {
padding: 5px;
padding: 5px 12px;
}
//FIXME: Style properly
code {
.first-line {
.monospace();
background-color: #428bca;
color: white;
margin: 0 -8px;
padding: 4px 8px;
border-radius: 5px;
word-break: break-all;
padding-left: 0;
}
}
//TODO: Move into some utils
.monospace(){
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
}
.flow-detail table {
.monospace();

View File

@ -17,10 +17,6 @@
box-shadow: 0 1px 0 #a6a6a6;
}
tbody {
outline: 0;
}
tr {
cursor: pointer;
&.selected {

View File

@ -25,9 +25,9 @@ var FlowDetailNav = React.createClass({
var Headers = React.createClass({
render: function(){
var rows = this.props.message.headers.map(function(header){
var rows = this.props.message.headers.map(function(header, i){
return (
<tr>
<tr key={i}>
<td className="header-name">{header[0]+":"}</td>
<td className="header-value">{header[1]}</td>
</tr>
@ -62,7 +62,7 @@ var FlowDetailRequest = React.createClass({
return (
<section>
<code>{ first_line }</code>
<div className="first-line">{ first_line }</div>
<Headers message={flow.request}/>
<hr/>
{content}
@ -90,7 +90,7 @@ var FlowDetailResponse = React.createClass({
return (
<section>
<code>{ first_line }</code>
<div className="first-line">{ first_line }</div>
<Headers message={flow.response}/>
<hr/>
{content}
@ -101,24 +101,22 @@ var FlowDetailResponse = React.createClass({
var TimeStamp = React.createClass({
render: function() {
var ts, delta;
if(!this.props.t && this.props.optional){
if(!this.props.t){
//should be return null, but that triggers a React bug.
return <tr></tr>;
} else if (!this.props.t){
ts = "active";
} else {
ts = (new Date(this.props.t * 1000)).toISOString();
}
var ts = (new Date(this.props.t * 1000)).toISOString();
ts = ts.replace("T", " ").replace("Z","");
var delta;
if(this.props.deltaTo){
delta = Math.round((this.props.t-this.props.deltaTo)*1000) + "ms";
delta = formatTimeDelta(1000 * (this.props.t-this.props.deltaTo));
delta = <span className="text-muted">{"(" + delta + ")"}</span>;
} else {
delta = null;
}
}
return <tr><td>{this.props.title + ":"}</td><td>{ts} {delta}</td></tr>;
}
@ -139,23 +137,6 @@ var ConnectionInfo = React.createClass({
<tbody>
<tr key="address"><td>Address:</td><td>{address}</td></tr>
{sni}
<TimeStamp title="Start time"
key="start"
t={conn.timestamp_start} />
<TimeStamp title="TCP Setup"
key="tcpsetup"
t={conn.timestamp_tcp_setup}
deltaTo={conn.timestamp_start}
optional={true} />
<TimeStamp title="SSL handshake"
key="sslsetup"
t={conn.timestamp_ssl_setup}
deltaTo={conn.timestamp_start}
optional={true} />
<TimeStamp title="End time"
key="end"
t={conn.timestamp_end}
deltaTo={conn.timestamp_start} />
</tbody>
</table>
);
@ -169,13 +150,92 @@ var CertificateInfo = React.createClass({
var flow = this.props.flow;
var client_conn = flow.client_conn;
var server_conn = flow.server_conn;
var preStyle = {maxHeight: 100};
return (
<div>
{client_conn.cert ? <h4>Client Certificate</h4> : null}
{client_conn.cert ? <pre>{client_conn.cert}</pre> : null}
{client_conn.cert ? <pre style={preStyle}>{client_conn.cert}</pre> : null}
{server_conn.cert ? <h4>Server Certificate</h4> : null}
{server_conn.cert ? <pre>{server_conn.cert}</pre> : null}
{server_conn.cert ? <pre style={preStyle}>{server_conn.cert}</pre> : null}
</div>
);
}
});
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 TimeStamp(e);
});
return (
<div>
<h4>Timing</h4>
<table>
<tbody>
{rows}
</tbody>
</table>
</div>
);
}
@ -197,6 +257,8 @@ var FlowDetailConnectionInfo = React.createClass({
<CertificateInfo flow={flow}/>
<Timing flow={flow}/>
</section>
);
}

View File

@ -34,7 +34,6 @@ var IconColumn = React.createClass({
var contentType = ResponseUtils.getContentType(flow.response);
//TODO: We should assign a type to the flow somewhere else.
var icon;
if(flow.response.code == 304) {
icon = "resource-icon-not-modified";
} else if(300 <= flow.response.code && flow.response.code < 400) {
@ -112,9 +111,10 @@ var SizeColumn = React.createClass({
},
render: function(){
var flow = this.props.flow;
var total = flow.request.contentLength;
if(flow.response){
total += flow.response.contentLength;
total += flow.response.contentLength || 0;
}
var size = formatSize(total);
return <td className="col-size">{size}</td>;

View File

@ -62,12 +62,12 @@ var Header = React.createClass({
console.log("File click");
},
render: function () {
var header = header_entries.map(function(entry){
var header = header_entries.map(function(entry, i){
var classes = React.addons.classSet({
active: entry == this.state.active
});
return (
<a key={entry.title}
<a key={i}
href="#"
className={classes}
onClick={this.handleClick.bind(this, entry)}

View File

@ -41,7 +41,7 @@ var Key = {
var formatSize = function (bytes) {
var size = bytes;
var prefix = ["B", "KB", "MB", "GB", "TB"];
while (size >= 1024 && prefix.length > 1) {
while (Math.abs(size) >= 1024 && prefix.length > 1) {
prefix.shift();
size = size / 1024;
}
@ -50,9 +50,9 @@ var formatSize = function (bytes) {
var formatTimeDelta = function (milliseconds) {
var time = milliseconds;
var prefix = ["ms", "s", "m", "h"];
var prefix = ["ms", "s", "min", "h"];
var div = [1000, 60, 60];
while (time >= div[0] && prefix.length > 1) {
while (Math.abs(time) >= div[0] && prefix.length > 1) {
prefix.shift();
time = time / div.shift();
}