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); } } }