X-Git-Url: http://git.indexdata.com/?a=blobdiff_plain;f=src%2Fhttp_command.c;h=a9057509038bc2d6dce78d00b3fc53431095691c;hb=b3b1946b82bece8b3aa5dedbab43afd24cfa9597;hp=badb77bf850c723660bfd66d81fd273ba6314342;hpb=ba5951a80fdd0da1d28c318852b69a1178cd6bfa;p=pazpar2-moved-to-github.git diff --git a/src/http_command.c b/src/http_command.c index badb77b..a905750 100644 --- a/src/http_command.c +++ b/src/http_command.c @@ -33,9 +33,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include #include +#include "ppmutex.h" #include "eventl.h" #include "parameters.h" -#include "pazpar2.h" +#include "session.h" #include "http.h" #include "settings.h" #include "client.h" @@ -50,11 +51,46 @@ struct http_session { struct session *psession; unsigned int session_id; int timestamp; + int destroy_counter; + int activity_counter; NMEM nmem; + http_sessions_t http_sessions; struct http_session *next; }; -static struct http_session *session_list = 0; /* thread pr */ +struct http_sessions { + struct http_session *session_list; + YAZ_MUTEX mutex; + int log_level; +}; + +http_sessions_t http_sessions_create(void) +{ + http_sessions_t hs = xmalloc(sizeof(*hs)); + hs->session_list = 0; + hs->mutex = 0; + pazpar2_mutex_create(&hs->mutex, "http_sessions"); + hs->log_level = yaz_log_module_level("HTTP"); + return hs; +} + +void http_sessions_destroy(http_sessions_t hs) +{ + if (hs) + { + struct http_session *s = hs->session_list; + while (s) + { + struct http_session *s_next = s->next; + iochan_destroy(s->timeout_iochan); + destroy_session(s->psession); + nmem_destroy(s->nmem); + s = s_next; + } + yaz_mutex_destroy(&hs->mutex); + xfree(hs); + } +} void http_session_destroy(struct http_session *s); @@ -64,39 +100,68 @@ static void session_timeout(IOCHAN i, int event) http_session_destroy(s); } -struct http_session *http_session_create(struct conf_service *service) +struct http_session *http_session_create(struct conf_service *service, + http_sessions_t http_sessions, + unsigned int sesid) { NMEM nmem = nmem_create(); struct http_session *r = nmem_malloc(nmem, sizeof(*r)); + char tmp_str[50]; - r->psession = new_session(nmem, service); - r->session_id = 0; + sprintf(tmp_str, "session#%u", sesid); + r->psession = new_session(nmem, service, tmp_str); + r->session_id = sesid; r->timestamp = 0; r->nmem = nmem; - r->next = session_list; - session_list = r; - r->timeout_iochan = iochan_create(-1, session_timeout, 0); + r->destroy_counter = r->activity_counter = 0; + r->http_sessions = http_sessions; + + yaz_mutex_enter(http_sessions->mutex); + r->next = http_sessions->session_list; + http_sessions->session_list = r; + yaz_mutex_leave(http_sessions->mutex); + + r->timeout_iochan = iochan_create(-1, session_timeout, 0, "http_session_timeout"); iochan_setdata(r->timeout_iochan, r); + yaz_log(http_sessions->log_level, "%p Session %u created. timeout chan=%p timeout=%d", r, sesid, r->timeout_iochan, service->session_timeout); iochan_settimeout(r->timeout_iochan, service->session_timeout); - pazpar2_add_channel(r->timeout_iochan); + iochan_add(service->server->iochan_man, r->timeout_iochan); return r; } void http_session_destroy(struct http_session *s) { - struct http_session **p; + int must_destroy = 0; + + http_sessions_t http_sessions = s->http_sessions; + + yaz_log(http_sessions->log_level, "%p Session %u destroyed", s, s->session_id); + yaz_mutex_enter(http_sessions->mutex); + /* only if http_session has no active http sessions on it can be destroyed */ + if (s->destroy_counter == s->activity_counter) { + struct http_session **p = 0; + must_destroy = 1; + for (p = &http_sessions->session_list; *p; p = &(*p)->next) + if (*p == s) + { + *p = (*p)->next; + break; + } + } + yaz_mutex_leave(http_sessions->mutex); + if (must_destroy) + { /* destroying for real */ + yaz_log(http_sessions->log_level, "%p Session %u destroyed", s, s->session_id); + iochan_destroy(s->timeout_iochan); + destroy_session(s->psession); + nmem_destroy(s->nmem); + } + else { + yaz_log(http_sessions->log_level, "%p Session %u destroyed delayed. Active clients (%d-%d). Waiting for new timeout.", + s, s->session_id, s->activity_counter, s->destroy_counter); + } - for (p = &session_list; *p; p = &(*p)->next) - if (*p == s) - { - *p = (*p)->next; - break; - } - yaz_log(YLOG_LOG, "Destroying session %u", s->session_id); - iochan_destroy(s->timeout_iochan); - destroy_session(s->psession); - nmem_destroy(s->nmem); } static const char *get_msg(enum pazpar2_error_code code) @@ -117,6 +182,7 @@ static const char *get_msg(enum pazpar2_error_code code) { PAZPAR2_CONFIG_TARGET, "Target cannot be configured"}, { PAZPAR2_RECORD_FAIL, "Record command failed"}, { PAZPAR2_NOT_IMPLEMENTED, "Not implemented"}, + { PAZPAR2_NO_SERVICE, "No service"}, { PAZPAR2_LAST_ERROR, "Last error"}, { 0, 0 } }; @@ -184,10 +250,13 @@ unsigned int make_sessionid(void) return res; } -static struct http_session *locate_session(struct http_request *rq, struct http_response *rs) +static struct http_session *locate_session(struct http_channel *c) { + struct http_response *rs = c->response; + struct http_request *rq = c->request; struct http_session *p; const char *session = http_argbyname(rq, "session"); + http_sessions_t http_sessions = c->http_sessions; unsigned int id; if (!session) @@ -196,14 +265,27 @@ static struct http_session *locate_session(struct http_request *rq, struct http_ return 0; } id = atoi(session); - for (p = session_list; p; p = p->next) + yaz_mutex_enter(http_sessions->mutex); + for (p = http_sessions->session_list; p; p = p->next) if (id == p->session_id) - { - iochan_activity(p->timeout_iochan); - return p; - } - error(rs, PAZPAR2_NO_SESSION, session); - return 0; + break; + if (p) + p->activity_counter++; + yaz_mutex_leave(http_sessions->mutex); + if (p) + iochan_activity(p->timeout_iochan); + else + error(rs, PAZPAR2_NO_SESSION, session); + return p; +} + +// Call after use of locate_session, in order to increment the destroy_counter +static void release_session(struct http_channel *c, struct http_session *session) { + http_sessions_t http_sessions = c->http_sessions; + yaz_mutex_enter(http_sessions->mutex); + if (session) + session->destroy_counter++; + yaz_mutex_leave(http_sessions->mutex); } // Decode settings parameters and apply to session @@ -279,25 +361,31 @@ static void cmd_init(struct http_channel *c) service = locate_service(c->server, service_name); if (!service) { - error(rs, PAZPAR2_MALFORMED_PARAMETER_VALUE, "service"); + error(rs, PAZPAR2_NO_SERVICE, service_name ? service_name : "unnamed"); return; } - service_incref(service); } - s = http_session_create(service); + sesid = make_sessionid(); + s = http_session_create(service, c->http_sessions, sesid); - yaz_log(YLOG_DEBUG, "HTTP Session init"); + yaz_log(c->http_sessions->log_level, "%p Session init %u ", s, sesid); if (!clear || *clear == '0') session_init_databases(s->psession); else - yaz_log(YLOG_LOG, "No databases preloaded"); + yaz_log(YLOG_LOG, "HTTP Session %u init: No databases preloaded", sesid); - sesid = make_sessionid(); - s->session_id = sesid; if (process_settings(s->psession, c->request, c->response) < 0) return; - sprintf(buf, HTTP_COMMAND_RESPONSE_PREFIX "OK%u" - "" PAZPAR2_PROTOCOL_VERSION "", sesid); + + sprintf(buf, HTTP_COMMAND_RESPONSE_PREFIX + "OK%d", sesid); + if (c->server->server_id) + { + strcat(buf, "."); + strcat(buf, c->server->server_id); + } + strcat(buf, "" + "" PAZPAR2_PROTOCOL_VERSION ""); rs->payload = nmem_strdup(c->nmem, buf); http_send_response(c); } @@ -316,7 +404,7 @@ static void cmd_settings(struct http_channel *c) { struct http_response *rs = c->response; struct http_request *rq = c->request; - struct http_session *s = locate_session(rq, rs); + struct http_session *s = locate_session(c); const char *content_type = http_lookup_header(rq->headers, "Content-Type"); if (!s) @@ -338,10 +426,13 @@ static void cmd_settings(struct http_channel *c) xmlFreeDoc(doc); } - if (process_settings(s->psession, rq, rs) < 0) + if (process_settings(s->psession, rq, rs) < 0) { + release_session(c,s); return; + } rs->payload = HTTP_COMMAND_RESPONSE_PREFIX "OK"; http_send_response(c); + release_session(c,s); } // Compares two hitsbytarget nodes by hitcount @@ -353,7 +444,7 @@ static int cmp_ht(const void *p1, const void *p2) } // This implements functionality somewhat similar to 'bytarget', but in a termlist form -static void targets_termlist(WRBUF wrbuf, struct session *se, int num, +static int targets_termlist(WRBUF wrbuf, struct session *se, int num, NMEM nmem) { struct hitsbytarget *ht; @@ -390,13 +481,14 @@ static void targets_termlist(WRBUF wrbuf, struct session *se, int num, ht[i].diagnostic); wrbuf_puts(wrbuf, "\n"); } + return count; } static void cmd_termlist(struct http_channel *c) { struct http_response *rs = c->response; struct http_request *rq = c->request; - struct http_session *s = locate_session(rq, rs); + struct http_session *s = locate_session(c); struct termlist_score **p; int len; int i; @@ -404,6 +496,7 @@ static void cmd_termlist(struct http_channel *c) const char *nums = http_argbyname(rq, "num"); int num = 15; int status; + WRBUF debug_log = wrbuf_alloc(); if (!s) return; @@ -430,16 +523,19 @@ static void cmd_termlist(struct http_channel *c) tp = name + strlen(name); strncpy(tname, name, tp - name); tname[tp - name] = '\0'; - wrbuf_puts(c->wrbuf, "wrbuf, tname); wrbuf_puts(c->wrbuf, "\">\n"); - if (!strcmp(tname, "xtargets")) - targets_termlist(c->wrbuf, s->psession, num, c->nmem); + if (!strcmp(tname, "xtargets")) { + int targets = targets_termlist(c->wrbuf, s->psession, num, c->nmem); + wrbuf_printf(debug_log, " xtargets: %d", targets); + } else { p = termlist(s->psession, tname, &len); - if (p) + if (p && len) + wrbuf_printf(debug_log, " %s: %d", tname, len); + if (p) { for (i = 0; i < len && i < num; i++){ // prevnt sending empty term elements if (!p[i]->term || !p[i]->term[0]) @@ -455,6 +551,7 @@ static void cmd_termlist(struct http_channel *c) p[i]->frequency); wrbuf_puts(c->wrbuf, "\n"); } + } } wrbuf_puts(c->wrbuf, "\n"); name = tp; @@ -462,8 +559,11 @@ static void cmd_termlist(struct http_channel *c) name++; } wrbuf_puts(c->wrbuf, "\n"); + yaz_log(YLOG_DEBUG, "termlist response: %s ", wrbuf_cstr(debug_log)); + wrbuf_destroy(debug_log); rs->payload = nmem_strdup(rq->channel->nmem, wrbuf_cstr(c->wrbuf)); http_send_response(c); + release_session(c,s); } @@ -471,7 +571,7 @@ static void cmd_bytarget(struct http_channel *c) { struct http_response *rs = c->response; struct http_request *rq = c->request; - struct http_session *s = locate_session(rq, rs); + struct http_session *s = locate_session(c); struct hitsbytarget *ht; const char *settings = http_argbyname(rq, "settings"); int count, i; @@ -517,6 +617,7 @@ static void cmd_bytarget(struct http_channel *c) wrbuf_puts(c->wrbuf, ""); rs->payload = nmem_strdup(c->nmem, wrbuf_cstr(c->wrbuf)); http_send_response(c); + release_session(c,s); } static void write_metadata(WRBUF w, struct conf_service *service, @@ -631,7 +732,7 @@ static void cmd_record(struct http_channel *c) { struct http_response *rs = c->response; struct http_request *rq = c->request; - struct http_session *s = locate_session(rq, rs); + struct http_session *s = locate_session(c); struct record_cluster *rec, *prev_r, *next_r; struct record *r; struct conf_service *service; @@ -648,7 +749,7 @@ static void cmd_record(struct http_channel *c) return; } wrbuf_rewind(c->wrbuf); - if (!(rec = show_single(s->psession, idstr, &prev_r, &next_r))) + if (!(rec = show_single_start(s->psession, idstr, &prev_r, &next_r))) { if (session_active_clients(s->psession) == 0) { @@ -659,6 +760,7 @@ static void cmd_record(struct http_channel *c) { error(rs, PAZPAR2_RECORD_MISSING, idstr); } + release_session(c, s); return; } if (offsetstr) @@ -678,7 +780,6 @@ static void cmd_record(struct http_channel *c) if (!r) { error(rs, PAZPAR2_RECORD_FAIL, "no record at offset given"); - return; } else { @@ -726,6 +827,8 @@ static void cmd_record(struct http_channel *c) rs->payload = nmem_strdup(c->nmem, wrbuf_cstr(c->wrbuf)); http_send_response(c); } + show_single_stop(s->psession, rec); + release_session(c, s); } static void cmd_record_ready(void *data) @@ -739,7 +842,7 @@ static void show_records(struct http_channel *c, int active) { struct http_request *rq = c->request; struct http_response *rs = c->response; - struct http_session *s = locate_session(rq, rs); + struct http_session *s = locate_session(c); struct record_cluster **rl; struct reclist_sortparms *sp; const char *start = http_argbyname(rq, "start"); @@ -767,10 +870,12 @@ static void show_records(struct http_channel *c, int active) if (!(sp = reclist_parse_sortparms(c->nmem, sort, s->psession->service))) { error(rs, PAZPAR2_MALFORMED_PARAMETER_VALUE, "sort"); + release_session(c, s); return; } - rl = show(s->psession, sp, startn, &numn, &total, &total_hits, c->nmem); + + rl = show_range_start(s->psession, sp, startn, &numn, &total, &total_hits); wrbuf_rewind(c->wrbuf); wrbuf_puts(c->wrbuf, HTTP_COMMAND_RESPONSE_PREFIX "\nOK\n"); @@ -794,16 +899,20 @@ static void show_records(struct http_channel *c, int active) if (ccount > 1) wrbuf_printf(c->wrbuf, "%d\n", ccount); if (strstr(sort, "relevance")) - wrbuf_printf(c->wrbuf, "%d\n", rec->relevance); + wrbuf_printf(c->wrbuf, "%d\n", + rec->relevance_score); wrbuf_puts(c->wrbuf, ""); wrbuf_xmlputs(c->wrbuf, rec->recid); wrbuf_puts(c->wrbuf, "\n"); wrbuf_puts(c->wrbuf, "\n"); } + show_range_stop(s->psession, rl); + wrbuf_puts(c->wrbuf, "\n"); rs->payload = nmem_strdup(c->nmem, wrbuf_cstr(c->wrbuf)); http_send_response(c); + release_session(c, s); } static void show_records_ready(void *data) @@ -816,8 +925,7 @@ static void show_records_ready(void *data) static void cmd_show(struct http_channel *c) { struct http_request *rq = c->request; - struct http_response *rs = c->response; - struct http_session *s = locate_session(rq, rs); + struct http_session *s = locate_session(c); const char *block = http_argbyname(rq, "block"); int status; @@ -834,24 +942,25 @@ static void cmd_show(struct http_channel *c) if (session_set_watch(s->psession, SESSION_WATCH_SHOW, show_records_ready, c, c) != 0) { - yaz_log(YLOG_DEBUG, "Blocking on cmd_show"); + yaz_log(c->http_sessions->log_level, "%p Session %u: Blocking on cmd_show", s, s->session_id); } + release_session(c,s); return; } } - show_records(c, status); + release_session(c,s); } static void cmd_ping(struct http_channel *c) { - struct http_request *rq = c->request; struct http_response *rs = c->response; - struct http_session *s = locate_session(rq, rs); + struct http_session *s = locate_session(c); if (!s) return; rs->payload = HTTP_COMMAND_RESPONSE_PREFIX "OK"; http_send_response(c); + release_session(c, s); } static int utf_8_valid(const char *str) @@ -883,7 +992,7 @@ static void cmd_search(struct http_channel *c) { struct http_request *rq = c->request; struct http_response *rs = c->response; - struct http_session *s = locate_session(rq, rs); + struct http_session *s = locate_session(c); const char *query = http_argbyname(rq, "query"); const char *filter = http_argbyname(rq, "filter"); const char *maxrecs = http_argbyname(rq, "maxrecs"); @@ -896,29 +1005,32 @@ static void cmd_search(struct http_channel *c) if (!query) { error(rs, PAZPAR2_MISSING_PARAMETER, "query"); + release_session(c,s); return; } if (!utf_8_valid(query)) { error(rs, PAZPAR2_MALFORMED_PARAMETER_ENCODING, "query"); + release_session(c,s); return; } code = search(s->psession, query, startrecs, maxrecs, filter, &addinfo); if (code) { error(rs, code, addinfo); + release_session(c,s); return; } rs->payload = HTTP_COMMAND_RESPONSE_PREFIX "OK"; http_send_response(c); + release_session(c,s); } static void cmd_stat(struct http_channel *c) { - struct http_request *rq = c->request; struct http_response *rs = c->response; - struct http_session *s = locate_session(rq, rs); + struct http_session *s = locate_session(c); struct statistics stat; int clients; @@ -950,6 +1062,7 @@ static void cmd_stat(struct http_channel *c) wrbuf_puts(c->wrbuf, ""); rs->payload = nmem_strdup(c->nmem, wrbuf_cstr(c->wrbuf)); http_send_response(c); + release_session(c,s); } static void cmd_info(struct http_channel *c)