start to fill detailpane

This commit is contained in:
Maximilian Hils 2014-09-19 01:35:36 +02:00
parent 390a435ac4
commit b0374710e4
11 changed files with 375 additions and 18 deletions

View File

@ -108,6 +108,12 @@ class HTTPMessage(stateobject.StateObject):
)
_stateobject_long_attributes = {"content"}
def get_state(self, short=False):
ret = super(HTTPMessage, self).get_state(short)
if short:
ret["contentLength"] = len(self.content)
return ret
def get_decoded_content(self):
"""
Returns the decoded content based on the current Content-Encoding

View File

@ -180,10 +180,14 @@ header .menu {
.flow-table .col-status {
width: 50px;
}
.flow-table .col-size {
width: 70px;
}
.flow-table .col-time {
width: 50px;
}
.flow-table td.col-time {
.flow-table td.col-time,
.flow-table td.col-size {
text-align: right;
}
.flow-detail {
@ -193,6 +197,29 @@ header .menu {
.flow-detail nav {
background-color: #F2F2F2;
}
.flow-detail section {
padding: 5px;
}
.flow-detail code {
word-break: break-all;
padding-left: 0;
}
.header-table {
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
width: 100%;
table-layout: fixed;
word-break: break-all;
}
.header-table tr {
border-top: 1px solid #f7f7f7;
}
.header-table td {
vertical-align: top;
}
.header-table .header-name {
width: 33%;
padding-right: 1em;
}
.eventlog {
flex: 0 0 auto;
margin: 0;

View File

@ -1,6 +1,7 @@
[{
"id": "b5e5483c-e124-45bb-aa2e-360706e03ef4",
"request": {
"contentLength": 0,
"timestamp_end": 1410651311.107,
"timestamp_start": 1410651311.106,
"form_in": "relative",
@ -54,6 +55,7 @@
]
},
"server_conn": {
"contentLength" : 54321,
"timestamp_tcp_setup": 1410651311.055,
"state": [],
"timestamp_ssl_setup": 1410651311.096,
@ -77,6 +79,7 @@
"ssl_established": true
},
"client_conn": {
"contentLength" : 54321,
"timestamp_start": 1410651310.36,
"address": {
"use_ipv6": false,
@ -160,6 +163,7 @@
{
"id": "85e9781f-d81d-43ca-a694-2cd86c76d991",
"request": {
"contentLength": 42000,
"timestamp_end": 1410651311.657,
"timestamp_start": 1410651311.653,
"form_in": "relative",
@ -217,6 +221,7 @@
]
},
"server_conn": {
"contentLength" : 54321,
"timestamp_tcp_setup": 1410651311.055,
"state": [],
"timestamp_ssl_setup": 1410651311.096,
@ -240,6 +245,7 @@
"ssl_established": true
},
"client_conn": {
"contentLength" : 54321,
"timestamp_start": 1410651310.36,
"address": {
"use_ipv6": false,
@ -323,6 +329,7 @@
{
"id": "1bf281fd-e02a-423c-a69c-aa65657bc3dd",
"request": {
"contentLength": 132121,
"timestamp_end": 1410651312.362,
"timestamp_start": 1410651312.359,
"form_in": "relative",
@ -380,6 +387,7 @@
]
},
"server_conn": {
"contentLength" : 54321,
"timestamp_tcp_setup": 1410651312.303,
"state": [],
"timestamp_ssl_setup": 1410651312.349,
@ -403,6 +411,7 @@
"ssl_established": true
},
"client_conn": {
"contentLength" : 54321,
"timestamp_start": 1410651312.193,
"address": {
"use_ipv6": false,
@ -490,6 +499,7 @@
{
"id": "833253a0-f7dd-48c7-893c-1f13a38a71ce",
"request": {
"contentLength": 13233121,
"timestamp_end": 1410651312.389,
"timestamp_start": 1410651312.368,
"form_in": "relative",
@ -547,6 +557,7 @@
]
},
"server_conn": {
"contentLength" : 54321,
"timestamp_tcp_setup": 1410651312.307,
"state": [],
"timestamp_ssl_setup": 1410651312.355,
@ -570,6 +581,7 @@
"ssl_established": true
},
"client_conn": {
"contentLength" : 54321,
"timestamp_start": 1410651312.2,
"address": {
"use_ipv6": false,
@ -657,6 +669,7 @@
{
"id": "152d8e71-2469-4034-8d6d-11099bbb4248",
"request": {
"contentLength": 132121231231,
"timestamp_end": 1410651312.386,
"timestamp_start": 1410651312.368,
"form_in": "relative",
@ -714,6 +727,7 @@
]
},
"server_conn": {
"contentLength" : 54321,
"timestamp_tcp_setup": 1410651312.303,
"state": [],
"timestamp_ssl_setup": 1410651312.355,
@ -737,6 +751,7 @@
"ssl_established": true
},
"client_conn": {
"contentLength" : 54321,
"timestamp_start": 1410651312.192,
"address": {
"use_ipv6": false,
@ -824,6 +839,7 @@
{
"id": "b3758e4d-7bae-4771-b154-e100c0722d00",
"request": {
"contentLength" : 54321,
"timestamp_end": 1410651373.965,
"timestamp_start": 1410651373.963,
"form_in": "absolute",
@ -865,6 +881,7 @@
]
},
"server_conn": {
"contentLength" : 54321,
"timestamp_tcp_setup": 1410651374.189,
"state": [],
"timestamp_ssl_setup": null,
@ -888,6 +905,7 @@
"ssl_established": false
},
"client_conn": {
"contentLength" : 54321,
"timestamp_start": 1410651373.958,
"address": {
"use_ipv6": false,
@ -955,6 +973,7 @@
{
"id": "ea9e47ab-fd7b-4463-bfea-cfd64cc5f78d",
"request": {
"contentLength" : 54321,
"timestamp_end": 1410651374.391,
"timestamp_start": 1410651374.387,
"form_in": "absolute",
@ -1000,6 +1019,7 @@
]
},
"server_conn": {
"contentLength" : 54321,
"timestamp_tcp_setup": 1410651374.189,
"state": [],
"timestamp_ssl_setup": null,
@ -1023,6 +1043,7 @@
"ssl_established": false
},
"client_conn": {
"contentLength" : 54321,
"timestamp_start": 1410651373.958,
"address": {
"use_ipv6": false,
@ -1090,6 +1111,7 @@
{
"id": "13ee4cd1-08e0-43ef-9bee-56fc0d9cbf3f",
"request": {
"contentLength" : 54321,
"timestamp_end": 1410651374.396,
"timestamp_start": 1410651374.394,
"form_in": "absolute",
@ -1135,6 +1157,7 @@
]
},
"server_conn": {
"contentLength" : 54321,
"timestamp_tcp_setup": 1410651374.567,
"state": [],
"timestamp_ssl_setup": null,
@ -1158,6 +1181,7 @@
"ssl_established": false
},
"client_conn": {
"contentLength" : 54321,
"timestamp_start": 1410651374.389,
"address": {
"use_ipv6": false,
@ -1221,6 +1245,7 @@
{
"id": "5c50e1fc-5ac4-4748-aed1-c969ede63e4e",
"request": {
"contentLength" : 54321,
"timestamp_end": 1410651374.795,
"timestamp_start": 1410651374.793,
"form_in": "absolute",
@ -1266,6 +1291,7 @@
]
},
"server_conn": {
"contentLength" : 54321,
"timestamp_tcp_setup": 1410651374.99,
"state": [],
"timestamp_ssl_setup": null,
@ -1289,6 +1315,7 @@
"ssl_established": false
},
"client_conn": {
"contentLength" : 54321,
"timestamp_start": 1410651374.389,
"address": {
"use_ipv6": false,
@ -1372,6 +1399,7 @@
{
"id": "0285a0b2-380e-43eb-a7a9-a18893950216",
"request": {
"contentLength" : 54321,
"timestamp_end": 1410651375.084,
"timestamp_start": 1410651375.078,
"form_in": "absolute",
@ -1417,6 +1445,7 @@
]
},
"server_conn": {
"contentLength" : 54321,
"timestamp_tcp_setup": 1410651374.99,
"state": [],
"timestamp_ssl_setup": null,
@ -1440,6 +1469,7 @@
"ssl_established": false
},
"client_conn": {
"contentLength" : 54321,
"timestamp_start": 1410651374.389,
"address": {
"use_ipv6": false,
@ -1519,6 +1549,7 @@
{
"id": "c9af9c71-dc68-462e-8446-f3a4b2782400",
"request": {
"contentLength" : 54321,
"timestamp_end": 1410651374.778,
"timestamp_start": 1410651374.766,
"form_in": "absolute",
@ -1564,6 +1595,7 @@
]
},
"server_conn": {
"contentLength" : 54321,
"timestamp_tcp_setup": 1410651374.952,
"state": [],
"timestamp_ssl_setup": null,
@ -1587,6 +1619,7 @@
"ssl_established": false
},
"client_conn": {
"contentLength" : 54321,
"timestamp_start": 1410651374.39,
"address": {
"use_ipv6": false,
@ -1650,6 +1683,7 @@
{
"id": "310386ab-3ae1-4129-9a2e-8dd2ce60ecdb",
"request": {
"contentLength" : 54321,
"timestamp_end": 1410651374.778,
"timestamp_start": 1410651374.766,
"form_in": "absolute",
@ -1695,6 +1729,7 @@
]
},
"server_conn": {
"contentLength" : 54321,
"timestamp_tcp_setup": 1410651374.189,
"state": [],
"timestamp_ssl_setup": null,
@ -1718,6 +1753,7 @@
"ssl_established": false
},
"client_conn": {
"contentLength" : 54321,
"timestamp_start": 1410651373.958,
"address": {
"use_ipv6": false,
@ -1781,6 +1817,7 @@
{
"id": "b92e5f6e-bb0f-4e47-a50c-ef4072ea40b3",
"request": {
"contentLength" : 54321,
"timestamp_end": 1410651376.078,
"timestamp_start": 1410651376.075,
"form_in": "absolute",
@ -1826,6 +1863,7 @@
]
},
"server_conn": {
"contentLength" : 54321,
"timestamp_tcp_setup": 1410651374.189,
"state": [],
"timestamp_ssl_setup": null,
@ -1849,6 +1887,7 @@
"ssl_established": false
},
"client_conn": {
"contentLength" : 54321,
"timestamp_start": 1410651373.958,
"address": {
"use_ipv6": false,
@ -1904,6 +1943,7 @@
{
"id": "597d086f-d836-49e3-85bb-77a983bed87f",
"request": {
"contentLength" : 54321,
"timestamp_end": 1410651376.282,
"timestamp_start": 1410651376.279,
"form_in": "absolute",
@ -1949,6 +1989,7 @@
]
},
"server_conn": {
"contentLength" : 54321,
"timestamp_tcp_setup": 1410651374.189,
"state": [],
"timestamp_ssl_setup": null,
@ -1972,6 +2013,7 @@
"ssl_established": false
},
"client_conn": {
"contentLength" : 54321,
"timestamp_start": 1410651373.958,
"address": {
"use_ipv6": false,

View File

@ -13,11 +13,11 @@ var AutoScrollMixin = {
};
var StickyHeadMixin = {
adjustHead: function(){
adjustHead: function () {
// Abusing CSS transforms to set the element
// referenced as head into some kind of position:sticky.
var head = this.refs.head.getDOMNode();
head.style.transform = "translate(0,"+this.getDOMNode().scrollTop+"px)";
head.style.transform = "translate(0," + this.getDOMNode().scrollTop + "px)";
}
};
@ -31,6 +31,15 @@ var Key = {
ENTER: 13,
ESC: 27
};
var formatSize = function (size) {
var prefix = ["B", "KB", "MB", "GB", "TB"];
while (size >= 1024 && prefix.length > 1) {
prefix.shift();
size = size / 1024;
}
return (Math.floor(size * 100) / 100.0) + prefix.shift();
};
const PayloadSources = {
VIEW: "view",
SERVER: "server"
@ -104,6 +113,53 @@ var EventLogActions = {
});
}
};
var _MessageUtils = {
getContentType: function (message) {
return MessageUtils.getHeader(message, /Content-Type/i);
},
get_first_header: function (message, regex) {
//FIXME: Cache Invalidation.
if (!message._headerLookups)
Object.defineProperty(message, "_headerLookups", {
value: {},
configurable: false,
enumerable: false,
writable: false
});
if (!(regex in message._headerLookups)) {
var header;
for (var i = 0; i < message.headers.length; i++) {
if (!!message.headers[i][0].match(regex)) {
header = message.headers[i];
break;
}
}
message._headerLookups[regex] = header ? header[1] : undefined;
}
return message._headerLookups[regex];
}
};
var defaultPorts = {
"http": 80,
"https": 443
};
var RequestUtils = _.extend(_MessageUtils, {
pretty_host: function (request) {
//FIXME: Add hostheader
return request.host;
},
pretty_url: function (request) {
var port = "";
if (defaultPorts[request.scheme] !== request.port) {
port = ":" + request.port;
}
return request.scheme + "://" + this.pretty_host(request) + port + request.path;
}
});
var ResponseUtils = _.extend(_MessageUtils, {});
function EventEmitter() {
this.listeners = {};
}
@ -654,6 +710,22 @@ var StatusColumn = React.createClass({displayName: 'StatusColumn',
});
var SizeColumn = React.createClass({displayName: 'SizeColumn',
statics: {
renderTitle: function(){
return React.DOM.th({key: "size", className: "col-size"}, "Size");
}
},
render: function(){
var flow = this.props.flow;
var size = formatSize(
flow.request.contentLength +
(flow.response.contentLength || 0));
return React.DOM.td({className: "col-size"}, size);
}
});
var TimeColumn = React.createClass({displayName: 'TimeColumn',
statics: {
renderTitle: function(){
@ -673,7 +745,14 @@ var TimeColumn = React.createClass({displayName: 'TimeColumn',
});
var all_columns = [TLSColumn, IconColumn, PathColumn, MethodColumn, StatusColumn, TimeColumn];
var all_columns = [
TLSColumn,
IconColumn,
PathColumn,
MethodColumn,
StatusColumn,
SizeColumn,
TimeColumn];
/** @jsx React.DOM */
@ -829,21 +908,59 @@ var FlowDetailNav = React.createClass({displayName: 'FlowDetailNav',
}
});
var Headers = React.createClass({displayName: 'Headers',
render: function(){
var rows = this.props.message.headers.map(function(header){
return (
React.DOM.tr(null,
React.DOM.td({className: "header-name"}, header[0]),
React.DOM.td({className: "header-value"}, header[1])
)
);
})
return (
React.DOM.table({className: "header-table"},
React.DOM.tbody(null,
rows
)
)
);
}
})
var FlowDetailRequest = React.createClass({displayName: 'FlowDetailRequest',
render: function(){
return React.DOM.div(null, "request");
var flow = this.props.flow;
var url = React.DOM.code(null, RequestUtils.pretty_url(flow.request) );
var content = null;
if(flow.request.contentLength > 0){
content = "Request Content Size: "+ formatSize(flow.request.contentLength);
} else {
content = React.DOM.div({className: "alert alert-info"}, "No Content");
}
//TODO: Styling
return (
React.DOM.section(null,
url,
Headers({message: flow.request}),
React.DOM.hr(null),
content
)
);
}
});
var FlowDetailResponse = React.createClass({displayName: 'FlowDetailResponse',
render: function(){
return React.DOM.div(null, "response");
return React.DOM.section(null, "response");
}
});
var FlowDetailConnectionInfo = React.createClass({displayName: 'FlowDetailConnectionInfo',
render: function(){
return React.DOM.div(null, "details");
return React.DOM.section(null, "details");
}
});
@ -860,8 +977,8 @@ var FlowDetail = React.createClass({displayName: 'FlowDetail',
var Tab = tabs[this.props.active];
return (
React.DOM.div({className: "flow-detail", onScroll: this.adjustHead},
FlowDetailNav({active: this.props.active, selectTab: this.props.selectTab}),
Tab(null)
FlowDetailNav({ref: "head", active: this.props.active, selectTab: this.props.selectTab}),
Tab({flow: this.props.flow})
)
);
}

View File

@ -36,6 +36,7 @@ var path = {
'js/utils.js',
'js/dispatcher.js',
'js/actions.js',
'js/flow/utils.js',
'js/stores/base.js',
'js/stores/settingstore.js',
'js/stores/eventlogstore.js',

View File

@ -5,4 +5,48 @@
nav {
background-color: #F2F2F2;
}
section {
padding: 5px;
}
//FIXME: Style properly
code {
word-break: break-all;
padding-left: 0;
}
}
//TODO: Move into some utils
.monospace(){
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
}
.header-table {
.monospace();
width: 100%;
table-layout: fixed;
word-break: break-all;
tr {
//&:not(:first-child){
border-top: 1px solid #f7f7f7;
//}
}
td {
vertical-align: top;
//alt:
//white-space: nowrap;
//overflow: hidden;
//text-overflow: ellipsis;
}
.header-name {
width: 33%;
padding-right: 1em;
}
.header-value {
}
}

View File

@ -51,10 +51,13 @@
.col-status {
width: 50px;
}
.col-size {
width: 70px;
}
.col-time {
width: 50px;
}
td.col-time {
td.col-time, td.col-size {
text-align: right;
}
}

View File

@ -23,21 +23,59 @@ var FlowDetailNav = React.createClass({
}
});
var Headers = React.createClass({
render: function(){
var rows = this.props.message.headers.map(function(header){
return (
<tr>
<td className="header-name">{header[0]}</td>
<td className="header-value">{header[1]}</td>
</tr>
);
})
return (
<table className="header-table">
<tbody>
{rows}
</tbody>
</table>
);
}
})
var FlowDetailRequest = React.createClass({
render: function(){
return <div>request</div>;
var flow = this.props.flow;
var url = <code>{ RequestUtils.pretty_url(flow.request) }</code>;
var content = null;
if(flow.request.contentLength > 0){
content = "Request Content Size: "+ formatSize(flow.request.contentLength);
} else {
content = <div className="alert alert-info">No Content</div>;
}
//TODO: Styling
return (
<section>
{url}
<Headers message={flow.request}/>
<hr/>
{content}
</section>
);
}
});
var FlowDetailResponse = React.createClass({
render: function(){
return <div>response</div>;
return <section>response</section>;
}
});
var FlowDetailConnectionInfo = React.createClass({
render: function(){
return <div>details</div>;
return <section>details</section>;
}
});
@ -54,8 +92,8 @@ var FlowDetail = React.createClass({
var Tab = tabs[this.props.active];
return (
<div className="flow-detail" onScroll={this.adjustHead}>
<FlowDetailNav active={this.props.active} selectTab={this.props.selectTab}/>
<Tab/>
<FlowDetailNav ref="head" active={this.props.active} selectTab={this.props.selectTab}/>
<Tab flow={this.props.flow}/>
</div>
);
}

View File

@ -77,6 +77,22 @@ var StatusColumn = React.createClass({
});
var SizeColumn = React.createClass({
statics: {
renderTitle: function(){
return <th key="size" className="col-size">Size</th>;
}
},
render: function(){
var flow = this.props.flow;
var size = formatSize(
flow.request.contentLength +
(flow.response.contentLength || 0));
return <td className="col-size">{size}</td>;
}
});
var TimeColumn = React.createClass({
statics: {
renderTitle: function(){
@ -96,5 +112,12 @@ var TimeColumn = React.createClass({
});
var all_columns = [TLSColumn, IconColumn, PathColumn, MethodColumn, StatusColumn, TimeColumn];
var all_columns = [
TLSColumn,
IconColumn,
PathColumn,
MethodColumn,
StatusColumn,
SizeColumn,
TimeColumn];

47
web/src/js/flow/utils.js Normal file
View File

@ -0,0 +1,47 @@
var _MessageUtils = {
getContentType: function (message) {
return MessageUtils.getHeader(message, /Content-Type/i);
},
get_first_header: function (message, regex) {
//FIXME: Cache Invalidation.
if (!message._headerLookups)
Object.defineProperty(message, "_headerLookups", {
value: {},
configurable: false,
enumerable: false,
writable: false
});
if (!(regex in message._headerLookups)) {
var header;
for (var i = 0; i < message.headers.length; i++) {
if (!!message.headers[i][0].match(regex)) {
header = message.headers[i];
break;
}
}
message._headerLookups[regex] = header ? header[1] : undefined;
}
return message._headerLookups[regex];
}
};
var defaultPorts = {
"http": 80,
"https": 443
};
var RequestUtils = _.extend(_MessageUtils, {
pretty_host: function (request) {
//FIXME: Add hostheader
return request.host;
},
pretty_url: function (request) {
var port = "";
if (defaultPorts[request.scheme] !== request.port) {
port = ":" + request.port;
}
return request.scheme + "://" + this.pretty_host(request) + port + request.path;
}
});
var ResponseUtils = _.extend(_MessageUtils, {});

View File

@ -13,11 +13,11 @@ var AutoScrollMixin = {
};
var StickyHeadMixin = {
adjustHead: function(){
adjustHead: function () {
// Abusing CSS transforms to set the element
// referenced as head into some kind of position:sticky.
var head = this.refs.head.getDOMNode();
head.style.transform = "translate(0,"+this.getDOMNode().scrollTop+"px)";
head.style.transform = "translate(0," + this.getDOMNode().scrollTop + "px)";
}
};
@ -30,4 +30,13 @@ var Key = {
RIGHT: 39,
ENTER: 13,
ESC: 27
};
var formatSize = function (size) {
var prefix = ["B", "KB", "MB", "GB", "TB"];
while (size >= 1024 && prefix.length > 1) {
prefix.shift();
size = size / 1024;
}
return (Math.floor(size * 100) / 100.0) + prefix.shift();
};