/* * KTCPVS An implementation of the TCP Virtual Server daemon inside * kernel for the LINUX operating system. KTCPVS can be used * to build a moderately scalable and highly available server * based on a cluster of servers, with more flexibility. * * Version: $Id$ * * Authors: Wensong Zhang * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "tcp_vs.h" /* * The interface of tcp_vs_http module needs a rewrite!!! * o the definition of http rules for this module * o how to represent those rules and do fast-matching * o how to insert/modify/display those http rules easily? * o a flexible model that other modules can follow too. */ static int tcp_vs_http_init_svc(struct tcp_vs_service *svc) { EnterFunction(5); if (!(svc->sched_data=(void *)get_free_page(GFP_KERNEL))) { MOD_DEC_USE_COUNT; return -1; } LeaveFunction(5); return 0; } static int tcp_vs_http_done_svc(struct tcp_vs_service *svc) { EnterFunction(5); if (svc->sched_data) free_page((unsigned long)svc->sched_data); LeaveFunction(5); return 0; } static int tcp_vs_http_update_svc(struct tcp_vs_service *svc) { return 0; } static inline struct tcp_vs_dest * __tcp_vs_http_wlc_schedule(struct list_head *destinations) { register struct list_head *e; struct tcp_vs_dest *dest, *least; list_for_each (e, destinations) { least = list_entry(e, struct tcp_vs_dest, r_list); if (least->weight > 0) { goto nextstage; } } return NULL; /* * Find the destination with the least load. */ nextstage: for (e=e->next; e!=destinations; e=e->next) { dest = list_entry(e, struct tcp_vs_dest, r_list); if (atomic_read(&least->conns) * dest->weight > atomic_read(&dest->conns) * least->weight) { least = dest; } } return least; } static struct tcp_vs_dest * tcp_vs_http_matchrule(struct tcp_vs_service *svc, const char *request) { struct list_head *l; struct tcp_vs_rule *r; struct tcp_vs_dest *dest = NULL; read_lock(&svc->lock); list_for_each(l, &svc->rule_list) { r = list_entry(l, struct tcp_vs_rule, list); if (!tcp_vs_exp_exec(&r->exp, request)) { /* HIT */ dest = __tcp_vs_http_wlc_schedule(&r->destinations); break; } } read_unlock(&svc->lock); return dest; } /* * Parse HTTP header */ int parse_http_header(char *buffer, size_t buflen, char* request, size_t reqlen) { char *eob,*eol,*tmp; EnterFunction(5); eob = buffer + buflen; /* parse only the first header if multiple headers are present */ tmp = strstr(buffer, "\r\n\r\n"); if (tmp!=NULL) eob = tmp; while (buffer < eob) { if (isspace(buffer[0])) { buffer++; continue; } eol=strchr(buffer, '\n'); if (eol==NULL) eol=eob; if (eol-buffer < 4) { buffer++; continue; } if (!strncmp("GET ", buffer, 4)) { int len; buffer += 4; tmp=strchr(buffer, ' '); if (tmp == NULL) tmp=eol-1; if (tmp > eob) continue; len = min((int)(reqlen-1), tmp-buffer); strncpy(request, buffer, len); request[len] = 0; buffer=eol+1; LeaveFunction(5); return 0; } /* next line */ buffer = eol+1; } LeaveFunction(5); return -1; } /* * HTTP content-based scheduling * Parse the http request, select a server according to the * request, and create a socket the server finally. */ static struct socket *tcp_vs_http_schedule(struct tcp_vs_conn *conn, struct tcp_vs_service *svc) { #define REQLEN 1024 struct tcp_vs_dest *dest; struct socket *csock, *dsock; int len; char *buffer; size_t buflen; static char request[REQLEN]; EnterFunction(5); buffer = conn->buffer; buflen = conn->buflen; csock = conn->csock; /* Do we have data ? */ while (skb_queue_empty(&(csock->sk->receive_queue))) { interruptible_sleep_on_timeout(&csock->wait, HZ); } /* fixme: what if the request overlap this receiving buffer */ len = tcp_vs_recvbuffer(csock, buffer, buflen); if (len < 0) { TCP_VS_ERR("error reading request from client\n"); return NULL; } if (parse_http_header(buffer, len, request, REQLEN)) { TCP_VS_ERR("cannot parse http request\n"); return NULL; } TCP_VS_DBG(5, "request: GET %s\n", request); /* Head.RemoteHost.s_addr = sock->sk->daddr; */ dest = tcp_vs_http_matchrule(svc, request); if (!dest) { TCP_VS_ERR("no suitable destination available\n"); return NULL; } TCP_VS_DBG(5, "HTTP: server %d.%d.%d.%d:%d " "conns %d refcnt %d weight %d\n", NIPQUAD(dest->addr), ntohs(dest->port), atomic_read(&dest->conns), atomic_read (&dest->refcnt), dest->weight); atomic_inc(&dest->conns); conn->dest = dest; dsock = tcp_vs_connect2dest(dest); if (!dsock) { TCP_VS_ERR("The destination is not available\n"); return NULL; } if (tcp_vs_sendbuffer(dsock, buffer, len)!=len) { TCP_VS_ERR("Error sending buffer\n"); } LeaveFunction(5); return dsock; } static struct tcp_vs_scheduler tcp_vs_http_scheduler = { {0}, /* n_list */ "http", /* name */ THIS_MODULE, /* this module */ tcp_vs_http_init_svc, /* initializer */ tcp_vs_http_done_svc, /* done */ tcp_vs_http_update_svc, /* update */ tcp_vs_http_schedule, /* select a server by http request */ }; static int __init tcp_vs_http_init(void) { INIT_LIST_HEAD(&tcp_vs_http_scheduler.n_list); return register_tcp_vs_scheduler(&tcp_vs_http_scheduler); } static void __exit tcp_vs_http_cleanup(void) { unregister_tcp_vs_scheduler(&tcp_vs_http_scheduler); } module_init(tcp_vs_http_init); module_exit(tcp_vs_http_cleanup);