A simple HTTP/1 server with no pipelining support.
| 221 | |
| 222 | |
| 223 | class Http1Server(Http1Connection): |
| 224 | """A simple HTTP/1 server with no pipelining support.""" |
| 225 | |
| 226 | ReceiveProtocolError = RequestProtocolError |
| 227 | ReceiveData = RequestData |
| 228 | ReceiveEndOfMessage = RequestEndOfMessage |
| 229 | stream_id: int |
| 230 | |
| 231 | def __init__(self, context: Context): |
| 232 | super().__init__(context, context.client) |
| 233 | self.stream_id = 1 |
| 234 | |
| 235 | def send(self, event: HttpEvent) -> layer.CommandGenerator[None]: |
| 236 | assert event.stream_id == self.stream_id |
| 237 | if isinstance(event, ResponseHeaders): |
| 238 | self.response = response = event.response |
| 239 | |
| 240 | if response.is_http2 or response.is_http3: |
| 241 | response = response.copy() |
| 242 | # Convert to an HTTP/1 response. |
| 243 | response.http_version = "HTTP/1.1" |
| 244 | # not everyone supports empty reason phrases, so we better make up one. |
| 245 | response.reason = status_codes.RESPONSES.get(response.status_code, "") |
| 246 | # Shall we set a Content-Length header here if there is none? |
| 247 | # For now, let's try to modify as little as possible. |
| 248 | |
| 249 | raw = http1.assemble_response_head(response) |
| 250 | yield commands.SendData(self.conn, raw) |
| 251 | elif isinstance(event, ResponseData): |
| 252 | assert self.response |
| 253 | if "chunked" in self.response.headers.get("transfer-encoding", "").lower(): |
| 254 | raw = b"%x\r\n%s\r\n" % (len(event.data), event.data) |
| 255 | else: |
| 256 | raw = event.data |
| 257 | if raw: |
| 258 | yield commands.SendData(self.conn, raw) |
| 259 | elif isinstance(event, ResponseEndOfMessage): |
| 260 | assert self.request |
| 261 | assert self.response |
| 262 | if ( |
| 263 | self.request.method.upper() != "HEAD" |
| 264 | and "chunked" |
| 265 | in self.response.headers.get("transfer-encoding", "").lower() |
| 266 | ): |
| 267 | yield commands.SendData(self.conn, b"0\r\n\r\n") |
| 268 | yield from self.mark_done(response=True) |
| 269 | elif isinstance(event, ResponseProtocolError): |
| 270 | if not (self.conn.state & ConnectionState.CAN_WRITE): |
| 271 | return |
| 272 | status = event.code.http_status_code() |
| 273 | if not self.response and status is not None: |
| 274 | yield commands.SendData( |
| 275 | self.conn, make_error_response(status, event.message) |
| 276 | ) |
| 277 | yield commands.CloseConnection(self.conn) |
| 278 | else: |
| 279 | raise AssertionError(f"Unexpected event: {event}") |
| 280 |
no outgoing calls
searching dependent graphs…