Permalink
Browse files

hack to make TRANSPARENT DTDs validate in html5. fixes for pyy_httpse…

…rver
  • Loading branch information...
1 parent 242f9b5 commit 3113e874cc5b8a81cb2f3661a502efbb20558476 @Knio committed May 14, 2010
Showing with 105 additions and 65 deletions.
  1. +1 −1 pyy_html/dtd/html5.py
  2. +27 −17 pyy_html/page.py
  3. +13 −5 pyy_httpserver/fileserver.py
  4. +36 −18 pyy_httpserver/http.py
  5. +5 −3 pyy_httpserver/httpserver.py
  6. +23 −21 pyy_httpserver/server.py
View
@@ -124,7 +124,7 @@ class html5(dtd):
#TEXT: http://www.w3.org/TR/html5/text-level-semantics.html#text-level-semantics
a : {VALID : GLOBAL | set(['href', 'target', 'ping', 'rel', 'media', 'hreflang', 'type']),
- CHILDREN: TRANSPARENT},
+ CHILDREN: TRANSPARENT | FLOW | PHRASING}, # TODO XXX This is wrong. Transparent is not a class of elements
em : {VALID : GLOBAL,
CHILDREN: PHRASING},
strong : {VALID : GLOBAL,
View
@@ -11,35 +11,45 @@ class page(document):
def __init__(self, **kwargs):
document.__init__(self, **kwargs)
for i in self.css:
- if self.inline: self.head += style(include(i), type="text/css")
- else: self.head += link (href=i, type="text/css", rel="stylesheet")
+
+ if self.inline:
+ self.head += style(include(i), type="text/css")
+ else:
+ if not i.startswith('http://'): i = self.prefix+i
+ self.head += link (href=i, type="text/css", rel="stylesheet")
for i in self.script:
- if self.inline: self.head += script(include(i), type="text/javascript")
- else: self.head += script(src=i, type="text/javascript")
+ if self.inline:
+ self.head += script(include(i), type="text/javascript")
+ else:
+ if not i.startswith('http://'): i = self.prefix+i
+ self.head += script(src=i, type="text/javascript")
@classmethod
def get(cls, handler ,req, res, *args, **kwargs):
- p = cls(*args, **kwargs)
+ p = cls()
+ p.do_get(*args, **kwargs)
res.headers['Content-Type'] = 'text/html'
res.body = p.render()
@classmethod
- def post(self, handler ,req, res, *args, **kwargs):
+ def post(cls, handler ,req, res, *args, **kwargs):
raise httperror(405)
-
@classmethod
- def handle(self, handler, req, res, *args, **kwargs):
- h = getattr(self, req.method.lower(), None)
- if not h:
- raise httperror(405)
+ def handle(cls, handler, req, res, *args, **kwargs):
+ h = getattr(cls, req.method.lower(), None)
+ if not h: raise httperror(405)
h(handler, req, res, *args, **kwargs)
-
-
-
-
-
-
+ @classmethod
+ def handle_error(cls, handler, req, res, status, *args):
+ p = cls()
+ h = getattr(p, 'do_%d' % status, getattr(p, 'do_error', None))
+ if not h: raise httperror(405)
+ h(status, *args)
+ res.headers['Content-Type'] = 'text/html'
+ res.body = p.render()
+
+
@@ -37,13 +37,18 @@ def handle(self, conn, req, res, *args):
if not req.method in ['GET', 'HEAD']:
raise httperror(405)
+ cache = True
+ modified = True
for k,v in req.headers.iteritems():
if k == 'If-Modified-Since':
try: t = http.httptime(v)
except: continue # malformed header value
if self.handler.get_mtime(*args) <= t: # I hate time libraries so much
- res.status = 304 # not modified
- return
+ modified = False
+
+ elif k == 'Cache-Control':
+ if v == 'no-cache':
+ cache = False
elif k == 'Expect': raise httperror(417)
elif k == 'If-Range': raise httperror(501)
@@ -53,7 +58,10 @@ def handle(self, conn, req, res, *args):
else:
warnings.warn('unhandled request header: %s: %s' % (k,v))
-
+
+ if not modified and cache:
+ raise httperror(304)
+
return self.handler.handle(conn, req, res, *args)
@@ -113,7 +121,7 @@ def check_path(self, path):
def get_mtime(self, *args):
path = self.check_path(*args)
- return datetime.fromtimestamp(os.path.getmtime(path))
+ return datetime.utcfromtimestamp(os.path.getmtime(path))
def handle(self, conn, req, res, path):
path = self.check_path(path)
@@ -141,7 +149,7 @@ def write_file(self, conn, req, res, path):
fsize = os.path.getsize(path)
f = file(path, 'rb')
- ft = threadio.threadio(f) # this causes blocking in select() ???
+ ft = f #threadio.threadio(f) # this causes blocking in select() ???
# ft = stacklessfile(path, 'rb') # use this instead, when it doesn't crash
View
@@ -33,22 +33,27 @@ def httptime(t=None):
CRLF = '\r\n'
-
class httphandler(object):
def __init__(self, server, conn, handler):
self.server = server
self.conn = conn
self.handler = handler
- try:
- while self.conn.status:
- self.do_request()
- except EOFError:
- # client closed the connection
- pass
- except Exception, e:
- import traceback
- traceback.print_exc()
+ def handle_requests():
+ try:
+ while self.conn.status:
+ self.do_request()
+ except EOFError:
+ # client closed the connection
+ pass
+ except Exception, e:
+ import traceback
+ traceback.print_exc()
+
+ import threading
+ self.thread = threading.Thread(target=handle_requests)
+ self.thread.start()
+
def do_request(self):
req = None
@@ -63,6 +68,9 @@ def do_request(self):
finish = self.handler.handle(self, req, res)
except Exception, e:
+ # import traceback
+ # traceback.print_exc()
+
try: raise
except httperror, e:
error = e.args
@@ -72,7 +80,11 @@ def do_request(self):
error = (500, e)
res = httpresponse()
res.status = error[0]
- res.body = '%s %s' % (res.status, res.statusmsg)
+ if (res.status <= 100) or (res.status in (204,304)) or (req and req.method == 'HEAD'):
+ # these messages cannot have a body
+ pass
+ else:
+ res.body = '%s %s' % (res.status, res.statusmsg)
try:
self.handler.handle_error(self, req, res, error[0], *error[1:])
except: # error handler had an error!
@@ -92,6 +104,7 @@ def do_request(self):
# nothing to tell the client at this point
import traceback
traceback.print_exc()
+ self.conn.close()
self.finish_response(res)
@@ -127,8 +140,9 @@ def next_line(self):
while len(self._lines) < 2:
data = self._lines.pop() + self.conn.read()
self._lines.extend(data.split(CRLF))
-
- return self._lines.pop(0)
+
+ line = self._lines.pop(0)
+ return line
def readrequest(self, request, line):
if not line: return
@@ -165,7 +179,9 @@ def end_headers(self, request):
del self.readline
# if we got extra data, push it back to the front of
# the connection's read buffer, so someone can read() later
- self.conn.readbuffer[0:0] = self._lines
+ last = self._lines.pop()
+ l = [i + CRLF for i in self._lines] + [last]
+ self.conn.readbuffer[0:0] = l
del self._lines
def make_response(self, req, res, finish):
@@ -195,8 +211,9 @@ def make_response(self, req, res, finish):
elif k == 'Host': pass
elif k == 'Referer': pass
elif k == 'Accept': pass
- elif k == 'Accept-Language': pass
- elif k == 'Accept-Charset': pass
+ elif k == 'If-Modified-Since': pass
+ elif k == 'Accept-Language': pass
+ elif k == 'Accept-Charset': pass
elif k == 'Accept-Encoding':
res.headers.setdefault('Content-Encoding', 'identity')
if not res.body: continue
@@ -227,8 +244,9 @@ def make_response(self, req, res, finish):
len2 = len(res.body)
res.headers['Content-Encoding'] = 'gzip'
- else: # unsupported encoding
+ else: # unsupported encoding. leave it as identity
continue
+ # [C-E] 'identity' ?
res.headers['Content-Length'] = len2
s.append('%d/%s (%2.0f%%)' % (len2, len1, 100.*len2/len1))
@@ -251,7 +269,7 @@ def make_response(self, req, res, finish):
if not finish:
res.headers['Content-Length'] = len(res.body)
- print '%d %s %s' % (res.statusnum, req.uri, ' '.join(s))
+ print '%d %s %s' % (res.statusnum, req and req.uri, ' '.join(s))
return res
@@ -105,8 +105,8 @@ def handle_error(self, conn, req, res, status, *errors):
elif status in (301, 302, 303):
if errors:
res.headers['Location'] = errors[0]
-
- handler, uri, args = self.find_handler(req.host, self.sites)
+
+ handler, uri, args = self.find_handler(req and req.host, self.sites)
uri = httperror(status, *errors)
try:
while 1:
@@ -128,7 +128,9 @@ def handle_error(self, conn, req, res, status, *errors):
else: raise ValueError(handler)
except httperror, e:
- return self.handle_error(conn, req, res, e.args[0], e.args[1:])
+ if e.args == (status,)+errors:
+ return self.handle_error(conn, req, res, 500, 'infinite recursion in error handler')
+ return self.handle_error(conn, req, res, e.args[0], *e.args[1:])
def run(self, *args):
View
@@ -52,7 +52,8 @@ def onreadable(self):
if e.args[0] == 10053 or \
e.args[0] == 10054 or \
e.args[0] == 9 or \
- e.args[0] == 105: # Connection reset by peer
+ e.args[0] == 105 or \
+ e.args[0] == 104: # Connection reset by peer
#print 'Client %s closed the connection' % (self.addr,)
self.onclose(e)
return
@@ -70,7 +71,7 @@ def onreadable(self):
self.onclose()
return
- #print '%s GOT DATA: %r' % (self.addr, data[:80])
+ # print '%s GOT DATA: %r' % (self.addr, data)
self.readbuffer.append(data)
if self.handler and self.handler.hasattr('ondata'):
self.handler.ondata(data)
@@ -162,21 +163,21 @@ def __init__(self, server, sock, addr, handler, *args):
self.server.set_readable(self)
def onreadable(self):
- try:
- sock, addr = self.sock.accept()
- except socket.error, e:
- if e.args[0] == 10035: # would have blocked
- return
- if e.args[0] == 11: # resource temp unavailable
- return
- else: raise
-
- sock.setblocking(0)
- conn = connection(self.server, sock, addr)
- self.server.requests.add(conn)
- handler = stackless.tasklet(self.handler)(
- self.server, conn, *self.args)
- #print 'Accepted connection: %s' % (addr,)
+ while 1:
+ try:
+ sock, addr = self.sock.accept()
+ except socket.error, e:
+ if e.args[0] == 10035: # would have blocked
+ return
+ if e.args[0] == 11: # resource temp unavailable
+ return
+ else: raise
+
+ sock.setblocking(0)
+ conn = connection(self.server, sock, addr)
+ self.server.requests.add(conn)
+ handler = stackless.tasklet(self.handler)(self.server, conn, *self.args)
+ #print 'Accepted connection: %s' % (addr,)
class handler(object):
@@ -199,7 +200,7 @@ def listen(self, addr, handler, *args):
sock.setblocking(0)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(addr)
- sock.listen(5)
+ sock.listen(100)
self.listeners.append(listener(
self, sock, addr, handler, *args))
print 'Listening on %s' % (addr,)
@@ -225,10 +226,10 @@ def interrupt(self):
def select(self, timeout):
if timeout==0:
- r = select.select(
+ r, w, e = select.select(
self.readable, self.writable, [], timeout)
-
- return r
+
+ return r, w, e
def thread():
r = select.select(
@@ -260,6 +261,7 @@ def mainloop():
self.tick(timeout)
stackless.schedule()
time.sleep(0.05)
+ except KeyboardInterrupt: pass
finally:
self.quit(timeout)

0 comments on commit 3113e87

Please sign in to comment.