static const char *test_parse_http_message(void) { static const char *a = "GET / HTTP/1.0\n\n"; static const char *b = "GET /blah HTTP/1.0\r\nFoo: bar \r\n\r\n"; static const char *c = "get b c\nz: k \nb: t\nvvv\n\n xx"; static const char *d = "a b c\nContent-Length: 21 \nb: t\nvvv\n\n"; struct ns_str *v; struct http_message req; ASSERT(ns_parse_http("\b23", 3, &req) == -1); ASSERT(ns_parse_http("get\n\n", 5, &req) == -1); ASSERT(ns_parse_http(a, strlen(a) - 1, &req) == 0); ASSERT(ns_parse_http(a, strlen(a), &req) == (int) strlen(a)); ASSERT(ns_parse_http(b, strlen(b), &req) == (int) strlen(b)); ASSERT(req.header_names[0].len == 3); ASSERT(req.header_values[0].len == 3); ASSERT(req.header_names[1].p == NULL); ASSERT(ns_parse_http(c, strlen(c), &req) == (int) strlen(c) - 3); ASSERT(req.header_names[2].p == NULL); ASSERT(req.header_names[0].p != NULL); ASSERT(req.header_names[1].p != NULL); ASSERT(memcmp(req.header_values[1].p, "t", 1) == 0); ASSERT(req.header_names[1].len == 1); ASSERT(req.body.len == 0); ASSERT(ns_parse_http(d, strlen(d), &req) == (int) strlen(d)); ASSERT(req.body.len == 21); ASSERT(req.message.len == 21 + strlen(d)); ASSERT(ns_get_http_header(&req, "foo") == NULL); ASSERT((v = ns_get_http_header(&req, "contENT-Length")) != NULL); ASSERT(v->len == 2 && memcmp(v->p, "21", 2) == 0); return NULL; }
static int is_keep_alive(struct http_message *hm) { const struct ns_str *connection_header = ns_get_http_header(hm, "Connection"); if (connection_header == NULL) { /* HTTP/1.1 connections are keep-alive by default. */ if (ns_vcasecmp(&hm->proto, "HTTP/1.1") != 0) return 0; } else if (ns_vcasecmp(connection_header, "keep-alive") != 0) { return 0; } // We must also have Content-Length. return ns_get_http_header(hm, "Content-Length") != NULL; }
static struct http_backend *choose_backend_from_list( struct http_message *hm, struct http_backend *backends, int num_backends) { int i; struct ns_str vhost = {"", 0}; const struct ns_str *host = ns_get_http_header(hm, "host"); if (host != NULL) vhost = *host; const char *vhost_end = vhost.p; while (vhost_end < vhost.p + vhost.len && *vhost_end != ':') { vhost_end++; } vhost.len = vhost_end - vhost.p; struct http_backend *chosen = NULL; for (i = 0; i < num_backends; i++) { struct http_backend *be = &backends[i]; if (has_prefix(&hm->uri, be->uri_prefix) && matches_vhost(&vhost, be->vhost) && (chosen == NULL || /* Prefer most specific URI prefixes */ strlen(be->uri_prefix) > strlen(chosen->uri_prefix) || /* Among prefixes of the same length chose the least used. */ (strlen(be->uri_prefix) == strlen(chosen->uri_prefix) && be->usage_counter < chosen->usage_counter))) { chosen = be; } } return chosen; }
static void choose_backend(struct ns_connection *nc) { struct http_message hm; struct iobuf *io = &nc->recv_iobuf; int req_len = ns_parse_http(io->buf, io->len, &hm); if (req_len < 0 || (req_len == 0 && io->len >= NS_MAX_HTTP_REQUEST_SIZE)) { /* Invalid, or too large request */ nc->flags |= NSF_CLOSE_IMMEDIATELY; } else if (req_len == 0) { /* Do nothing, request is not yet fully buffered */ } else { /* * Got HTTP request, look which backend to use. Round-robin over the * backends with the same uri_prefix and vhost. */ struct ns_str vhost = *ns_get_http_header(&hm, "host"); const char *vhost_end = vhost.p; while(vhost_end < vhost.p + vhost.len && *vhost_end != ':') { vhost_end++; } vhost.len = vhost_end - vhost.p; int i, chosen = -1; for (i = 0; i < s_num_http_backends; i++) { if (has_prefix(&hm.uri, s_http_backends[i].uri_prefix) && matches_vhost(&vhost, s_http_backends[i].vhost) && (chosen == -1 || s_http_backends[i].usage_counter < s_http_backends[chosen].usage_counter)) { chosen = i; } } if (chosen == -1) { /* No backend with given uri_prefix found, bail out */ ns_printf(nc, "%s%s\r\n", s_error_404, s_content_len_0); } else if (s_http_backends[chosen].redirect != 0) { ns_printf(nc, "HTTP/1.1 302 Found\r\nLocation: %s\r\n\r\n", s_http_backends[chosen].host_port); nc->flags |= NSF_SEND_AND_CLOSE; } else if ((nc->proto_data = ns_connect(nc->mgr, s_http_backends[chosen].host_port, ev_handler)) == NULL) { /* Connection to backend failed */ ns_printf(nc, "%s%s\r\n", s_error_500, s_content_len_0); } else { /* * Forward request to the backend. Note that we can insert extra headers * to pass information to the backend. * Store backend index as user_data for the backend connection. */ ((struct ns_connection *) nc->proto_data)->proto_data = nc; ((struct ns_connection *) nc->proto_data)->user_data = (void *) (long) chosen; s_http_backends[chosen].usage_counter++; ns_send(nc->proto_data, io->buf, io->len); iobuf_remove(io, io->len); } } }
static void cb10(struct ns_connection *nc, int ev, void *ev_data) { struct http_message *hm = (struct http_message *) ev_data; struct ns_str *s; if (ev == NS_HTTP_REPLY && (s = ns_get_http_header(hm, "Content-Type")) != NULL) { sprintf((char *) nc->user_data, "%.*s", (int) s->len, s->p); } }
static void cb7(struct ns_connection *nc, int ev, void *ev_data) { struct http_message *hm = (struct http_message *) ev_data; struct ns_str *s; size_t size; char *data; if (ev == NS_HTTP_REPLY) { /* Make sure that we've downloaded this executable, byte-to-byte */ data = read_file(s_argv_0, &size); strcpy((char *) nc->user_data, data == NULL || size != hm->body.len || (s = ns_get_http_header(hm, "Content-Type")) == NULL || (ns_vcmp(s, "text/plain")) != 0 || memcmp(hm->body.p, data, size) != 0 ? "fail" : "success"); free(data); nc->flags |= NSF_CLOSE_IMMEDIATELY; } }