/* * 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 #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 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 *vs, const char *request) { struct list_head *l; struct tcp_vs_rule *r; list_for_each(l, &vs->rule_list) { r = list_entry(l, struct tcp_vs_rule, list); if (!tcp_vs_exp_exec(&r->exp, request)) /* HIT */ return __tcp_vs_http_wlc_schedule(&r->destinations); } return NULL; } static int tcp_vs_http_init_vs(struct tcp_vs *vs) { EnterFunction(5); MOD_INC_USE_COUNT; if (!(vs->sched_data=(void *)get_free_page(GFP_KERNEL))) { MOD_DEC_USE_COUNT; return -1; } LeaveFunction(5); return 0; } static int tcp_vs_http_done_vs(struct tcp_vs *vs) { EnterFunction(5); if (vs->sched_data) free_page((unsigned long)vs->sched_data); MOD_DEC_USE_COUNT; LeaveFunction(5); return 0; } static int tcp_vs_http_update_vs(struct tcp_vs *vs) { return 0; } /* * 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(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 *vs) { #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); } 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(vs, 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 */ ATOMIC_INIT(0), /* refcnt */ tcp_vs_http_init_vs, /* initializer */ tcp_vs_http_done_vs, /* done */ tcp_vs_http_update_vs, /* update */ NULL, /* control */ tcp_vs_http_schedule, /* select a server by http CBS */ }; 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);