def set_receiver(self): """Once the headers have been read, decide how to read the body. We use the standard library's email package to parse the headers. The receiver is stored on self so it persists across calls to our own self.received, since it actually holds data between such calls. Later on we will add the request body to self.message. """ content_length = int(self.headers.get('Content-Length', 0)) transfer_encoding = self.headers.get('Transfer-Encoding', '').lower() if not content_length and not transfer_encoding: self._receiver = None elif content_length and not transfer_encoding: buffer_ = OverflowableBuffer(self.adj.inbuf_overflow) self._receiver = FixedStreamReceiver(content_length, buffer_) elif transfer_encoding == 'chunked': buffer_ = OverflowableBuffer(self.adj.inbuf_overflow) self._receiver = ChunkedReceiver(buffer_) else: self._receiver = None
def __init__(self, conn, addr, adj=None): self.addr = addr if adj is None: adj = default_adj self.adj = adj self.outbuf = OverflowableBuffer(adj.outbuf_overflow) self.creation_time = time() asyncore.dispatcher.__init__(self, conn)
def parse_header(self, header_plus): """ Parses the header_plus block of text (the headers plus the first line of the request). """ index = header_plus.find('\n') if index >= 0: first_line = header_plus[:index].rstrip() header = header_plus[index + 1:] else: first_line = header_plus.rstrip() header = '' self.first_line = first_line self.header = header lines = self.get_header_lines() headers = self.headers for line in lines: index = line.find(':') if index > 0: key = line[:index] value = line[index + 1:].strip() key1 = key.upper().replace('-', '_') # If a header already exists, we append subsequent values # seperated by a comma. Applications already need to handle # the comma seperated values, as HTTP front ends might do # the concatenation for you (behavior specified in RFC2616). try: headers[key1] += ', %s' % value except KeyError: headers[key1] = value # else there's garbage in the headers? command, uri, version = self.crack_first_line() self.command = str(command) self.uri = str(uri) self.version = version self.split_uri() if version == '1.1': te = headers.get('TRANSFER_ENCODING', '') if te == 'chunked': from httpy._zope.server.http.chunking import ChunkedReceiver self.chunked = 1 buf = OverflowableBuffer(self.adj.inbuf_overflow) self.body_rcv = ChunkedReceiver(buf) if not self.chunked: try: cl = int(headers.get('CONTENT_LENGTH', 0)) except ValueError: cl = 0 self.content_length = cl if cl > 0: buf = OverflowableBuffer(self.adj.inbuf_overflow) self.body_rcv = FixedStreamReceiver(cl, buf)
class DualModeChannel(asyncore.dispatcher): """Channel that switches between asynchronous and synchronous mode. Call set_sync() before using a channel in a thread other than the thread handling the main loop. Call set_async() to give the channel back to the thread handling the main loop. """ # will_close is set to True to close the socket. will_close = False # boolean: async or sync mode async_mode = True def __init__(self, conn, addr, adj=None): self.addr = addr if adj is None: adj = default_adj self.adj = adj self.outbuf = OverflowableBuffer(adj.outbuf_overflow) self.creation_time = time() asyncore.dispatcher.__init__(self, conn) # # ASYNCHRONOUS METHODS # def handle_close(self): self.close() def writable(self): if not self.async_mode: return 0 return self.will_close or self.outbuf def handle_write(self): if not self.async_mode: return if self.outbuf: try: self._flush_some() except socket.error: self.handle_comm_error() elif self.will_close: self.close() def readable(self): if not self.async_mode: return 0 return not self.will_close def handle_read(self): if not self.async_mode or self.will_close: return try: data = self.recv(self.adj.recv_bytes) except socket.error: self.handle_comm_error() return self.received(data) def received(self, data): """ Override to receive data in async mode. """ pass def handle_comm_error(self): """ Designed for handling communication errors that occur during asynchronous operations *only*. Probably should log this, but in a different place. """ self.handle_error() def set_sync(self): """Switches to synchronous mode. The main thread will stop calling received(). """ self.async_mode = False # # SYNCHRONOUS METHODS # def flush(self, block=True): """Sends pending data. If block is set, this pauses the application. If it is turned off, only the amount of data that can be sent without blocking is sent. """ if not block: while self._flush_some(): pass return blocked = False try: while self.outbuf: # We propagate errors to the application on purpose. if not blocked: self.socket.setblocking(1) blocked = True self._flush_some() finally: if blocked: self.socket.setblocking(0) def set_async(self): """Switches to asynchronous mode. The main thread will begin calling received() again. """ self.async_mode = True self.pull_trigger() # # METHODS USED IN BOTH MODES # def write(self, data): if data: self.outbuf.append(data) while len(self.outbuf) >= self.adj.send_bytes: # Send what we can without blocking. # We propagate errors to the application on purpose # (to stop the application if the connection closes). if not self._flush_some(): break def pull_trigger(self): """Wakes up the main loop. """ the_trigger.pull_trigger() def _flush_some(self): """Flushes data. Returns 1 if some data was sent.""" outbuf = self.outbuf if outbuf and self.connected: chunk = outbuf.get(self.adj.send_bytes) num_sent = self.send(chunk) if num_sent: outbuf.skip(num_sent, 1) return 1 return 0 def close_when_done(self): # Flush all possible. while self._flush_some(): pass self.will_close = True if not self.async_mode: # For safety, don't close the socket until the # main thread calls handle_write(). self.async_mode = True self.pull_trigger() def close(self): # Always close in asynchronous mode. If the connection is # closed in a thread, the main loop can end up with a bad file # descriptor. assert self.async_mode self.connected = False asyncore.dispatcher.close(self)