X-Git-Url: http://git.indexdata.com/?a=blobdiff_plain;f=src%2Fhttp.c;h=aec359316291b16426358e817d359db28d6ea399;hb=e20b3a5698a749237de8a93fa4e707a3f6dc4f64;hp=0e1c47befa1b5d10f368140741d6ae2b988ab77b;hpb=e921cd65ea18c86a132c2e5eac480f934dde2046;p=pazpar2-moved-to-github.git diff --git a/src/http.c b/src/http.c index 0e1c47b..aec3593 100644 --- a/src/http.c +++ b/src/http.c @@ -1,5 +1,5 @@ /* This file is part of Pazpar2. - Copyright (C) 2006-2009 Index Data + Copyright (C) 2006-2010 Index Data Pazpar2 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 @@ -61,21 +61,47 @@ typedef int socklen_t; #include #include #include +#include -#include "util.h" -#include "eventl.h" -#include "pazpar2.h" +#include "ppmutex.h" +#include "session.h" #include "http.h" #define MAX_HTTP_HEADER 4096 -static void proxy_io(IOCHAN i, int event); -static struct http_channel *http_create(const char *addr, - struct conf_server *server); -static void http_destroy(IOCHAN i); +#ifdef WIN32 +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#endif + +struct http_buf +{ +#define HTTP_BUF_SIZE 4096 + char buf[4096]; + int offset; + int len; + struct http_buf *next; +}; -static struct http_buf *http_buf_freelist = 0; /* thread pr */ -static struct http_channel *http_channel_freelist = 0; /* thread pr */ + +static void proxy_io(IOCHAN i, int event); +static struct http_channel *http_channel_create(http_server_t http_server, + const char *addr, + struct conf_server *server); +static void http_channel_destroy(IOCHAN i); +static http_server_t http_server_create(void); +static void http_server_incref(http_server_t hs); + +struct http_server +{ + struct http_buf *http_buf_freelist; + struct http_channel *http_channel_freelist; + YAZ_MUTEX mutex; + int listener_socket; + int ref_count; + http_sessions_t http_sessions; + struct sockaddr_in *proxy_addr; +}; struct http_channel_observer_s { void *data; @@ -86,8 +112,8 @@ struct http_channel_observer_s { }; -static const char *http_lookup_header(struct http_header *header, - const char *name) +const char *http_lookup_header(struct http_header *header, + const char *name) { for (; header; header = header->next) if (!strcasecmp(name, header->name)) @@ -95,16 +121,18 @@ static const char *http_lookup_header(struct http_header *header, return 0; } -static struct http_buf *http_buf_create(void) +static struct http_buf *http_buf_create(http_server_t hs) { - struct http_buf *r; + struct http_buf *r = 0; - if (http_buf_freelist) + yaz_mutex_enter(hs->mutex); + if (hs->http_buf_freelist) { - r = http_buf_freelist; - http_buf_freelist = http_buf_freelist->next; + r = hs->http_buf_freelist; + hs->http_buf_freelist = hs->http_buf_freelist->next; } - else + yaz_mutex_leave(hs->mutex); + if (!r) r = xmalloc(sizeof(struct http_buf)); r->offset = 0; r->len = 0; @@ -112,24 +140,26 @@ static struct http_buf *http_buf_create(void) return r; } -static void http_buf_destroy(struct http_buf *b) +static void http_buf_destroy(http_server_t hs, struct http_buf *b) { - b->next = http_buf_freelist; - http_buf_freelist = b; + yaz_mutex_enter(hs->mutex); + b->next = hs->http_buf_freelist; + hs->http_buf_freelist = b; + yaz_mutex_leave(hs->mutex); } -static void http_buf_destroy_queue(struct http_buf *b) +static void http_buf_destroy_queue(http_server_t hs, struct http_buf *b) { struct http_buf *p; while (b) { p = b->next; - http_buf_destroy(b); + http_buf_destroy(hs, b); b = p; } } -static struct http_buf *http_buf_bybuf(char *b, int len) +static struct http_buf *http_buf_bybuf(http_server_t hs, char *b, int len) { struct http_buf *res = 0; struct http_buf **p = &res; @@ -139,7 +169,7 @@ static struct http_buf *http_buf_bybuf(char *b, int len) int tocopy = len; if (tocopy > HTTP_BUF_SIZE) tocopy = HTTP_BUF_SIZE; - *p = http_buf_create(); + *p = http_buf_create(hs); memcpy((*p)->buf, b, tocopy); (*p)->len = tocopy; len -= tocopy; @@ -157,10 +187,10 @@ static void http_buf_enqueue(struct http_buf **queue, struct http_buf *b) *queue = b; } -static struct http_buf *http_buf_bywrbuf(WRBUF wrbuf) +static struct http_buf *http_buf_bywrbuf(http_server_t hs, WRBUF wrbuf) { // Heavens to Betsy (buf)! - return http_buf_bybuf(wrbuf_buf(wrbuf), wrbuf_len(wrbuf)); + return http_buf_bybuf(hs, wrbuf_buf(wrbuf), wrbuf_len(wrbuf)); } // Non-destructively collapse chain of buffers into a string (max *len) @@ -189,7 +219,8 @@ static int http_buf_size(struct http_buf *b) } // Ddestructively munch up to len from head of queue. -static int http_buf_read(struct http_buf **b, char *buf, int len) +static int http_buf_read(http_server_t hs, + struct http_buf **b, char *buf, int len) { int rd = 0; while ((*b) && rd < len) @@ -208,7 +239,7 @@ static int http_buf_read(struct http_buf **b, char *buf, int len) else { struct http_buf *n = (*b)->next; - http_buf_destroy(*b); + http_buf_destroy(hs, *b); *b = n; } } @@ -463,7 +494,7 @@ struct http_request *http_parse_request(struct http_channel *c, char *start = nmem_malloc(c->nmem, len+1); char *buf = start; - if (http_buf_read(queue, buf, len) < len) + if (http_buf_read(c->http_server, queue, buf, len) < len) { yaz_log(YLOG_WARN, "http_buf_read < len (%d)", len); return 0; @@ -628,7 +659,7 @@ static struct http_buf *http_serialize_response(struct http_channel *c, if (r->payload) wrbuf_puts(c->wrbuf, r->payload); - return http_buf_bywrbuf(c->wrbuf); + return http_buf_bywrbuf(c->http_server, c->wrbuf); } // Serialize a HTTP request @@ -655,14 +686,14 @@ static struct http_buf *http_serialize_request(struct http_request *r) yaz_log(YLOG_LOG, "WRITING TO PROXY:\n%s\n----", wrbuf_cstr(c->wrbuf)); #endif - return http_buf_bywrbuf(c->wrbuf); + return http_buf_bywrbuf(c->http_server, c->wrbuf); } static int http_weshouldproxy(struct http_request *rq) { struct http_channel *c = rq->channel; - if (c->server->proxy_addr && !strstr(rq->path, "search.pz2")) + if (c->server->http_server->proxy_addr && !strstr(rq->path, "search.pz2")) return 1; return 0; } @@ -751,8 +782,9 @@ static int http_proxy(struct http_request *rq) &one, sizeof(one)) < 0) abort(); enable_nonblock(sock); - if (connect(sock, (struct sockaddr *) c->server->proxy_addr, - sizeof(*c->server->proxy_addr)) < 0) + if (connect(sock, (struct sockaddr *) + c->server->http_server->proxy_addr, + sizeof(*c->server->http_server->proxy_addr)) < 0) { if (!is_inprogress()) { @@ -766,9 +798,10 @@ static int http_proxy(struct http_request *rq) p->first_response = 1; c->proxy = p; // We will add EVENT_OUTPUT below - p->iochan = iochan_create(sock, proxy_io, EVENT_INPUT); + p->iochan = iochan_create(sock, proxy_io, EVENT_INPUT, "http_proxy"); iochan_setdata(p->iochan, p); - pazpar2_add_channel(p->iochan); + + iochan_add(ser->iochan_man, p->iochan); } // Do _not_ modify Host: header, just checking it's existence @@ -816,7 +849,7 @@ void http_send_response(struct http_channel *ch) if (!hb) { yaz_log(YLOG_WARN, "Failed to serialize HTTP response"); - http_destroy(ch->iochan); + http_channel_destroy(ch->iochan); } else { @@ -845,24 +878,24 @@ static void http_error(struct http_channel *hc, int no, const char *msg) static void http_io(IOCHAN i, int event) { struct http_channel *hc = iochan_getdata(i); - - switch (event) + while (event) { - int res, reqlen; - struct http_buf *htbuf; - - case EVENT_INPUT: - htbuf = http_buf_create(); + if (event == EVENT_INPUT) + { + int res, reqlen; + struct http_buf *htbuf; + + htbuf = http_buf_create(hc->http_server); res = recv(iochan_getfd(i), htbuf->buf, HTTP_BUF_SIZE -1, 0); if (res == -1 && errno == EAGAIN) { - http_buf_destroy(htbuf); + http_buf_destroy(hc->http_server, htbuf); return; } if (res <= 0) { - http_buf_destroy(htbuf); - http_destroy(i); + http_buf_destroy(hc->http_server, htbuf); + http_channel_destroy(i); return; } htbuf->buf[res] = '\0'; @@ -900,49 +933,56 @@ static void http_io(IOCHAN i, int event) http_command(hc); } } - break; - case EVENT_OUTPUT: + } + else if (event == EVENT_OUTPUT) + { + event = 0; if (hc->oqueue) { struct http_buf *wb = hc->oqueue; - res = send(iochan_getfd(hc->iochan), wb->buf + wb->offset, wb->len, 0); + int res; + res = send(iochan_getfd(hc->iochan), + wb->buf + wb->offset, wb->len, 0); if (res <= 0) { yaz_log(YLOG_WARN|YLOG_ERRNO, "write"); - http_destroy(i); + http_channel_destroy(i); return; } if (res == wb->len) { hc->oqueue = hc->oqueue->next; - http_buf_destroy(wb); + http_buf_destroy(hc->http_server, wb); } else { wb->len -= res; wb->offset += res; } - if (!hc->oqueue) { + if (!hc->oqueue) + { if (!hc->keep_alive) { - http_destroy(i); + http_channel_destroy(i); return; } else { iochan_clearflag(i, EVENT_OUTPUT); if (hc->iqueue) - iochan_setevent(hc->iochan, EVENT_INPUT); + event = EVENT_INPUT; } } } - if (!hc->oqueue && hc->proxy && !hc->proxy->iochan) - http_destroy(i); // Server closed; we're done - break; - default: + http_channel_destroy(i); // Server closed; we're done + } + else + { yaz_log(YLOG_WARN, "Unexpected event on connection"); - http_destroy(i); + http_channel_destroy(i); + event = 0; + } } } @@ -958,7 +998,7 @@ static void proxy_io(IOCHAN pi, int event) struct http_buf *htbuf; case EVENT_INPUT: - htbuf = http_buf_create(); + htbuf = http_buf_create(hc->http_server); res = recv(iochan_getfd(pi), htbuf->buf, HTTP_BUF_SIZE -1, 0); if (res == 0 || (res < 0 && !is_inprogress())) { @@ -966,7 +1006,7 @@ static void proxy_io(IOCHAN pi, int event) { yaz_log(YLOG_WARN, "Proxy read came up short"); // Close channel and alert client HTTP channel that we're gone - http_buf_destroy(htbuf); + http_buf_destroy(hc->http_server, htbuf); #ifdef WIN32 closesocket(iochan_getfd(pi)); #else @@ -977,7 +1017,7 @@ static void proxy_io(IOCHAN pi, int event) } else { - http_destroy(hc->iochan); + http_channel_destroy(hc->iochan); return; } } @@ -1002,13 +1042,13 @@ static void proxy_io(IOCHAN pi, int event) if (res <= 0) { yaz_log(YLOG_WARN|YLOG_ERRNO, "write"); - http_destroy(hc->iochan); + http_channel_destroy(hc->iochan); return; } if (res == htbuf->len) { struct http_buf *np = htbuf->next; - http_buf_destroy(htbuf); + http_buf_destroy(hc->http_server, htbuf); pc->oqueue = np; } else @@ -1023,7 +1063,7 @@ static void proxy_io(IOCHAN pi, int event) break; default: yaz_log(YLOG_WARN, "Unexpected event on connection"); - http_destroy(hc->iochan); + http_channel_destroy(hc->iochan); } } @@ -1031,9 +1071,10 @@ static void http_fire_observers(struct http_channel *c); static void http_destroy_observers(struct http_channel *c); // Cleanup channel -static void http_destroy(IOCHAN i) +static void http_channel_destroy(IOCHAN i) { struct http_channel *s = iochan_getdata(i); + http_server_t http_server; if (s->proxy) { @@ -1046,15 +1087,23 @@ static void http_destroy(IOCHAN i) #endif iochan_destroy(s->proxy->iochan); } - http_buf_destroy_queue(s->proxy->oqueue); + http_buf_destroy_queue(s->http_server, s->proxy->oqueue); xfree(s->proxy); } - http_buf_destroy_queue(s->iqueue); - http_buf_destroy_queue(s->oqueue); + http_buf_destroy_queue(s->http_server, s->iqueue); + http_buf_destroy_queue(s->http_server, s->oqueue); http_fire_observers(s); http_destroy_observers(s); - s->next = http_channel_freelist; - http_channel_freelist = s; + + http_server = s->http_server; /* save it for destroy (decref) */ + + yaz_mutex_enter(s->http_server->mutex); + s->next = s->http_server->http_channel_freelist; + s->http_server->http_channel_freelist = s; + yaz_mutex_leave(s->http_server->mutex); + + http_server_destroy(http_server); + #ifdef WIN32 closesocket(iochan_getfd(i)); #else @@ -1063,14 +1112,20 @@ static void http_destroy(IOCHAN i) iochan_destroy(i); } -static struct http_channel *http_create(const char *addr, - struct conf_server *server) +static struct http_channel *http_channel_create(http_server_t hs, + const char *addr, + struct conf_server *server) { - struct http_channel *r = http_channel_freelist; + struct http_channel *r; + + yaz_mutex_enter(hs->mutex); + r = hs->http_channel_freelist; + if (r) + hs->http_channel_freelist = r->next; + yaz_mutex_leave(hs->mutex); if (r) { - http_channel_freelist = r->next; nmem_reset(r->nmem); wrbuf_rewind(r->wrbuf); } @@ -1080,6 +1135,10 @@ static struct http_channel *http_create(const char *addr, r->nmem = nmem_create(); r->wrbuf = wrbuf_alloc(); } + http_server_incref(hs); + r->http_server = hs; + r->http_sessions = hs->http_sessions; + assert(r->http_sessions); r->server = server; r->proxy = 0; r->iochan = 0; @@ -1119,13 +1178,13 @@ static void http_accept(IOCHAN i, int event) enable_nonblock(s); yaz_log(YLOG_DEBUG, "New command connection"); - c = iochan_create(s, http_io, EVENT_INPUT | EVENT_EXCEPT); + c = iochan_create(s, http_io, EVENT_INPUT | EVENT_EXCEPT, "http_session_socket"); - ch = http_create(inet_ntoa(addr.sin_addr), server); + ch = http_channel_create(server->http_server, inet_ntoa(addr.sin_addr), + server); ch->iochan = c; iochan_setdata(c, ch); - - pazpar2_add_channel(c); + iochan_add(server->iochan_man, c); } /* Create a http-channel listener, syntax [host:]port */ @@ -1190,23 +1249,26 @@ int http_init(const char *addr, struct conf_server *server) return 1; } - server->listener_socket = l; + server->http_server = http_server_create(); - c = iochan_create(l, http_accept, EVENT_INPUT | EVENT_EXCEPT); + server->http_server->listener_socket = l; + + c = iochan_create(l, http_accept, EVENT_INPUT | EVENT_EXCEPT, "http_server"); iochan_setdata(c, server); - pazpar2_add_channel(c); + + iochan_add(server->iochan_man, c); return 0; } void http_close_server(struct conf_server *server) { /* break the event_loop (select) by closing down the HTTP listener sock */ - if (server->listener_socket) + if (server->http_server->listener_socket) { #ifdef WIN32 - closesocket(server->listener_socket); + closesocket(server->http_server->listener_socket); #else - close(server->listener_socket); + close(server->http_server->listener_socket); #endif } } @@ -1239,10 +1301,11 @@ void http_set_proxyaddr(const char *host, struct conf_server *server) } wrbuf_destroy(w); - server->proxy_addr = xmalloc(sizeof(struct sockaddr_in)); - server->proxy_addr->sin_family = he->h_addrtype; - memcpy(&server->proxy_addr->sin_addr.s_addr, he->h_addr_list[0], he->h_length); - server->proxy_addr->sin_port = htons(port); + server->http_server->proxy_addr = xmalloc(sizeof(struct sockaddr_in)); + server->http_server->proxy_addr->sin_family = he->h_addrtype; + memcpy(&server->http_server->proxy_addr->sin_addr.s_addr, + he->h_addr_list[0], he->h_length); + server->http_server->proxy_addr->sin_port = htons(port); } static void http_fire_observers(struct http_channel *c) @@ -1300,6 +1363,70 @@ void http_observer_set_data2(http_channel_observer_t obs, void *data2) obs->data2 = data2; } +http_server_t http_server_create(void) +{ + http_server_t hs = xmalloc(sizeof(*hs)); + hs->mutex = 0; + hs->proxy_addr = 0; + hs->ref_count = 1; + hs->http_buf_freelist = 0; + hs->http_channel_freelist = 0; + hs->http_sessions = 0; + return hs; +} + +void http_server_destroy(http_server_t hs) +{ + if (hs) + { + int r; + + yaz_mutex_enter(hs->mutex); /* OK: hs->mutex may be NULL */ + r = --(hs->ref_count); + yaz_mutex_leave(hs->mutex); + + if (r == 0) + { + struct http_buf *b = hs->http_buf_freelist; + struct http_channel *c = hs->http_channel_freelist; + while (b) + { + struct http_buf *b_next = b->next; + xfree(b); + b = b_next; + } + while (c) + { + struct http_channel *c_next = c->next; + nmem_destroy(c->nmem); + wrbuf_destroy(c->wrbuf); + xfree(c); + c = c_next; + } + http_sessions_destroy(hs->http_sessions); + xfree(hs->proxy_addr); + yaz_mutex_destroy(&hs->mutex); + xfree(hs); + } + } +} + +void http_server_incref(http_server_t hs) +{ + assert(hs); + yaz_mutex_enter(hs->mutex); + (hs->ref_count)++; + yaz_mutex_leave(hs->mutex); +} + +void http_mutex_init(struct conf_server *server) +{ + assert(server); + + assert(server->http_server->mutex == 0); + pazpar2_mutex_create(&server->http_server->mutex, "http_server"); + server->http_server->http_sessions = http_sessions_create(); +} /* * Local variables: