Rewrite of server code with Go.

This commit is contained in:
Simon Eisenmann 2014-03-09 16:53:21 +01:00
parent ec371f324e
commit 54db38bac6
11 changed files with 573 additions and 247 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/vendor
/bin

60
Makefile Normal file
View file

@ -0,0 +1,60 @@
PKG := realtimetraffic
EXENAME := realtimetraffic
GOPATH = "$(CURDIR)/vendor:$(CURDIR)"
SYSTEM_GOPATH := /usr/share/gocode/src/
OUTPUT := $(CURDIR)/bin
DESTDIR ?= /
BIN := $(DESTDIR)/usr/sbin
CONF := $(DESTDIR)/$(CONFIG_PATH)
BUILD_ARCH := $(shell go env GOARCH)
DIST := $(CURDIR)/dist_$(BUILD_ARCH)
DIST_SRC := $(DIST)/src
DIST_BIN := $(DIST)/bin
build: get binary
gopath:
@echo GOPATH=$(GOPATH)
get:
GOPATH=$(GOPATH) go get $(PKG)
binary:
GOPATH=$(GOPATH) go build -o $(OUTPUT)/$(EXENAME) -ldflags '$(LDFLAGS)' $(PKG)
binaryrace:
GOPATH=$(GOPATH) go build -race -o $(OUTPUT)/$(EXENAME) -ldflags '$(LDFLAGS)' $(PKG)
fmt:
GOPATH=$(GOPATH) go fmt $(PKG)/...
test: TESTDEPS = $(shell GOPATH=$(GOPATH) go list -f '{{.ImportPath}}{{"\n"}}{{join .Deps "\n"}}' $(PKG) |grep $(PKG))
test: get
GOPATH=$(GOPATH) go test -i $(TESTDEPS)
GOPATH=$(GOPATH) go test -v $(TESTDEPS)
clean:
GOPATH=$(GOPATH) go clean -i $(PKG)
rm -rf $(CURDIR)/pkg
distclean: clean
rm -rf $(DIST)
pristine: distclean
rm -rf vendor/*
$(DIST_SRC):
mkdir -p $@
$(DIST_BIN):
mkdir -p $@
dist_gopath: $(DIST_SRC)
find $(SYSTEM_GOPATH) -mindepth 1 -maxdepth 1 -type d \
-exec ln -sf {} $(DIST_SRC) \;
.PHONY: clean distclean pristine get build gopath binary

View file

@ -8,46 +8,28 @@ Realtime Traffic is a Linux realtime trafic monitoring tool, graphing rx and tx
Dowload the software, either using Git, or grab a [ZIP](https://github.com/longsleep/realtimetraffic/archive/master.zip) and extract it somewhere.
You can use the client right away without installation. Just open the file client/realtimetraffic.html in any modern browser, type in a WebSocket address of a server you started somewhere and press start.
To install the server, make sure you have [Python](http://www.python.org) (2.5, 2.6, 2.7 tested) and [tornado](http://pypi.python.org/pypi/tornado). For Python << 2.6 you also need [simplejson](http://pypi.python.org/pypi/simplejson). Then just startup the server.
On Ubuntu this is simple like this:
Getting started:
$ wget -O rtt.zip https://github.com/longsleep/realtimetraffic/archive/master.zip
$ unzip rtt.zip
$ cd realtimetraffic-master
$ sudo apt-get install python-tornado
$ python trafficserver/trafficserver.py
Server running on 127.0.0.1:8088 (ssl:False) ...
$ make
$ ./bin/realtimetraffic
Now just open up your browser:
$ firefox http://127.0.0.1:8088/?autostart=1
## Getting Started
Startup the traffice server on a Linux machine of your choice.
$ python trafficserver/trafficserver.py
And open up the server's web page (http://yourserver:8088/).
See the usage information (--help) for options.
See the usage information (-h) for options.
## Server usage options
The trafficserver is basically a Websocket server pushing traffic data to any connected client.
```
Usage: trafficserver.py [options]
Options:
-h, --help show this help message and exit
-l LISTEN, --listen=LISTEN
listen address (default: [127.0.0.1:8088])
--ssl_keyfile=FILE SSL key file
--ssl_certfile=FILE SSL certificate file
Usage of ./bin/realtimetraffic:
-client="./client": Path to client directory.
-listen="127.0.0.1:8088": Listen address.
```
## Client parameters
@ -66,7 +48,7 @@ This library was developed by Simon Eisenmann at [struktur AG](http://www.strukt
## License
Copyright (C) 2012 struktur AG
Copyright (C) 2012-2014 struktur AG
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View file

@ -17,10 +17,18 @@
(function() {
var add = function(a, b) {
return a+b;
}
var RealtimeTraffic = function() {
this.tv = 500;
this.tv = 1000;
this.last_data = null;
this.history = {
rx_bytes: [],
tx_bytes: []
};
this.connection = null;
this.initialize();
@ -33,10 +41,10 @@
height: 300,
width: 690,
renderer: 'line',
interpolation: 'linear',
interpolation: 'basis',
series: new Rickshaw.Series.FixedDuration([ { name: 'rx_tx_abs_diff', color: "palegreen" }, { name: 'rx_kbits', color: "mediumslateblue" }, { name: 'tx_kbits', color: "lightcoral" }], undefined, {
timeInterval: this.tv,
maxDataPoints: 200,
maxDataPoints: 100,
timeBase: new Date().getTime() / 1000
})
} );
@ -126,14 +134,22 @@
connection.onmessage = function(e) {
var data = JSON.parse(e.data);
var interface_data = data[interf];
var interface_data = data[data.name];
if (typeof(interface_data) !== "undefined") {
if (that.last_data !== null) {
that.history.rx_bytes.push(interface_data.rx_bytes - that.last_data.rx_bytes);
that.history.tx_bytes.push(interface_data.tx_bytes - that.last_data.tx_bytes);
var d = {
rx_kbits: ((interface_data.rx_bytes - that.last_data.rx_bytes) * 8 / 1024)*2,
tx_kbits: ((interface_data.tx_bytes - that.last_data.tx_bytes) * 8 / 1024)*2
rx_kbits: (that.history.rx_bytes.reduce(add, 0) / that.history.rx_bytes.length) * 8 / 1024,
tx_kbits: (that.history.tx_bytes.reduce(add, 0) / that.history.tx_bytes.length) * 8 / 1024
};
d.rx_tx_abs_diff = Math.abs(d.rx_kbits - d.tx_kbits);
if (that.history.rx_bytes.length > 1) {
that.history.rx_bytes.shift();
}
if (that.history.tx_bytes.length > 1) {
that.history.tx_bytes.shift();
}
setTimeout(function() {
that.graph.series.addData(d);
that.graph.render();

136
src/realtimetraffic/conn.go Normal file
View file

@ -0,0 +1,136 @@
/*
* Copyright (C) 2014 struktur AG
* http://www.strukturag.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file is baeed on ideas from the chat example of gorilla/websocket.
* Copyright 2013 Gary Burd. All rights reserved.
*
* 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.
*
* 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 HOLDER 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.
*/
package main
import (
"github.com/gorilla/websocket"
"log"
"net/http"
"time"
)
const (
writeWait = 10 * time.Second
pongWait = 60 * time.Second
pingPeriod = (pongWait * 9) / 10
maxMessageSize = 512
)
type connection struct {
ws *websocket.Conn
send chan []byte
iface string
}
func (c *connection) readPump() {
defer func() {
h.unregister <- c
c.ws.Close()
}()
c.ws.SetReadLimit(maxMessageSize)
c.ws.SetReadDeadline(time.Now().Add(pongWait))
c.ws.SetPongHandler(func(string) error {
c.ws.SetReadDeadline(time.Now().Add(pongWait))
return nil
})
for {
_, _, err := c.ws.ReadMessage()
if err != nil {
break
}
}
}
func (c *connection) write(mt int, payload []byte) error {
c.ws.SetWriteDeadline(time.Now().Add(writeWait))
return c.ws.WriteMessage(mt, payload)
}
func (c *connection) writePump() {
ticker := time.NewTicker(pingPeriod)
defer func() {
ticker.Stop()
c.ws.Close()
}()
for {
select {
case message, ok := <-c.send:
if !ok {
c.write(websocket.CloseMessage, []byte{})
return
}
if err := c.write(websocket.TextMessage, message); err != nil {
return
}
case <-ticker.C:
if err := c.write(websocket.PingMessage, []byte{}); err != nil {
return
}
}
}
}
func serveWs(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
http.Error(w, "Method not allowed", 405)
return
}
// Read request details.
r.ParseForm()
iface := r.FormValue("if")
if iface == "" {
iface = "eth0"
}
ws, err := websocket.Upgrade(w, r, nil, 1024, 1024)
if _, ok := err.(websocket.HandshakeError); ok {
http.Error(w, "Not a websocket handshake", 400)
return
} else if err != nil {
log.Println(err)
return
}
c := &connection{send: make(chan []byte, 256), ws: ws, iface: iface}
h.register <- c
go c.writePump()
c.readPump()
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (C) 2014 struktur AG
* http://www.strukturag.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"encoding/json"
)
type interfacedata struct {
data interface{}
iface string
}
func (i *interfacedata) set(iface string, data interface{}) {
i.data = map[string]interface{}{
iface: data,
"name": iface,
}
i.iface = iface
}
func (i *interfacedata) encode() ([]byte, error) {
return json.Marshal(i.data)
}

View file

@ -0,0 +1,140 @@
/*
* Copyright (C) 2014 struktur AG
* http://www.strukturag.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"fmt"
"log"
"os"
"path"
"strconv"
"strings"
"time"
)
var grabkeys = []string{
"collisions",
"multicast",
"rx_bytes",
"rx_compressed",
"rx_crc_errors",
"rx_dropped",
"rx_errors",
"rx_fifo_errors",
"rx_frame_errors",
"rx_length_errors",
"rx_missed_errors",
"rx_over_errors",
"rx_packets",
"tx_aborted_errors",
"tx_bytes",
"tx_carrier_errors",
"tx_compressed",
"tx_dropped",
"tx_errors",
"tx_fifo_errors",
"tx_heartbeat_errors",
"tx_packets",
"tx_window_errors",
}
type grabber struct {
path string
iface string
quit chan bool
running bool
count uint
}
func newGrabber(iface string) *grabber {
return &grabber{
iface: iface,
path: fmt.Sprintf("/sys/class/net/%s/statistics", iface),
}
}
func (g *grabber) grab() {
data := &interfacedata{}
statistics := map[string]interface{}{}
value := make([]byte, 100)
var file *os.File
var err error
for _, key := range grabkeys {
file, err = os.Open(path.Join(g.path, key))
if err == nil {
count, err := file.Read(value)
if err == nil {
statistics[key], err = strconv.ParseInt(strings.TrimSpace(string(value[:count])), 10, 64)
if err != nil {
log.Println("Failed to process data", err, value[:count], count)
}
} else {
log.Println("Failed to read data", err)
}
file.Close()
} else {
log.Println("Failed to load data", err)
}
}
data.set(g.iface, statistics)
h.broadcast <- data
}
func (g *grabber) start() {
g.count++
if g.running {
return
}
g.quit = make(chan bool)
g.running = true
go func() {
fmt.Printf("Grabbing started: %s\n", g.iface)
ticker := time.NewTicker(1000 * time.Millisecond)
for {
select {
case <-ticker.C:
g.grab()
case <-g.quit:
ticker.Stop()
return
}
}
}()
}
func (g *grabber) stop() {
if !g.running {
return
}
g.count--
if g.count == 0 {
g.running = false
close(g.quit)
fmt.Printf("Grabbing stopped: %s\n", g.iface)
}
}

View file

@ -0,0 +1,94 @@
/*
* Copyright (C) 2014 struktur AG
* http://www.strukturag.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file is baeed on ideas from the chat example of gorilla/websocket.
* Copyright 2013 Gary Burd. All rights reserved.
*
* 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.
*
* 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 HOLDER 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.
*/
package main
type hub struct {
grabbers map[string]*grabber
connections map[*connection]bool
broadcast chan *interfacedata
register chan *connection
unregister chan *connection
}
var h = hub{
broadcast: make(chan *interfacedata),
register: make(chan *connection),
unregister: make(chan *connection),
connections: make(map[*connection]bool),
grabbers: make(map[string]*grabber),
}
func (h *hub) run() {
var eg *grabber
var ok bool
for {
select {
case c := <-h.register:
h.connections[c] = true
if eg, ok = h.grabbers[c.iface]; !ok {
eg = newGrabber(c.iface)
h.grabbers[c.iface] = eg
}
eg.start()
case c := <-h.unregister:
delete(h.connections, c)
close(c.send)
if eg, ok = h.grabbers[c.iface]; ok {
eg.stop()
}
case d := <-h.broadcast:
for c := range h.connections {
if c.iface == d.iface {
if m, err := d.encode(); err == nil {
select {
case c.send <- m:
default:
close(c.send)
delete(h.connections, c)
}
}
}
}
}
}
}

View file

@ -0,0 +1,73 @@
/*
* Copyright (C) 2014 struktur AG
* http://www.strukturag.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"flag"
"log"
"net/http"
"os"
"path"
"text/template"
)
var templates *template.Template
func serveClient(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.Error(w, "Not found", 404)
return
}
if r.Method != "GET" {
http.Error(w, "Method nod allowed", 405)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
err := templates.ExecuteTemplate(w, "realtimetraffic.html", nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func main() {
var err error
cwd, _ := os.Getwd()
client := flag.String("client", path.Join(cwd, "client"), "Full path to client directory.")
addr := flag.String("listen", "127.0.0.1:8088", "Listen address.")
templates, err = template.ParseGlob(path.Join(*client, "*.html"))
if err != nil {
log.Fatal("Failed to load templates: ", err)
}
flag.Parse()
go h.run()
http.HandleFunc("/", serveClient)
http.HandleFunc("/realtimetraffic", serveWs)
http.Handle("/css/", http.FileServer(http.Dir(*client)))
http.Handle("/scripts/", http.FileServer(http.Dir(*client)))
http.Handle("/img/", http.FileServer(http.Dir(*client)))
http.Handle("/favicon.ico", http.FileServer(http.Dir(*client)))
err = http.ListenAndServe(*addr, nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}

View file

@ -1 +0,0 @@
import trafficserver

View file

@ -1,214 +0,0 @@
#!/usr/bin/python
"""
Copyright (C) 2012 struktur AG
http://www.strukturag.com
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
import os
import sys
try:
import json
except ImportError:
import simplejson as json
# Globals
dataGrabbers = {}
dataGrabbed = {}
CLIENT_ROOT = os.path.abspath(os.path.normpath(os.path.join(os.path.dirname(__file__), "..", "client")))
class Grabber(object):
keys = [
"collisions",
"multicast",
"rx_bytes",
"rx_compressed",
"rx_crc_errors",
"rx_dropped",
"rx_errors",
"rx_fifo_errors",
"rx_frame_errors",
"rx_length_errors",
"rx_missed_errors",
"rx_over_errors",
"rx_packets",
"tx_aborted_errors",
"tx_bytes",
"tx_carrier_errors",
"tx_compressed",
"tx_dropped",
"tx_errors",
"tx_fifo_errors",
"tx_heartbeat_errors",
"tx_packets",
"tx_window_errors"
]
def __init__(self, interface):
self.interface = interface
self.base = "/sys/class/net/%s/statistics" % interface
def grab(self):
data = {}
for k in self.keys:
data[k] = self.read(k)
dataGrabbed[self.interface]=data
def read(self, key):
data = 0
fn = os.path.join(self.base, key)
if os.path.exists(fn):
fp = file(fn, "rb")
try:
data = fp.read()
finally:
fp.close()
data = int(data.strip())
return data
class WSHandler(tornado.websocket.WebSocketHandler):
dataSender = None
dataInterface = None
def open(self):
interface = self.dataInterface = self.get_argument("if", "eth0").lower()
grabber = dataGrabbers.get(interface, None)
if grabber is None:
# create new grabber
print >>sys.stdout, "Starting grabber for %s." % interface
callback = Grabber(interface)
grabber = tornado.ioloop.PeriodicCallback(callback.grab, 240)
grabber.start()
dataGrabbers[interface]=[1, grabber]
else:
dataGrabbers[interface][0] = grabber[0]+1
sender = self.dataSender = tornado.ioloop.PeriodicCallback(self.doSendData, 500)
sender.start()
def on_message(self, message):
pass
def on_close(self):
self.dataSender.stop()
self.dataSender = None
grabber = dataGrabbers.get(self.dataInterface, None)
if grabber is not None:
dataGrabbers[self.dataInterface][0] = grabber[0]-1
if grabber[0] <= 0:
print >>sys.stdout, "Stopping grabber for %s." % self.dataInterface
grabber[1].stop()
del dataGrabbers[self.dataInterface]
def doSendData(self):
data = {
self.dataInterface: dataGrabbed.get(self.dataInterface, {})
}
self.write_message(json.dumps(data))
class ClientHandler(tornado.web.RequestHandler):
def get(self, filename):
if not filename or filename == "index.html":
filename = "realtimetraffic.html"
fn = os.path.abspath(os.path.join(CLIENT_ROOT, os.path.normpath(filename)))
if not fn.startswith(CLIENT_ROOT):
raise tornado.web.HTTPError(404)
if not os.path.isfile(fn):
raise tornado.web.HTTPError(404)
fp = file(fn, "rb")
try:
self.write(fp.read())
finally:
fp.close()
def main(listen="127.0.0.1:8088"):
from optparse import OptionParser
parser = OptionParser()
parser.add_option("-l", "--listen", dest="listen", help="listen address (default: [%s])" % listen, default=listen)
parser.add_option("--ssl_keyfile", dest="ssl_keyfile", help="SSL key file", metavar="FILE")
parser.add_option("--ssl_certfile", dest="ssl_certfile", help="SSL certificate file", metavar="FILE")
(options, args) = parser.parse_args()
if ":" in options.listen:
address, port = options.listen.split(":", 1)
port = int(port)
listen = options.listen
else:
address = options.listen
port = 8088
listen = "%s:%s" % (address, port)
application = tornado.web.Application([
(r'^/realtimetraffic$', WSHandler),
(r'^/css/(.*)$', tornado.web.StaticFileHandler, {'path': os.path.join(CLIENT_ROOT, 'css')}),
(r'^/scripts/(.*)$', tornado.web.StaticFileHandler, {'path': os.path.join(CLIENT_ROOT, 'scripts')}),
(r'^/img/(.*)$', tornado.web.StaticFileHandler, {'path': os.path.join(CLIENT_ROOT, 'img')}),
(r'^/(.*)$', ClientHandler)
], debug=False, static_path=CLIENT_ROOT)
params = {}
ssl = False
if options.ssl_keyfile and options.ssl_certfile:
if not os.path.isfile(options.ssl_keyfile):
print >>sys.stderr, "SSL key file not found: %s" % options.ssl_keyfile
return 1
if not os.path.isfile(options.ssl_certfile):
print >>sys.stderr, "SSL certificate file not found: %s" % options.ssl_certfile
return 1
params["ssl_options"] = {
"keyfile": options.ssl_keyfile,
"certfile": options.ssl_certfile
}
ssl = True
http_server = tornado.httpserver.HTTPServer(application, **params)
http_server.listen(port=port, address=address)
print >>sys.stdout, "Server running on %s (ssl:%r) ..." % (listen, ssl)
try:
tornado.ioloop.IOLoop.instance().start()
except KeyboardInterrupt:
pass
print >>sys.stdout, "Server stopped."
return 0
if __name__ == "__main__":
status = main()
sys.exit(status)