mirror of
https://github.com/django/daphne.git
synced 2024-11-21 23:46:33 +03:00
Match to the new ASGI-HTTP spec.
This commit is contained in:
parent
7fb3e9a167
commit
20ff8fec28
|
@ -1,7 +1,6 @@
|
||||||
import sys
|
import sys
|
||||||
import argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
import importlib
|
|
||||||
from .server import Server
|
from .server import Server
|
||||||
from .endpoints import build_endpoint_description_strings
|
from .endpoints import build_endpoint_description_strings
|
||||||
from .access import AccessLogGenerator
|
from .access import AccessLogGenerator
|
||||||
|
|
|
@ -198,7 +198,7 @@ class WebRequest(http.Request):
|
||||||
"""
|
"""
|
||||||
if "type" not in message:
|
if "type" not in message:
|
||||||
raise ValueError("Message has no type defined")
|
raise ValueError("Message has no type defined")
|
||||||
if message["type"] == "http.response":
|
if message["type"] == "http.response.start":
|
||||||
if self._response_started:
|
if self._response_started:
|
||||||
raise ValueError("HTTP response has already been started")
|
raise ValueError("HTTP response has already been started")
|
||||||
self._response_started = True
|
self._response_started = True
|
||||||
|
@ -213,33 +213,31 @@ class WebRequest(http.Request):
|
||||||
header = header.encode("latin1")
|
header = header.encode("latin1")
|
||||||
self.responseHeaders.addRawHeader(header, value)
|
self.responseHeaders.addRawHeader(header, value)
|
||||||
logger.debug("HTTP %s response started for %s", message["status"], self.client_addr)
|
logger.debug("HTTP %s response started for %s", message["status"], self.client_addr)
|
||||||
elif message["type"] == "http.response.hunk":
|
elif message["type"] == "http.response.content":
|
||||||
if not self._response_started:
|
if not self._response_started:
|
||||||
raise ValueError("HTTP response has not yet been started but got %s" % message["type"])
|
raise ValueError("HTTP response has not yet been started but got %s" % message["type"])# Write out body
|
||||||
|
http.Request.write(self, message.get("content", b""))
|
||||||
|
|
||||||
|
# End if there's no more content
|
||||||
|
if not message.get("more_content", False):
|
||||||
|
self.finish()
|
||||||
|
logger.debug("HTTP response complete for %s", self.client_addr)
|
||||||
|
try:
|
||||||
|
self.server.log_action("http", "complete", {
|
||||||
|
"path": self.uri.decode("ascii"),
|
||||||
|
"status": self.code,
|
||||||
|
"method": self.method.decode("ascii"),
|
||||||
|
"client": "%s:%s" % tuple(self.client_addr) if self.client_addr else None,
|
||||||
|
"time_taken": self.duration(),
|
||||||
|
"size": self.sentLength,
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(traceback.format_exc())
|
||||||
|
else:
|
||||||
|
logger.debug("HTTP response chunk for %s", self.client_addr)
|
||||||
else:
|
else:
|
||||||
raise ValueError("Cannot handle message type %s!" % message["type"])
|
raise ValueError("Cannot handle message type %s!" % message["type"])
|
||||||
|
|
||||||
# Write out body
|
|
||||||
http.Request.write(self, message.get("content", b""))
|
|
||||||
|
|
||||||
# End if there's no more content
|
|
||||||
if not message.get("more_content", False):
|
|
||||||
self.finish()
|
|
||||||
logger.debug("HTTP response complete for %s", self.client_addr)
|
|
||||||
try:
|
|
||||||
self.server.log_action("http", "complete", {
|
|
||||||
"path": self.uri.decode("ascii"),
|
|
||||||
"status": self.code,
|
|
||||||
"method": self.method.decode("ascii"),
|
|
||||||
"client": "%s:%s" % tuple(self.client_addr) if self.client_addr else None,
|
|
||||||
"time_taken": self.duration(),
|
|
||||||
"size": self.sentLength,
|
|
||||||
})
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(traceback.format_exc())
|
|
||||||
else:
|
|
||||||
logger.debug("HTTP response chunk for %s", self.client_addr)
|
|
||||||
|
|
||||||
def handle_exception(self, exception):
|
def handle_exception(self, exception):
|
||||||
"""
|
"""
|
||||||
Called by the server when our application tracebacks
|
Called by the server when our application tracebacks
|
||||||
|
@ -298,12 +296,14 @@ class WebRequest(http.Request):
|
||||||
Responds with a server-level error page (very basic)
|
Responds with a server-level error page (very basic)
|
||||||
"""
|
"""
|
||||||
self.handle_reply({
|
self.handle_reply({
|
||||||
"type": "http.response",
|
"type": "http.response.start",
|
||||||
"status": status,
|
"status": status,
|
||||||
"status_text": status_text,
|
|
||||||
"headers": [
|
"headers": [
|
||||||
(b"Content-Type", b"text/html; charset=utf-8"),
|
(b"Content-Type", b"text/html; charset=utf-8"),
|
||||||
],
|
],
|
||||||
|
})
|
||||||
|
self.handle_reply({
|
||||||
|
"type": "http.response.content",
|
||||||
"content": (self.error_template % {
|
"content": (self.error_template % {
|
||||||
"title": six.text_type(status) + " " + status_text.decode("ascii"),
|
"title": six.text_type(status) + " " + status_text.decode("ascii"),
|
||||||
"body": body,
|
"body": body,
|
||||||
|
|
|
@ -158,7 +158,10 @@ class DaphneTestCase(unittest.TestCase):
|
||||||
body=body,
|
body=body,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
xff=xff,
|
xff=xff,
|
||||||
responses=[{"type": "http.response", "status": 200, "content": b"OK"}],
|
responses=[
|
||||||
|
{"type": "http.response.start", "status": 200},
|
||||||
|
{"type": "http.response.content", "content": b"OK"},
|
||||||
|
],
|
||||||
)
|
)
|
||||||
return scope, messages
|
return scope, messages
|
||||||
|
|
||||||
|
@ -247,7 +250,6 @@ class DaphneTestCase(unittest.TestCase):
|
||||||
frame += b"\0\0\0\0"
|
frame += b"\0\0\0\0"
|
||||||
# Payload
|
# Payload
|
||||||
frame += value
|
frame += value
|
||||||
print("sending %r" % frame)
|
|
||||||
sock.sendall(frame)
|
sock.sendall(frame)
|
||||||
|
|
||||||
def receive_from_socket(self, sock, length, timeout=1):
|
def receive_from_socket(self, sock, length, timeout=1):
|
||||||
|
|
|
@ -27,8 +27,11 @@ class TestHTTPResponse(DaphneTestCase):
|
||||||
"""
|
"""
|
||||||
response = self.run_daphne_response([
|
response = self.run_daphne_response([
|
||||||
{
|
{
|
||||||
"type": "http.response",
|
"type": "http.response.start",
|
||||||
"status": 200,
|
"status": 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "http.response.content",
|
||||||
"content": b"hello world",
|
"content": b"hello world",
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
@ -45,7 +48,10 @@ class TestHTTPResponse(DaphneTestCase):
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
self.run_daphne_response([
|
self.run_daphne_response([
|
||||||
{
|
{
|
||||||
"type": "http.response",
|
"type": "http.response.start",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "http.response.content",
|
||||||
"content": b"hello world",
|
"content": b"hello world",
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
@ -56,14 +62,65 @@ class TestHTTPResponse(DaphneTestCase):
|
||||||
"""
|
"""
|
||||||
response = self.run_daphne_response([
|
response = self.run_daphne_response([
|
||||||
{
|
{
|
||||||
"type": "http.response",
|
"type": "http.response.start",
|
||||||
"status": 201,
|
"status": 201,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "http.response.content",
|
||||||
"content": b"i made a thing!",
|
"content": b"i made a thing!",
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
self.assertEqual(response.status, 201)
|
self.assertEqual(response.status, 201)
|
||||||
self.assertEqual(response.read(), b"i made a thing!")
|
self.assertEqual(response.read(), b"i made a thing!")
|
||||||
|
|
||||||
|
def test_chunked_response(self):
|
||||||
|
"""
|
||||||
|
Tries sending a response in multiple parts.
|
||||||
|
"""
|
||||||
|
response = self.run_daphne_response([
|
||||||
|
{
|
||||||
|
"type": "http.response.start",
|
||||||
|
"status": 201,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "http.response.content",
|
||||||
|
"content": b"chunk 1 ",
|
||||||
|
"more_content": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "http.response.content",
|
||||||
|
"content": b"chunk 2",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
self.assertEqual(response.status, 201)
|
||||||
|
self.assertEqual(response.read(), b"chunk 1 chunk 2")
|
||||||
|
|
||||||
|
def test_chunked_response_empty(self):
|
||||||
|
"""
|
||||||
|
Tries sending a response in multiple parts and an empty end.
|
||||||
|
"""
|
||||||
|
response = self.run_daphne_response([
|
||||||
|
{
|
||||||
|
"type": "http.response.start",
|
||||||
|
"status": 201,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "http.response.content",
|
||||||
|
"content": b"chunk 1 ",
|
||||||
|
"more_content": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "http.response.content",
|
||||||
|
"content": b"chunk 2",
|
||||||
|
"more_content": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "http.response.content",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
self.assertEqual(response.status, 201)
|
||||||
|
self.assertEqual(response.read(), b"chunk 1 chunk 2")
|
||||||
|
|
||||||
@given(body=http_strategies.http_body())
|
@given(body=http_strategies.http_body())
|
||||||
@settings(max_examples=5, deadline=2000)
|
@settings(max_examples=5, deadline=2000)
|
||||||
def test_body(self, body):
|
def test_body(self, body):
|
||||||
|
@ -72,8 +129,11 @@ class TestHTTPResponse(DaphneTestCase):
|
||||||
"""
|
"""
|
||||||
response = self.run_daphne_response([
|
response = self.run_daphne_response([
|
||||||
{
|
{
|
||||||
"type": "http.response",
|
"type": "http.response.start",
|
||||||
"status": 200,
|
"status": 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "http.response.content",
|
||||||
"content": body,
|
"content": body,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
@ -86,10 +146,13 @@ class TestHTTPResponse(DaphneTestCase):
|
||||||
# The ASGI spec requires us to lowercase our header names
|
# The ASGI spec requires us to lowercase our header names
|
||||||
response = self.run_daphne_response([
|
response = self.run_daphne_response([
|
||||||
{
|
{
|
||||||
"type": "http.response",
|
"type": "http.response.start",
|
||||||
"status": 200,
|
"status": 200,
|
||||||
"headers": self.normalize_headers(headers),
|
"headers": self.normalize_headers(headers),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "http.response.content",
|
||||||
|
},
|
||||||
])
|
])
|
||||||
# Check headers in a sensible way. Ignore transfer-encoding.
|
# Check headers in a sensible way. Ignore transfer-encoding.
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
|
Loading…
Reference in New Issue
Block a user