Skip to content

Adding a line of description and minor pep8 cleanup #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions examples/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@
from asyncio import coroutine, sleep
import argparse


# simplest handler taking no arguments
def hi():
return 'hi'


@coroutine
def hi3(response):
yield from response.send_headers(length=3)
response.write('hi3')
yield from response.close()


# coroutine that sleeps
@coroutine
def slow(response, t):
Expand All @@ -32,14 +35,15 @@ def echo(request, response):
yield from response.send_headers(length=len(body))
response.send(body)


# coroutine handler receiving arguments from re patterns in the route path
@coroutine
def groups(g1, g2):
return 'groups! {0} {1}'.format(g1, g2)

if __name__ == '__main__':
logging.basicConfig(stream=sys.stdout, level=logging.ERROR,
format='%(asctime)s | %(levelname)s | %(message)s')
format='%(asctime)s | %(levelname)s | %(message)s')

p = argparse.ArgumentParser()
p.add_argument('-l', '--log-level', default='ERROR')
Expand All @@ -53,10 +57,9 @@ def groups(g1, g2):
(r'^/slow/(?P<t>.*)', 'GET', slow),
(r'^/groups/(?P<g1>.*)/(?P<g2>.*)$', 'GET', groups),
(r'^/site/(?P<path>.*)$', 'GET', Application.static,
{'doc_root':'/tmp/site'}),
{'doc_root': '/tmp/site'}),
(r'^/hi3', 'GET', hi3),
(r'^/.*$', 'GET', hi),
])
])

a.serve('0.0.0.0', 2020, keep_alive=True)

66 changes: 34 additions & 32 deletions microhttpd.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@

log = logging.getLogger('microhttpd')


class HTTPException(Exception):
def __init__(self, status_code, msg):
self.status_code = status_code
self.message = msg


class Request(object):
TOTAL_TIME = 0
TOTAL_REQ = 0
Expand All @@ -33,7 +35,7 @@ def __init__(self, reader, can_keep_alive=False):
self.start_time = time.time()
self.body_consumed = False

@coroutine
@coroutine
def _nextline(self):
return (yield from self.reader.readline()).decode('latin-1').rstrip()

Expand Down Expand Up @@ -68,12 +70,10 @@ def consume_headers(self):
self.keep_alive = True
if self.headers.get('connection') == 'close':
self.keep_alive = False

if self.keep_alive:
log.debug('%s KEEP ALIVE REQUEST', self.http_version)



@coroutine
def body(self):
if self.body_consumed:
Expand All @@ -85,15 +85,15 @@ def body(self):
b = yield from self.reader.readexactly(l)
self.body_consumed = True
return b

def end(self):
dur = time.time() - self.start_time
Request.TOTAL_REQ += 1
Request.TOTAL_TIME += dur
log.info('%s %s %s %sms', self.status_code, self.method,
self.full_path, round(dur*1000, 2))
log.info('%s %s %s %sms', self.status_code, self.method,
self.full_path, round(dur*1000, 2))
log.debug('avg req time %0.6f for %d requests',
Request.TOTAL_TIME / Request.TOTAL_REQ, Request.TOTAL_REQ)
Request.TOTAL_TIME / Request.TOTAL_REQ, Request.TOTAL_REQ)


class Response(object):
Expand All @@ -106,7 +106,7 @@ def __init__(self, writer, request, server):
self.is_head_request = False
self.headers_sent = False
self.status_code = -1

@coroutine
def send_headers(self, length=0, status=200, headers={}):
log.debug('in send headers')
Expand All @@ -119,7 +119,7 @@ def send_headers(self, length=0, status=200, headers={}):
log.debug('headers written')
self.headers_sent = True
self.request.status_code = status

@coroutine
def send(self, d):
self.write(d)
Expand All @@ -132,13 +132,13 @@ def write(self, d):
if isinstance(d, str):
d = d.encode('utf-8')
self.writer.write(d)

@coroutine
def close(self):
yield from self.writer.drain()
log.debug('drained')
self.request.end()

# we've completed this request
self.is_sent = True

Expand All @@ -149,6 +149,7 @@ def close(self):
log.debug('ready for next connection')
yield from self.server.recycle(self.request, self)


class HTTPServer(object):
def __init__(self, application):
self.c = 0
Expand Down Expand Up @@ -177,7 +178,7 @@ def cb_wrapper(cb, cb_kwargs, is_head=False):
def f(req, res):
res.is_head_request = is_head
kwargs = cb_kwargs

# figure out whether cb wants request and response objects
cb_args = inspect.getargspec(cb).args
log.debug('cb args=%s', cb_args)
Expand All @@ -202,32 +203,33 @@ def f(req, res):
log.debug('sending reply of length %d', len(r))
yield from res.send_headers(length=len(r))
yield from res.send(r)

except HTTPException as e:
yield from self.send_error(res,e.status_code,msg=e.message)
yield from self.send_error(res, e.status_code,
msg=e.message)
except ConnectionResetError as e:
log.debug('connection reset by peer')
except Exception as e:
yield from self.send_error(res, 500, exception=e)

# callback is all wrapped up and ready to be scheduled!
# callback is all wrapped up and ready to be scheduled!
return f

cb = None

for pattern, method, callback, kwargs in self.app.routes:
m = pattern.match(path)
if not m: continue

if not m:
continue
cb_kwargs = m.groupdict()
cb_kwargs.update(kwargs)

if meth.lower() == method.lower():
cb = cb_wrapper(callback, cb_kwargs)
elif meth.lower() == 'head':
cb = cb_wrapper(callback, cb_kwargs, is_head=True)
if cb:
break # we matched one
break # we matched one
return cb

@coroutine
Expand All @@ -252,7 +254,7 @@ def handle(self, reader, writer, reused=False):
def recycle(self, request, response):
# ok, recycle this reader and writer for a new connection
# used for keep alive requests

# first make sure we consumed the body
log.debug('recycling')
if not request.body_consumed:
Expand All @@ -261,14 +263,14 @@ def recycle(self, request, response):
log.debug('done')

Task(self.handle(request.reader, response.writer, True))

@coroutine
def send_error(self, response, status=500, msg='', exception=None):
log.debug('sending %s', status)
yield from response.send_headers(status=status,length=len(msg))
yield from response.send_headers(status=status, length=len(msg))
yield from response.send(msg)
if exception: log.exception(exception)

if exception:
log.exception(exception)

def client_connected(self, client_reader, client_writer):
# a new client connected, our reader _ReaderWrapper, will let
Expand All @@ -278,14 +280,16 @@ def client_connected(self, client_reader, client_writer):

Task(self.handle(client_reader, client_writer))


class Application(object):
def __init__(self, routes):
self.routes = []
for route in routes:
pattern, meth, cb = route[:3]
if len(route) == 4: kwargs = route[3]
else: kwargs = {}

pattern, meth, cb = route[:3]
if len(route) == 4:
kwargs = route[3]
else:
kwargs = {}
self.routes.append((re.compile(pattern), meth, cb, kwargs))

@classmethod
Expand All @@ -299,7 +303,7 @@ def static(cls, doc_root, response, path):
L = os.stat(f).st_size
yield from response.send_headers(length=L)
log.debug('serving %s size %d', f, L)

with open(f, 'rb') as the_file:
while True:
data = the_file.read(chunk_size)
Expand All @@ -310,5 +314,3 @@ def static(cls, doc_root, response, path):

def serve(self, host, port, **kwargs):
HTTPServer(self).serve(host, port, **kwargs)


3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
author='Rob Tandy',
author_email='[email protected]',
url='https://github.com/robtandy/microhttpd',
description="A small, fast HTTP server based around asyncio",
long_description="""
A simple, small and fast web server based around asyncio.
""",
py_modules=['microhttpd'],
)
)