static void proxy_send_headers(liVRequest *vr, proxy_connection *pcon) { GString *head = g_string_sized_new(4095); liHttpHeader *header; GList *iter; gchar *enc_path; g_string_append_len(head, GSTR_LEN(vr->request.http_method_str)); g_string_append_len(head, CONST_STR_LEN(" ")); enc_path = g_uri_escape_string(vr->request.uri.path->str, "/", FALSE); g_string_append(head, enc_path); g_free(enc_path); if (vr->request.uri.query->len > 0) { g_string_append_len(head, CONST_STR_LEN("?")); g_string_append_len(head, GSTR_LEN(vr->request.uri.query)); } switch (vr->request.http_version) { case LI_HTTP_VERSION_1_1: /* g_string_append_len(head, CONST_STR_LEN(" HTTP/1.1\r\n")); */ g_string_append_len(head, CONST_STR_LEN(" HTTP/1.0\r\n")); break; case LI_HTTP_VERSION_1_0: default: g_string_append_len(head, CONST_STR_LEN(" HTTP/1.0\r\n")); break; } 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; g_string_append_len(head, GSTR_LEN(header->data)); g_string_append_len(head, CONST_STR_LEN("\r\n")); } g_string_append_len(head, CONST_STR_LEN("X-Forwarded-For: ")); g_string_append_len(head, GSTR_LEN(vr->coninfo->remote_addr_str)); g_string_append_len(head, CONST_STR_LEN("\r\n")); if (vr->coninfo->is_ssl) { g_string_append_len(head, CONST_STR_LEN("X-Forwarded-Proto: https\r\n")); } else { g_string_append_len(head, CONST_STR_LEN("X-Forwarded-Proto: http\r\n")); } /* terminate http header */ g_string_append_len(head, CONST_STR_LEN("\r\n")); li_chunkqueue_append_string(pcon->proxy_out, head); }
static void proxy_send_headers(liVRequest *vr, liChunkQueue *out) { GString *head = g_string_sized_new(4095); liHttpHeader *header; GList *iter; 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(" ")); g_string_append_len(head, GSTR_LEN(vr->request.uri.raw_path)); switch (vr->request.http_version) { case LI_HTTP_VERSION_1_1: /* g_string_append_len(head, CONST_STR_LEN(" HTTP/1.1\r\n")); */ g_string_append_len(head, CONST_STR_LEN(" HTTP/1.0\r\n")); break; case LI_HTTP_VERSION_1_0: default: g_string_append_len(head, CONST_STR_LEN(" HTTP/1.0\r\n")); 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")); } } if (LI_HTTP_METHOD_GET != vr->request.http_method && LI_HTTP_METHOD_HEAD != vr->request.http_method) { g_string_append_printf(head, "Content-Length: %" LI_GOFFSET_MODIFIER "i\r\n", vr->request.content_length); } 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("Content-Length"))) continue; if (li_http_header_key_is(header, CONST_STR_LEN("Transfer-Encoding"))) continue; if (li_http_header_key_is(header, CONST_STR_LEN("TE"))) continue; 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")); } g_string_append_len(head, CONST_STR_LEN("X-Forwarded-For: ")); g_string_append_len(head, GSTR_LEN(vr->coninfo->remote_addr_str)); g_string_append_len(head, CONST_STR_LEN("\r\n")); if (vr->coninfo->is_ssl) { g_string_append_len(head, CONST_STR_LEN("X-Forwarded-Proto: https\r\n")); } else { g_string_append_len(head, CONST_STR_LEN("X-Forwarded-Proto: http\r\n")); } /* terminate http header */ g_string_append_len(head, CONST_STR_LEN("\r\n")); li_chunkqueue_append_string(out, head); }
gboolean li_response_send_headers(liConnection *con) { GString *head; liVRequest *vr = con->mainvr; if (vr->response.http_status < 100 || vr->response.http_status > 999) { VR_ERROR(vr, "wrong status: %i", vr->response.http_status); return FALSE; } head = g_string_sized_new(8*1024-1); if (0 == con->out->length && con->mainvr->backend == NULL && vr->response.http_status >= 400 && vr->response.http_status < 600) { li_response_send_error_page(con); } if ((vr->response.http_status >= 100 && vr->response.http_status < 200) || vr->response.http_status == 204 || vr->response.http_status == 205 || vr->response.http_status == 304) { /* They never have a content-body/length */ li_chunkqueue_reset(con->out); con->out->is_closed = TRUE; con->raw_out->is_closed = TRUE; } else if (con->out->is_closed) { if (vr->request.http_method != LI_HTTP_METHOD_HEAD || con->out->length > 0) { /* do not send content-length: 0 if backend already skipped content generation for HEAD */ g_string_printf(con->wrk->tmp_str, "%"L_GOFFSET_FORMAT, con->out->length); li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Length"), GSTR_LEN(con->wrk->tmp_str)); } } else if (con->info.keep_alive && vr->request.http_version == LI_HTTP_VERSION_1_1) { /* TODO: maybe someone set a content length header? */ if (!(vr->response.transfer_encoding & LI_HTTP_TRANSFER_ENCODING_CHUNKED)) { vr->response.transfer_encoding |= LI_HTTP_TRANSFER_ENCODING_CHUNKED; li_http_header_append(vr->response.headers, CONST_STR_LEN("Transfer-Encoding"), CONST_STR_LEN("chunked")); } } else { /* Unknown content length, no chunked encoding */ con->info.keep_alive = FALSE; } if (vr->request.http_method == LI_HTTP_METHOD_HEAD) { /* content-length is set, but no body */ li_chunkqueue_reset(con->out); con->out->is_closed = TRUE; con->raw_out->is_closed = TRUE; } /* Status line */ if (vr->request.http_version == LI_HTTP_VERSION_1_1) { g_string_append_len(head, CONST_STR_LEN("HTTP/1.1 ")); if (!con->info.keep_alive) li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Connection"), CONST_STR_LEN("close")); } else { g_string_append_len(head, CONST_STR_LEN("HTTP/1.0 ")); if (con->info.keep_alive) li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Connection"), CONST_STR_LEN("keep-alive")); } { guint len; gchar status_str[4]; gchar *str = li_http_status_string(vr->response.http_status, &len); li_http_status_to_str(vr->response.http_status, status_str); status_str[3] = ' '; g_string_append_len(head, status_str, 4); g_string_append_len(head, str, len); g_string_append_len(head, CONST_STR_LEN("\r\n")); } /* Append headers */ { liHttpHeader *header; GList *iter; gboolean have_date = FALSE, have_server = FALSE; for (iter = g_queue_peek_head_link(&vr->response.headers->entries); iter; iter = g_list_next(iter)) { header = (liHttpHeader*) iter->data; g_string_append_len(head, GSTR_LEN(header->data)); g_string_append_len(head, CONST_STR_LEN("\r\n")); if (!have_date && li_http_header_key_is(header, CONST_STR_LEN("date"))) have_date = TRUE; if (!have_server && li_http_header_key_is(header, CONST_STR_LEN("server"))) have_server = TRUE; } if (!have_date) { GString *d = li_worker_current_timestamp(con->wrk, LI_GMTIME, LI_TS_FORMAT_HEADER); /* HTTP/1.1 requires a Date: header */ g_string_append_len(head, CONST_STR_LEN("Date: ")); g_string_append_len(head, GSTR_LEN(d)); g_string_append_len(head, CONST_STR_LEN("\r\n")); } if (!have_server) { GString *tag = CORE_OPTIONPTR(LI_CORE_OPTION_SERVER_TAG).string; if (tag->len) { g_string_append_len(head, CONST_STR_LEN("Server: ")); g_string_append_len(head, GSTR_LEN(tag)); g_string_append_len(head, CONST_STR_LEN("\r\n")); } } } g_string_append_len(head, CONST_STR_LEN("\r\n")); li_chunkqueue_append_string(con->raw_out, head); return TRUE; }