Skip to content
Browse files

FIXME-ify; tweak logging some

  • Loading branch information...
1 parent 3601de7 commit c143173643437976c3c8427028ec0d4782659c49 @mnot committed Nov 30, 2009
Showing with 62 additions and 65 deletions.
  1. +0 −1 scripts/spdy_http_proxy.py
  2. +1 −2 setup.py
  3. +11 −16 src/spdy_client.py
  4. +28 −27 src/spdy_common.py
  5. +22 −19 src/spdy_server.py
View
1 scripts/spdy_http_proxy.py
@@ -27,7 +27,6 @@ class ProxyClient(Client):
def proxy_handler(method, uri, req_hdrs, s_res_start, req_pause):
# can modify method, uri, req_hdrs here
- print uri
if backend_authority:
(scheme, authority, path, query, fragid) = urlsplit(uri)
uri = urlunsplit((scheme, backend_authority, path, query, fragid))
View
3 setup.py
@@ -10,7 +10,6 @@
url='http://github.com/mnot/nbhttp/',
packages=['nbhttp'],
package_dir={'nbhttp': 'src'},
- scripts=['scripts/proxy.py'],
classifiers=[
'Development Status :: 4 - Beta',
'Intended Audience :: Developers',
@@ -21,4 +20,4 @@
'Topic :: Internet :: WWW/HTTP :: HTTP Servers',
'Topic :: Software Development :: Libraries :: Python Modules',
]
- )
+ )
View
27 src/spdy_client.py
@@ -62,6 +62,8 @@
with the appropriate error dictionary.
"""
+# FIXME: update docs for API change (move res_start)
+
__author__ = "Mark Nottingham <mnot@mnot.net>"
__copyright__ = """\
Copyright (c) 2008-2009 Mark Nottingham
@@ -85,23 +87,16 @@
THE SOFTWARE.
"""
-import logging
from urlparse import urlsplit
-logging.basicConfig()
-log = logging.getLogger('client')
-log.setLevel(logging.INFO)
-
import push_tcp
+from error import ERR_CONNECT, ERR_URL
from http_common import WAITING, \
- idempotent_methods, no_body_status, hop_by_hop_hdrs, \
- dummy, get_hdr
+ hop_by_hop_hdrs, dummy, get_hdr
from spdy_common import SpdyMessageHandler, CTL_SYN_STREAM, FLAG_NONE, FLAG_FIN
req_remove_hdrs = hop_by_hop_hdrs + ['host']
-# TODO: proxy support
-# TODO: next-hop version cache for Expect/Continue, etc.
# TODO: read timeout support (needs to be in push_tcp?)
class SpdyClient(SpdyMessageHandler):
@@ -117,8 +112,6 @@ def req_start(self, method, uri, req_hdrs, res_start_cb, req_body_pause):
Returns a (req_body, req_done) tuple.
"""
- req_hdrs = [i for i in req_hdrs \
- if not i[0].lower() in req_remove_hdrs]
if self.proxy:
(host, port) = self.proxy
else: # find out where to connect to the hard way
@@ -144,16 +137,17 @@ def req_start(self, method, uri, req_hdrs, res_start_cb, req_body_pause):
class SpdyConnection(SpdyMessageHandler):
"A SPDY connection."
- def __init__(self):
+ def __init__(self, log=None):
SpdyMessageHandler.__init__(self)
+ self.log = log or dummy
self._tcp_conn = None
- self._req_body_pause_cb = None # FIXME
+ self._req_body_pause_cb = None # FIXME: re-think pausing
self._streams = {}
self._output_buffer = []
self._highest_stream_id = -1
- self._debug = log.debug
def req_start(self, method, uri, req_hdrs, res_start_cb, req_body_pause):
+ req_hdrs = [i for i in req_hdrs if not i[0].lower() in req_remove_hdrs]
req_hdrs.append(('method', method))
req_hdrs.append(('url', uri))
req_hdrs.append(('version', 'HTTP/1.1'))
@@ -169,7 +163,6 @@ def req_done(*args):
def req_body(self, stream_id, chunk):
"Send part of the request body. May be called zero to many times."
- # FIXME: self._handle_error(ERR_LEN_REQ)
self._output(self._ser_data_frame(stream_id, FLAG_NONE, chunk))
def req_done(self, stream_id, err):
@@ -300,7 +293,9 @@ def get(self, host, port, connection_handler, connect_timeout):
)
self._conns[(host, port)] = conn
return conn
-
+
+ #TODO: remove conns from _conns when they close
+
_conn_pool = _SpdyConnectionPool()
View
55 src/spdy_common.py
@@ -4,7 +4,7 @@
shared SPDY infrastructure
This module contains utility functions for nbhttp and a base class
-for the parsing portions of the client and server.
+for the SPDY-specific portions of the client and server.
"""
__author__ = "Mark Nottingham <mnot@mnot.net>"
@@ -31,7 +31,9 @@
"""
import struct
+
import c_zlib
+from http_common import dummy
compressed_hdrs = True
dictionary = \
@@ -80,6 +82,7 @@ class SpdyMessageHandler:
"""
def __init__(self):
+ self.log = lambda a:a
self._input_buffer = ""
self._input_state = WAITING
self._input_frame_type = None
@@ -90,8 +93,8 @@ def __init__(self):
self._compress = c_zlib.Compressor(-1, dictionary)
self._decompress = c_zlib.Decompressor(dictionary)
else:
- self._compress = lambda a:a
- self._decompress = lambda a:a
+ self._compress = dummy
+ self._decompress = dummy
# input-related methods
@@ -125,18 +128,17 @@ def _handle_input(self, data):
self._input_buffer = ""
if self._input_state == WAITING: # waiting for a complete frame header
if len(data) >= 8:
- (d1, self._input_flags, l1, l2) = struct.unpack("!IBBH", data[:8])
+ (d1, self._input_flags, d2, d3) = struct.unpack("!IBBH", data[:8])
if d1 >> 31 & 0x01: # control frame
+ version = ( d1 >> 16 ) & 0x7fff # TODO: check version
# FIXME: we use 0x00 internally to indicate data frame
- version = ( d1 >> 16 ) & 0x7fff
self._input_frame_type = d1 & 0x0000ffff
self._input_stream_id = None
else: # data frame
self._input_frame_type = DATA_FRAME
self._input_stream_id = d1 & STREAM_MASK
- self._input_frame_len = (( l1 << 16 ) + l2)
+ self._input_frame_len = (( d2 << 16 ) + d3)
self._input_state = READING_FRAME_DATA
- self._debug("frame type %s len %s" % (self._input_frame_type, self._input_frame_len))
self._handle_input(data[8:])
else:
self._input_buffer = data
@@ -148,23 +150,22 @@ def _handle_input(self, data):
self._input_body(self._input_stream_id, frame_data)
stream_id = self._input_stream_id # for FLAG_FIN below
elif self._input_frame_type in [CTL_SYN_STREAM, CTL_SYN_REPLY]:
- stream_id = struct.unpack("!I", frame_data[:4])[0] & STREAM_MASK
- self._debug("incoming stream_id %s" % stream_id)
- hdr_tuples = self._parse_hdrs(frame_data[6:]) or self._input_error(stream_id, 1) # FIXME
- # throw away num pri, unused
+ stream_id = struct.unpack("!I", frame_data[:4])[0] & STREAM_MASK # FIXME: what if they lied about the frame len?
+ hdr_tuples = self._parse_hdrs(frame_data[6:]) or self._input_error(stream_id, 1) # FIXME: proper error here
+ # FIXME: expose pri
self._input_start(stream_id, hdr_tuples)
elif self._input_frame_type == CTL_FIN_STREAM:
- self._input_error(stream_id, err=1) # FIXME
+ self._input_error(stream_id, err=1) # FIXME: proper error here
elif self._input_frame_type == CTL_HELLO:
- pass
+ pass # FIXME
elif self._input_frame_type == CTL_NOOP:
pass
elif self._input_frame_type == CTL_PING:
- pass
+ pass # FIXME
elif self._input_frame_type == CTL_GOAWAY:
pass # FIXME
else: # unknown frame type
- raise ValueError, "Unknown frame type" # FIXME
+ raise ValueError, "Unknown frame type" # FIXME: don't puke
if self._input_flags & FLAG_FIN: # FIXME: invalid on FIN_STREAM
self._input_end(stream_id)
self._input_state = WAITING
@@ -173,34 +174,34 @@ def _handle_input(self, data):
else: # don't have complete frame yet
self._input_buffer = data
else:
- raise Exception, "Unknown state %s" % self._input_state
+ raise Exception, "Unknown input state %s" % self._input_state
def _parse_hdrs(self, data):
"Given a control frame data block, return a list of (name, value) tuples."
# TODO: separate null-delimited into separate instances
- data = self._decompress(data)
+ data = self._decompress(data) # FIXME: catch errors
cursor = 2
- (num_hdrs,) = struct.unpack("!h", data[:cursor])
+ (num_hdrs,) = struct.unpack("!h", data[:cursor]) # FIXME: catch errors
hdrs = []
while cursor < len(data):
try:
- (name_len,) = struct.unpack("!h", data[cursor:cursor+2])
+ (name_len,) = struct.unpack("!h", data[cursor:cursor+2]) # FIXME: catch errors
cursor += 2
- name = data[cursor:cursor+name_len]
+ name = data[cursor:cursor+name_len] # FIXME: catch errors
cursor += name_len
except IndexError:
raise
except struct.error:
raise
try:
- (val_len,) = struct.unpack("!h", data[cursor:cursor+2])
+ (val_len,) = struct.unpack("!h", data[cursor:cursor+2]) # FIXME: catch errors
cursor += 2
- value = data[cursor:cursor+val_len]
+ value = data[cursor:cursor+val_len] # FIXME: catch errors
cursor += val_len
except IndexError:
raise
except struct.error:
- print len(data), cursor, data
+ print len(data), cursor, data # FIXME
raise
hdrs.append((name, value))
return hdrs
@@ -215,7 +216,7 @@ def _handle_error(self, err):
def _ser_syn_frame(self, type, flags, stream_id, hdr_tuples):
"Returns a SPDY SYN_[STREAM|REPLY] frame."
- hdrs = self._ser_hdrs(hdr_tuples)
+ hdrs = self._compress(self._ser_hdrs(hdr_tuples))
data = struct.pack("!IH%ds" % len(hdrs),
STREAM_MASK & stream_id,
0x00, # unused
@@ -244,7 +245,8 @@ def _ser_data_frame(stream_id, flags, data):
data
)
- def _ser_hdrs(self, hdr_tuples):
+ @staticmethod
+ def _ser_hdrs(hdr_tuples):
"Returns a SPDY header block from a list of (name, value) tuples."
# TODO: collapse dups into null-delimited
hdr_tuples.sort() # required by Chromium
@@ -254,5 +256,4 @@ def _ser_hdrs(self, hdr_tuples):
# TODO: check for overflowing n, v lengths
fmt.append("H%dsH%ds" % (len(n), len(v)))
args.extend([len(n), n, len(v), v])
- data = struct.pack("".join(fmt), *args)
- return self._compress(data)
+ return struct.pack("".join(fmt), *args)
View
41 src/spdy_server.py
@@ -1,9 +1,9 @@
#!/usr/bin/env python
"""
-Non-Blocking HTTP Server
+Non-Blocking SPDY Server
-This library allow implementation of an HTTP/1.1 server that is "non-blocking,"
+This library allow implementation of an SPDY server that is "non-blocking,"
"asynchronous" and "event-driven" -- i.e., it achieves very high performance
and concurrency, so long as the application code does not block (e.g.,
upon network, disk or database access). Blocking on one request will block
@@ -113,18 +113,18 @@ def handle_connection(self, tcp_conn):
class SpdyServerConnection(SpdyMessageHandler):
"A handler for a SPDY server connection."
- def __init__(self, request_handler, tcp_conn):
+ def __init__(self, request_handler, tcp_conn, log=None):
SpdyMessageHandler.__init__(self)
self.request_handler = request_handler
self._tcp_conn = tcp_conn
+ self.log = log or dummy
self._streams = {}
self._res_body_pause_cb = False
- self._debug = log.debug
- self._debug("new connection %s" % id(self))
+ self.log.debug("new connection %s" % id(self))
def res_start(self, stream_id, status_code, status_phrase, res_hdrs, res_body_pause):
"Start a response. Must only be called once per response."
- log.debug("res_start %s" % stream_id)
+ self.log.debug("res_start %s" % stream_id)
self._res_body_pause_cb = res_body_pause
res_hdrs.append(('status', "%s %s" % (status_code, status_phrase)))
# TODO: hop-by-hop headers?
@@ -137,10 +137,8 @@ def res_done(*args):
def res_body(self, stream_id, chunk):
"Send part of the response body. May be called zero to many times."
- if not chunk:
- return
- log.debug("res_body %s" % stream_id)
- self._output(self._ser_data_frame(stream_id, FLAG_NONE, chunk))
+ if chunk:
+ self._output(self._ser_data_frame(stream_id, FLAG_NONE, chunk))
def res_done(self, stream_id, err):
"""
@@ -151,7 +149,6 @@ def res_done(self, stream_id, err):
indicating that an HTTP-specific (i.e., non-application) error occured
in the generation of the response; this is useful for debugging.
"""
- log.debug("res_done %s" % stream_id)
self._output(self._ser_data_frame(stream_id, FLAG_FIN, ""))
# TODO: delete stream after checking that input side is half-closed
@@ -171,22 +168,23 @@ def _conn_closed(self):
"The server connection has closed."
pass # FIXME: any cleanup necessary?
# self.pause()
-# self._queue = []
# self.tcp_conn.handler = None
# self.tcp_conn = None
- # Methods called by common.HttpRequestHandler
+ # Methods called by common.SpdyRequestHandler
def _output(self, chunk):
- self._tcp_conn.write(chunk)
+ if self._tcp_conn:
+ self._tcp_conn.write(chunk)
def _input_start(self, stream_id, hdr_tuples):
- log.debug("request start %s %s" % (stream_id, hdr_tuples))
- method = get_hdr(hdr_tuples, 'method')[0]
- uri = get_hdr(hdr_tuples, 'url')[0]
+ self.log.debug("request start %s %s" % (stream_id, hdr_tuples))
+ method = get_hdr(hdr_tuples, 'method')[0] # FIXME: error handling
+ uri = get_hdr(hdr_tuples, 'url')[0] # FIXME: error handling
assert not self._streams.has_key(stream_id) # FIXME
def res_start(*args):
return self.res_start(stream_id, *args)
+ # TODO: sanity checks / catch errors from requst_handler
self._streams[stream_id] = self.request_handler(
method, uri, hdr_tuples, res_start, self.req_body_pause)
@@ -201,12 +199,14 @@ def _input_end(self, stream_id):
def _input_error(self, err, detail=None):
"Indicate a parsing problem with the request body."
+ # FIXME: rework after fixing spdy_common
err['detail'] = detail
if self._tcp_conn:
self._tcp_conn.close()
self._tcp_conn = None
self._streams[stream_id][1](err)
+ # TODO: re-evaluate if this is necessary in SPDY
def _handle_error(self, err, detail=None):
"Handle a problem with the request by generating an appropriate response."
if detail:
@@ -236,7 +236,10 @@ def test_handler(method, uri, hdrs, res_start, req_pause):
return dummy, dummy
if __name__ == "__main__":
- sys.stderr.write("PID: %s\n" % os.getpid())
+ logging.basicConfig()
+ log = logging.getLogger('server')
+ log.setLevel(logging.INFO)
+ log.info("PID: %s\n" % os.getpid())
h, p = '127.0.0.1', int(sys.argv[1])
- server = SpdyServer(h, p, test_handler)
+ server = SpdyServer(h, p, test_handler, log)
push_tcp.run()

0 comments on commit c143173

Please sign in to comment.
Something went wrong with that request. Please try again.