diff --git a/gunicorn/asgi/protocol.py b/gunicorn/asgi/protocol.py index 0eb1d045..01569ce4 100644 --- a/gunicorn/asgi/protocol.py +++ b/gunicorn/asgi/protocol.py @@ -143,7 +143,17 @@ class ASGIProtocol(asyncio.Protocol): self._close_transport() def _is_websocket_upgrade(self, request): - """Check if request is a WebSocket upgrade.""" + """Check if request is a WebSocket upgrade. + + Per RFC 6455 Section 4.1, the opening handshake requires: + - HTTP method MUST be GET + - Upgrade header MUST be "websocket" (case-insensitive) + - Connection header MUST contain "Upgrade" + """ + # RFC 6455: The method of the request MUST be GET + if request.method != "GET": + return False + upgrade = None connection = None for name, value in request.headers: diff --git a/gunicorn/uwsgi/message.py b/gunicorn/uwsgi/message.py index a63172eb..db69b5e2 100644 --- a/gunicorn/uwsgi/message.py +++ b/gunicorn/uwsgi/message.py @@ -172,7 +172,29 @@ class UWSGIRequest: var_count += 1 def _extract_request_info(self): - """Extract HTTP request info from uWSGI vars.""" + """Extract HTTP request info from uWSGI vars. + + Header Mapping (CGI/WSGI to HTTP): + + The uWSGI protocol passes HTTP headers using CGI-style environment + variable naming. This method converts them back to HTTP header format: + + - HTTP_* vars: Strip 'HTTP_' prefix, replace '_' with '-' + Example: HTTP_X_FORWARDED_FOR -> X-FORWARDED-FOR + Example: HTTP_ACCEPT_ENCODING -> ACCEPT-ENCODING + + - CONTENT_TYPE: Mapped directly to CONTENT-TYPE header + (CGI spec excludes HTTP_ prefix for this header) + + - CONTENT_LENGTH: Mapped directly to CONTENT-LENGTH header + (CGI spec excludes HTTP_ prefix for this header) + + Note: The underscore-to-hyphen conversion is lossy. Headers that + originally contained underscores (e.g., X_Custom_Header) cannot be + distinguished from hyphenated headers (X-Custom-Header) after + passing through nginx/uWSGI. This is a CGI/WSGI specification + limitation, not specific to this implementation. + """ # Method self.method = self.uwsgi_vars.get('REQUEST_METHOD', 'GET') @@ -192,7 +214,8 @@ class UWSGIRequest: elif 'wsgi.url_scheme' in self.uwsgi_vars: self.scheme = self.uwsgi_vars['wsgi.url_scheme'] - # Extract HTTP headers (HTTP_* vars) + # Extract HTTP headers from CGI-style vars + # See docstring above for mapping details for key, value in self.uwsgi_vars.items(): if key.startswith('HTTP_'): # Convert HTTP_HEADER_NAME to HEADER-NAME