[core] fix setting of headers previously reset (fixes #2919)
bug may result in long delays when using mod_deflate on connections with keep-alive, as the result is sent without Content-Length or Transfer-Encoding (regression in lighttpd 1.4.51) (thx GilGalaad) x-ref: "high latency on 1.4.51 + proxy + deflate" https://redmine.lighttpd.net/boards/2/topics/8365 https://redmine.lighttpd.net/issues/2919
This commit is contained in:
parent
41b50cfa71
commit
f13db69012
|
@ -439,7 +439,7 @@ handler_t connection_handle_read_post_state(server *srv, connection *con) {
|
|||
&& chunkqueue_is_empty(con->write_queue) && con->is_writable) {
|
||||
buffer *vb = http_header_request_get(con, HTTP_HEADER_EXPECT, CONST_STR_LEN("Expect"));
|
||||
if (NULL != vb && 0 == buffer_caseless_compare(CONST_BUF_LEN(vb), CONST_STR_LEN("100-continue"))) {
|
||||
buffer_reset(vb); /* unset value in request headers */
|
||||
http_header_request_unset(con, HTTP_HEADER_EXPECT, CONST_STR_LEN("Expect"));
|
||||
if (!connection_write_100_continue(srv, con)) {
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
|
|
|
@ -373,8 +373,7 @@ static int connection_handle_write_prepare(server *srv, connection *con) {
|
|||
con->http_status == 204 ||
|
||||
con->http_status == 304) {
|
||||
/* no Content-Body, no Content-Length */
|
||||
buffer *vb = http_header_response_get(con, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length"));
|
||||
if (NULL != vb) buffer_reset(vb); /* Headers with empty values are ignored for output */
|
||||
http_header_response_unset(con, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length"));
|
||||
} else if (qlen > 0 || con->request.http_method != HTTP_METHOD_HEAD) {
|
||||
/* qlen = 0 is important for Redirects (301, ...) as they MAY have
|
||||
* a content. Browsers are waiting for a Content otherwise
|
||||
|
|
|
@ -189,12 +189,12 @@ int http_response_handle_cachable(server *srv, connection *con, buffer *mtime) {
|
|||
void http_response_body_clear (connection *con, int preserve_length) {
|
||||
con->response.send_chunked = 0;
|
||||
if (con->response.htags & HTTP_HEADER_TRANSFER_ENCODING) {
|
||||
http_header_response_set(con, HTTP_HEADER_TRANSFER_ENCODING, CONST_STR_LEN("Transfer-Encoding"), CONST_STR_LEN(""));
|
||||
http_header_response_unset(con, HTTP_HEADER_TRANSFER_ENCODING, CONST_STR_LEN("Transfer-Encoding"));
|
||||
}
|
||||
if (!preserve_length) { /* preserve for HEAD responses and no-content responses (204, 205, 304) */
|
||||
con->response.content_length = -1;
|
||||
if (con->response.htags & HTTP_HEADER_CONTENT_LENGTH) {
|
||||
http_header_response_set(con, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length"), CONST_STR_LEN(""));
|
||||
http_header_response_unset(con, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length"));
|
||||
}
|
||||
}
|
||||
chunkqueue_reset(con->write_queue);
|
||||
|
@ -549,7 +549,7 @@ static void http_response_xsendfile (server *srv, connection *con, buffer *path,
|
|||
* determined by open(), fstat() to reduces race conditions if the file
|
||||
* is modified between stat() (stat_cache_get_entry()) and open(). */
|
||||
if (con->response.htags & HTTP_HEADER_CONTENT_LENGTH) {
|
||||
http_header_response_set(con, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length"), CONST_STR_LEN(""));
|
||||
http_header_response_unset(con, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length"));
|
||||
con->response.content_length = -1;
|
||||
}
|
||||
|
||||
|
@ -601,7 +601,7 @@ static void http_response_xsendfile2(server *srv, connection *con, const buffer
|
|||
|
||||
/* reset Content-Length, if set by backend */
|
||||
if (con->response.htags & HTTP_HEADER_CONTENT_LENGTH) {
|
||||
http_header_response_set(con, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length"), CONST_STR_LEN(""));
|
||||
http_header_response_unset(con, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length"));
|
||||
con->response.content_length = -1;
|
||||
}
|
||||
|
||||
|
@ -1102,6 +1102,7 @@ handler_t http_response_parse_headers(server *srv, connection *con, http_respons
|
|||
if (opts->backend == BACKEND_FASTCGI
|
||||
&& NULL != (vb = http_header_response_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("X-Sendfile2")))) {
|
||||
http_response_xsendfile2(srv, con, vb, opts->xsendfile_docroot);
|
||||
/* http_header_response_unset() shortcut for HTTP_HEADER_OTHER */
|
||||
buffer_reset(vb); /*(do not send to client)*/
|
||||
if (con->mode == DIRECT) con->file_started = 0;
|
||||
return HANDLER_FINISHED;
|
||||
|
@ -1109,6 +1110,7 @@ handler_t http_response_parse_headers(server *srv, connection *con, http_respons
|
|||
|| (opts->backend == BACKEND_FASTCGI /* X-LIGHTTPD-send-file is deprecated; historical for fastcgi */
|
||||
&& NULL != (vb = http_header_response_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("X-LIGHTTPD-send-file"))))) {
|
||||
http_response_xsendfile(srv, con, vb, opts->xsendfile_docroot);
|
||||
/* http_header_response_unset() shortcut for HTTP_HEADER_OTHER */
|
||||
buffer_reset(vb); /*(do not send to client)*/
|
||||
if (con->mode == DIRECT) con->file_started = 0;
|
||||
return HANDLER_FINISHED;
|
||||
|
|
|
@ -64,26 +64,36 @@ buffer * http_header_response_get(connection *con, enum http_header_e id, const
|
|||
return ds && !buffer_string_is_empty(ds->value) ? ds->value : NULL;
|
||||
}
|
||||
|
||||
void http_header_response_unset(connection *con, enum http_header_e id, const char *k, size_t klen) {
|
||||
if (id <= HTTP_HEADER_OTHER || (con->response.htags & id)) {
|
||||
if (id > HTTP_HEADER_OTHER) con->response.htags &= ~id;
|
||||
array_set_key_value(con->response.headers, k, klen, CONST_STR_LEN(""));
|
||||
}
|
||||
}
|
||||
|
||||
void http_header_response_set(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen) {
|
||||
/* set value, including setting blank value if 0 == vlen
|
||||
* (note: if 0 == vlen, header is still inserted with blank value,
|
||||
* which is used to indicate a "removed" header)
|
||||
*/
|
||||
con->response.htags |= id;
|
||||
if (id > HTTP_HEADER_OTHER)
|
||||
(vlen) ? (con->response.htags |= id) : (con->response.htags &= ~id);
|
||||
array_set_key_value(con->response.headers, k, klen, v, vlen);
|
||||
}
|
||||
|
||||
void http_header_response_append(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen) {
|
||||
if (vlen) {
|
||||
buffer *vb = (id <= HTTP_HEADER_OTHER || (con->response.htags & id))
|
||||
? http_header_response_get(con, id, k, klen)
|
||||
data_string *ds= (id <= HTTP_HEADER_OTHER || (con->response.htags & id))
|
||||
? (data_string *)array_get_element_klen(con->response.headers,k,klen)
|
||||
: NULL;
|
||||
if (NULL == vb) {
|
||||
if (id > HTTP_HEADER_OTHER) con->response.htags |= id;
|
||||
if (NULL == ds) {
|
||||
array_insert_key_value(con->response.headers, k, klen, v, vlen);
|
||||
con->response.htags |= id;
|
||||
}
|
||||
else { /* append value */
|
||||
buffer_append_string_len(vb, CONST_STR_LEN(", "));
|
||||
buffer *vb = ds->value;
|
||||
if (!buffer_string_is_empty(vb))
|
||||
buffer_append_string_len(vb, CONST_STR_LEN(", "));
|
||||
buffer_append_string_len(vb, v, vlen);
|
||||
}
|
||||
}
|
||||
|
@ -91,17 +101,20 @@ void http_header_response_append(connection *con, enum http_header_e id, const c
|
|||
|
||||
void http_header_response_insert(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen) {
|
||||
if (vlen) {
|
||||
buffer *vb = (id <= HTTP_HEADER_OTHER || (con->response.htags & id))
|
||||
? http_header_response_get(con, id, k, klen)
|
||||
data_string *ds= (id <= HTTP_HEADER_OTHER || (con->response.htags & id))
|
||||
? (data_string *)array_get_element_klen(con->response.headers,k,klen)
|
||||
: NULL;
|
||||
if (NULL == vb) {
|
||||
if (id > HTTP_HEADER_OTHER) con->response.htags |= id;
|
||||
if (NULL == ds) {
|
||||
array_insert_key_value(con->response.headers, k, klen, v, vlen);
|
||||
con->response.htags |= id;
|
||||
}
|
||||
else { /* append value */
|
||||
buffer_append_string_len(vb, CONST_STR_LEN("\r\n"));
|
||||
buffer_append_string_len(vb, k, klen);
|
||||
buffer_append_string_len(vb, CONST_STR_LEN(": "));
|
||||
buffer *vb = ds->value;
|
||||
if (!buffer_string_is_empty(vb)) {
|
||||
buffer_append_string_len(vb, CONST_STR_LEN("\r\n"));
|
||||
buffer_append_string_len(vb, k, klen);
|
||||
buffer_append_string_len(vb, CONST_STR_LEN(": "));
|
||||
}
|
||||
buffer_append_string_len(vb, v, vlen);
|
||||
}
|
||||
}
|
||||
|
@ -116,26 +129,36 @@ buffer * http_header_request_get(connection *con, enum http_header_e id, const c
|
|||
return ds && !buffer_string_is_empty(ds->value) ? ds->value : NULL;
|
||||
}
|
||||
|
||||
void http_header_request_unset(connection *con, enum http_header_e id, const char *k, size_t klen) {
|
||||
if (id <= HTTP_HEADER_OTHER || (con->request.htags & id)) {
|
||||
if (id > HTTP_HEADER_OTHER) con->request.htags &= ~id;
|
||||
array_set_key_value(con->request.headers, k, klen, CONST_STR_LEN(""));
|
||||
}
|
||||
}
|
||||
|
||||
void http_header_request_set(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen) {
|
||||
/* set value, including setting blank value if 0 == vlen
|
||||
* (note: if 0 == vlen, header is still inserted with blank value,
|
||||
* which is used to indicate a "removed" header)
|
||||
*/
|
||||
con->request.htags |= id;
|
||||
if (id > HTTP_HEADER_OTHER)
|
||||
(vlen) ? (con->request.htags |= id) : (con->request.htags &= ~id);
|
||||
array_set_key_value(con->request.headers, k, klen, v, vlen);
|
||||
}
|
||||
|
||||
void http_header_request_append(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen) {
|
||||
if (vlen) {
|
||||
buffer *vb = (id <= HTTP_HEADER_OTHER || (con->request.htags & id))
|
||||
? http_header_request_get(con, id, k, klen)
|
||||
data_string *ds = (id <= HTTP_HEADER_OTHER || (con->request.htags & id))
|
||||
? (data_string *)array_get_element_klen(con->request.headers, k, klen)
|
||||
: NULL;
|
||||
if (NULL == vb) {
|
||||
if (id > HTTP_HEADER_OTHER) con->request.htags |= id;
|
||||
if (NULL == ds) {
|
||||
array_insert_key_value(con->request.headers, k, klen, v, vlen);
|
||||
con->request.htags |= id;
|
||||
}
|
||||
else { /* append value */
|
||||
buffer_append_string_len(vb, CONST_STR_LEN(", "));
|
||||
buffer *vb = ds->value;
|
||||
if (!buffer_string_is_empty(vb))
|
||||
buffer_append_string_len(vb, CONST_STR_LEN(", "));
|
||||
buffer_append_string_len(vb, v, vlen);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,11 +42,13 @@ enum http_header_e {
|
|||
enum http_header_e http_header_hkey_get(const char *s, size_t slen);
|
||||
|
||||
buffer * http_header_response_get(connection *con, enum http_header_e id, const char *k, size_t klen);
|
||||
void http_header_response_unset(connection *con, enum http_header_e id, const char *k, size_t klen);
|
||||
void http_header_response_set(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen);
|
||||
void http_header_response_append(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen);
|
||||
void http_header_response_insert(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen);
|
||||
|
||||
buffer * http_header_request_get(connection *con, enum http_header_e id, const char *k, size_t klen);
|
||||
void http_header_request_unset(connection *con, enum http_header_e id, const char *k, size_t klen);
|
||||
void http_header_request_set(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen);
|
||||
void http_header_request_append(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen);
|
||||
|
||||
|
|
|
@ -394,9 +394,8 @@ static handler_t cgi_response_headers(server *srv, connection *con, struct http_
|
|||
/* preserve prior questionable behavior; likely broken behavior
|
||||
* anyway if backend thinks connection is being upgraded but client
|
||||
* does not receive Connection: upgrade */
|
||||
http_header_response_set(con, HTTP_HEADER_UPGRADE,
|
||||
CONST_STR_LEN("Upgrade"),
|
||||
CONST_STR_LEN(""));
|
||||
http_header_response_unset(con, HTTP_HEADER_UPGRADE,
|
||||
CONST_STR_LEN("Upgrade"));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1196,13 +1196,12 @@ CONNECTION_FUNC(mod_deflate_handle_response_start) {
|
|||
vb->ptr[etaglen-1] = '"'; /*(overwrite '-')*/
|
||||
buffer_string_set_length(vb, etaglen);
|
||||
}
|
||||
vb = http_header_response_get(con, HTTP_HEADER_CONTENT_ENCODING, CONST_STR_LEN("Content-Encoding"));
|
||||
if (vb) buffer_reset(vb); /* headers with empty values are ignored for output */
|
||||
http_header_response_unset(con, HTTP_HEADER_CONTENT_ENCODING, CONST_STR_LEN("Content-Encoding"));
|
||||
return HANDLER_GO_ON;
|
||||
}
|
||||
|
||||
if (con->response.htags & HTTP_HEADER_CONTENT_LENGTH) {
|
||||
http_header_response_set(con, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length"), CONST_STR_LEN(""));
|
||||
http_header_response_unset(con, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length"));
|
||||
}
|
||||
con->plugin_ctx[p->id] = hctx;
|
||||
|
||||
|
|
|
@ -672,7 +672,9 @@ static int magnet_copy_response_header(connection *con, lua_State *L, int lighty
|
|||
const_buffer val = magnet_checkconstbuffer(L, -1);
|
||||
enum http_header_e id = http_header_hkey_get(key.ptr, key.len);
|
||||
|
||||
http_header_response_set(con, id, key.ptr, key.len, val.ptr, val.len);
|
||||
val.len
|
||||
? http_header_response_set(con, id, key.ptr, key.len, val.ptr, val.len)
|
||||
: http_header_response_unset(con, id, key.ptr, key.len);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
|
|
|
@ -936,9 +936,8 @@ static handler_t proxy_response_headers(server *srv, connection *con, struct htt
|
|||
/* preserve prior questionable behavior; likely broken behavior
|
||||
* anyway if backend thinks connection is being upgraded but client
|
||||
* does not receive Connection: upgrade */
|
||||
http_header_response_set(con, HTTP_HEADER_UPGRADE,
|
||||
CONST_STR_LEN("Upgrade"),
|
||||
CONST_STR_LEN(""));
|
||||
http_header_response_unset(con, HTTP_HEADER_UPGRADE,
|
||||
CONST_STR_LEN("Upgrade"))
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -227,7 +227,9 @@ URIHANDLER_FUNC(mod_setenv_uri_handler) {
|
|||
for (k = 0; k < hctx->conf.set_request_header->used; ++k) {
|
||||
data_string *ds = (data_string *)hctx->conf.set_request_header->data[k];
|
||||
enum http_header_e id = http_header_hkey_get(CONST_BUF_LEN(ds->key));
|
||||
http_header_request_set(con, id, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value));
|
||||
!buffer_string_is_empty(ds->value)
|
||||
? http_header_request_set(con, id, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value))
|
||||
: http_header_request_unset(con, id, CONST_BUF_LEN(ds->key));
|
||||
}
|
||||
|
||||
return HANDLER_GO_ON;
|
||||
|
@ -269,7 +271,9 @@ CONNECTION_FUNC(mod_setenv_handle_response_start) {
|
|||
for (size_t k = 0; k < hctx->conf.set_response_header->used; ++k) {
|
||||
data_string *ds = (data_string *)hctx->conf.set_response_header->data[k];
|
||||
enum http_header_e id = http_header_hkey_get(CONST_BUF_LEN(ds->key));
|
||||
http_header_response_set(con, id, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value));
|
||||
!buffer_string_is_empty(ds->value)
|
||||
? http_header_response_set(con, id, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value))
|
||||
: http_header_response_unset(con, id, CONST_BUF_LEN(ds->key));
|
||||
}
|
||||
|
||||
return HANDLER_GO_ON;
|
||||
|
|
|
@ -1127,12 +1127,11 @@ int http_request_parse(server *srv, connection *con) {
|
|||
|
||||
/* reset value for Transfer-Encoding, a hop-by-hop header,
|
||||
* which must not be blindly forwarded to backends */
|
||||
buffer_reset(vb); /* headers with empty values are ignored */
|
||||
http_header_request_unset(con, HTTP_HEADER_TRANSFER_ENCODING, CONST_STR_LEN("Transfer-Encoding"));
|
||||
|
||||
/*(note: ignore whether or not Content-Length was provided)*/
|
||||
if (con->request.htags & HTTP_HEADER_CONTENT_LENGTH) {
|
||||
vb = http_header_request_get(con, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length"));
|
||||
if (NULL != vb) buffer_reset(vb); /* headers with empty values are ignored */
|
||||
http_header_request_unset(con, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length"));
|
||||
}
|
||||
|
||||
state.con_length_set = 1;
|
||||
|
|
|
@ -49,8 +49,7 @@ int http_response_write_header(server *srv, connection *con) {
|
|||
}
|
||||
|
||||
if (304 == con->http_status && (con->response.htags & HTTP_HEADER_CONTENT_ENCODING)) {
|
||||
buffer *vb = http_header_response_get(con, HTTP_HEADER_CONTENT_ENCODING, CONST_STR_LEN("Content-Encoding"));
|
||||
if (NULL != vb) buffer_reset(vb);
|
||||
http_header_response_unset(con, HTTP_HEADER_CONTENT_ENCODING, CONST_STR_LEN("Content-Encoding"));
|
||||
}
|
||||
|
||||
/* add all headers */
|
||||
|
|
Loading…
Reference in New Issue