[stream-http-response] support chunk encoded responses
parent
3deb7c9e79
commit
5e0a58be53
|
@ -7,17 +7,58 @@ struct liStreamHttpResponse {
|
|||
|
||||
liStream stream;
|
||||
liVRequest *vr;
|
||||
gboolean response_headers_finished;
|
||||
gboolean response_headers_finished, transfer_encoding_chunked;
|
||||
liFilterChunkedDecodeState chunked_decode_state;
|
||||
};
|
||||
|
||||
static gboolean check_response_header(liStreamHttpResponse* shr) {
|
||||
liResponse *resp = &shr->vr->response;
|
||||
liHttpHeader *hh;
|
||||
GList *l;
|
||||
|
||||
shr->transfer_encoding_chunked = FALSE;
|
||||
|
||||
/* Transfer-Encoding: chunked */
|
||||
l = li_http_header_find_first(resp->headers, CONST_STR_LEN("transfer-encoding"));
|
||||
if (l) {
|
||||
for ( ; l ; l = li_http_header_find_next(l, CONST_STR_LEN("transfer-encoding")) ) {
|
||||
hh = (liHttpHeader*) l->data;
|
||||
if (0 == g_ascii_strcasecmp( LI_HEADER_VALUE(hh), "identity" )) {
|
||||
/* ignore */
|
||||
continue;
|
||||
} if (0 == g_ascii_strcasecmp( LI_HEADER_VALUE(hh), "chunked" )) {
|
||||
if (shr->transfer_encoding_chunked) {
|
||||
VR_ERROR(shr->vr, "%s", "Response is chunked encoded twice");
|
||||
li_vrequest_error(shr->vr);
|
||||
return FALSE;
|
||||
}
|
||||
shr->transfer_encoding_chunked = TRUE;
|
||||
} else {
|
||||
VR_ERROR(shr->vr, "Response has unsupported Transfer-Encoding: %s", LI_HEADER_VALUE(hh));
|
||||
li_vrequest_error(shr->vr);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
li_http_header_remove(resp->headers, CONST_STR_LEN("transfer-encoding"));
|
||||
/* any non trivial transfer-encoding overwrites content-length */
|
||||
if (shr->transfer_encoding_chunked) {
|
||||
li_http_header_remove(resp->headers, CONST_STR_LEN("content-length"));
|
||||
}
|
||||
}
|
||||
|
||||
shr->response_headers_finished = TRUE;
|
||||
li_vrequest_indirect_headers_ready(shr->vr);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void stream_http_response_data(liStreamHttpResponse* shr) {
|
||||
if (NULL == shr->stream.source) return;
|
||||
|
||||
if (!shr->response_headers_finished) {
|
||||
switch (li_http_response_parse(shr->vr, &shr->parse_response_ctx)) {
|
||||
case LI_HANDLER_GO_ON:
|
||||
shr->response_headers_finished = TRUE;
|
||||
li_vrequest_indirect_headers_ready(shr->vr);
|
||||
if (!check_response_header(shr)) return;
|
||||
break;
|
||||
case LI_HANDLER_ERROR:
|
||||
VR_ERROR(shr->vr, "%s", "Parsing response header failed");
|
||||
|
@ -34,10 +75,24 @@ static void stream_http_response_data(liStreamHttpResponse* shr) {
|
|||
}
|
||||
}
|
||||
|
||||
li_chunkqueue_steal_all(shr->stream.out, shr->stream.source->out);
|
||||
if (shr->stream.source->out->is_closed) {
|
||||
shr->stream.out->is_closed = TRUE;
|
||||
li_stream_disconnect(&shr->stream);
|
||||
if (shr->transfer_encoding_chunked) {
|
||||
if (!li_filter_chunked_decode(shr->vr, shr->stream.out, shr->stream.source->out, &shr->chunked_decode_state)) {
|
||||
if (NULL != shr->vr) {
|
||||
VR_ERROR(shr->vr, "%s", "Decoding chunks failed");
|
||||
li_vrequest_error(shr->vr);
|
||||
} else {
|
||||
li_stream_reset(&shr->stream);
|
||||
}
|
||||
}
|
||||
if (shr->stream.source->out->is_closed) {
|
||||
li_stream_disconnect(&shr->stream);
|
||||
}
|
||||
} else {
|
||||
li_chunkqueue_steal_all(shr->stream.out, shr->stream.source->out);
|
||||
if (shr->stream.source->out->is_closed) {
|
||||
shr->stream.out->is_closed = TRUE;
|
||||
li_stream_disconnect(&shr->stream);
|
||||
}
|
||||
}
|
||||
li_stream_notify(&shr->stream);
|
||||
}
|
||||
|
|
|
@ -38,6 +38,15 @@ printf '%s' "${csum}"
|
|||
|
||||
"""
|
||||
|
||||
SCRIPT_CHUNKEDENCODINGCHECK="""#!/bin/sh
|
||||
|
||||
printf 'Status: 200\\r\\nContent-Type: text/plain\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n'
|
||||
|
||||
printf 'd\\r\\nHello World!\\n\\r\\n0\\r\\n\\r\\n'
|
||||
|
||||
"""
|
||||
|
||||
|
||||
class TestPathInfo1(CurlRequest):
|
||||
URL = "/envcheck.cgi/abc/xyz?PATH_INFO"
|
||||
EXPECT_RESPONSE_BODY = "/abc/xyz"
|
||||
|
@ -81,12 +90,18 @@ class TestUploadLargeChunked1(CurlRequest):
|
|||
c.setopt(c.UPLOAD, 1)
|
||||
c.setopt(pycurl.READFUNCTION, ChunkedBodyReader(BODY).read)
|
||||
|
||||
class TestChunkedEncoding1(CurlRequest):
|
||||
URL = "/chunkedencodingcheck.cgi"
|
||||
EXPECT_RESPONSE_BODY = "Hello World!\n"
|
||||
EXPECT_RESPONSE_CODE = 200
|
||||
|
||||
class Test(GroupTest):
|
||||
group = [
|
||||
TestPathInfo1,
|
||||
TestRequestUri1,
|
||||
TestUploadLarge1,
|
||||
TestUploadLargeChunked1
|
||||
TestUploadLargeChunked1,
|
||||
TestChunkedEncoding1,
|
||||
]
|
||||
|
||||
config = """
|
||||
|
@ -117,3 +132,4 @@ cgi = {{
|
|||
def Prepare(self):
|
||||
self.PrepareVHostFile("envcheck.cgi", SCRIPT_ENVCHECK, mode = 0755)
|
||||
self.PrepareVHostFile("uploadcheck.cgi", SCRIPT_UPLOADCHECK, mode = 0755)
|
||||
self.PrepareVHostFile("chunkedencodingcheck.cgi", SCRIPT_CHUNKEDENCODINGCHECK, mode = 0755)
|
||||
|
|
Loading…
Reference in New Issue