-void web_client_process(struct web_client *w)
-{
- int code = 500;
- int bytes;
-
- w->wait_receive = 0;
-
- // check if we have an empty line (end of HTTP header)
- if(strstr(w->data->buffer, "\r\n\r\n")) {
- global_statistics_lock();
- global_statistics.web_requests++;
- global_statistics_unlock();
-
- gettimeofday(&w->tv_in, NULL);
- debug(D_WEB_DATA, "%llu: Processing data buffer of %d bytes: '%s'.", w->id, w->data->bytes, w->data->buffer);
-
- // check if the client requested keep-alive HTTP
- if(strcasestr(w->data->buffer, "Connection: keep-alive")) w->keepalive = 1;
- else w->keepalive = 0;
-
- // check if the client accepts deflate
- if(strstr(w->data->buffer, "gzip"))
- web_client_enable_deflate(w);
-
- int datasource_type = DATASOURCE_GOOGLE_JSONP;
- //if(strstr(w->data->buffer, "X-DataSource-Auth"))
- // datasource_type = DATASOURCE_GOOGLE_JSON;
-
- char *buf = w->data->buffer;
- char *tok = strsep(&buf, " \r\n");
- char *url = NULL;
- char *pointer_to_free = NULL; // keep url_decode() allocated buffer
-
- if(buf && strcmp(tok, "GET") == 0) {
- tok = strsep(&buf, " \r\n");
- pointer_to_free = url = url_decode(tok);
- debug(D_WEB_CLIENT, "%llu: Processing HTTP GET on url '%s'.", w->id, url);
- }
- else if (buf && strcmp(tok, "POST") == 0) {
- w->keepalive = 0;
- tok = strsep(&buf, " \r\n");
- pointer_to_free = url = url_decode(tok);
-
- debug(D_WEB_CLIENT, "%llu: I don't know how to handle POST with form data. Assuming it is a GET on url '%s'.", w->id, url);
- }
-
- w->last_url[0] = '\0';
- if(url) {
- strncpy(w->last_url, url, URL_MAX);
- w->last_url[URL_MAX] = '\0';
-
- tok = mystrsep(&url, "/?&");
-
- debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
-
- if(strcmp(tok, WEB_PATH_DATA) == 0) { // "data"
- // the client is requesting rrd data
- datasource_type = DATASOURCE_JSON;
- code = web_client_data_request(w, url, datasource_type);
- }
- else if(strcmp(tok, WEB_PATH_DATASOURCE) == 0) { // "datasource"
- // the client is requesting google datasource
- code = web_client_data_request(w, url, datasource_type);
- }
- else if(strcmp(tok, WEB_PATH_GRAPH) == 0) { // "graph"
- // the client is requesting an rrd graph
-
- // get the name of the data to show
- tok = mystrsep(&url, "/?&");
- debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
-
- // do we have such a data set?
- RRDSET *st = rrdset_find_byname(tok);
- if(!st) st = rrdset_find(tok);
- if(!st) {
- // we don't have it
- // try to send a file with that name
- w->data->bytes = 0;
- code = mysendfile(w, tok);
- }
- else {
- code = 200;
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending %s.json of RRD_STATS...", w->id, st->name);
- w->data->contenttype = CT_APPLICATION_JSON;
- w->data->bytes = 0;
- rrd_stats_graph_json(st, url, w->data);
- }
- }
- else if(strcmp(tok, "debug") == 0) {
- w->data->bytes = 0;
-
- // get the name of the data to show
- tok = mystrsep(&url, "/?&");
- debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
-
- // do we have such a data set?
- RRDSET *st = rrdset_find_byname(tok);
- if(!st) st = rrdset_find(tok);
- if(!st) {
- code = 404;
- web_buffer_printf(w->data, "Chart %s is not found.\r\n", tok);
- debug(D_WEB_CLIENT_ACCESS, "%llu: %s is not found.", w->id, tok);
- }
- else {
- code = 200;
- debug_flags |= D_RRD_STATS;
- st->debug = st->debug?0:1;
- web_buffer_printf(w->data, "Chart %s has now debug %s.\r\n", tok, st->debug?"enabled":"disabled");
- debug(D_WEB_CLIENT_ACCESS, "%llu: debug for %s is %s.", w->id, tok, st->debug?"enabled":"disabled");
- }
- }
- else if(strcmp(tok, "mirror") == 0) {
- code = 200;
-
- debug(D_WEB_CLIENT_ACCESS, "%llu: Mirroring...", w->id);
-
- // replace the zero bytes with spaces
- int i;
- for(i = 0; i < w->data->size; i++)
- if(w->data->buffer[i] == '\0') w->data->buffer[i] = ' ';
-
- // just leave the buffer as is
- // it will be copied back to the client
- }
- else if(strcmp(tok, "list") == 0) {
- code = 200;
-
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending list of RRD_STATS...", w->id);
-
- w->data->bytes = 0;
- RRDSET *st = rrdset_root;
-
- for ( ; st ; st = st->next )
- web_buffer_printf(w->data, "%s\n", st->name);
- }
- else if(strcmp(tok, "all.json") == 0) {
- code = 200;
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending JSON list of all monitors of RRD_STATS...", w->id);
-
- w->data->contenttype = CT_APPLICATION_JSON;
- w->data->bytes = 0;
- rrd_stats_all_json(w->data);
- }
- else if(strcmp(tok, "netdata.conf") == 0) {
- code = 200;
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending netdata.conf ...", w->id);
-
- w->data->contenttype = CT_TEXT_PLAIN;
- w->data->bytes = 0;
- generate_config(w->data, 0);
- }
- else if(strcmp(tok, WEB_PATH_FILE) == 0) { // "file"
- tok = mystrsep(&url, "/?&");
- if(tok && *tok) code = mysendfile(w, tok);
- else {
- code = 400;
- w->data->bytes = 0;
- strcpy(w->data->buffer, "You have to give a filename to get.\r\n");
- w->data->bytes = strlen(w->data->buffer);
- }
- }
- else if(!tok[0]) {
- w->data->bytes = 0;
- code = mysendfile(w, "index.html");
- }
- else {
- w->data->bytes = 0;
- code = mysendfile(w, tok);
- }
-
- }
- else {
- strcpy(w->last_url, "not a valid response");
-
- if(buf) debug(D_WEB_CLIENT_ACCESS, "%llu: Cannot understand '%s'.", w->id, buf);
-
- code = 500;
- w->data->bytes = 0;
- strcpy(w->data->buffer, "I don't understand you...\r\n");
- w->data->bytes = strlen(w->data->buffer);
- }
-
- // free url_decode() buffer
- if(pointer_to_free) free(pointer_to_free);
- }
- else if(w->data->bytes > 8192) {
- strcpy(w->last_url, "too big request");
-
- debug(D_WEB_CLIENT_ACCESS, "%llu: Received request is too big.", w->id);
-
- code = 400;
- w->data->bytes = 0;
- strcpy(w->data->buffer, "Received request is too big.\r\n");
- w->data->bytes = strlen(w->data->buffer);
- }
- else {
- // wait for more data
- w->wait_receive = 1;
- return;
- }
-
- if(w->data->bytes > w->data->size) {
- error("%llu: memory overflow encountered (size is %ld, written %ld).", w->data->size, w->data->bytes);
- }
-
- gettimeofday(&w->tv_ready, NULL);
- w->data->date = time(NULL);
- w->data->sent = 0;
-
- // prepare the HTTP response header
- debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, code);
-
- char *content_type_string = "";
- switch(w->data->contenttype) {
- case CT_TEXT_HTML:
- content_type_string = "text/html";
- break;
-
- case CT_APPLICATION_XML:
- content_type_string = "application/xml";
- break;
-
- case CT_APPLICATION_JSON:
- content_type_string = "application/json";
- break;
-
- case CT_APPLICATION_X_JAVASCRIPT:
- content_type_string = "application/x-javascript";
- break;
-
- case CT_TEXT_CSS:
- content_type_string = "text/css";
- break;
-
- case CT_TEXT_XML:
- content_type_string = "text/xml";
- break;
-
- case CT_TEXT_XSL:
- content_type_string = "text/xsl";
- break;
-
- case CT_APPLICATION_OCTET_STREAM:
- content_type_string = "application/octet-stream";
- break;
-
- case CT_IMAGE_SVG_XML:
- content_type_string = "image/svg+xml";
- break;
-
- case CT_APPLICATION_X_FONT_TRUETYPE:
- content_type_string = "application/x-font-truetype";
- break;
-
- case CT_APPLICATION_X_FONT_OPENTYPE:
- content_type_string = "application/x-font-opentype";
- break;
-
- case CT_APPLICATION_FONT_WOFF:
- content_type_string = "application/font-woff";
- break;
-
- case CT_APPLICATION_VND_MS_FONTOBJ:
- content_type_string = "application/vnd.ms-fontobject";
- break;
-
- default:
- case CT_TEXT_PLAIN:
- content_type_string = "text/plain";
- break;
- }
-
- char *code_msg = "";
- switch(code) {
- case 200:
- code_msg = "OK";
- break;
-
- case 307:
- code_msg = "Temporary Redirect";
- break;
-
- case 400:
- code_msg = "Bad Request";
- break;
-
- case 403:
- code_msg = "Forbidden";
- break;
-
- case 404:
- code_msg = "Not Found";
- break;
-
- default:
- code_msg = "Internal Server Error";
- break;
- }
-
- char date[100];
- struct tm tm = *gmtime(&w->data->date);
- strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %Z", &tm);
-
- char custom_header[MAX_HTTP_HEADER_SIZE + 1] = "";
- if(w->response_header[0])
- strcpy(custom_header, w->response_header);
-
- int headerlen = 0;
- headerlen += snprintf(&w->response_header[headerlen], MAX_HTTP_HEADER_SIZE - headerlen,
- "HTTP/1.1 %d %s\r\n"
- "Connection: %s\r\n"
- "Server: NetData Embedded HTTP Server\r\n"
- "Content-Type: %s\r\n"
- "Access-Control-Allow-Origin: *\r\n"
- "Date: %s\r\n"
- , code, code_msg
- , w->keepalive?"keep-alive":"close"
- , content_type_string
- , date
- );
-
- if(custom_header[0])
- headerlen += snprintf(&w->response_header[headerlen], MAX_HTTP_HEADER_SIZE - headerlen, "%s", custom_header);
-
- if(w->mode == WEB_CLIENT_MODE_NORMAL) {
- headerlen += snprintf(&w->response_header[headerlen], MAX_HTTP_HEADER_SIZE - headerlen,
- "Expires: %s\r\n"
- "Cache-Control: no-cache\r\n"
- , date
- );
- }
- else {
- headerlen += snprintf(&w->response_header[headerlen], MAX_HTTP_HEADER_SIZE - headerlen,
- "Cache-Control: public\r\n"
- );
- }
-
- // if we know the content length, put it
- if(!w->zoutput && (w->data->bytes || w->data->rbytes))
- headerlen += snprintf(&w->response_header[headerlen], MAX_HTTP_HEADER_SIZE - headerlen,
- "Content-Length: %ld\r\n"
- , w->data->bytes?w->data->bytes:w->data->rbytes
- );
- else if(!w->zoutput)
- w->keepalive = 0; // content-length is required for keep-alive
-
- if(w->zoutput) {
- headerlen += snprintf(&w->response_header[headerlen], MAX_HTTP_HEADER_SIZE - headerlen,
- "Content-Encoding: gzip\r\n"
- "Transfer-Encoding: chunked\r\n"
- );
- }
-
- headerlen += snprintf(&w->response_header[headerlen], MAX_HTTP_HEADER_SIZE - headerlen, "\r\n");
-
- // disable TCP_NODELAY, to buffer the header
- int flag = 0;
- if(setsockopt(w->ofd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) != 0) error("%llu: failed to disable TCP_NODELAY on socket.", w->id);
-
- // sent the HTTP header
- debug(D_WEB_DATA, "%llu: Sending response HTTP header of size %d: '%s'", w->id, headerlen, w->response_header);
-
- bytes = send(w->ofd, w->response_header, headerlen, 0);
- if(bytes != headerlen)
- error("%llu: HTTP Header failed to be sent (I sent %d bytes but the system sent %d bytes).", w->id, headerlen, bytes);
- else {
- global_statistics_lock();
- global_statistics.bytes_sent += bytes;
- global_statistics_unlock();
- }
-
- // enable TCP_NODELAY, to send all data immediately at the next send()
- flag = 1;
- if(setsockopt(w->ofd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) != 0) error("%llu: failed to enable TCP_NODELAY on socket.", w->id);
-
- // enable sending immediately if we have data
- if(w->data->bytes) w->wait_send = 1;
- else w->wait_send = 0;
-
- // pretty logging
- switch(w->mode) {
- case WEB_CLIENT_MODE_NORMAL:
- debug(D_WEB_CLIENT, "%llu: Done preparing the response. Sending data (%d bytes) to client.", w->id, w->data->bytes);
- break;
-
- case WEB_CLIENT_MODE_FILECOPY:
- if(w->data->rbytes) {
- debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending data file of %d bytes to client.", w->id, w->data->rbytes);
- w->wait_receive = 1;
-
- /*
- // utilize the kernel sendfile() for copying the file to the socket.
- // this block of code can be commented, without anything missing.
- // when it is commented, the program will copy the data using async I/O.
- {
- long len = sendfile(w->ofd, w->ifd, NULL, w->data->rbytes);
- if(len != w->data->rbytes) error("%llu: sendfile() should copy %ld bytes, but copied %ld. Falling back to manual copy.", w->id, w->data->rbytes, len);
- else web_client_reset(w);
- }
- */
- }
- else
- debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending an unknown amount of bytes to client.", w->id);
- break;
-
- default:
- fatal("%llu: Unknown client mode %d.", w->id, w->mode);
- break;
- }