From 5e4a94b0c66e2d2818e74e6a8584ca218dc7866f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20B=C3=BChler?= Date: Sat, 25 May 2013 23:51:46 +0200 Subject: [PATCH] [core] handle Connection: Upgrade --- include/lighttpd/connection.h | 5 +- include/lighttpd/http_headers.h | 11 +++ include/lighttpd/response.h | 2 +- include/lighttpd/virtualrequest.h | 5 ++ src/main/connection.c | 48 +++++++++++- src/main/http_headers.c | 118 ++++++++++++++++++++++++++++++ src/main/response.c | 14 ++-- src/main/stream_http_response.c | 53 ++++++++++++-- src/main/subrequest_lua.c | 8 +- src/main/virtualrequest.c | 16 ++++ src/modules/fastcgi_stream.c | 4 +- src/modules/mod_proxy.c | 10 +++ src/modules/mod_status.c | 24 +++--- 13 files changed, 289 insertions(+), 29 deletions(-) diff --git a/include/lighttpd/connection.h b/include/lighttpd/connection.h index af05816..b9f4d40 100644 --- a/include/lighttpd/connection.h +++ b/include/lighttpd/connection.h @@ -26,8 +26,11 @@ typedef enum { /** write remaining bytes from raw_out, mainvr finished (or not started) */ LI_CON_STATE_WRITE, + + /** connection was upgraded */ + LI_CON_STATE_UPGRADED } liConnectionState; -#define LI_CON_STATE_LAST LI_CON_STATE_WRITE +#define LI_CON_STATE_LAST LI_CON_STATE_UPGRADED /* update mod_status too */ typedef struct liConnectionSocketCallbacks liConnectionSocketCallbacks; diff --git a/include/lighttpd/http_headers.h b/include/lighttpd/http_headers.h index 9a21a64..ca16ca3 100644 --- a/include/lighttpd/http_headers.h +++ b/include/lighttpd/http_headers.h @@ -23,6 +23,12 @@ struct liHttpHeaders { GQueue entries; }; +typedef struct liHttpHeaderTokenizer liHttpHeaderTokenizer; +struct liHttpHeaderTokenizer { + GList *cur; + guint pos; +}; + /* strings always get copied, so you should free key and value yourself */ LI_API liHttpHeaders* li_http_headers_new(void); @@ -59,4 +65,9 @@ INLINE gboolean li_http_header_key_is(liHttpHeader *h, const gchar *key, size_t return (h->keylen == keylen && 0 == g_ascii_strncasecmp(key, h->data->str, keylen)); } +/* very simple tokenizer. splits at ' ' and ',', unquotes \\ escapes and "..." tokens */ +LI_API void li_http_header_tokenizer_start(liHttpHeaderTokenizer *tokenizer, liHttpHeaders *headers, const gchar *key, size_t keylen); +LI_API gboolean li_http_header_tokenizer_next(liHttpHeaderTokenizer *tokenizer, GString *token); + + #endif diff --git a/include/lighttpd/response.h b/include/lighttpd/response.h index 714ee06..0e88894 100644 --- a/include/lighttpd/response.h +++ b/include/lighttpd/response.h @@ -15,6 +15,6 @@ LI_API void li_response_init(liResponse *resp); LI_API void li_response_reset(liResponse *resp); LI_API void li_response_clear(liResponse *resp); -LI_API void li_response_send_headers(liVRequest *vr, liChunkQueue *raw_out, liChunkQueue *response_body); +LI_API void li_response_send_headers(liVRequest *vr, liChunkQueue *raw_out, liChunkQueue *response_body, gboolean upgraded); #endif diff --git a/include/lighttpd/virtualrequest.h b/include/lighttpd/virtualrequest.h index d22c878..d2ba826 100644 --- a/include/lighttpd/virtualrequest.h +++ b/include/lighttpd/virtualrequest.h @@ -36,10 +36,12 @@ typedef enum { typedef void (*liVRequestHandlerCB)(liVRequest *vr); typedef liThrottleState* (*liVRequestThrottleCB)(liVRequest *vr); +typedef void (*liVRequestConnectionUpgradeCB)(liVRequest *vr, liStream *backend_drain, liStream *backend_source); struct liConCallbacks { liVRequestHandlerCB handle_response_error; /* this is _not_ for 500 - internal error */ liVRequestThrottleCB throttle_out, throttle_in; + liVRequestConnectionUpgradeCB connection_upgrade; }; /* this data "belongs" to a vrequest, but is updated by the connection code */ @@ -167,6 +169,9 @@ LI_API void li_vrequest_indirect_connect(liVRequest *vr, liStream *backend_drain /* received all response headers/status code - call once from your indirect handler */ LI_API void li_vrequest_indirect_headers_ready(liVRequest *vr); +/* call instead of headers_ready */ +LI_API void li_vrequest_connection_upgrade(liVRequest *vr, liStream *backend_drain, liStream *backend_source); + /* Signals an internal error; handles the error in the _next_ loop */ LI_API void li_vrequest_error(liVRequest *vr); diff --git a/src/main/connection.c b/src/main/connection.c index 5a3b3cc..f9a01ec 100644 --- a/src/main/connection.c +++ b/src/main/connection.c @@ -129,7 +129,6 @@ static liThrottleState* simple_tcp_throttle_in(liConnection *con) { return data->sock_stream->throttle_in; } - static const liConnectionSocketCallbacks simple_tcp_cbs = { simple_tcp_finished, simple_tcp_throttle_out, @@ -220,6 +219,12 @@ static void _connection_http_in_cb(liStream *stream, liStreamEvent event) { if (0 == raw_in->length) return; /* no (new) data */ + if (LI_CON_STATE_UPGRADED == con->state) { + li_chunkqueue_steal_all(in, raw_in); + li_stream_notify(stream); + return; + } + if (con->state == LI_CON_STATE_KEEP_ALIVE) { /* stop keep alive timeout watchers */ if (con->keep_alive_data.link) { @@ -407,7 +412,7 @@ static void _connection_http_out_cb(liStream *stream, liStreamEvent event) { VR_DEBUG(vr, "%s", "write response headers"); } con->response_headers_sent = TRUE; - li_response_send_headers(vr, raw_out, out); + li_response_send_headers(vr, raw_out, out, FALSE); } if (!con->out_has_all_data && !raw_out->is_closed && NULL != out) { @@ -573,10 +578,45 @@ static liThrottleState* mainvr_throttle_in(liVRequest *vr) { return con->con_sock.callbacks->throttle_in(con); } +static void mainvr_connection_upgrade(liVRequest *vr, liStream *backend_drain, liStream *backend_source) { + liConnection* con = li_connection_from_vrequest(vr); + assert(NULL != con); + + if (con->response_headers_sent || NULL != con->out.source) { + li_connection_error(con); + return; + } + if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { + VR_DEBUG(vr, "%s", "connection upgrade: write response headers"); + } + con->response_headers_sent = TRUE; + con->info.keep_alive = FALSE; + li_response_send_headers(vr, con->out.out, NULL, TRUE); + con->state = LI_CON_STATE_UPGRADED; + vr->response.transfer_encoding = 0; + + li_stream_disconnect_dest(&con->in); + con->in.out->is_closed = FALSE; + + li_stream_connect(&con->in, backend_drain); + li_stream_connect(backend_source, &con->out); + + li_vrequest_reset(con->mainvr, TRUE); + + if (NULL != con->in.source) { + li_chunkqueue_steal_all(con->out.out, backend_drain->out); + } + con->info.out_queue_length = con->out.out->length; + + li_stream_notify(&con->out); + li_stream_notify(&con->in); +} + static const liConCallbacks con_callbacks = { mainvr_handle_response_error, mainvr_throttle_out, - mainvr_throttle_in + mainvr_throttle_in, + mainvr_connection_upgrade }; liConnection* li_connection_new(liWorker *wrk) { @@ -815,6 +855,8 @@ gchar *li_connection_state_str(liConnectionState state) { return "handle main vrequest"; case LI_CON_STATE_WRITE: return "write"; + case LI_CON_STATE_UPGRADED: + return "upgraded"; } return "undefined"; diff --git a/src/main/http_headers.c b/src/main/http_headers.c index 89deeeb..61bf748 100644 --- a/src/main/http_headers.c +++ b/src/main/http_headers.c @@ -7,6 +7,45 @@ static void _http_header_free(gpointer p) { g_slice_free(liHttpHeader, h); } +/* remove folding */ +static void _http_header_sanitize(liHttpHeader *h) { + guint i, j, len = h->data->len; + gboolean folding = FALSE; + char *str = h->data->str; + for (i = h->keylen + 2; i < len; ++i) { + switch (str[i]) { + case '\r': + case '\n': + goto cleanup; + default: + break; + } + } + return; + +cleanup: + for (j = i; i < len; ++i) { + switch (str[i]) { + case '\r': + case '\n': + folding = TRUE; + break; + case ' ': + case '\t': + if (!folding) str[j++] = str[i]; + break; + default: + if (folding) { + str[j++] = ' '; + folding = FALSE; + } + str[j++] = str[i]; + break; + } + } + g_string_truncate(h->data, j); +} + static liHttpHeader* _http_header_new(const gchar *key, size_t keylen, const gchar *val, size_t valuelen) { liHttpHeader *h = g_slice_new0(liHttpHeader); gchar *s; @@ -19,6 +58,7 @@ static liHttpHeader* _http_header_new(const gchar *key, size_t keylen, const gch memcpy(s, ": ", 2); s += 2; memcpy(s, val, valuelen); + _http_header_sanitize(h); return h; } @@ -173,3 +213,81 @@ void li_http_header_get_all(GString *dest, liHttpHeaders *headers, const gchar * g_string_append_len(dest, &h->data->str[h->keylen+2], h->data->len - (h->keylen + 2)); } } + +void li_http_header_tokenizer_start(liHttpHeaderTokenizer *tokenizer, liHttpHeaders *headers, const gchar *key, size_t keylen) { + if (NULL != (tokenizer->cur = li_http_header_find_first(headers, key, keylen))) { + liHttpHeader *h = (liHttpHeader*) tokenizer->cur->data; + tokenizer->pos = h->keylen + 2; + } else { + tokenizer->pos = 0; + } +} + +gboolean li_http_header_tokenizer_next(liHttpHeaderTokenizer *tokenizer, GString *token) { + liHttpHeader *h; + guint len; + guint pos = tokenizer->pos; + gchar *str; + + g_string_truncate(token, 0); + + if (NULL == tokenizer->cur) return FALSE; + h = (liHttpHeader*) tokenizer->cur->data; + len = h->data->len; + str = h->data->str; + + for (;;++pos) { + while (pos >= len) { + if (token->len > 0) { + tokenizer->pos = pos; + return TRUE; + } + if (NULL != (tokenizer->cur = li_http_header_find_next(tokenizer->cur, LI_HEADER_KEY_LEN(h)))) { + h = (liHttpHeader*) tokenizer->cur->data; + pos = tokenizer->pos = h->keylen + 2; + len = h->data->len; + str = h->data->str; + } else { + tokenizer->pos = 0; + return FALSE; + } + } + + switch (str[pos]) { + case '"': + ++pos; + if (token->len > 0) return FALSE; /* either the complete token is quoted or nothing */ + goto quoted; + case ' ': + case ',': + if (token->len == 0) continue; + tokenizer->pos = pos+1; + return TRUE; + case '\\': + ++pos; + if (pos >= len) return FALSE; /* no character after backslash */ + /* fall through, append whatever comes */ + default: + g_string_append_c(token, str[pos]); + break; + } + } + +quoted: + for (; pos < len; ++pos) { + switch (str[pos]) { + case '"': + ++pos; + tokenizer->pos = pos; + return TRUE; + case '\\': + ++pos; + if (pos >= len) return FALSE; /* no character after backslash */ + /* fall through, append whatever comes */ + default: + g_string_append_c(token, str[pos]); + break; + } + } + return FALSE; /* no terminating quote found */ +} diff --git a/src/main/response.c b/src/main/response.c index 4bdc0d4..8fd01bf 100644 --- a/src/main/response.c +++ b/src/main/response.c @@ -24,7 +24,7 @@ void li_response_clear(liResponse *resp) { static void li_response_send_error_page(liVRequest *vr, liChunkQueue *response_body); -void li_response_send_headers(liVRequest *vr, liChunkQueue *raw_out, liChunkQueue *response_body) { +void li_response_send_headers(liVRequest *vr, liChunkQueue *raw_out, liChunkQueue *response_body, gboolean upgraded) { GString *head; gboolean have_real_body, response_complete; liChunkQueue *tmp_cq = NULL; @@ -53,8 +53,8 @@ void li_response_send_headers(liVRequest *vr, liChunkQueue *raw_out, liChunkQueu vr->response.http_status == 205 || vr->response.http_status == 304) { /* They never have a content-body/length */ - li_chunkqueue_skip_all(response_body); - raw_out->is_closed = TRUE; + if (NULL != response_body) li_chunkqueue_skip_all(response_body); + if (!upgraded) raw_out->is_closed = TRUE; } else if (response_complete) { if (vr->request.http_method != LI_HTTP_METHOD_HEAD || response_body->length > 0) { /* do not send content-length: 0 if backend already skipped content generation for HEAD */ @@ -74,8 +74,8 @@ void li_response_send_headers(liVRequest *vr, liChunkQueue *raw_out, liChunkQueu if (vr->request.http_method == LI_HTTP_METHOD_HEAD) { /* content-length is set, but no body */ - li_chunkqueue_skip_all(response_body); - raw_out->is_closed = TRUE; + if (NULL != response_body) li_chunkqueue_skip_all(response_body); + if (!upgraded) raw_out->is_closed = TRUE; } /* Status line */ @@ -97,7 +97,9 @@ void li_response_send_headers(liVRequest *vr, liChunkQueue *raw_out, liChunkQueu } /* connection header, if needed. connection entries in the list are ignored below, send them directly */ - if (vr->request.http_version == LI_HTTP_VERSION_1_1) { + if (upgraded) { + g_string_append_len(head, CONST_STR_LEN("Connection: Upgrade\r\n")); + } else if (vr->request.http_version == LI_HTTP_VERSION_1_1) { if (!vr->coninfo->keep_alive) g_string_append_len(head, CONST_STR_LEN("Connection: close\r\n")); } else { diff --git a/src/main/stream_http_response.c b/src/main/stream_http_response.c index 72be3f1..34e0d87 100644 --- a/src/main/stream_http_response.c +++ b/src/main/stream_http_response.c @@ -11,9 +11,8 @@ struct liStreamHttpResponse { liFilterChunkedDecodeState chunked_decode_state; }; -static gboolean check_response_header(liStreamHttpResponse* shr) { +static void check_response_header(liStreamHttpResponse* shr) { liResponse *resp = &shr->vr->response; - liHttpHeader *hh; GList *l; shr->transfer_encoding_chunked = FALSE; @@ -22,7 +21,7 @@ static gboolean check_response_header(liStreamHttpResponse* shr) { 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; + liHttpHeader *hh = (liHttpHeader*) l->data; if (0 == g_ascii_strcasecmp( LI_HEADER_VALUE(hh), "identity" )) { /* ignore */ continue; @@ -30,13 +29,13 @@ static gboolean check_response_header(liStreamHttpResponse* shr) { if (shr->transfer_encoding_chunked) { VR_ERROR(shr->vr, "%s", "Response is chunked encoded twice"); li_vrequest_error(shr->vr); - return FALSE; + return; } 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; + return; } } li_http_header_remove(resp->headers, CONST_STR_LEN("transfer-encoding")); @@ -46,10 +45,49 @@ static gboolean check_response_header(liStreamHttpResponse* shr) { } } + /* Upgrade: */ + l = li_http_header_find_first(resp->headers, CONST_STR_LEN("upgrade")); + if (l) { + gboolean have_connection_upgrade = FALSE; + liHttpHeaderTokenizer header_tokenizer; + GString *token; + if (101 != resp->http_status) { + VR_ERROR(shr->vr, "Upgrade but status is %i instead of 101 'Switching Protocols'", resp->http_status); + li_vrequest_error(shr->vr); + return; + } + if (shr->transfer_encoding_chunked) { + VR_ERROR(shr->vr, "%s", "Upgrade with Transfer-Encoding: chunked"); + li_vrequest_error(shr->vr); + return; + } + /* requires Connection: Upgrade header */ + token = g_string_sized_new(15); + li_http_header_tokenizer_start(&header_tokenizer, resp->headers, CONST_STR_LEN("Connection")); + while (li_http_header_tokenizer_next(&header_tokenizer, token)) { + VR_ERROR(shr->vr, "Parsing header '%s'", ((liHttpHeader*)header_tokenizer.cur->data)->data->str); + VR_ERROR(shr->vr, "Connection token '%s'", token->str); + if (0 == g_ascii_strcasecmp(token->str, "Upgrade")) { + have_connection_upgrade = TRUE; + break; + } + } + g_string_free(token, TRUE); token = NULL; + if (!have_connection_upgrade) { + VR_ERROR(shr->vr, "%s", "Upgrade without Connection: Upgrade Transfer"); + li_vrequest_error(shr->vr); + return; + } + shr->response_headers_finished = TRUE; + shr->vr->backend_drain->out->is_closed = FALSE; + li_vrequest_connection_upgrade(shr->vr, shr->vr->backend_drain, &shr->stream); + return; + } + shr->response_headers_finished = TRUE; li_vrequest_indirect_headers_ready(shr->vr); - return TRUE; + return; } static void stream_http_response_data(liStreamHttpResponse* shr) { @@ -58,7 +96,8 @@ static void stream_http_response_data(liStreamHttpResponse* shr) { if (!shr->response_headers_finished) { switch (li_http_response_parse(shr->vr, &shr->parse_response_ctx)) { case LI_HANDLER_GO_ON: - if (!check_response_header(shr)) return; + check_response_header(shr); + if (NULL == shr->stream.source) return; break; case LI_HANDLER_ERROR: VR_ERROR(shr->vr, "%s", "Parsing response header failed"); diff --git a/src/main/subrequest_lua.c b/src/main/subrequest_lua.c index 272411e..c445fef 100644 --- a/src/main/subrequest_lua.c +++ b/src/main/subrequest_lua.c @@ -288,10 +288,16 @@ static liThrottleState* subvr_handle_throttle_in(liVRequest *vr) { return NULL; } +static void subvr_connection_upgrade(liVRequest *vr, liStream *backend_drain, liStream *backend_source) { + UNUSED(backend_drain); UNUSED(backend_source); + subvr_handle_response_error(vr); +} + static const liConCallbacks subrequest_callbacks = { subvr_handle_response_error, subvr_handle_throttle_out, - subvr_handle_throttle_in + subvr_handle_throttle_in, + subvr_connection_upgrade }; static liSubrequest* subrequest_new(liVRequest *vr) { diff --git a/src/main/virtualrequest.c b/src/main/virtualrequest.c index 46e40b7..a1d23ae 100644 --- a/src/main/virtualrequest.c +++ b/src/main/virtualrequest.c @@ -399,7 +399,23 @@ void li_vrequest_indirect_headers_ready(liVRequest* vr) { li_vrequest_joblist_append(vr); } +void li_vrequest_connection_upgrade(liVRequest *vr, liStream *backend_drain, liStream *backend_source) { + assert(LI_VRS_HANDLE_RESPONSE_HEADERS > vr->state); + /* abort config handling. no filter, no more headers, ... */ + vr->state = LI_VRS_WRITE_CONTENT; + li_action_stack_reset(vr, &vr->action_stack); + + if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { + VR_DEBUG(vr, "%s", "connection uprade"); + } + + /* we don't want these to be disconnected by a li_vrequest_reset */ + li_stream_safe_release(&vr->backend_drain); + li_stream_safe_release(&vr->backend_source); + + vr->coninfo->callbacks->connection_upgrade(vr, backend_drain, backend_source); +} gboolean li_vrequest_is_handled(liVRequest *vr) { return vr->state >= LI_VRS_READ_CONTENT; diff --git a/src/modules/fastcgi_stream.c b/src/modules/fastcgi_stream.c index c1bd805..7f5afd9 100644 --- a/src/modules/fastcgi_stream.c +++ b/src/modules/fastcgi_stream.c @@ -568,7 +568,9 @@ static void fastcgi_stream_out(liStream *stream, liStreamEvent event) { li_stream_notify(stream); break; case LI_STREAM_CONNECTED_SOURCE: - assert(!ctx->stdin_closed); + /* support Connection: Upgrade by reopening stdin. not standard compliant, + * but the backend asked for it :) */ + ctx->stdin_closed = FALSE; break; case LI_STREAM_DISCONNECTED_SOURCE: if (!ctx->stdin_closed) { diff --git a/src/modules/mod_proxy.c b/src/modules/mod_proxy.c index 1220602..ebab67b 100644 --- a/src/modules/mod_proxy.c +++ b/src/modules/mod_proxy.c @@ -57,6 +57,8 @@ static void proxy_send_headers(liVRequest *vr, liChunkQueue *out) { liHttpHeader *header; GList *iter; gchar *enc_path; + liHttpHeaderTokenizer header_tokenizer; + GString *tmp_str = vr->wrk->tmp_str; g_string_append_len(head, GSTR_LEN(vr->request.http_method_str)); g_string_append_len(head, CONST_STR_LEN(" ")); @@ -81,11 +83,19 @@ static void proxy_send_headers(liVRequest *vr, liChunkQueue *out) { break; } + li_http_header_tokenizer_start(&header_tokenizer, vr->request.headers, CONST_STR_LEN("Connection")); + while (li_http_header_tokenizer_next(&header_tokenizer, tmp_str)) { + if (0 == g_ascii_strcasecmp(tmp_str->str, "Upgrade")) { + g_string_append_len(head, CONST_STR_LEN("Connection: Upgrade\r\n")); + } + } + for (iter = g_queue_peek_head_link(&vr->request.headers->entries); iter; iter = g_list_next(iter)) { header = (liHttpHeader*) iter->data; if (li_http_header_key_is(header, CONST_STR_LEN("Connection"))) continue; if (li_http_header_key_is(header, CONST_STR_LEN("Proxy-Connection"))) continue; if (li_http_header_key_is(header, CONST_STR_LEN("X-Forwarded-Proto"))) continue; + if (li_http_header_key_is(header, CONST_STR_LEN("X-Forwarded-For"))) continue; g_string_append_len(head, GSTR_LEN(header->data)); g_string_append_len(head, CONST_STR_LEN("\r\n")); } diff --git a/src/modules/mod_status.c b/src/modules/mod_status.c index 0793d69..d8ff6cd 100644 --- a/src/modules/mod_status.c +++ b/src/modules/mod_status.c @@ -56,7 +56,7 @@ static liHandlerResult status_info_runtime(liVRequest *vr, liPlugin *p); static gint str_comp(gconstpointer a, gconstpointer b); /* auto format constants */ -static gchar liConnectionState_short[LI_CON_STATE_LAST+2] = "_cKqrhw"; +static gchar liConnectionState_short[LI_CON_STATE_LAST+2] = "_cKqrhwu"; /* html snippet constants */ static const gchar html_header[] = @@ -176,10 +176,11 @@ static const gchar html_connections_sum[] = " \n" " inactive\n" " request start\n" - " read request header\n" - " handle request\n" - " write response\n" - " keep-alive\n" + " read header\n" + " handle request\n" + " write response\n" + " keep-alive\n" + " upgraded\n" " \n" " \n" " %u\n" @@ -188,6 +189,7 @@ static const gchar html_connections_sum[] = " %u\n" " %u\n" " %u\n" + " %u\n" " \n" " \n"; static const gchar html_status_codes[] = @@ -737,9 +739,11 @@ static GString *status_info_full(liVRequest *vr, liPlugin *p, gboolean short_inf /* connection counts */ g_string_append_len(html, CONST_STR_LEN("
Connections (states, sum)
\n")); - g_string_append_printf(html, html_connections_sum, connection_count[0] + connection_count[1], - connection_count[3], connection_count[4], connection_count[5], connection_count[6], - connection_count[2] + g_string_append_printf(html, html_connections_sum, + connection_count[LI_CON_STATE_DEAD] + connection_count[LI_CON_STATE_CLOSE], + connection_count[LI_CON_STATE_REQUEST_START], connection_count[LI_CON_STATE_READ_REQUEST_HEADER], + connection_count[LI_CON_STATE_HANDLE_MAINVR], connection_count[LI_CON_STATE_WRITE], + connection_count[LI_CON_STATE_KEEP_ALIVE], connection_count[LI_CON_STATE_UPGRADED] ); /* response status codes */ @@ -874,6 +878,8 @@ static GString *status_info_plain(liVRequest *vr, guint uptime, liStatistics *to li_string_append_int(html, connection_count[LI_CON_STATE_WRITE]); g_string_append_len(html, CONST_STR_LEN("\nconnection_state_keep_alive: ")); li_string_append_int(html, connection_count[LI_CON_STATE_KEEP_ALIVE]); + g_string_append_len(html, CONST_STR_LEN("\nconnection_state_upgraded: ")); + li_string_append_int(html, connection_count[LI_CON_STATE_UPGRADED]); /* status cpdes */ g_string_append_len(html, CONST_STR_LEN("\n\n# Status Codes (since start)\nstatus_1xx: ")); li_string_append_int(html, mod_status_response_codes[0]); @@ -906,7 +912,7 @@ static GString *status_info_auto(liVRequest *vr, guint uptime, liStatistics *tot li_string_append_int(html, (gint64)uptime); /* connection states */ g_string_append_len(html, CONST_STR_LEN("\nBusyServers: ")); - li_string_append_int(html, connection_count[3]+connection_count[4]+connection_count[5]+connection_count[6]); + li_string_append_int(html, connection_count[3]+connection_count[4]+connection_count[5]+connection_count[6]+connection_count[7]); g_string_append_len(html, CONST_STR_LEN("\nIdleServers: ")); li_string_append_int(html, connection_count[0]+connection_count[1]+connection_count[2]); /* average since start */