aboutsummaryrefslogtreecommitdiffstats
path: root/web
diff options
context:
space:
mode:
authorMaximilian Hils <git@maximilianhils.com>2014-09-17 21:14:55 +0200
committerMaximilian Hils <git@maximilianhils.com>2014-09-17 21:14:55 +0200
commit4ca720b55680e40b3a4361141a2ad39f9de81111 (patch)
treeec50f6a42cd4f1cda7e2f3f6026359adc198ec67 /web
parent102bd075689892b06765fb857c89604fe9cf33e5 (diff)
downloadmitmproxy-4ca720b55680e40b3a4361141a2ad39f9de81111.tar.gz
mitmproxy-4ca720b55680e40b3a4361141a2ad39f9de81111.tar.bz2
mitmproxy-4ca720b55680e40b3a4361141a2ad39f9de81111.zip
add features to the traffic table, introduce image spriting
Diffstat (limited to 'web')
-rw-r--r--web/gulpfile.js30
-rw-r--r--web/package.json3
-rw-r--r--web/src/css/app.less1
-rw-r--r--web/src/css/flowtable.less29
-rw-r--r--web/src/css/sprites.compiled.less58
-rw-r--r--web/src/css/sprites.less38
-rw-r--r--web/src/images/chrome-devtools/LICENSE30
-rw-r--r--web/src/images/chrome-devtools/resourceCSSIcon.pngbin0 -> 1005 bytes
-rw-r--r--web/src/images/chrome-devtools/resourceDocumentIcon.pngbin0 -> 951 bytes
-rw-r--r--web/src/images/chrome-devtools/resourceJSIcon.pngbin0 -> 787 bytes
-rw-r--r--web/src/images/chrome-devtools/resourcePlainIcon.pngbin0 -> 295 bytes
-rw-r--r--web/src/images/resourceExecutableIcon.pngbin0 -> 853 bytes
-rw-r--r--web/src/images/resourceFlashIcon.pngbin0 -> 921 bytes
-rw-r--r--web/src/images/resourceImageIcon.pngbin0 -> 976 bytes
-rw-r--r--web/src/images/resourceJavaIcon.pngbin0 -> 861 bytes
-rw-r--r--web/src/images/resourceNotModifiedIcon.pngbin0 -> 1072 bytes
-rw-r--r--web/src/images/resourceRedirectIcon.pngbin0 -> 1174 bytes
-rw-r--r--web/src/js/components/flowtable.jsx63
-rw-r--r--web/src/js/stores/flowstore.js15
19 files changed, 247 insertions, 20 deletions
diff --git a/web/gulpfile.js b/web/gulpfile.js
index f34bc4a8..c1ca8e67 100644
--- a/web/gulpfile.js
+++ b/web/gulpfile.js
@@ -1,7 +1,7 @@
var gulp = require("gulp");
+var merge = require('merge-stream');
var concat = require('gulp-concat');
-var gutil = require('gulp-util');
var jshint = require("gulp-jshint");
var less = require("gulp-less");
var livereload = require("gulp-livereload");
@@ -10,7 +10,9 @@ var notify = require("gulp-notify");
var plumber = require("gulp-plumber");
var qunit = require("gulp-qunit");
var react = require("gulp-react");
+var rename = require("gulp-rename");
var sourcemaps = require('gulp-sourcemaps');
+var sprite = require('gulp-sprite-generator');
var uglify = require('gulp-uglify');
@@ -50,10 +52,12 @@ var path = {
},
css: {
vendor: ["css/vendor.less"],
- app: ["css/app.less"]
+ app: ["css/app.less"],
+ spritefile: "css/sprites.less"
},
fonts: ["src/vendor/fontawesome/fontawesome-webfont.*"],
- html: ["src/*.html", "!src/benchmark.html", "!src/test.html"]
+ html: ["src/*.html", "!src/benchmark.html", "!src/test.html"],
+ images: "src/images",
};
@@ -124,9 +128,24 @@ gulp.task("jshint", function () {
.pipe(dont_break_on_errors())
.pipe(react())
.pipe(jshint())
- .pipe(jshint.reporter("jshint-stylish"))
+ .pipe(jshint.reporter("jshint-stylish"));
});
+gulp.task("sprites", function () {
+ // Sprite generator is a gulp task, which accepts options object and
+ // returns two streams for style and image piping.
+ var spriteOutput = gulp.src([path.css.spritefile], {base: "src", cwd: "src"})
+ .pipe(sprite({
+ spriteSheetName: "sprite.png",
+ spriteSheetPath: "../images",
+ }));
+ var css = spriteOutput.css
+ .pipe(rename({extname:".compiled.less"}))
+ .pipe(gulp.dest("src/css"));
+ var img = spriteOutput.img.pipe(gulp.dest(path.dist + "static/images"));
+ // https://github.com/gulpjs/gulp/blob/master/docs/recipes/using-multiple-sources-in-one-task.md
+ return merge(css, img);
+});
gulp.task("html", function () {
return gulp.src(path.html)
@@ -141,7 +160,7 @@ gulp.task('test', function() {
});
-common = ["fonts", "html", "jshint"];
+common = ["fonts", "html", "jshint", "sprites"];
gulp.task("dev", common.concat(["styles-dev", "scripts-dev"]));
gulp.task("prod", common.concat(["styles-prod", "scripts-prod"]));
@@ -150,5 +169,6 @@ gulp.task("default", ["dev"], function () {
gulp.watch(["src/vendor/**"], ["scripts-vendor-dev", "styles-vendor-dev"]);
gulp.watch(["src/js/**"], ["scripts-app-dev", "jshint"]);
gulp.watch(["src/css/**"], ["styles-app-dev"]);
+ gulp.watch(["src/images/**", "src/css/sprites.less"], ["sprites"]);
gulp.watch(["src/*.html"], ["html"]);
});
diff --git a/web/package.json b/web/package.json
index 6adb6687..de90587f 100644
--- a/web/package.json
+++ b/web/package.json
@@ -15,10 +15,13 @@
"gulp-plumber": "^0.6.5",
"gulp-qunit": "^0.3.3",
"gulp-react": "^1.0.1",
+ "gulp-rename": "^1.2.0",
"gulp-sourcemaps": "^1.1.5",
+ "gulp-sprite-generator": "^0.2.0",
"gulp-uglify": "^1.0.1",
"gulp-util": "^3.0.1",
"jshint-stylish": "^0.4.0",
+ "merge-stream": "^0.1.5",
"react": "",
"react-tools": ""
}
diff --git a/web/src/css/app.less b/web/src/css/app.less
index 1eec0687..39ac14cd 100644
--- a/web/src/css/app.less
+++ b/web/src/css/app.less
@@ -7,6 +7,7 @@ html {
box-sizing: inherit;
}
+@import (less) "sprites.compiled.less";
@import (less) "layout.less";
@import (less) "header.less";
@import (less) "flowtable.less";
diff --git a/web/src/css/flowtable.less b/web/src/css/flowtable.less
index 95f235f4..deef9c81 100644
--- a/web/src/css/flowtable.less
+++ b/web/src/css/flowtable.less
@@ -1,5 +1,34 @@
.flow-table {
width: 100%;
+ table-layout: fixed;
+ thead {
+ background-color: #dadada;
+ }
+ td {
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ }
+
+
+ .col-tls {
+ width: 10px;
+ }
+ .col-tls-https {
+ background-color: rgba(0, 185, 0, 0.5);
+ }
+ .col-icon {
+ width: 32px;
+ }
+ .col-method {
+ width: 60px;
+ }
+ .col-status {
+ width: 50px;
+ }
+ .col-time {
+ width: 120px;
+ }
} \ No newline at end of file
diff --git a/web/src/css/sprites.compiled.less b/web/src/css/sprites.compiled.less
new file mode 100644
index 00000000..27951ee5
--- /dev/null
+++ b/web/src/css/sprites.compiled.less
@@ -0,0 +1,58 @@
+.resource-icon {
+ width: 32px;
+ height: 32px;
+}
+
+// From Chrome Dev Tools
+.resource-icon-css {
+ background-image: url("../images/sprite.png");
+ background-position: -0px -0px;
+ background-size: 32px 320px!important;
+}
+.resource-icon-document {
+ background-image: url("../images/sprite.png");
+ background-position: -0px -32px;
+ background-size: 32px 320px!important;
+}
+.resource-icon-js {
+ background-image: url("../images/sprite.png");
+ background-position: -0px -64px;
+ background-size: 32px 320px!important;
+}
+.resource-icon-plain {
+ background-image: url("../images/sprite.png");
+ background-position: -0px -96px;
+ background-size: 32px 320px!important;
+}
+
+// Own
+.resource-icon-executable {
+ background-image: url("../images/sprite.png");
+ background-position: -0px -128px;
+ background-size: 32px 320px!important;
+}
+.resource-icon-flash {
+ background-image: url("../images/sprite.png");
+ background-position: -0px -160px;
+ background-size: 32px 320px!important;
+}
+.resource-icon-image {
+ background-image: url("../images/sprite.png");
+ background-position: -0px -192px;
+ background-size: 32px 320px!important;
+}
+.resource-icon-java {
+ background-image: url("../images/sprite.png");
+ background-position: -0px -224px;
+ background-size: 32px 320px!important;
+}
+.resource-icon-not-modified {
+ background-image: url("../images/sprite.png");
+ background-position: -0px -256px;
+ background-size: 32px 320px!important;
+}
+.resource-icon-redirect {
+ background-image: url("../images/sprite.png");
+ background-position: -0px -288px;
+ background-size: 32px 320px!important;
+} \ No newline at end of file
diff --git a/web/src/css/sprites.less b/web/src/css/sprites.less
new file mode 100644
index 00000000..9a63fdd3
--- /dev/null
+++ b/web/src/css/sprites.less
@@ -0,0 +1,38 @@
+.resource-icon {
+ width: 32px;
+ height: 32px;
+}
+
+// From Chrome Dev Tools
+.resource-icon-css {
+ background-image: url(../images/chrome-devtools/resourceCSSIcon.png);
+}
+.resource-icon-document {
+ background-image: url(../images/chrome-devtools/resourceDocumentIcon.png);
+}
+.resource-icon-js {
+ background-image: url(../images/chrome-devtools/resourceJSIcon.png);
+}
+.resource-icon-plain {
+ background-image: url(../images/chrome-devtools/resourcePlainIcon.png);
+}
+
+// Own
+.resource-icon-executable {
+ background-image: url(../images/resourceExecutableIcon.png);
+}
+.resource-icon-flash {
+ background-image: url(../images/resourceFlashIcon.png);
+}
+.resource-icon-image {
+ background-image: url(../images/resourceImageIcon.png);
+}
+.resource-icon-java {
+ background-image: url(../images/resourceJavaIcon.png);
+}
+.resource-icon-not-modified {
+ background-image: url(../images/resourceNotModifiedIcon.png);
+}
+.resource-icon-redirect {
+ background-image: url(../images/resourceRedirectIcon.png);
+} \ No newline at end of file
diff --git a/web/src/images/chrome-devtools/LICENSE b/web/src/images/chrome-devtools/LICENSE
new file mode 100644
index 00000000..6e4f8b9f
--- /dev/null
+++ b/web/src/images/chrome-devtools/LICENSE
@@ -0,0 +1,30 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+//
+// The Chromium Authors can be found at
+// http://src.chromium.org/svn/trunk/src/AUTHORS
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file
diff --git a/web/src/images/chrome-devtools/resourceCSSIcon.png b/web/src/images/chrome-devtools/resourceCSSIcon.png
new file mode 100644
index 00000000..18828d06
--- /dev/null
+++ b/web/src/images/chrome-devtools/resourceCSSIcon.png
Binary files differ
diff --git a/web/src/images/chrome-devtools/resourceDocumentIcon.png b/web/src/images/chrome-devtools/resourceDocumentIcon.png
new file mode 100644
index 00000000..fdc10e47
--- /dev/null
+++ b/web/src/images/chrome-devtools/resourceDocumentIcon.png
Binary files differ
diff --git a/web/src/images/chrome-devtools/resourceJSIcon.png b/web/src/images/chrome-devtools/resourceJSIcon.png
new file mode 100644
index 00000000..c1b72189
--- /dev/null
+++ b/web/src/images/chrome-devtools/resourceJSIcon.png
Binary files differ
diff --git a/web/src/images/chrome-devtools/resourcePlainIcon.png b/web/src/images/chrome-devtools/resourcePlainIcon.png
new file mode 100644
index 00000000..8c82a4c7
--- /dev/null
+++ b/web/src/images/chrome-devtools/resourcePlainIcon.png
Binary files differ
diff --git a/web/src/images/resourceExecutableIcon.png b/web/src/images/resourceExecutableIcon.png
new file mode 100644
index 00000000..fa70c2fd
--- /dev/null
+++ b/web/src/images/resourceExecutableIcon.png
Binary files differ
diff --git a/web/src/images/resourceFlashIcon.png b/web/src/images/resourceFlashIcon.png
new file mode 100644
index 00000000..ead5a4d0
--- /dev/null
+++ b/web/src/images/resourceFlashIcon.png
Binary files differ
diff --git a/web/src/images/resourceImageIcon.png b/web/src/images/resourceImageIcon.png
new file mode 100644
index 00000000..23163042
--- /dev/null
+++ b/web/src/images/resourceImageIcon.png
Binary files differ
diff --git a/web/src/images/resourceJavaIcon.png b/web/src/images/resourceJavaIcon.png
new file mode 100644
index 00000000..553b3391
--- /dev/null
+++ b/web/src/images/resourceJavaIcon.png
Binary files differ
diff --git a/web/src/images/resourceNotModifiedIcon.png b/web/src/images/resourceNotModifiedIcon.png
new file mode 100644
index 00000000..9c6a879d
--- /dev/null
+++ b/web/src/images/resourceNotModifiedIcon.png
Binary files differ
diff --git a/web/src/images/resourceRedirectIcon.png b/web/src/images/resourceRedirectIcon.png
new file mode 100644
index 00000000..58fe3ac1
--- /dev/null
+++ b/web/src/images/resourceRedirectIcon.png
Binary files differ
diff --git a/web/src/js/components/flowtable.jsx b/web/src/js/components/flowtable.jsx
index 5e9f6718..a94e559f 100644
--- a/web/src/js/components/flowtable.jsx
+++ b/web/src/js/components/flowtable.jsx
@@ -4,7 +4,10 @@ var FlowRow = React.createClass({
render: function(){
var flow = this.props.flow;
var columns = this.props.columns.map(function(column){
- return column({flow: flow});
+ return column({
+ key: column.displayName,
+ flow: flow
+ });
}.bind(this));
return <tr>{columns}</tr>;
}
@@ -22,55 +25,89 @@ var FlowTableHead = React.createClass({
var FlowTableBody = React.createClass({
render: function(){
var rows = this.props.flows.map(function(flow){
- return <FlowRow flow={flow} columns={this.props.columns}/>
+ //TODO: Add UUID
+ return <FlowRow flow={flow} columns={this.props.columns}/>;
}.bind(this));
return <tbody>{rows}</tbody>;
}
});
+
+var TLSColumn = React.createClass({
+ statics: {
+ renderTitle: function(){
+ return <th key="tls" className="col-tls"></th>;
+ }
+ },
+ render: function(){
+ var flow = this.props.flow;
+ var ssl = (flow.request.scheme == "https");
+ return <td className={ssl ? "col-tls-https" : "col-tls-http"}></td>;
+ }
+});
+
+
+var IconColumn = React.createClass({
+ statics: {
+ renderTitle: function(){
+ return <th key="icon" className="col-icon"></th>;
+ }
+ },
+ render: function(){
+ var flow = this.props.flow;
+ return <td className="resource-icon resource-icon-plain"></td>;
+ }
+});
+
var PathColumn = React.createClass({
statics: {
renderTitle: function(){
- return <th key="PathColumn">Path</th>;
+ return <th key="path" className="col-path">Path</th>;
}
},
render: function(){
var flow = this.props.flow;
- return <td key="PathColumn">{flow.request.scheme + "://" + flow.request.host + flow.request.path}</td>;
+ return <td>{flow.request.scheme + "://" + flow.request.host + flow.request.path}</td>;
}
});
+
+
var MethodColumn = React.createClass({
statics: {
renderTitle: function(){
- return <th key="MethodColumn">Method</th>;
+ return <th key="method" className="col-method">Method</th>;
}
},
render: function(){
var flow = this.props.flow;
- return <td key="MethodColumn">{flow.request.method}</td>;
+ return <td>{flow.request.method}</td>;
}
});
+
+
var StatusColumn = React.createClass({
statics: {
renderTitle: function(){
- return <th key="StatusColumn">Status</th>;
+ return <th key="status" className="col-status">Status</th>;
}
},
render: function(){
var flow = this.props.flow;
var status;
if(flow.response){
- status = flow.response.code + " " + flow.response.msg;
+ status = flow.response.code;
} else {
status = null;
}
- return <td key="StatusColumn">{status}</td>;
+ return <td>{status}</td>;
}
});
+
+
var TimeColumn = React.createClass({
statics: {
renderTitle: function(){
- return <th key="TimeColumn">Time</th>;
+ return <th key="time" className="col-time">Time</th>;
}
},
render: function(){
@@ -81,11 +118,13 @@ var TimeColumn = React.createClass({
} else {
time = "...";
}
- return <td key="TimeColumn">{time}</td>;
+ return <td>{time}</td>;
}
});
-var all_columns = [PathColumn, MethodColumn, StatusColumn, TimeColumn];
+
+var all_columns = [TLSColumn, IconColumn, PathColumn, MethodColumn, StatusColumn, TimeColumn];
+
var FlowTable = React.createClass({
getInitialState: function () {
diff --git a/web/src/js/stores/flowstore.js b/web/src/js/stores/flowstore.js
index 006eeb24..a5cb74ba 100644
--- a/web/src/js/stores/flowstore.js
+++ b/web/src/js/stores/flowstore.js
@@ -30,13 +30,14 @@ _.extend(FlowView.prototype, EventEmitter.prototype, {
var updates = this.flows;
this.flows = flows;
updates.forEach(function(flow){
- this.update(flow);
+ this._update(flow);
}.bind(this));
+ this.emit("change");
},
- update: function(flow){
+ _update: function(flow){
console.debug("FIXME: Use UUID");
var idx = _.findIndex(this.flows, function(f){
- return flow.request.timestamp_start == f.request.timestamp_start
+ return flow.request.timestamp_start == f.request.timestamp_start;
});
if(idx < 0){
@@ -44,6 +45,9 @@ _.extend(FlowView.prototype, EventEmitter.prototype, {
} else {
this.flows[idx] = flow;
}
+ },
+ update: function(flow){
+ this._update(flow);
this.emit("change");
},
});
@@ -55,6 +59,11 @@ function _FlowStore() {
_.extend(_FlowStore.prototype, EventEmitter.prototype, {
getView: function (since) {
var view = new FlowView(this, !since);
+
+ $.getJSON("/static/flows.json", function(flows){
+ view.add_bulk(flows);
+ });
+
return view;
},
handle: function (action) {