def __call__(self, out, buf): try: # read http message (response line + headers) try: raw_data = yield from buf.readuntil( b'\r\n\r\n', self.max_line_size + self.max_headers) except errors.LineLimitExceededParserError as exc: raise errors.LineTooLong(exc.limit) from None lines = raw_data.decode('ascii', 'surrogateescape').splitlines(True) line = lines[0] try: version, status = line.split(None, 1) except ValueError: raise errors.BadStatusLine(line) from None else: try: status, reason = status.split(None, 1) except ValueError: reason = '' # version match = VERSRE.match(version) if match is None: raise errors.BadStatusLine(line) version = (int(match.group(1)), int(match.group(2))) # The status code is a three-digit number try: status = int(status) except ValueError: raise errors.BadStatusLine(line) from None if status < 100 or status > 999: raise errors.BadStatusLine(line) # read headers headers, close, compression = self.parse_headers(lines) if close is None: close = version <= (1, 0) out.feed_data( RawResponseMessage(version, status, reason.strip(), headers, close, compression)) out.feed_eof() except aiohttp.EofStream: # Presumably, the server closed the connection before # sending a valid response. raise errors.ClientConnectionError( 'Can not read status line') from None
def __call__(self, out, buf): # read http message (response line + headers) try: raw_data = yield from buf.readuntil( b'\r\n\r\n', self.max_line_size + self.max_headers) except errors.LineLimitExceededParserError as exc: raise errors.LineTooLong(exc.limit) from None lines = raw_data.decode('ascii', 'surrogateescape').split('\r\n') line = lines[0] try: version, status = line.split(None, 1) except ValueError: raise errors.BadStatusLine(line) from None else: try: status, reason = status.split(None, 1) except ValueError: reason = '' # version match = VERSRE.match(version) if match is None: raise errors.BadStatusLine(line) version = HttpVersion(int(match.group(1)), int(match.group(2))) # The status code is a three-digit number try: status = int(status) except ValueError: raise errors.BadStatusLine(line) from None if status < 100 or status > 999: raise errors.BadStatusLine(line) # read headers headers, close, compression = self.parse_headers(lines) if close is None: close = version <= HttpVersion10 out.feed_data( RawResponseMessage(version, status, reason.strip(), headers, close, compression)) out.feed_eof()
def __call__(self, out, buf): # read http message (request line + headers) try: raw_data = yield from buf.readuntil(b'\r\n\r\n', self.max_headers) except errors.LineLimitExceededParserError as exc: raise errors.LineTooLong(exc.limit) from None lines = raw_data.decode('ascii', 'surrogateescape').split('\r\n') # request line line = lines[0] try: method, path, version = line.split(None, 2) except ValueError: raise errors.BadStatusLine(line) from None # method method = method.upper() if not METHRE.match(method): raise errors.BadStatusLine(method) # version match = VERSRE.match(version) if match is None: raise errors.BadStatusLine(version) version = HttpVersion(int(match.group(1)), int(match.group(2))) # read headers headers, close, compression = self.parse_headers(lines) if version <= HttpVersion10: close = True elif close is None: close = False out.feed_data( RawRequestMessage(method, path, version, headers, close, compression)) out.feed_eof()
def parse_headers(self, lines): """Parses RFC2822 headers from a stream. Line continuations are supported. Returns list of header name and value pairs. Header name is in upper case. """ close_conn = None encoding = None headers = [] lines_idx = 1 line = lines[1] while line: header_length = len(line) # Parse initial header name : value pair. try: name, value = line.split(':', 1) except ValueError: raise ValueError('Invalid header: {}'.format(line)) from None name = name.strip(' \t').upper() if HDRRE.search(name): raise ValueError('Invalid header name: {}'.format(name)) # next line lines_idx += 1 line = lines[lines_idx] # consume continuation lines continuation = line and line[0] in CONTINUATION if continuation: value = [value] while continuation: header_length += len(line) if header_length > self.max_field_size: raise errors.LineTooLong( 'limit request headers fields size') value.append(line) # next line lines_idx += 1 line = lines[lines_idx] continuation = line[0] in CONTINUATION value = '\r\n'.join(value) else: if header_length > self.max_field_size: raise errors.LineTooLong( 'limit request headers fields size') value = value.strip() # keep-alive and encoding if name == 'CONNECTION': v = value.lower() if v == 'close': close_conn = True elif v == 'keep-alive': close_conn = False elif name == 'CONTENT-ENCODING': enc = value.lower() if enc in ('gzip', 'deflate'): encoding = enc headers.append((name, value)) return multidict.MultiDict(headers), close_conn, encoding