void li_request_copy(liRequest *dest, const liRequest *src) { GList *iter; dest->http_method = src->http_method; li_string_assign_len(dest->http_method_str, GSTR_LEN(src->http_method_str)); dest->http_version = src->http_version; li_string_assign_len(dest->uri.raw, GSTR_LEN(src->uri.raw)); li_string_assign_len(dest->uri.raw_path, GSTR_LEN(src->uri.raw_path)); li_string_assign_len(dest->uri.raw_orig_path, GSTR_LEN(src->uri.raw_orig_path)); li_string_assign_len(dest->uri.scheme, GSTR_LEN(src->uri.scheme)); li_string_assign_len(dest->uri.authority, GSTR_LEN(src->uri.authority)); li_string_assign_len(dest->uri.path, GSTR_LEN(src->uri.path)); li_string_assign_len(dest->uri.query, GSTR_LEN(src->uri.query)); li_string_assign_len(dest->uri.host, GSTR_LEN(src->uri.host)); li_http_headers_reset(dest->headers); for (iter = g_queue_peek_head_link(&src->headers->entries); iter; iter = g_list_next(iter)) { liHttpHeader* header = (liHttpHeader*) iter->data; li_http_header_insert(dest->headers, LI_HEADER_KEY_LEN(header), LI_HEADER_VALUE_LEN(header)); } dest->content_length = src->content_length; }
static GString* createFileName(liVRequest *vr, GString *path, liHttpHeader *etagheader) { GString *file = g_string_sized_new(255); gchar* etag_base64 = g_base64_encode((guchar*) LI_HEADER_VALUE_LEN(etagheader)); g_string_append_len(file, GSTR_LEN(path)); g_string_append_len(file, GSTR_LEN(vr->request.uri.path)); g_string_append_len(file, CONST_STR_LEN("-")); g_string_append(file, etag_base64); g_free(etag_base64); return file; }
gboolean li_request_validate_header(liConnection *con) { liRequest *req = &con->mainvr->request; liHttpHeader *hh; GList *l; if (con->info.is_ssl) { g_string_append_len(req->uri.scheme, CONST_STR_LEN("https")); } else { g_string_append_len(req->uri.scheme, CONST_STR_LEN("http")); } switch (req->http_version) { case LI_HTTP_VERSION_1_0: if (!li_http_header_is(req->headers, CONST_STR_LEN("connection"), CONST_STR_LEN("keep-alive"))) con->info.keep_alive = FALSE; break; case LI_HTTP_VERSION_1_1: if (li_http_header_is(req->headers, CONST_STR_LEN("connection"), CONST_STR_LEN("close"))) con->info.keep_alive = FALSE; break; case LI_HTTP_VERSION_UNSET: bad_request(con, 505); /* Version not Supported */ return FALSE; } if (req->uri.raw->len == 0) { bad_request(con, 400); /* bad request */ return FALSE; } /* get hostname */ l = li_http_header_find_first(req->headers, CONST_STR_LEN("host")); if (NULL != l) { if (NULL != li_http_header_find_next(l, CONST_STR_LEN("host"))) { /* more than one "host" header */ bad_request(con, 400); /* bad request */ return FALSE; } hh = (liHttpHeader*) l->data; g_string_append_len(req->uri.authority, LI_HEADER_VALUE_LEN(hh)); /* check header after we parsed the url, as it may override uri.authority */ } /* Need hostname in HTTP/1.1 */ if (req->uri.authority->len == 0 && req->http_version == LI_HTTP_VERSION_1_1) { bad_request(con, 400); /* bad request */ return FALSE; } /* may override hostname */ if (!request_parse_url(con->mainvr)) { bad_request(con, 400); /* bad request */ return FALSE; } if (req->uri.host->len == 0 && req->uri.authority->len != 0) { if (!li_parse_hostname(&req->uri)) { bad_request(con, 400); /* bad request */ return FALSE; } } /* remove trailing dots from hostname */ { guint i = req->uri.host->len; while (i > 0 && req->uri.host->str[i-1] == '.') i--; g_string_truncate(req->uri.host, i); } /* content-length */ hh = li_http_header_lookup(req->headers, CONST_STR_LEN("content-length")); if (hh) { const gchar *val = LI_HEADER_VALUE(hh); gint64 r; char *err; r = g_ascii_strtoll(val, &err, 10); if (*err != '\0') { _VR_DEBUG(con->srv, con->mainvr, "content-length is not a number: %s (Status: 400)", err); bad_request(con, 400); /* bad request */ return FALSE; } /** * negative content-length is not supported * and is a bad request */ if (r < 0) { bad_request(con, 400); /* bad request */ return FALSE; } /** * check if we had a over- or underrun in the string conversion */ if (r == G_MININT64 || r == G_MAXINT64) { if (errno == ERANGE) { bad_request(con, 413); /* Request Entity Too Large */ return FALSE; } } con->mainvr->request.content_length = r; } /* Expect: 100-continue */ l = li_http_header_find_first(req->headers, CONST_STR_LEN("expect")); if (l) { gboolean expect_100_cont = FALSE; for ( ; l ; l = li_http_header_find_next(l, CONST_STR_LEN("expect")) ) { hh = (liHttpHeader*) l->data; if (0 == g_ascii_strcasecmp( LI_HEADER_VALUE(hh), "100-continue" )) { expect_100_cont = TRUE; } else { /* we only support 100-continue */ bad_request(con, 417); /* Expectation Failed */ return FALSE; } } if (expect_100_cont && req->http_version == LI_HTTP_VERSION_1_0) { /* only HTTP/1.1 clients can send us this header */ bad_request(con, 417); /* Expectation Failed */ return FALSE; } con->expect_100_cont = expect_100_cont; } /* TODO: headers: * - If-Modified-Since (different duplicate check) * - If-None-Match (different duplicate check) * - Range (duplicate check) */ switch(con->mainvr->request.http_method) { case LI_HTTP_METHOD_GET: case LI_HTTP_METHOD_HEAD: /* content-length is forbidden for those */ if (con->mainvr->request.content_length > 0) { VR_ERROR(con->mainvr, "%s", "GET/HEAD with content-length -> 400"); bad_request(con, 400); /* bad request */ return FALSE; } con->mainvr->request.content_length = 0; break; case LI_HTTP_METHOD_POST: /* content-length is required for them */ if (con->mainvr->request.content_length == -1) { /* content-length is missing */ VR_ERROR(con->mainvr, "%s", "POST-request, but content-length missing -> 411"); bad_request(con, 411); /* Length Required */ return FALSE; } break; default: if (con->mainvr->request.content_length == -1) con->mainvr->request.content_length = 0; /* the may have a content-length */ break; } return TRUE; }