/************************************************************************************************* * A server of Tokyo Dystopia * Copyright (C) 2007-2009 Mikio Hirabayashi * Copyright (C) 2009 Yusuke Kawasaki * This file is not a part of Tokyo Dystopia. * Tokyo Tyrant is free software; you can redistribute it and/or modify it under the terms of * the GNU Lesser General Public License as published by the Free Software Foundation; either * version 2.1 of the License or any later version. Tokyo Tyrant is distributed in the hope * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * You should have received a copy of the GNU Lesser General Public License along with Tokyo * Tyrant; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include #include "myconf.h" #define DEFPORT 1977 // default port #define DEFTHNUM 8 // default thread number #define DEFPIDPATH "tdserver.pid" // default name of the PID file #define DEFSEARCHMAX 10 // default maximum number of printed IDs #define DEFBINFILE "result.bin" // default name of binary file downloaded #define LINEBUFSIZ 8192 // size of a line buffer #define TOKENUNIT 256 // unit number of tokens #define TDMSKPUT (1ULL<<0) /* bit mask of put command */ #define TDMSKOUT (1ULL<<5) /* bit mask of out command */ #define TDMSKGET (1ULL<<6) /* bit mask of get command */ #define TDMSKALLHTTP (1ULL<<29) /* bit mask of all commands the HTTP */ #define TDMSKALLREAD (1ULL<<30) /* bit mask of all commands of reading */ #define TDMSKALLWRITE (1ULL<<31) /* bit mask of all commands of writing */ typedef struct { // type of structure of logging opaque object int fd; } LOGARG; typedef struct { // type of structure of task opaque object uint64_t mask; TCIDB *idb; } TASKARG; /* global variables */ const char *g_progname = NULL; // program name double g_starttime = 0.0; // start time TTSERV *g_serv = NULL; // server object int g_loglevel = TTLOGINFO; // whether to log debug information bool g_restart = false; // restart flag /* function prototypes */ int main(int argc, char **argv); static void usage(void); static uint64_t getcmdmask(const char *expr); static void sigtermhandler(int signum); static void sigchldhandler(int signum); static int proc(const char *dbname, const char *host, int port, int thnum, int tout, bool dmn, const char *pidpath, bool kl, const char *logpath, uint64_t mask); static void do_log(int level, const char *msg, void *opq); static void do_task(TTSOCK *sock, void *opq, TTREQ *req); static char **tokenize(char *str, int *np); static void do_http_get(TTSOCK *sock, TASKARG *arg, TTREQ *req, int ver, const char *uri); static void do_http_head(TTSOCK *sock, TASKARG *arg, TTREQ *req, int ver, const char *uri); static void do_http_put(TTSOCK *sock, TASKARG *arg, TTREQ *req, int ver, const char *uri); static void do_http_post(TTSOCK *sock, TASKARG *arg, TTREQ *req, int ver, const char *uri); static void do_http_delete(TTSOCK *sock, TASKARG *arg, TTREQ *req, int ver, const char *uri); static void readcgiparam(char *buf, TCMAP *map); /* main routine */ int main(int argc, char **argv){ g_progname = argv[0]; g_starttime = tctime(); char *dbname = NULL; char *host = NULL; char *pidpath = NULL; char *logpath = NULL; int port = DEFPORT; int thnum = DEFTHNUM; int tout = 0; bool dmn = false; bool kl = false; int mport = DEFPORT; uint64_t mask = 0; for(int i = 1; i < argc; i++){ if(!dbname && argv[i][0] == '-'){ if(!strcmp(argv[i], "-host")){ if(++i >= argc) usage(); host = argv[i]; } else if(!strcmp(argv[i], "-port")){ if(++i >= argc) usage(); port = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-thnum")){ if(++i >= argc) usage(); thnum = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-tout")){ if(++i >= argc) usage(); tout = tcatoi(argv[i]); } else if(!strcmp(argv[i], "-dmn")){ dmn = true; } else if(!strcmp(argv[i], "-pid")){ if(++i >= argc) usage(); pidpath = argv[i]; } else if(!strcmp(argv[i], "-kl")){ kl = true; } else if(!strcmp(argv[i], "-log")){ if(++i >= argc) usage(); logpath = argv[i]; } else if(!strcmp(argv[i], "-ld")){ g_loglevel = TTLOGDEBUG; } else if(!strcmp(argv[i], "-le")){ g_loglevel = TTLOGERROR; } else if(!strcmp(argv[i], "-mask")){ if(++i >= argc) usage(); mask |= getcmdmask(argv[i]); } else if(!strcmp(argv[i], "-unmask")){ if(++i >= argc) usage(); mask &= ~getcmdmask(argv[i]); } else if(!strcmp(argv[i], "--version")){ printf("Tokyo Dystopia version %s (%d:%s) for %s\n", tdversion, _TD_LIBVER, _TD_FORMATVER, TDSYSNAME); printf("Copyright (C) 2007-2009 Mikio Hirabayashi\n"); printf("Copyright (C) 2009 Yusuke Kawasaki\n"); exit(0); } else { usage(); } } else if(!dbname){ dbname = argv[i]; } else { usage(); } } if(!dbname || thnum < 1 || mport < 1) usage(); if(dmn && !pidpath) pidpath = DEFPIDPATH; g_serv = ttservnew(); int rv = proc(dbname, host, port, thnum, tout, dmn, pidpath, kl, logpath, mask); ttservdel(g_serv); return rv; } /* print the usage and exit */ static void usage(void){ fprintf(stderr, "%s: A server of Tokyo Dystopia\n", g_progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s [-host name] [-port num] [-thnum num] [-tout num]" " [-dmn] [-pid path] [-kl] [-log path] [-ld|-le] [-sid num]" " [-mask expr] [-unmask expr] [dbname]\n", g_progname); fprintf(stderr, "\n"); exit(1); } /* get the bit mask of a command name */ static uint64_t getcmdmask(const char *expr){ uint64_t mask = 0; TCLIST *fields = tcstrsplit(expr, " ,"); for(int i = 0; i < tclistnum(fields); i++){ const char *name = tclistval2(fields, i); if(tcstrifwm(name, "0x")){ mask |= strtoll(name, NULL, 16); } else if(!tcstricmp(name, "put")){ mask |= TDMSKPUT; } else if(!tcstricmp(name, "out")){ mask |= TDMSKOUT; } else if(!tcstricmp(name, "get")){ mask |= TDMSKGET; } else if(!tcstricmp(name, "all")){ mask |= UINT64_MAX; } else if(!tcstricmp(name, "allhttp")){ mask |= TDMSKALLHTTP; } else if(!tcstricmp(name, "allread")){ mask |= TDMSKALLREAD; } else if(!tcstricmp(name, "allwrite")){ mask |= TDMSKALLWRITE; } } tclistdel(fields); return mask; } /* handle termination signals */ static void sigtermhandler(int signum){ if(signum == SIGHUP) g_restart = true; ttservkill(g_serv); } /* handle child event signals */ static void sigchldhandler(int signum){ return; } /* perform the command */ static int proc(const char *dbname, const char *host, int port, int thnum, int tout, bool dmn, const char *pidpath, bool kl, const char *logpath, uint64_t mask){ LOGARG larg; larg.fd = 1; ttservsetloghandler(g_serv, do_log, &larg); if(dmn){ if(dbname && *dbname != MYPATHCHR) ttservlog(g_serv, TTLOGINFO, "warning: dbname(%s) is not the absolute path", dbname); if(port == 0 && host && *host != MYPATHCHR) ttservlog(g_serv, TTLOGINFO, "warning: host(%s) is not the absolute path", host); if(pidpath && *pidpath != MYPATHCHR) ttservlog(g_serv, TTLOGINFO, "warning: pid(%s) is not the absolute path", pidpath); if(logpath && *logpath != MYPATHCHR) ttservlog(g_serv, TTLOGINFO, "warning: log(%s) is not the absolute path", logpath); if(chdir("/") == -1){ ttservlog(g_serv, TTLOGERROR, "chdir failed"); return 1; } } if(dbname && (!strcmp(dbname, "*") || !strcmp(dbname, "+"))){ ttservlog(g_serv, TTLOGERROR, "on-memory database not supported: %s", dbname); return 1; } if(pidpath){ char *numstr = tcreadfile(pidpath, -1, NULL); if(numstr && kl){ int64_t pid = tcatoi(numstr); tcfree(numstr); ttservlog(g_serv, TTLOGINFO, "warning: killing the process %lld with SIGTERM", (long long)pid); if(kill(pid, SIGTERM) != 0) ttservlog(g_serv, TTLOGERROR, "kill failed"); int cnt = 0; while(true){ usleep(1000 * 100); if((numstr = tcreadfile(pidpath, -1, NULL)) != NULL){ tcfree(numstr); } else { break; } if(++cnt >= 100){ ttservlog(g_serv, TTLOGINFO, "warning: killing the process %lld with SIGKILL", (long long)pid); if(kill(pid, SIGKILL) != 0) ttservlog(g_serv, TTLOGERROR, "kill failed"); unlink(pidpath); break; } } numstr = tcreadfile(pidpath, -1, NULL); } if(numstr){ int64_t pid = tcatoi(numstr); tcfree(numstr); ttservlog(g_serv, TTLOGERROR, "the process %lld may be already running", (long long)pid); return 1; } } if(dmn && !ttdaemonize()){ ttservlog(g_serv, TTLOGERROR, "ttdaemonize failed"); return 1; } if(logpath){ int fd = open(logpath, O_WRONLY | O_APPEND | O_CREAT, 00644); if(fd != -1){ larg.fd = fd; } else { ttservlog(g_serv, TTLOGERROR, "the log file %s could not be opened", logpath); return 1; } } int64_t pid = getpid(); ttservlog(g_serv, TTLOGSYSTEM, "--------- logging started [%lld] --------", (long long)pid); if(pidpath){ char buf[32]; sprintf(buf, "%lld\n", (long long)pid); if(!tcwritefile(pidpath, buf, strlen(buf))){ ttservlog(g_serv, TTLOGERROR, "tcwritefile failed"); return 1; } ttservlog(g_serv, TTLOGSYSTEM, "process ID configuration: path=%s pid=%lld", pidpath, (long long)pid); } ttservlog(g_serv, TTLOGSYSTEM, "server configuration: host=%s port=%d", host ? host : "(any)", port); if(!ttservconf(g_serv, host, port)) return 1; bool err = false; ttservlog(g_serv, TTLOGSYSTEM, "database configuration: name=%s", dbname); TCIDB *idb = tcidbnew(); int omode = IDBOWRITER | IDBOCREAT; if(mask & TDMSKALLWRITE){ omode = IDBOREADER; } if(!tcidbopen(idb, dbname, omode)){ err = true; ttservlog(g_serv, TTLOGERROR, "tcidbopen failed"); return 1; } ttservtune(g_serv, thnum, tout); if(mask != 0) ttservlog(g_serv, TTLOGSYSTEM, "command bit mask: 0x%llx", (unsigned long long)mask); TASKARG targ; targ.mask = mask; targ.idb = idb; ttservsettaskhandler(g_serv, do_task, &targ); if(larg.fd != 1){ close(larg.fd); larg.fd = 1; } do { g_restart = false; if(logpath){ int fd = open(logpath, O_WRONLY | O_APPEND | O_CREAT, 00644); if(fd != -1){ larg.fd = fd; } else { err = true; ttservlog(g_serv, TTLOGERROR, "open failed"); } } if(signal(SIGTERM, sigtermhandler) == SIG_ERR || signal(SIGINT, sigtermhandler) == SIG_ERR || signal(SIGHUP, sigtermhandler) == SIG_ERR || signal(SIGPIPE, SIG_IGN) == SIG_ERR || signal(SIGCHLD, sigchldhandler) == SIG_ERR){ err = true; ttservlog(g_serv, TTLOGERROR, "signal failed"); } if(!ttservstart(g_serv)) err = true; } while(g_restart); if(!tcidbclose(idb)){ err = true; ttservlog(g_serv, TTLOGERROR, "tcidbclose failed"); } tcidbdel(idb); if(pidpath && unlink(pidpath) != 0){ err = true; ttservlog(g_serv, TTLOGERROR, "unlink failed"); } ttservlog(g_serv, TTLOGSYSTEM, "--------- logging finished [%d] --------", pid); if(logpath && close(larg.fd) == -1) err = true; return err ? 1 : 0; } /* handle a log message */ static void do_log(int level, const char *msg, void *opq){ if(level < g_loglevel) return; LOGARG *arg = (LOGARG *)opq; char date[48]; tcdatestrwww(INT64_MAX, INT_MAX, date); const char *lvstr = "unknown"; switch(level){ case TTLOGDEBUG: lvstr = "DEBUG"; break; case TTLOGINFO: lvstr = "INFO"; break; case TTLOGERROR: lvstr = "ERROR"; break; case TTLOGSYSTEM: lvstr = "SYSTEM"; break; } char buf[LINEBUFSIZ]; int len = snprintf(buf, LINEBUFSIZ, "%s\t%s\t%s\n", date, lvstr, msg); tcwrite(arg ? arg->fd : 1, buf, len); } /* handle a task and dispatch it */ static void do_task(TTSOCK *sock, void *opq, TTREQ *req){ TASKARG *arg = (TASKARG *)opq; char *line = ttsockgets2(sock); if(line){ int tnum; char **tokens = tokenize(line, &tnum); if(tnum > 0){ const char *cmd = tokens[0]; if(tnum > 2 && tcstrfwm(tokens[2], "HTTP/1.")){ int ver = tcatoi(tokens[2] + 7); const char *uri = tokens[1]; if(tcstrifwm(uri, "http://")){ const char *pv = strchr(uri + 7, '/'); if(pv) uri = pv; } if(!strcmp(cmd, "GET")){ do_http_get(sock, arg, req, ver, uri); } else if(!strcmp(cmd, "HEAD")){ do_http_head(sock, arg, req, ver, uri); } else if(!strcmp(cmd, "PUT")){ do_http_put(sock, arg, req, ver, uri); } else if(!strcmp(cmd, "POST")){ do_http_post(sock, arg, req, ver, uri); } else if(!strcmp(cmd, "DELETE")){ do_http_delete(sock, arg, req, ver, uri); } } } tcfree(tokens); tcfree(line); } } /* tokenize a string */ static char **tokenize(char *str, int *np){ int anum = TOKENUNIT; char **tokens = tcmalloc(sizeof(*tokens) * anum); int tnum = 0; while(*str == ' ' || *str == '\t'){ str++; } while(*str != '\0'){ if(tnum >= anum){ anum *= 2; tokens = tcrealloc(tokens, sizeof(*tokens) * anum); } tokens[tnum++] = str; while(*str != '\0' && *str != ' ' && *str != '\t'){ str++; } while(*str == ' ' || *str == '\t'){ *(str++) = '\0'; } } *np = tnum; return tokens; } /* handle the HTTP GET command */ static void do_http_get(TTSOCK *sock, TASKARG *arg, TTREQ *req, int ver, const char *uri){ ttservlog(g_serv, TTLOGDEBUG, "doing http_get command"); uint64_t mask = arg->mask; TCIDB *idb = arg->idb; bool keep = ver >= 1; char line[LINEBUFSIZ]; while(ttsockgets(sock, line, LINEBUFSIZ) && *line != '\0'){ char *pv = strchr(line, ':'); if(!pv) continue; *(pv++) = '\0'; while(*pv == ' ' || *pv == '\t'){ pv++; } if(!tcstricmp(line, "connection")){ if(!tcstricmp(pv, "close")){ keep = false; } else if(!tcstricmp(pv, "keep-alive")){ keep = true; } } } TCXSTR *xstr = tcxstrnew(); pthread_cleanup_push((void (*)(void *))tcxstrdel, xstr); if(mask & (TDMSKGET | TDMSKALLHTTP | TDMSKALLREAD)){ int len = sprintf(line, "// Forbidden\n"); tcxstrprintf(xstr, "HTTP/1.1 403 Forbidden\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); ttservlog(g_serv, TTLOGINFO, "do_http_get: forbidden"); } else { if(*uri == '/') uri++; char *pstr = strchr(uri, '?'); if (pstr) { *(pstr++) = '\0'; // end of uri } TCMAP *pmap = tcmapnew2(TOKENUNIT); pthread_cleanup_push((void (*)(void *))tcmapdel, pmap); readcgiparam(pstr?pstr:"",pmap); const char *query = tcmapget2(pmap, "q"); if( query && strlen(query)==0 ) query = NULL; int ksiz; char *kbuf = tcurldecode(uri, &ksiz); pthread_cleanup_push(free, kbuf); int64_t key = tcatoi(kbuf); if ( key>0 && !query ) { double sttime = tctime(); char *val = tcidbget(idb, key); double sptime = tctime() - sttime; if(val){ int len = strlen(val); tcxstrprintf(xstr, "HTTP/1.1 200 OK\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "X-TD-Time: %.6f\r\n", sptime); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, val, len); free(val); } else { int len = sprintf(line, "// Not Found\n"); tcxstrprintf(xstr, "HTTP/1.1 404 Not Found\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "X-TD-Time: %.6f\r\n", sptime); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); } } else if ( key<1 && query ) { int rnum = 0; double sttime = tctime(); uint64_t *result = tcidbsearch2(idb, query, &rnum); double sptime = tctime() - sttime; if(result){ const char *format = tcmapget2(pmap, "format"); const char *qnum = tcmapget2(pmap, "num"); int ilimit = qnum ? tcatoi(qnum) : DEFSEARCHMAX; const char *qstart = tcmapget2(pmap, "start"); int ioffset = qstart ? tcatoi(qstart)-1 : 0; if (ioffset < 1) ioffset = 0; uint64_t *resptr = result + ioffset; int rlen = (rnum > ioffset) ? rnum - ioffset : 0; if (rlen > ilimit) rlen = ilimit; if(rlen>0){ if(format && ! strcmp(format, "binary")){ const char *filename = tcmapget2(pmap, "filename"); int len = (sizeof(*result) * rlen); tcxstrprintf(xstr, "HTTP/1.1 200 OK\r\n"); tcxstrprintf(xstr, "Content-Type: application/octet-stream\r\n"); if(!filename) filename = DEFBINFILE; tcxstrprintf(xstr, "Content-Disposition: attachment; filename=%s\r\n", filename); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "X-TD-Hits: %d\r\n", rnum); tcxstrprintf(xstr, "X-TD-Time: %.6f\r\n", sptime); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, resptr, len); } else { TCXSTR *bstr = tcxstrnew(); pthread_cleanup_push((void (*)(void *))tcxstrdel, bstr); for(int i = 0; i < rlen; i++){ if (i>0) tcxstrprintf(bstr, ","); tcxstrprintf(bstr, "%lld", resptr[i]); } tcxstrprintf(bstr, "\n"); int len = tcxstrsize(bstr); tcxstrprintf(xstr, "HTTP/1.1 200 OK\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "X-TD-Hits: %d\r\n", rnum); tcxstrprintf(xstr, "X-TD-Time: %.6f\r\n", sptime); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, tcxstrptr(bstr), len); pthread_cleanup_pop(1); // bstr } } else { int len = sprintf(line, "// Not Found\n"); tcxstrprintf(xstr, "HTTP/1.1 404 Not Found\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "X-TD-Hits: %d\r\n", rnum); tcxstrprintf(xstr, "X-TD-Time: %.6f\r\n", sptime); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); } free(result); } else { int ecode = tcidbecode(idb); int len = sprintf(line, "// %s\n", tcidberrmsg(ecode)); tcxstrprintf(xstr, "HTTP/1.1 500 Internal Server Error\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); } } else { int len = sprintf(line, "// Forbidden\n"); tcxstrprintf(xstr, "HTTP/1.1 403 Forbidden\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); } pthread_cleanup_pop(1); // kbuf pthread_cleanup_pop(1); // pmap } if(ttsocksend(sock, tcxstrptr(xstr), tcxstrsize(xstr))){ req->keep = keep; } else { ttservlog(g_serv, TTLOGINFO, "do_http_get: response failed"); } pthread_cleanup_pop(1); // xstr } /* handle the HTTP HEAD command */ static void do_http_head(TTSOCK *sock, TASKARG *arg, TTREQ *req, int ver, const char *uri){ ttservlog(g_serv, TTLOGDEBUG, "doing http_head command"); uint64_t mask = arg->mask; TCIDB *idb = arg->idb; bool keep = ver >= 1; char line[LINEBUFSIZ]; while(ttsockgets(sock, line, LINEBUFSIZ) && *line != '\0'){ char *pv = strchr(line, ':'); if(!pv) continue; *(pv++) = '\0'; while(*pv == ' ' || *pv == '\t'){ pv++; } if(!tcstricmp(line, "connection")){ if(!tcstricmp(pv, "close")){ keep = false; } else if(!tcstricmp(pv, "keep-alive")){ keep = true; } } } TCXSTR *xstr = tcxstrnew(); pthread_cleanup_push((void (*)(void *))tcxstrdel, xstr); if(mask & (TDMSKGET | TDMSKALLHTTP | TDMSKALLREAD)){ tcxstrprintf(xstr, "HTTP/1.1 403 Forbidden\r\n"); tcxstrprintf(xstr, "\r\n"); ttservlog(g_serv, TTLOGINFO, "do_http_head: forbidden"); } else { if(*uri == '/') uri++; char *pstr = strchr(uri, '?'); if (pstr) { *(pstr++) = '\0'; // end of uri } TCMAP *pmap = tcmapnew2(TOKENUNIT); pthread_cleanup_push((void (*)(void *))tcmapdel, pmap); readcgiparam(pstr?pstr:"",pmap); const char *query = tcmapget2(pmap, "q"); if( query && strlen(query)==0 ) query = NULL; int ksiz; char *kbuf = tcurldecode(uri, &ksiz); pthread_cleanup_push(free, kbuf); int64_t key = tcatoi(kbuf); if ( key>0 && !query ) { double sttime = tctime(); char *val = tcidbget(idb, key); double sptime = tctime() - sttime; if(val){ tcxstrprintf(xstr, "HTTP/1.1 200 OK\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "X-TD-Time: %.6f\r\n", sptime); free(val); } else { tcxstrprintf(xstr, "HTTP/1.1 404 Not Found\r\n"); tcxstrprintf(xstr, "X-TD-Time: %.6f\r\n", sptime); } } else if ( key<1 && query ) { int rnum = 0; double sttime = tctime(); uint64_t *result = tcidbsearch2(idb, query, &rnum); double sptime = tctime() - sttime; if(result){ const char *format = tcmapget2(pmap, "format"); const char *qnum = tcmapget2(pmap, "num"); int ilimit = qnum ? tcatoi(qnum) : DEFSEARCHMAX; const char *qstart = tcmapget2(pmap, "start"); int ioffset = qstart ? tcatoi(qstart)-1 : 0; if (ioffset < 1) ioffset = 0; int rlen = (rnum > ioffset) ? rnum - ioffset : 0; if (rlen > ilimit) rlen = ilimit; if(rlen>0){ tcxstrprintf(xstr, "HTTP/1.1 200 OK\r\n"); if(format && ! strcmp(format, "binary")){ tcxstrprintf(xstr, "Content-Type: application/octet-stream\r\n"); } else { tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); } tcxstrprintf(xstr, "X-TD-Hits: %d\r\n", rnum); tcxstrprintf(xstr, "X-TD-Time: %.6f\r\n", sptime); } else { tcxstrprintf(xstr, "HTTP/1.1 404 Not Found\r\n"); tcxstrprintf(xstr, "X-TD-Hits: %d\r\n", rnum); tcxstrprintf(xstr, "X-TD-Time: %.6f\r\n", sptime); } free(result); } else { tcxstrprintf(xstr, "HTTP/1.1 500 Internal Server Error\r\n"); } } else { tcxstrprintf(xstr, "HTTP/1.1 403 Forbidden\r\n"); } tcxstrprintf(xstr, "\r\n"); pthread_cleanup_pop(1); // kbuf pthread_cleanup_pop(1); // pmap } if(ttsocksend(sock, tcxstrptr(xstr), tcxstrsize(xstr))){ req->keep = keep; } else { ttservlog(g_serv, TTLOGINFO, "do_http_head: response failed"); } pthread_cleanup_pop(1); // xstr } /* handle the HTTP PUT command */ static void do_http_put(TTSOCK *sock, TASKARG *arg, TTREQ *req, int ver, const char *uri){ ttservlog(g_serv, TTLOGDEBUG, "doing http_put command"); uint64_t mask = arg->mask; TCIDB *idb = arg->idb; bool keep = ver >= 1; int vsiz = 0; char line[LINEBUFSIZ]; while(ttsockgets(sock, line, LINEBUFSIZ) && *line != '\0'){ char *pv = strchr(line, ':'); if(!pv) continue; *(pv++) = '\0'; while(*pv == ' ' || *pv == '\t'){ pv++; } if(!tcstricmp(line, "connection")){ if(!tcstricmp(pv, "close")){ keep = false; } else if(!tcstricmp(pv, "keep-alive")){ keep = true; } } else if(!tcstricmp(line, "content-length")){ vsiz = tcatoi(pv); } } if(*uri == '/') uri++; int ksiz; char *kbuf = tcurldecode(uri, &ksiz); pthread_cleanup_push(free, kbuf); int64_t key = tcatoi(kbuf); char stack[TTIOBUFSIZ]; char *vbuf = (vsiz < TTIOBUFSIZ) ? stack : tcmalloc(vsiz + 1); pthread_cleanup_push(free, (vbuf == stack) ? NULL : vbuf); if(vsiz >= 0 && ttsockrecv(sock, vbuf, vsiz) && !ttsockcheckend(sock)){ TCXSTR *xstr = tcxstrnew(); pthread_cleanup_push((void (*)(void *))tcxstrdel, xstr); if(mask & (TDMSKPUT | TDMSKALLHTTP | TDMSKALLWRITE)){ int len = sprintf(line, "// Forbidden\n"); tcxstrprintf(xstr, "HTTP/1.1 403 Forbidden\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); ttservlog(g_serv, TTLOGINFO, "do_http_put: forbidden"); } else if(key < 1){ int len = sprintf(line, "// Forbidden\n"); tcxstrprintf(xstr, "HTTP/1.1 403 Forbidden\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); } else { double sttime = tctime(); bool ok = tcidbput(idb, key, vbuf); double sptime = tctime() - sttime; if(ok){ int len = sprintf(line, "%llu\n", key); tcxstrprintf(xstr, "HTTP/1.1 201 Created\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "X-TD-Time: %.6f\r\n", sptime); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); } else { int ecode = tcidbecode(idb); int len = sprintf(line, "// %s\n", tcidberrmsg(ecode)); tcxstrprintf(xstr, "HTTP/1.1 500 Internal Server Error\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "X-TD-Time: %.6f\r\n", sptime); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); ttservlog(g_serv, TTLOGERROR, "do_http_put: operation failed"); } } if(ttsocksend(sock, tcxstrptr(xstr), tcxstrsize(xstr))){ req->keep = keep; } else { ttservlog(g_serv, TTLOGINFO, "do_http_put: response failed"); } pthread_cleanup_pop(1); // xstr } else { ttservlog(g_serv, TTLOGINFO, "do_http_put: invalid entity"); } pthread_cleanup_pop(1); // vbuf pthread_cleanup_pop(1); // kbuf } /* handle the HTTP POST command */ static void do_http_post(TTSOCK *sock, TASKARG *arg, TTREQ *req, int ver, const char *uri){ ttservlog(g_serv, TTLOGDEBUG, "doing http_post command"); uint64_t mask = arg->mask; TCIDB *idb = arg->idb; bool keep = ver >= 1; int vsiz = 0; char line[LINEBUFSIZ]; while(ttsockgets(sock, line, LINEBUFSIZ) && *line != '\0'){ char *pv = strchr(line, ':'); if(!pv) continue; *(pv++) = '\0'; while(*pv == ' ' || *pv == '\t'){ pv++; } if(!tcstricmp(line, "connection")){ if(!tcstricmp(pv, "close")){ keep = false; } else if(!tcstricmp(pv, "keep-alive")){ keep = true; } } else if(!tcstricmp(line, "content-length")){ vsiz = tcatoi(pv); } } if(*uri == '/') uri++; int ksiz; char *kbuf = tcurldecode(uri, &ksiz); pthread_cleanup_push(free, kbuf); char stack[TTIOBUFSIZ]; char *vbuf = (vsiz+1 < TTIOBUFSIZ) ? stack : tcmalloc(vsiz + 1); pthread_cleanup_push(free, (vbuf == stack) ? NULL : vbuf); if(vsiz >= 0 && ttsockrecv(sock, vbuf, vsiz) && !ttsockcheckend(sock)){ TCXSTR *xstr = tcxstrnew(); pthread_cleanup_push((void (*)(void *))tcxstrdel, xstr); TCMAP *pmap = tcmapnew2(TOKENUNIT); pthread_cleanup_push((void (*)(void *))tcmapdel, pmap); *(vbuf+vsiz) = '\0'; readcgiparam(vbuf?vbuf:"",pmap); const char *cmd = tcmapget2(pmap, "cmd"); if(cmd && strlen(cmd)==0 ) cmd = NULL; if(cmd && !strcmp(cmd, "optimize")){ if(mask & (TDMSKALLHTTP | TDMSKALLWRITE)){ int len = sprintf(line, "// Forbidden\n"); tcxstrprintf(xstr, "HTTP/1.1 403 Forbidden\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); ttservlog(g_serv, TTLOGINFO, "do_http_post: forbidden"); } else { double sttime = tctime(); bool ok = tcidboptimize(idb); double sptime = tctime() - sttime; if(ok){ int len = sprintf(line, "%llu\n", tcidbrnum(idb)); tcxstrprintf(xstr, "HTTP/1.1 200 OK\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "X-TD-Time: %.6f\r\n", sptime); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); } else { int ecode = tcidbecode(idb); int len = sprintf(line, "// %s\n", tcidberrmsg(ecode)); tcxstrprintf(xstr, "HTTP/1.1 500 Internal Server Error\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "X-TD-Time: %.6f\r\n", sptime); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); } } } else { int len = sprintf(line, "// Not Implemented\n"); tcxstrprintf(xstr, "HTTP/1.1 501 Not Implemented\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); ttservlog(g_serv, TTLOGINFO, "do_http_post: not implemented"); } if(ttsocksend(sock, tcxstrptr(xstr), tcxstrsize(xstr))){ req->keep = keep; } else { ttservlog(g_serv, TTLOGINFO, "do_http_post: response failed"); } pthread_cleanup_pop(1); // pmap pthread_cleanup_pop(1); // xstr } else { ttservlog(g_serv, TTLOGINFO, "do_http_post: invalid entity"); } pthread_cleanup_pop(1); // vbuf pthread_cleanup_pop(1); // kbuf } /* handle the HTTP DELETE command */ static void do_http_delete(TTSOCK *sock, TASKARG *arg, TTREQ *req, int ver, const char *uri){ ttservlog(g_serv, TTLOGDEBUG, "doing http_delete command"); uint64_t mask = arg->mask; TCIDB *idb = arg->idb; bool keep = ver >= 1; char line[LINEBUFSIZ]; while(ttsockgets(sock, line, LINEBUFSIZ) && *line != '\0'){ char *pv = strchr(line, ':'); if(!pv) continue; *(pv++) = '\0'; while(*pv == ' ' || *pv == '\t'){ pv++; } if(!tcstricmp(line, "connection")){ if(!tcstricmp(pv, "close")){ keep = false; } else if(!tcstricmp(pv, "keep-alive")){ keep = true; } } } if(*uri == '/') uri++; int ksiz; char *kbuf = tcurldecode(uri, &ksiz); pthread_cleanup_push(free, kbuf); int64_t key = tcatoi(kbuf); TCXSTR *xstr = tcxstrnew(); pthread_cleanup_push((void (*)(void *))tcxstrdel, xstr); if(mask & (TDMSKOUT | TDMSKALLHTTP | TDMSKALLWRITE)){ int len = sprintf(line, "// Forbidden\n"); tcxstrprintf(xstr, "HTTP/1.1 403 Forbidden\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); ttservlog(g_serv, TTLOGINFO, "do_http_delete: forbidden"); } else if(key < 1){ int len = sprintf(line, "// Forbidden\n"); tcxstrprintf(xstr, "HTTP/1.1 403 Forbidden\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); } else { double sttime = tctime(); bool ok = tcidbout(idb, key); double sptime = tctime() - sttime; if(ok){ int len = sprintf(line, "%llu\n", key); tcxstrprintf(xstr, "HTTP/1.1 200 OK\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "X-TD-Time: %.6f\r\n", sptime); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); } else { int len = sprintf(line, "// Not Found\n"); tcxstrprintf(xstr, "HTTP/1.1 404 Not Found\r\n"); tcxstrprintf(xstr, "Content-Type: text/plain\r\n"); tcxstrprintf(xstr, "Content-Length: %d\r\n", len); tcxstrprintf(xstr, "X-TD-Time: %.6f\r\n", sptime); tcxstrprintf(xstr, "\r\n"); tcxstrcat(xstr, line, len); } } if(ttsocksend(sock, tcxstrptr(xstr), tcxstrsize(xstr))){ req->keep = keep; } else { ttservlog(g_serv, TTLOGINFO, "do_http_delete: response failed"); } pthread_cleanup_pop(1); // xstr pthread_cleanup_pop(1); // kbuf } /* read CGI parameters */ static void readcgiparam(char *buf, TCMAP *map){ int len; TCLIST *pairs = tcstrsplit(buf, "&;"); int num = tclistnum(pairs); for(int i = 0; i < num; i++){ char *key = tcstrdup(tclistval2(pairs, i)); char *val = strchr(key, '='); if(val){ *(val++) = '\0'; char *dkey = tcurldecode(key, &len); char *dval = tcurldecode(val, &len); tcmapput2(map, dkey, dval); tcfree(dval); tcfree(dkey); } tcfree(key); } tclistdel(pairs); } // END OF FILE