static void check_response_header(liStreamHttpResponse* shr) {
	liResponse *resp = &shr->vr->response;
	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")) ) {
			liHttpHeader *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;
				}
				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;
			}
		}
		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"));
		}
	}

	/* 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 releases vr->backend_drain; keep our own reference */
			liStream *backend_drain = shr->vr->backend_drain;
			shr->vr->backend_drain = NULL;
			li_vrequest_connection_upgrade(shr->vr, backend_drain, &shr->stream);
			li_stream_release(backend_drain);
		}
		return;
	}

	shr->response_headers_finished = TRUE;
	li_vrequest_indirect_headers_ready(shr->vr);

	return;
}
Esempio n. 2
0
void li_response_send_error_page(liConnection *con) {
	liVRequest *vr = con->mainvr;
	gchar status_code[3];
	guint len;
	gchar *str;
	GString *html;

	html = g_string_sized_new(1023);

	g_string_append_len(html, CONST_STR_LEN(
		"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n"
		"<html>\n"
		"	<head>\n"
		"		<title>"
	));

	li_http_status_to_str(con->mainvr->response.http_status, status_code);

	g_string_append_len(html, status_code, 3);
	g_string_append_len(html, CONST_STR_LEN(" - "));
	str = li_http_status_string(con->mainvr->response.http_status, &len);
	g_string_append_len(html, str, len);

	g_string_append_len(html, CONST_STR_LEN(
		"</title>\n"
		"		<style type=\"text/css\">\n"
		"			body { font-size: 62.5%; }\n"
		"			#container {\n"
		"				font-size: 62.5%;\n"
		"				max-width: 600px;\n"
		"				margin: auto;\n"
		"				margin-top: 2%;\n"
		"				border: 4px solid #efefef;\n"
		"				padding: 0px 20px;\n"
		"				color: #444;\n"
		"				font-family: Verdana,helvetica,sans-serif;\n"
		"				font-size: 1.25em;\n"
		"			}\n"
		"			h1 { color: #6D84B4; font-size: 1.5em; }\n"
		"			#footer { text-align: right; margin-top: 25px; }\n"
		"		</style>\n"
		"	</head>\n"
		"	<body>\n"
		"		<div id=\"container\">\n"
		"			<h1>Error "
	));

	g_string_append_len(html, status_code, 3);
	g_string_append_len(html, CONST_STR_LEN(" - "));
	g_string_append_len(html, str, len);
	g_string_append_len(html, CONST_STR_LEN("</h1>\n"));

	str = li_response_error_description(con->mainvr->response.http_status, &len);
	g_string_append_len(html, str, len);
	
	g_string_append_len(html, CONST_STR_LEN("			<p id=\"footer\">"));
	g_string_append_len(html, GSTR_LEN(CORE_OPTIONPTR(LI_CORE_OPTION_SERVER_TAG).string));
	g_string_append_len(html, CONST_STR_LEN(
		"</p>\n"
		"		</div>\n"
		"	</body>\n"
		"</html>\n"
	));

	li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html; charset=utf-8"));

	li_chunkqueue_append_string(con->out, html);
	li_http_header_remove(vr->response.headers, CONST_STR_LEN("transfer-encoding"));
	li_http_header_remove(vr->response.headers, CONST_STR_LEN("content-encoding"));
	li_http_header_remove(vr->response.headers, CONST_STR_LEN("etag"));
}