static int http_chunk_append_len(server *srv, connection *con, size_t len) { size_t i, olen = len, j; buffer *b; b = srv->tmp_chunk_len; if (len == 0) { buffer_copy_string_len(b, CONST_STR_LEN("0")); } else { for (i = 0; i < 8 && len; i++) { len >>= 4; } /* i is the number of hex digits we have */ buffer_prepare_copy(b, i + 1); for (j = i-1, len = olen; j+1 > 0; j--) { b->ptr[j] = (len & 0xf) + (((len & 0xf) <= 9) ? '0' : 'a' - 10); len >>= 4; } b->used = i; b->ptr[b->used++] = '\0'; } buffer_append_string_len(b, CONST_STR_LEN("\r\n")); chunkqueue_append_buffer(con->write_queue, b); return 0; }
static handler_t mod_status_handle_server_statistics(server *srv, connection *con, void *p_d) { buffer *b; size_t i; array *st = srv->status; UNUSED(p_d); if (0 == st->used) { /* we have nothing to send */ con->http_status = 204; con->file_finished = 1; return HANDLER_FINISHED; } b = buffer_init(); for (i = 0; i < st->used; i++) { size_t ndx = st->sorted[i]; buffer_append_string_buffer(b, st->data[ndx]->key); buffer_append_string_len(b, CONST_STR_LEN(": ")); buffer_append_int(b, ((data_integer *)(st->data[ndx]))->value); buffer_append_string_len(b, CONST_STR_LEN("\n")); } chunkqueue_append_buffer(con->write_queue, b); buffer_free(b); response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain")); con->http_status = 200; con->file_finished = 1; return HANDLER_FINISHED; }
static handler_t mod_status_handle_server_status_text(server *srv, connection *con, void *p_d) { plugin_data *p = p_d; buffer *b = buffer_init(); double avg; time_t ts; char buf[32]; unsigned int k; unsigned int l; /* output total number of requests */ buffer_append_string_len(b, CONST_STR_LEN("Total Accesses: ")); avg = p->abs_requests; snprintf(buf, sizeof(buf) - 1, "%.0f", avg); buffer_append_string(b, buf); buffer_append_string_len(b, CONST_STR_LEN("\n")); /* output total traffic out in kbytes */ buffer_append_string_len(b, CONST_STR_LEN("Total kBytes: ")); avg = p->abs_traffic_out / 1024; snprintf(buf, sizeof(buf) - 1, "%.0f", avg); buffer_append_string(b, buf); buffer_append_string_len(b, CONST_STR_LEN("\n")); /* output uptime */ buffer_append_string_len(b, CONST_STR_LEN("Uptime: ")); ts = srv->cur_ts - srv->startup_ts; buffer_append_int(b, ts); buffer_append_string_len(b, CONST_STR_LEN("\n")); /* output busy servers */ buffer_append_string_len(b, CONST_STR_LEN("BusyServers: ")); buffer_append_int(b, srv->conns->used); buffer_append_string_len(b, CONST_STR_LEN("\n")); buffer_append_string_len(b, CONST_STR_LEN("IdleServers: ")); buffer_append_int(b, srv->conns->size - srv->conns->used); buffer_append_string_len(b, CONST_STR_LEN("\n")); /* output scoreboard */ buffer_append_string_len(b, CONST_STR_LEN("Scoreboard: ")); for (k = 0; k < srv->conns->used; k++) { connection *c = srv->conns->ptr[k]; const char *state = connection_get_short_state(c->state); buffer_append_string_len(b, state, 1); } for (l = 0; l < srv->conns->size - srv->conns->used; l++) { buffer_append_string_len(b, CONST_STR_LEN("_")); } buffer_append_string_len(b, CONST_STR_LEN("\n")); chunkqueue_append_buffer(con->write_queue, b); buffer_free(b); /* set text/plain output */ response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain")); return 0; }
static int proxy_create_env(server *srv, handler_ctx *hctx) { size_t i; connection *con = hctx->remote_conn; buffer *b; /* build header */ b = buffer_init(); /* request line */ buffer_copy_string(b, get_http_method_name(con->request.http_method)); buffer_append_string_len(b, CONST_STR_LEN(" ")); buffer_append_string_buffer(b, con->request.uri); buffer_append_string_len(b, CONST_STR_LEN(" HTTP/1.0\r\n")); proxy_append_header(con, "X-Forwarded-For", (char *)inet_ntop_cache_get_ip(srv, &(con->dst_addr))); /* http_host is NOT is just a pointer to a buffer * which is NULL if it is not set */ if (!buffer_string_is_empty(con->request.http_host)) { proxy_set_header(con, "X-Host", con->request.http_host->ptr); } proxy_set_header(con, "X-Forwarded-Proto", con->uri.scheme->ptr); /* request header */ for (i = 0; i < con->request.headers->used; i++) { data_string *ds; ds = (data_string *)con->request.headers->data[i]; if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) { if (buffer_is_equal_string(ds->key, CONST_STR_LEN("Connection"))) continue; if (buffer_is_equal_string(ds->key, CONST_STR_LEN("Proxy-Connection"))) continue; buffer_append_string_buffer(b, ds->key); buffer_append_string_len(b, CONST_STR_LEN(": ")); buffer_append_string_buffer(b, ds->value); buffer_append_string_len(b, CONST_STR_LEN("\r\n")); } } buffer_append_string_len(b, CONST_STR_LEN("\r\n")); hctx->wb->bytes_in += buffer_string_length(b); chunkqueue_append_buffer(hctx->wb, b); buffer_free(b); /* body */ if (con->request.content_length) { chunkqueue *req_cq = con->request_content_queue; chunkqueue_steal(hctx->wb, req_cq, req_cq->bytes_in); } return 0; }
static void http_chunk_append_len(server *srv, connection *con, uintmax_t len) { buffer *b; force_assert(NULL != srv); b = srv->tmp_chunk_len; buffer_string_set_length(b, 0); buffer_append_uint_hex(b, len); buffer_append_string_len(b, CONST_STR_LEN("\r\n")); chunkqueue_append_buffer(con->write_queue, b); }
int http_chunk_append_buffer(server *srv, connection *con, buffer *mem) { chunkqueue *cq; if (!con) return -1; cq = con->write_queue; if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { http_chunk_append_len(srv, con, mem->used - 1); } chunkqueue_append_buffer(cq, mem); if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED && mem->used > 0) { chunkqueue_append_mem(cq, "\r\n", 2 + 1); } return 0; }
static int http_chunk_append_data(server *srv, connection *con, buffer *b, const char * mem, size_t len) { chunkqueue * const cq = con->write_queue; chunk *c = cq->last; if (0 == len) return 0; /* current usage does not append_mem or append_buffer after appending * file, so not checking if users of this interface have appended large * (references to) files to chunkqueue, which would not be in memory */ /*(allow slightly larger mem use if FDEVENT_STREAM_RESPONSE_BUFMIN * to reduce creation of temp files when backend producer will be * blocked until more data is sent to network to client)*/ if ((c && c->type == FILE_CHUNK && c->file.is_temp) || cq->bytes_in - cq->bytes_out + len > 1024 * ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) ? 128 : 64)) { return http_chunk_append_to_tempfile(srv, con, b ? b->ptr : mem, len); } /* not appending to prior mem chunk just in case using openssl * and need to resubmit same args as prior call to openssl (required?)*/ if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { http_chunk_append_len(srv, con, len); } /*(chunkqueue_append_buffer() might steal buffer contents)*/ b ? chunkqueue_append_buffer(cq, b) : chunkqueue_append_mem(cq, mem, len); if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { chunkqueue_append_mem(cq, CONST_STR_LEN("\r\n")); } return 0; }
static int http_list_directory(server *srv, connection *con, plugin_data *p, buffer *dir) { DIR *dp; buffer *out; struct dirent *dent; struct stat st; char *path, *path_file; size_t i; int hide_dotfiles = p->conf.hide_dot_files; dirls_list_t dirs, files, *list; dirls_entry_t *tmp; char sizebuf[sizeof("999.9K")]; char datebuf[sizeof("2005-Jan-01 22:23:24")]; size_t k; const char *content_type; long name_max; #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) char attrval[128]; int attrlen; #endif #ifdef HAVE_LOCALTIME_R struct tm tm; #endif if (buffer_string_is_empty(dir)) return -1; i = buffer_string_length(dir); #ifdef HAVE_PATHCONF if (0 >= (name_max = pathconf(dir->ptr, _PC_NAME_MAX))) { /* some broken fs (fuse) return 0 instead of -1 */ #ifdef NAME_MAX name_max = NAME_MAX; #else name_max = 255; /* stupid default */ #endif } #elif defined __WIN32 name_max = FILENAME_MAX; #else name_max = NAME_MAX; #endif path = malloc(i + name_max + 1); force_assert(NULL != path); memcpy(path, dir->ptr, i+1); path_file = path + i; if (NULL == (dp = opendir(path))) { log_error_write(srv, __FILE__, __LINE__, "sbs", "opendir failed:", dir, strerror(errno)); free(path); return -1; } dirs.ent = (dirls_entry_t**) malloc(sizeof(dirls_entry_t*) * DIRLIST_BLOB_SIZE); force_assert(dirs.ent); dirs.size = DIRLIST_BLOB_SIZE; dirs.used = 0; files.ent = (dirls_entry_t**) malloc(sizeof(dirls_entry_t*) * DIRLIST_BLOB_SIZE); force_assert(files.ent); files.size = DIRLIST_BLOB_SIZE; files.used = 0; while ((dent = readdir(dp)) != NULL) { unsigned short exclude_match = 0; if (dent->d_name[0] == '.') { if (hide_dotfiles) continue; if (dent->d_name[1] == '\0') continue; if (dent->d_name[1] == '.' && dent->d_name[2] == '\0') continue; } if (p->conf.hide_readme_file && !buffer_string_is_empty(p->conf.show_readme)) { if (strcmp(dent->d_name, p->conf.show_readme->ptr) == 0) continue; } if (p->conf.hide_header_file && !buffer_string_is_empty(p->conf.show_header)) { if (strcmp(dent->d_name, p->conf.show_header->ptr) == 0) continue; } /* compare d_name against excludes array * elements, skipping any that match. */ #ifdef HAVE_PCRE_H for(i = 0; i < p->conf.excludes->used; i++) { int n; #define N 10 int ovec[N * 3]; pcre *regex = p->conf.excludes->ptr[i]->regex; if ((n = pcre_exec(regex, NULL, dent->d_name, strlen(dent->d_name), 0, 0, ovec, 3 * N)) < 0) { if (n != PCRE_ERROR_NOMATCH) { log_error_write(srv, __FILE__, __LINE__, "sd", "execution error while matching:", n); /* aborting would require a lot of manual cleanup here. * skip instead (to not leak names that break pcre matching) */ exclude_match = 1; break; } } else { exclude_match = 1; break; } } if (exclude_match) { continue; } #endif i = strlen(dent->d_name); /* NOTE: the manual says, d_name is never more than NAME_MAX * so this should actually not be a buffer-overflow-risk */ if (i > (size_t)name_max) continue; memcpy(path_file, dent->d_name, i + 1); if (stat(path, &st) != 0) continue; list = &files; if (S_ISDIR(st.st_mode)) list = &dirs; if (list->used == list->size) { list->size += DIRLIST_BLOB_SIZE; list->ent = (dirls_entry_t**) realloc(list->ent, sizeof(dirls_entry_t*) * list->size); force_assert(list->ent); } tmp = (dirls_entry_t*) malloc(sizeof(dirls_entry_t) + 1 + i); tmp->mtime = st.st_mtime; tmp->size = st.st_size; tmp->namelen = i; memcpy(DIRLIST_ENT_NAME(tmp), dent->d_name, i + 1); list->ent[list->used++] = tmp; } closedir(dp); if (dirs.used) http_dirls_sort(dirs.ent, dirs.used); if (files.used) http_dirls_sort(files.ent, files.used); out = buffer_init(); http_list_directory_header(srv, con, p, out); /* directories */ for (i = 0; i < dirs.used; i++) { tmp = dirs.ent[i]; #ifdef HAVE_LOCALTIME_R localtime_r(&(tmp->mtime), &tm); strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", &tm); #else strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", localtime(&(tmp->mtime))); #endif buffer_append_string_len(out, CONST_STR_LEN("<tr class=\"d\"><td class=\"n\"><a href=\"")); buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_REL_URI_PART); buffer_append_string_len(out, CONST_STR_LEN("/\">")); buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_MINIMAL_XML); buffer_append_string_len(out, CONST_STR_LEN("</a>/</td><td class=\"m\">")); buffer_append_string_len(out, datebuf, sizeof(datebuf) - 1); buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"s\">- </td><td class=\"t\">Directory</td></tr>\n")); free(tmp); } /* files */ for (i = 0; i < files.used; i++) { tmp = files.ent[i]; content_type = NULL; #if defined(HAVE_XATTR) if (con->conf.use_xattr) { memcpy(path_file, DIRLIST_ENT_NAME(tmp), tmp->namelen + 1); attrlen = sizeof(attrval) - 1; if (attr_get(path, srv->srvconf.xattr_name->ptr, attrval, &attrlen, 0) == 0) { attrval[attrlen] = '\0'; content_type = attrval; } } #elif defined(HAVE_EXTATTR) if (con->conf.use_xattr) { memcpy(path_file, DIRLIST_ENT_NAME(tmp), tmp->namelen + 1); if(-1 != (attrlen = extattr_get_file(path, EXTATTR_NAMESPACE_USER, srv->srvconf.xattr_name->ptr, attrval, sizeof(attrval)-1))) { attrval[attrlen] = '\0'; content_type = attrval; } } #endif if (content_type == NULL) { content_type = "application/octet-stream"; for (k = 0; k < con->conf.mimetypes->used; k++) { data_string *ds = (data_string *)con->conf.mimetypes->data[k]; size_t ct_len; if (buffer_is_empty(ds->key)) continue; ct_len = buffer_string_length(ds->key); if (tmp->namelen < ct_len) continue; if (0 == strncasecmp(DIRLIST_ENT_NAME(tmp) + tmp->namelen - ct_len, ds->key->ptr, ct_len)) { content_type = ds->value->ptr; break; } } } #ifdef HAVE_LOCALTIME_R localtime_r(&(tmp->mtime), &tm); strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", &tm); #else strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", localtime(&(tmp->mtime))); #endif http_list_directory_sizefmt(sizebuf, sizeof(sizebuf), tmp->size); buffer_append_string_len(out, CONST_STR_LEN("<tr><td class=\"n\"><a href=\"")); buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_REL_URI_PART); buffer_append_string_len(out, CONST_STR_LEN("\">")); buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_MINIMAL_XML); buffer_append_string_len(out, CONST_STR_LEN("</a></td><td class=\"m\">")); buffer_append_string_len(out, datebuf, sizeof(datebuf) - 1); buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"s\">")); buffer_append_string(out, sizebuf); buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"t\">")); buffer_append_string(out, content_type); buffer_append_string_len(out, CONST_STR_LEN("</td></tr>\n")); free(tmp); } free(files.ent); free(dirs.ent); free(path); http_list_directory_footer(srv, con, p, out); /* Insert possible charset to Content-Type */ if (buffer_string_is_empty(p->conf.encoding)) { response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); } else { buffer_copy_string_len(p->content_charset, CONST_STR_LEN("text/html; charset=")); buffer_append_string_buffer(p->content_charset, p->conf.encoding); response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->content_charset)); } con->file_finished = 1; chunkqueue_append_buffer(con->write_queue, out); buffer_free(out); return 0; }
static handler_t mod_status_handle_server_config(server *srv, connection *con, void *p_d) { plugin_data *p = p_d; buffer *b = buffer_init(); buffer *m = p->module_list; size_t i; struct ev_map { fdevent_handler_t et; const char *name; } event_handlers[] = { /* - epoll is most reliable * - select works everywhere */ #ifdef USE_LINUX_EPOLL { FDEVENT_HANDLER_LINUX_SYSEPOLL, "linux-sysepoll" }, #endif #ifdef USE_POLL { FDEVENT_HANDLER_POLL, "poll" }, #endif #ifdef USE_SELECT { FDEVENT_HANDLER_SELECT, "select" }, #endif #ifdef USE_LIBEV { FDEVENT_HANDLER_LIBEV, "libev" }, #endif #ifdef USE_SOLARIS_DEVPOLL { FDEVENT_HANDLER_SOLARIS_DEVPOLL,"solaris-devpoll" }, #endif #ifdef USE_SOLARIS_PORT { FDEVENT_HANDLER_SOLARIS_PORT, "solaris-eventports" }, #endif #ifdef USE_FREEBSD_KQUEUE { FDEVENT_HANDLER_FREEBSD_KQUEUE, "freebsd-kqueue" }, #endif { FDEVENT_HANDLER_UNSET, NULL } }; buffer_copy_string_len(b, CONST_STR_LEN( "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n" " <head>\n" " <title>Status</title>\n" " </head>\n" " <body>\n" " <h1>" PACKAGE_DESC "</h1>\n" " <table summary=\"status\" border=\"1\">\n")); mod_status_header_append(b, "Server-Features"); #ifdef HAVE_PCRE_H mod_status_row_append(b, "RegEx Conditionals", "enabled"); #else mod_status_row_append(b, "RegEx Conditionals", "disabled - pcre missing"); #endif mod_status_header_append(b, "Network Engine"); for (i = 0; event_handlers[i].name; i++) { if (event_handlers[i].et == srv->event_handler) { mod_status_row_append(b, "fd-Event-Handler", event_handlers[i].name); break; } } mod_status_header_append(b, "Config-File-Settings"); for (i = 0; i < srv->plugins.used; i++) { plugin **ps = srv->plugins.ptr; plugin *pl = ps[i]; if (i == 0) { buffer_copy_buffer(m, pl->name); } else { buffer_append_string_len(m, CONST_STR_LEN("<br />")); buffer_append_string_buffer(m, pl->name); } } mod_status_row_append(b, "Loaded Modules", m->ptr); buffer_append_string_len(b, CONST_STR_LEN(" </table>\n")); buffer_append_string_len(b, CONST_STR_LEN( " </body>\n" "</html>\n" )); chunkqueue_append_buffer(con->write_queue, b); buffer_free(b); response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); con->http_status = 200; con->file_finished = 1; return HANDLER_FINISHED; }
static handler_t mod_status_handle_server_status_html(server *srv, connection *con, void *p_d) { plugin_data *p = p_d; buffer *b = buffer_init(); size_t j; double avg; char multiplier = '\0'; char buf[32]; time_t ts; int days, hours, mins, seconds; buffer_copy_string_len(b, CONST_STR_LEN( "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n" " <head>\n" " <title>Status</title>\n" " <style type=\"text/css\">\n" " table.status { border: black solid thin; }\n" " td { white-space: nowrap; }\n" " td.int { background-color: #f0f0f0; text-align: right }\n" " td.string { background-color: #f0f0f0; text-align: left }\n" " th.status { background-color: black; color: white; font-weight: bold; }\n" " a.sortheader { background-color: black; color: white; font-weight: bold; text-decoration: none; display: block; }\n" " span.sortarrow { color: white; text-decoration: none; }\n" " </style>\n")); if (p->conf.sort) { buffer_append_string_len(b, CONST_STR_LEN( "<script type=\"text/javascript\">\n" "// <!--\n" "var sort_column;\n" "var prev_span = null;\n" "function get_inner_text(el) {\n" " if((typeof el == 'string')||(typeof el == 'undefined'))\n" " return el;\n" " if(el.innerText)\n" " return el.innerText;\n" " else {\n" " var str = \"\";\n" " var cs = el.childNodes;\n" " var l = cs.length;\n" " for (i=0;i<l;i++) {\n" " if (cs[i].nodeType==1) str += get_inner_text(cs[i]);\n" " else if (cs[i].nodeType==3) str += cs[i].nodeValue;\n" " }\n" " }\n" " return str;\n" "}\n" "function sortfn(a,b) {\n" " var at = get_inner_text(a.cells[sort_column]);\n" " var bt = get_inner_text(b.cells[sort_column]);\n" " if (a.cells[sort_column].className == 'int') {\n" " return parseInt(at)-parseInt(bt);\n" " } else {\n" " aa = at.toLowerCase();\n" " bb = bt.toLowerCase();\n" " if (aa==bb) return 0;\n" " else if (aa<bb) return -1;\n" " else return 1;\n" " }\n" "}\n" "function resort(lnk) {\n" " var span = lnk.childNodes[1];\n" " var table = lnk.parentNode.parentNode.parentNode.parentNode;\n" " var rows = new Array();\n" " for (j=1;j<table.rows.length;j++)\n" " rows[j-1] = table.rows[j];\n" " sort_column = lnk.parentNode.cellIndex;\n" " rows.sort(sortfn);\n" " if (prev_span != null) prev_span.innerHTML = '';\n" " if (span.getAttribute('sortdir')=='down') {\n" " span.innerHTML = '↑';\n" " span.setAttribute('sortdir','up');\n" " rows.reverse();\n" " } else {\n" " span.innerHTML = '↓';\n" " span.setAttribute('sortdir','down');\n" " }\n" " for (i=0;i<rows.length;i++)\n" " table.tBodies[0].appendChild(rows[i]);\n" " prev_span = span;\n" "}\n" "// -->\n" "</script>\n")); } buffer_append_string_len(b, CONST_STR_LEN( " </head>\n" " <body>\n")); /* connection listing */ buffer_append_string_len(b, CONST_STR_LEN("<h1>Server-Status (" PACKAGE_NAME " " PACKAGE_VERSION ")</h1>")); buffer_append_string_len(b, CONST_STR_LEN("<table summary=\"status\" class=\"status\">")); buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Hostname</td><td class=\"string\">")); buffer_append_string_buffer(b, con->uri.authority); buffer_append_string_len(b, CONST_STR_LEN(" (")); buffer_append_string_buffer(b, con->server_name); buffer_append_string_len(b, CONST_STR_LEN(")</td></tr>\n")); buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Uptime</td><td class=\"string\">")); ts = srv->cur_ts - srv->startup_ts; days = ts / (60 * 60 * 24); ts %= (60 * 60 * 24); hours = ts / (60 * 60); ts %= (60 * 60); mins = ts / (60); ts %= (60); seconds = ts; if (days) { buffer_append_int(b, days); buffer_append_string_len(b, CONST_STR_LEN(" days ")); } if (hours) { buffer_append_int(b, hours); buffer_append_string_len(b, CONST_STR_LEN(" hours ")); } if (mins) { buffer_append_int(b, mins); buffer_append_string_len(b, CONST_STR_LEN(" min ")); } buffer_append_int(b, seconds); buffer_append_string_len(b, CONST_STR_LEN(" s")); buffer_append_string_len(b, CONST_STR_LEN("</td></tr>\n")); buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Started at</td><td class=\"string\">")); ts = srv->startup_ts; strftime(buf, sizeof(buf) - 1, "%Y-%m-%d %H:%M:%S", localtime(&ts)); buffer_append_string(b, buf); buffer_append_string_len(b, CONST_STR_LEN("</td></tr>\n")); buffer_append_string_len(b, CONST_STR_LEN("<tr><th colspan=\"2\">absolute (since start)</th></tr>\n")); buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">")); avg = p->abs_requests; mod_status_get_multiplier(&avg, &multiplier, 1000); buffer_append_int(b, avg); buffer_append_string_len(b, CONST_STR_LEN(" ")); if (multiplier) buffer_append_string_len(b, &multiplier, 1); buffer_append_string_len(b, CONST_STR_LEN("req</td></tr>\n")); buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">")); avg = p->abs_traffic_out; mod_status_get_multiplier(&avg, &multiplier, 1024); sprintf(buf, "%.2f", avg); buffer_append_string(b, buf); buffer_append_string_len(b, CONST_STR_LEN(" ")); if (multiplier) buffer_append_string_len(b, &multiplier, 1); buffer_append_string_len(b, CONST_STR_LEN("byte</td></tr>\n")); buffer_append_string_len(b, CONST_STR_LEN("<tr><th colspan=\"2\">average (since start)</th></tr>\n")); buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">")); avg = p->abs_requests / (srv->cur_ts - srv->startup_ts); mod_status_get_multiplier(&avg, &multiplier, 1000); buffer_append_int(b, avg); buffer_append_string_len(b, CONST_STR_LEN(" ")); if (multiplier) buffer_append_string_len(b, &multiplier, 1); buffer_append_string_len(b, CONST_STR_LEN("req/s</td></tr>\n")); buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">")); avg = p->abs_traffic_out / (srv->cur_ts - srv->startup_ts); mod_status_get_multiplier(&avg, &multiplier, 1024); sprintf(buf, "%.2f", avg); buffer_append_string(b, buf); buffer_append_string_len(b, CONST_STR_LEN(" ")); if (multiplier) buffer_append_string_len(b, &multiplier, 1); buffer_append_string_len(b, CONST_STR_LEN("byte/s</td></tr>\n")); buffer_append_string_len(b, CONST_STR_LEN("<tr><th colspan=\"2\">average (5s sliding average)</th></tr>\n")); for (j = 0, avg = 0; j < 5; j++) { avg += p->mod_5s_requests[j]; } avg /= 5; buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">")); mod_status_get_multiplier(&avg, &multiplier, 1000); buffer_append_int(b, avg); buffer_append_string_len(b, CONST_STR_LEN(" ")); if (multiplier) buffer_append_string_len(b, &multiplier, 1); buffer_append_string_len(b, CONST_STR_LEN("req/s</td></tr>\n")); for (j = 0, avg = 0; j < 5; j++) { avg += p->mod_5s_traffic_out[j]; } avg /= 5; buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">")); mod_status_get_multiplier(&avg, &multiplier, 1024); sprintf(buf, "%.2f", avg); buffer_append_string(b, buf); buffer_append_string_len(b, CONST_STR_LEN(" ")); if (multiplier) buffer_append_string_len(b, &multiplier, 1); buffer_append_string_len(b, CONST_STR_LEN("byte/s</td></tr>\n")); buffer_append_string_len(b, CONST_STR_LEN("</table>\n")); buffer_append_string_len(b, CONST_STR_LEN( "<hr />\n<pre><b>legend</b>\n" ". = connect, C = close, E = hard error, k = keep-alive\n" "r = read, R = read-POST, W = write, h = handle-request\n" "q = request-start, Q = request-end\n" "s = response-start, S = response-end\n")); buffer_append_string_len(b, CONST_STR_LEN("<b>")); buffer_append_int(b, srv->conns->used); buffer_append_string_len(b, CONST_STR_LEN(" connections</b>\n")); for (j = 0; j < srv->conns->used; j++) { connection *c = srv->conns->ptr[j]; const char *state; if (CON_STATE_READ == c->state && !buffer_string_is_empty(c->request.orig_uri)) { state = "k"; } else { state = connection_get_short_state(c->state); } buffer_append_string_len(b, state, 1); if (((j + 1) % 50) == 0) { buffer_append_string_len(b, CONST_STR_LEN("\n")); } } buffer_append_string_len(b, CONST_STR_LEN("\n</pre><hr />\n<h2>Connections</h2>\n")); buffer_append_string_len(b, CONST_STR_LEN("<table summary=\"status\" class=\"status\">\n")); buffer_append_string_len(b, CONST_STR_LEN("<tr>")); mod_status_header_append_sort(b, p_d, "Client IP"); mod_status_header_append_sort(b, p_d, "Read"); mod_status_header_append_sort(b, p_d, "Written"); mod_status_header_append_sort(b, p_d, "State"); mod_status_header_append_sort(b, p_d, "Time"); mod_status_header_append_sort(b, p_d, "Host"); mod_status_header_append_sort(b, p_d, "URI"); mod_status_header_append_sort(b, p_d, "File"); buffer_append_string_len(b, CONST_STR_LEN("</tr>\n")); for (j = 0; j < srv->conns->used; j++) { connection *c = srv->conns->ptr[j]; buffer_append_string_len(b, CONST_STR_LEN("<tr><td class=\"string\">")); buffer_append_string(b, inet_ntop_cache_get_ip(srv, &(c->dst_addr))); buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int\">")); if (c->request.content_length) { buffer_append_int(b, c->request_content_queue->bytes_in); buffer_append_string_len(b, CONST_STR_LEN("/")); buffer_append_int(b, c->request.content_length); } else { buffer_append_string_len(b, CONST_STR_LEN("0/0")); } buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int\">")); buffer_append_int(b, c->write_queue->bytes_out); buffer_append_string_len(b, CONST_STR_LEN("/")); buffer_append_int(b, c->write_queue->bytes_out + chunkqueue_length(c->write_queue)); buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">")); if (CON_STATE_READ == c->state && !buffer_string_is_empty(c->request.orig_uri)) { buffer_append_string_len(b, CONST_STR_LEN("keep-alive")); } else { buffer_append_string(b, connection_get_state(c->state)); } buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int\">")); buffer_append_int(b, srv->cur_ts - c->request_start); buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">")); if (buffer_string_is_empty(c->server_name)) { buffer_append_string_buffer(b, c->uri.authority); } else { buffer_append_string_buffer(b, c->server_name); } buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">")); if (!buffer_string_is_empty(c->uri.path)) { buffer_append_string_encoded(b, CONST_BUF_LEN(c->uri.path), ENCODING_HTML); } if (!buffer_string_is_empty(c->uri.query)) { buffer_append_string_len(b, CONST_STR_LEN("?")); buffer_append_string_encoded(b, CONST_BUF_LEN(c->uri.query), ENCODING_HTML); } if (!buffer_string_is_empty(c->request.orig_uri)) { buffer_append_string_len(b, CONST_STR_LEN(" (")); buffer_append_string_encoded(b, CONST_BUF_LEN(c->request.orig_uri), ENCODING_HTML); buffer_append_string_len(b, CONST_STR_LEN(")")); } buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">")); buffer_append_string_buffer(b, c->physical.path); buffer_append_string_len(b, CONST_STR_LEN("</td></tr>\n")); } buffer_append_string_len(b, CONST_STR_LEN( "</table>\n")); buffer_append_string_len(b, CONST_STR_LEN( " </body>\n" "</html>\n" )); chunkqueue_append_buffer(con->write_queue, b); buffer_free(b); response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); return 0; }
static int http_response_parse_range(server *srv, connection *con, plugin_data *p) { int multipart = 0; int error; off_t start, end; const char *s, *minus; char *boundary = "fkj49sn38dcn3"; data_string *ds; stat_cache_entry *sce = NULL; buffer *content_type = NULL; if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) { SEGFAULT(); } start = 0; end = sce->st.st_size - 1; con->response.content_length = 0; if (NULL != (ds = (data_string *)array_get_element(con->response.headers, "Content-Type"))) { content_type = ds->value; } for (s = con->request.http_range, error = 0; !error && *s && NULL != (minus = strchr(s, '-')); ) { char *err; off_t la, le; if (s == minus) { /* -<stop> */ le = strtoll(s, &err, 10); if (le == 0) { /* RFC 2616 - 14.35.1 */ con->http_status = 416; error = 1; } else if (*err == '\0') { /* end */ s = err; end = sce->st.st_size - 1; start = sce->st.st_size + le; } else if (*err == ',') { multipart = 1; s = err + 1; end = sce->st.st_size - 1; start = sce->st.st_size + le; } else { error = 1; } } else if (*(minus+1) == '\0' || *(minus+1) == ',') { /* <start>- */ la = strtoll(s, &err, 10); if (err == minus) { /* ok */ if (*(err + 1) == '\0') { s = err + 1; end = sce->st.st_size - 1; start = la; } else if (*(err + 1) == ',') { multipart = 1; s = err + 2; end = sce->st.st_size - 1; start = la; } else { error = 1; } } else { /* error */ error = 1; } } else { /* <start>-<stop> */ la = strtoll(s, &err, 10); if (err == minus) { le = strtoll(minus+1, &err, 10); /* RFC 2616 - 14.35.1 */ if (la > le) { error = 1; } if (*err == '\0') { /* ok, end*/ s = err; end = le; start = la; } else if (*err == ',') { multipart = 1; s = err + 1; end = le; start = la; } else { /* error */ error = 1; } } else { /* error */ error = 1; } } if (!error) { if (start < 0) start = 0; /* RFC 2616 - 14.35.1 */ if (end > sce->st.st_size - 1) end = sce->st.st_size - 1; if (start > sce->st.st_size - 1) { error = 1; con->http_status = 416; } } if (!error) { if (multipart) { /* write boundary-header */ buffer *b = buffer_init(); buffer_copy_string_len(b, CONST_STR_LEN("\r\n--")); buffer_append_string(b, boundary); /* write Content-Range */ buffer_append_string_len(b, CONST_STR_LEN("\r\nContent-Range: bytes ")); buffer_append_int(b, start); buffer_append_string_len(b, CONST_STR_LEN("-")); buffer_append_int(b, end); buffer_append_string_len(b, CONST_STR_LEN("/")); buffer_append_int(b, sce->st.st_size); buffer_append_string_len(b, CONST_STR_LEN("\r\nContent-Type: ")); buffer_append_string_buffer(b, content_type); /* write END-OF-HEADER */ buffer_append_string_len(b, CONST_STR_LEN("\r\n\r\n")); con->response.content_length += buffer_string_length(b); chunkqueue_append_buffer(con->write_queue, b); buffer_free(b); } chunkqueue_append_file(con->write_queue, con->physical.path, start, end - start + 1); con->response.content_length += end - start + 1; } } /* something went wrong */ if (error) return -1; if (multipart) { /* add boundary end */ buffer *b = buffer_init(); buffer_copy_string_len(b, "\r\n--", 4); buffer_append_string(b, boundary); buffer_append_string_len(b, "--\r\n", 4); con->response.content_length += buffer_string_length(b); chunkqueue_append_buffer(con->write_queue, b); buffer_free(b); /* set header-fields */ buffer_copy_string_len(p->range_buf, CONST_STR_LEN("multipart/byteranges; boundary=")); buffer_append_string(p->range_buf, boundary); /* overwrite content-type */ response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->range_buf)); } else { /* add Content-Range-header */ buffer_copy_string_len(p->range_buf, CONST_STR_LEN("bytes ")); buffer_append_int(p->range_buf, start); buffer_append_string_len(p->range_buf, CONST_STR_LEN("-")); buffer_append_int(p->range_buf, end); buffer_append_string_len(p->range_buf, CONST_STR_LEN("/")); buffer_append_int(p->range_buf, sce->st.st_size); response_header_insert(srv, con, CONST_STR_LEN("Content-Range"), CONST_BUF_LEN(p->range_buf)); } /* ok, the file is set-up */ return 0; }