Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

New version that is starting to get useful.

  • Loading branch information...
commit ae4fbcc7e29ee23f0f1173408fffeab285e4bff5 1 parent 6953b67
Matt Ranney authored
Showing with 256 additions and 187 deletions.
  1. +16 −24 graph_server.js
  2. +178 −135 grapher.js
  3. +62 −28 index.html
40 graph_server.js
View
@@ -8,7 +8,7 @@ var node = {
pcap: require('pcap')
},
ws_start_byte, ws_stop_byte, ws_server, ws_waiters = [],
- pcap_session, dns_cache, tcp_tracker, http_server;
+ pcap_session, tcp_tracker, http_server;
function upgrade_client(request, socket, key_3) {
try {
@@ -70,6 +70,7 @@ function upgrade_client(request, socket, key_3) {
].join('\r\n'), 'utf8');
}
+ socket.setTimeout(60 * 60 * 1000); // allow WS sockets to hang out for an hour
console.log(request.connection.remoteAddress + " WebSocket upgrade");
ws_waiters.push(socket);
} catch (err) {
@@ -230,12 +231,11 @@ server.listen(80);
pcap_session = node.pcap.createSession(process.argv[2], process.argv[3]);
tcp_tracker = new node.pcap.TCP_tracker();
-dns_cache = node.pcap.dns_cache;
tcp_tracker.on('reverse', function (name, value) {
if (value) {
send_to_waiters({
- event: "reverse_map",
+ event: "reverse map",
name: name,
value: value
});
@@ -244,7 +244,7 @@ tcp_tracker.on('reverse', function (name, value) {
}
});
-tcp_tracker.on('http_request', function (session, http) {
+tcp_tracker.on('http request', function (session, http) {
if (session.http_request_count) {
session.http_request_count += 1;
} else {
@@ -252,7 +252,7 @@ tcp_tracker.on('http_request', function (session, http) {
}
send_to_waiters({
- event: "http_request",
+ event: "http request",
key: session.key,
method: http.request.method,
url: http.request.url,
@@ -260,56 +260,48 @@ tcp_tracker.on('http_request', function (session, http) {
});
});
-tcp_tracker.on('http_request_body', function (session, http, data) {
+tcp_tracker.on('http request complete', function (session, http, data) {
send_to_waiters({
- event: "http_request_body",
- key: session.key,
- data_length: data.length
- });
-});
-
-tcp_tracker.on('http_request_complete', function (session, http, data) {
- send_to_waiters({
- event: "http_request_complete",
+ event: "http request complete",
key: session.key,
body_len: http.request.body_len
});
});
-tcp_tracker.on('http_response', function (session, http) {
+tcp_tracker.on('http response', function (session, http) {
send_to_waiters({
- event: "http_response",
+ event: "http response",
key: session.key,
status_code: http.response.status_code,
headers: http.response.headers
});
});
-tcp_tracker.on('http_response_body', function (session, http, data) {
+tcp_tracker.on('http response body', function (session, http, data) {
send_to_waiters({
- event: "http_response_body",
+ event: "http response body",
key: session.key,
data_length: data.length
});
});
-tcp_tracker.on('http_response_complete', function (session, http, data) {
+tcp_tracker.on('http response complete', function (session, http, data) {
send_to_waiters({
- event: "http_response_complete",
+ event: "http response complete",
key: session.key,
body_len: http.response.body_len
});
});
-tcp_tracker.on('websocket_upgrade', function (session, http) {
+tcp_tracker.on('websocket upgrade', function (session, http) {
send_to_waiters({
- event: "websocket_upgrade",
+ event: "websocket upgrade",
key: session.key,
headers: http.response.headers
});
});
-tcp_tracker.on('websocket_message', function (session, dir, message) {
+tcp_tracker.on('websocket message', function (session, dir, message) {
// var message_obj = JSON.parse(message), key_parts, new_message;
//
// console.log("considering: " + message);
313 grapher.js
View
@@ -1,12 +1,77 @@
-var log_elem = document.getElementById('log'),
- socket = new WebSocket("ws://" + window.location.host),
- dns_cache = {}, sessions = {};
+/*global WebSocket window */
+"use strict";
-socket.addEventListener('open', function (event) {
- console.log("WS open");
- log_elem.style.background = "rgb(128,255,128)";
- log_elem.innerText = 'WebSocket Connected';
-});
+var log_elem = document.getElementById('log'), socket,
+ dns_cache = {}, sessions = {}, status_lookup;
+
+if (typeof WebSocket === 'object') {
+ log_elem.innerText = "Connecting to WebSocket server...";
+ socket = new WebSocket("ws://" + window.location.host);
+} else {
+ alert("No WebSocket support in this browser, sorry.");
+}
+
+status_lookup = (function () {
+ // from node.js, lib/http.js
+ var STATUS_CODES = {
+ 100 : 'Continue',
+ 101 : 'Switching Protocols',
+ 102 : 'Processing', // RFC 2518, obsoleted by RFC 4918
+ 200 : 'OK',
+ 201 : 'Created',
+ 202 : 'Accepted',
+ 203 : 'Non-Authoritative Information',
+ 204 : 'No Content',
+ 205 : 'Reset Content',
+ 206 : 'Partial Content',
+ 207 : 'Multi-Status', // RFC 4918
+ 300 : 'Multiple Choices',
+ 301 : 'Moved Permanently',
+ 302 : 'Moved Temporarily',
+ 303 : 'See Other',
+ 304 : 'Not Modified',
+ 305 : 'Use Proxy',
+ 307 : 'Temporary Redirect',
+ 400 : 'Bad Request',
+ 401 : 'Unauthorized',
+ 402 : 'Payment Required',
+ 403 : 'Forbidden',
+ 404 : 'Not Found',
+ 405 : 'Method Not Allowed',
+ 406 : 'Not Acceptable',
+ 407 : 'Proxy Authentication Required',
+ 408 : 'Request Time-out',
+ 409 : 'Conflict',
+ 410 : 'Gone',
+ 411 : 'Length Required',
+ 412 : 'Precondition Failed',
+ 413 : 'Request Entity Too Large',
+ 414 : 'Request-URI Too Large',
+ 415 : 'Unsupported Media Type',
+ 416 : 'Requested Range Not Satisfiable',
+ 417 : 'Expectation Failed',
+ 418 : 'I\'m a teapot', // RFC 2324
+ 422 : 'Unprocessable Entity', // RFC 4918
+ 423 : 'Locked', // RFC 4918
+ 424 : 'Failed Dependency', // RFC 4918
+ 425 : 'Unordered Collection', // RFC 4918
+ 426 : 'Upgrade Required', // RFC 2817
+ 500 : 'Internal Server Error',
+ 501 : 'Not Implemented',
+ 502 : 'Bad Gateway',
+ 503 : 'Service Unavailable',
+ 504 : 'Gateway Time-out',
+ 505 : 'HTTP Version not supported',
+ 506 : 'Variant Also Negotiates', // RFC 2295
+ 507 : 'Insufficient Storage', // RFC 4918
+ 509 : 'Bandwidth Limit Exceeded',
+ 510 : 'Not Extended' // RFC 2774
+ };
+
+ return function (code) {
+ return STATUS_CODES[code];
+ };
+})();
function parse_key(key) {
var addr_pairs = key.split('-', 2);
@@ -16,39 +81,6 @@ function parse_key(key) {
};
}
-function new_session(obj) {
- var elem = document.createElement('div'),
- addrs = parse_key(obj.key);
-
- sessions[obj.key] = {
- method: obj.method,
- url: obj.url,
- request_headers: obj.headers,
- elem: elem
- }
-
- elem.className = "session";
- elem.innerHTML = "<span class=\"address\">" + addrs.src + "</span><span class=\"method\">" + obj.method + " " + obj.url + "</span><br />" +
- "<div class=\"headers\">" +
- Object.keys(obj.headers).map(function (k) {
- return k + ": " + obj.headers[k];
- }).join("<br />") +
- "</div>";
-
- document.getElementById('sessions').appendChild(elem);
-}
-
-function add_response(obj) {
- var session = sessions[obj.key];
-
- session.elem.innerHTML += "<span class=\"response\">" + obj.status_code + "</span>" +
- "<div class=\"headers\">" +
- Object.keys(obj.headers).map(function (k) {
- return k + ": " + obj.headers[k];
- }).join("<br />") +
- "</div>";
-}
-
function update_response_body(obj) {
var session = sessions[obj.key];
@@ -57,6 +89,7 @@ function update_response_body(obj) {
} else {
session.elem.innerHTML += "<span class=\"body_chunk\">" + obj.data_length + "B<span>";
}
+}
function dns_lookup(addr) {
var parts = addr.split(':', 2);
@@ -67,7 +100,7 @@ function dns_lookup(addr) {
}
function new_request(obj) {
- var addrs = parse_key(obj.key), requests, session_elem, request_elem, row, col1, col2;
+ var addrs = parse_key(obj.key), requests, session_elem, session, tmp, new_request_elem, row, col1, col2;
if (! sessions[obj.key]) {
session_elem = document.createElement('table');
@@ -75,30 +108,31 @@ function new_request(obj) {
row = session_elem.insertRow(0);
col1 = row.insertCell(0);
col2 = row.insertCell(1);
- col1.className = "src_addr";
+ col1.className = "src_col";
col1.innerHTML = dns_lookup(addrs.src) || addrs.src;
- col2.className = "requests";
- document.getElementById('sessions').appendChild(session_elem);
+ col2.className = "req_col";
+ tmp = document.getElementById('sessions');
+ tmp.insertBefore(session_elem, tmp.firstChild);
sessions[obj.key] = {
requests: [],
- elem: session_elem
- }
- } else {
- session_elem = sessions[obj.key].elem;
+ request_container: col2
+ };
}
- requests = sessions[obj.key].requests;
+ session = sessions[obj.key];
- request_elem = document.createElement('div');
- request_elem.className = "request start";
- request_elem.innerHTML = obj.method + " " + obj.url + " " + (obj.headers["User-Agent"] || obj.headers["Upgrade"] || "");
+ new_request_elem = document.createElement('div');
+ new_request_elem.className = "request start";
+ new_request_elem.innerHTML = '<div class="url">' + obj.method + " " + obj.url + '</div>' +
+ '<div class="headers">Host: ' + obj.headers["Host"] + '</div>' +
+ '<div class="headers">' + (obj.headers["User-Agent"] || obj.headers.Upgrade || "") + '</div>';
- session_elem.getElementsByClassName('requests')[0].appendChild(request_elem);
+ session.request_container.appendChild(new_request_elem);
- requests.push({
+ session.requests.push({
url: obj.url,
method: obj.method,
req_headers: obj.headers,
- elem: request_elem
+ elem: new_request_elem
});
}
@@ -120,52 +154,61 @@ function request_complete(obj) {
}
function response_start(obj) {
- var addrs = parse_key(obj.key),
- session = sessions[obj.key],
- response_elem;
+ var session = sessions[obj.key],
+ request = session.requests[session.requests.length - 1];
if (typeof session !== 'object') {
console.log("Couldn't find session " + obj.key + " in list, ignoring.");
return;
}
- session.requests[session.requests.length - 1].elem.className = "request";
-
- response_elem = document.createElement('div');
- response_elem.className = "response start";
- response_elem.innerHTML = obj.status_code + " " + obj.headers['Content-Type'];
-
- session.requests[session.requests.length - 1].elem.parentElement.appendChild(response_elem);
+ request.elem.innerHTML += '<div class="response_code">' + obj.status_code + " " + status_lookup(obj.status_code) + '</div>';
+ if (obj.status_code === 304 || obj.status_code == 204 || obj.status_code === 302 || request.method === "HEAD") {
+ request.elem.className = "request";
+ request.response_bytes = 0;
+ request.response_elem = document.createElement('DIV');
+ request.response_elem.className = "response_status";
+ request.response_elem.innerText = "No body";
+ } else {
+ request.elem.className = "response start";
+ request.response_bytes = 0;
+ request.response_elem = document.createElement('DIV');
+ request.response_elem.className = "response_status";
+ request.response_elem.innerText = "0 Bytes starting";
+ }
+ request.elem.appendChild(request.response_elem);
}
function response_body_chunk(obj) {
- var addrs = parse_key(obj.key),
- session = sessions[obj.key],
- response_elem;
+ var session = sessions[obj.key],
+ request = session.requests[session.requests.length - 1];
if (typeof session !== 'object') {
console.log("Couldn't find session " + obj.key + " in list, ignoring.");
return;
}
- response_elem = session.requests[session.requests.length - 1].elem.nextSibling;
- response_elem.className = "response data";
- response_elem.innerHTML += " [data " + obj.data_length + "] ";
+ request.elem.className = "response data";
+ request.response_bytes += obj.data_length;
+ if (request.response_elem) {
+ request.response_elem.innerText = request.response_bytes + " Bytes so far";
+ }
}
function response_complete(obj) {
- var addrs = parse_key(obj.key),
- session = sessions[obj.key],
- response_elem;
+ var session = sessions[obj.key],
+ request = session.requests[session.requests.length - 1];
if (typeof session !== 'object') {
console.log("Couldn't find session " + obj.key + " in list, ignoring.");
return;
}
- response_elem = session.requests[session.requests.length - 1].elem.nextSibling;
- response_elem.className = "response";
- response_elem.innerHTML += " complete";
+ request.elem.className = "response";
+ request.response_bytes = obj.body_len;
+ if (request.response_elem) {
+ request.response_elem.innerText = request.response_bytes + " Bytes complete";
+ }
}
function reverse_map(obj) {
@@ -175,7 +218,7 @@ function reverse_map(obj) {
function websocket_start(obj) {
var addrs = parse_key(obj.key),
- session = sessions[obj.key], elem;
+ session = sessions[obj.key], elem, response_elem;
if (typeof session !== 'object') {
console.log("Couldn't find session " + obj.key + " in list, ignoring.");
@@ -197,65 +240,65 @@ function websocket_start(obj) {
session.requests[session.requests.length - 1].elem.parentElement.appendChild(response_elem);
}
-socket.addEventListener('message', function (event) {
- var obj;
+if (socket) {
+ socket.addEventListener('open', function (event) {
+ console.log("WS open");
+ log_elem.style.background = "rgb(128,255,128)";
+ log_elem.innerText = 'WebSocket Connected';
+ });
- try {
- obj = JSON.parse(event.data);
- } catch (err) {
- log_elem.innerText = "Error parsing JSON response";
- }
+ socket.addEventListener('message', function (event) {
+ var obj;
+
+ try {
+ obj = JSON.parse(event.data);
+ } catch (err) {
+ log_elem.innerText = "Error parsing JSON response";
+ }
- try {
- switch (obj.event) {
- case "http_request":
- new_session(obj);
- new_request(obj);
- break;
- case "http_request_body":
- break;
- case "http_request_complete":
- request_complete(obj);
- break;
- case "http_response":
- add_response(obj);
- break;
- case "http_response_body":
- update_response_body(obj);
- response_start(obj);
- break;
- case "http_response_body":
- response_body_chunk(obj);
- break;
- case "http_response_complete":
- response_complete(obj);
- break;
- case "reverse_map":
- reverse_map(obj);
- break;
- case "websocket_upgrade":
- websocket_start(obj);
- break;
- default:
- console.log("Don't know how to handle event type " + obj.event);
+ try {
+ switch (obj.event) {
+ case "http request":
+ new_request(obj);
+ break;
+ case "http request complete":
+ request_complete(obj);
+ break;
+ case "http response":
+ response_start(obj);
+ break;
+ case "http response body":
+ response_body_chunk(obj);
+ break;
+ case "http response complete":
+ response_complete(obj);
+ break;
+ case "reverse map":
+ reverse_map(obj);
+ break;
+ case "websocket upgrade":
+ websocket_start(obj);
+ break;
+ default:
+ console.log("Don't know how to handle event type " + obj.event);
+ }
+ } catch (err) {
+ log_elem.innerText = "Error dispatching event: " + err;
+ throw err;
}
- } catch (err) {
- log_elem.innerText = "Error dispatching event: " + err;
- throw err;
- }
-});
-
-socket.addEventListener('close', function (event) {
- console.log("WS close");
- log_elem.style.background = "rgb(170,170,170)";
- log_elem.innerText = 'WebSocket closed';
+ });
- console.log(event);
-});
+ socket.addEventListener('close', function (event) {
+ console.log("WS close");
+ log_elem.style.background = "rgb(170,170,170)";
+ log_elem.innerText = 'WebSocket closed';
-socket.addEventListener('error', function (event) {
- console.log("WS error");
- log_elem.style.background = "rgb(255,128,128)";
- log_elem.innerText = 'WebSocket error';
-});
+ console.log(event);
+ });
+ socket.addEventListener('error', function (event) {
+ console.log("WS error");
+ log_elem.style.background = "rgb(255,128,128)";
+ log_elem.innerText = 'WebSocket error';
+ });
+}
90 index.html
View
@@ -4,25 +4,38 @@
<title>Traffic Graph</title>
<style>
body {
- font-family: "ff-tisa-web-pro-1","ff-tisa-web-pro-2", sans-serif;
- font-size: 14px;
+ font-family: "Museo Sans", Helvetica, sans-serif;
+ margin: 0;
+ padding: 0;
+ }
+
+ #title_text {
+ position: absolute;
+ top: 4px;
+ left: 6px;
+ font-weight: bold;
+ font-size: 22px;
}
#sessions {
- width: 100%;
- overflow-x: hidden;
- font-family: Helvetica, sans-serif;
+ position: absolute;
+ top: 40px;
+ bottom: 0px;
+ left: 4px;
+ right: 4px;
+ overflow: hidden;
+ font-family: "Museo Sans", Helvetica, sans-serif;
font-size: 12px;
}
.session {
width: 100%;
- overflow-x: hidden;
+ word-wrap: break-word;
border: 1px solid black;
background: rgb(240,240,240);
margin-bottom: 5px;
}
-
+
.address {
width: 300px;
margin-right: 10px;
@@ -30,27 +43,53 @@
.headers {
margin-left: 10px;
+ font-size: 12px;
}
.body_chunk {
margin-right: 5px;
+ }
+ .src_col {
+ width: 150px;
+ min-width: 150px;
+ overflow-x: hidden;
+ vertical-align: top;
+ text-align: left;
+ }
+
+ .req_col {
+ text-align: left;
+ vertical-align: top;
+ }
#log {
-webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ font-size: 14px;
padding: 5px;
- width: 90%
- height: 20px;
+ position: absolute;
+ top: 4px;
+ right: 4px;
+ height: 17px;
text-align: center;
background: rgb(128,128,128);
border: 1px solid black;
+ }
+
+ .url {
+ font-weight: bold;
+ }
+
+ .response_status {
+ font-size: 12px;
+ margin-left: 10px;
margin-bottom: 5px;
}
- .src_addr {
- width: 150px;
- overflow-x: hidden;
- vertical-align: top;
+ .request {
+ -webkit-transition-property: background;
+ -webkit-transition-duration: 1s;
}
.request.start {
@@ -62,7 +101,12 @@
}
.response {
- margin-bottom: 5px;
+ -webkit-transition-property: background;
+ -webkit-transition-duration: .8s;
+ }
+
+ .response_code {
+ font-weight: bold;
}
.response.start {
@@ -76,24 +120,14 @@
.websocket.start {
background: rgb(200,240,128);
}
-
- #sessions {
- overflow-x: hidden;
- width: 100%;
- }
-
- .session {
- width: 100%;
- margin: 5px;
- }
</style>
- <script type="text/javascript" src="http://use.typekit.com/pje2hfb.js"></script>
- <script type="text/javascript">try{Typekit.load();}catch(e){console.log("TypeKit error: " + e)}</script>
+ <!-- <script type="text/javascript" src="http://use.typekit.com/pje2hfb.js"></script>
+ <script type="text/javascript">try{Typekit.load();}catch(e){console.log("TypeKit error: " + e)}</script> -->
</head>
<body>
-
-<div id="log">Connecting to WebSocket server...</div>
+<div id="title_text">Live Traffic Stats by Packet</div>
+<div id="log">Checking for WebSocket support...</div>
<div id="sessions"></div>
Please sign in to comment.
Something went wrong with that request. Please try again.