Пример #1
0
static handler_t mod_status_handle_server_statistics(server *srv, connection *con, void *p_d) {
	plugin_data *p = p_d;
	buffer *b = p->module_list;
	size_t i;
	array *st = srv->status;

	if (0 == st->used) {
		/* we have nothing to send */
		con->http_status = 204;
		con->file_finished = 1;

		return HANDLER_FINISHED;
	}

	b = chunkqueue_get_append_buffer(con->write_queue);

	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(b, ": ");
		buffer_append_long(b, ((data_integer *)(st->data[ndx]))->value);
		buffer_append_string(b, "\n");
	}

	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;
}
Пример #2
0
static handler_t mod_status_handle_server_status_text(server *srv, connection *con, void *p_d) {
    plugin_data *p = p_d;
    buffer *b;
    double avg;
    time_t ts;
    char buf[32];
    unsigned int k;
    unsigned int l;

    b = chunkqueue_get_append_buffer(con->write_queue);

    /* 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_long(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_long(b, srv->conns->used);
    buffer_append_string_len(b, CONST_STR_LEN("\n"));

    buffer_append_string_len(b, CONST_STR_LEN("IdleServers: "));
    buffer_append_long(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"));

    /* set text/plain output */

    response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain"));

    return 0;
}
Пример #3
0
static void prepare_lisp_request (server *srv, handler_ctx *hctx)
{
  size_t i;
  buffer *buf;
  connection *con = hctx->connection;
  chunkqueue *hr_cq = hctx->request_queue;

  buf = chunkqueue_get_append_buffer(hr_cq);

#define APPEND_HEADER(k, vt, v)            \
  BUFFER_APPEND_STRING_CONST(buf, k);      \
  BUFFER_APPEND_STRING_CONST(buf, "\n");   \
  buffer_append_##vt(buf, v);              \
  BUFFER_APPEND_STRING_CONST(buf, "\n")

#define KEY_IS(string) \
  (buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN(string)) == 0)

#if 0
  for (i = 0; i < srv->srv_sockets.used; i++) {
      log_error_write(srv, __FILE__, __LINE__, "sd<S>", 
                      "srv_sockets", i,
                      inet_ntop_cache_get_ip(srv, &(srv->srv_sockets.ptr[i]->addr)));
  }
#endif

  /* Mod_lisp configuration and connection info. */
  APPEND_HEADER("server-id", string_buffer, hctx->socket_data->id);
  APPEND_HEADER("server-baseversion", string, PACKAGE_STRING);
  APPEND_HEADER("modlisp-version", string, MOD_LISP_VERSION);
  /* Server/connection configuration info. */
  APPEND_HEADER("url", string_buffer, con->request.uri);
  APPEND_HEADER("method", string, get_http_method_name(con->request.http_method));
  APPEND_HEADER("script-filename", string_buffer, con->physical.path);
  APPEND_HEADER("server-protocol", string, get_http_version_name(con->request.http_version));
  APPEND_HEADER("remote-ip-port", long, get_remote_port(srv, con));
  APPEND_HEADER("server-ip-port", long, srv->srvconf.port);
  APPEND_HEADER("remote-ip-addr", string, inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
  APPEND_HEADER("server-ip-addr", string, get_local_ip(srv, con));
  if (con->request.http_content_type) {
    APPEND_HEADER("content-type", string, con->request.http_content_type);
  }
  if (con->request.content_length) {
    APPEND_HEADER("content-length", long, con->request.content_length);
  }
Пример #4
0
static handler_t mod_status_handle_server_status_text(server *srv, connection *con, void *p_d) {
	plugin_data *p = p_d;
	buffer *b;
	double avg;
	time_t ts;
	char buf[32];

	b = chunkqueue_get_append_buffer(con->write_queue);

	/* output total number of requests */
	BUFFER_APPEND_STRING_CONST(b, "Total Accesses: ");
	avg = p->abs_requests;
	snprintf(buf, sizeof(buf) - 1, "%.0f", avg);
	buffer_append_string(b, buf);
	BUFFER_APPEND_STRING_CONST(b, "\n");

	/* output total traffic out in kbytes */
	BUFFER_APPEND_STRING_CONST(b, "Total kBytes: ");
	avg = p->abs_traffic_out / 1024;
	snprintf(buf, sizeof(buf) - 1, "%.0f", avg);
	buffer_append_string(b, buf);
	BUFFER_APPEND_STRING_CONST(b, "\n");

	/* output uptime */
	BUFFER_APPEND_STRING_CONST(b, "Uptime: ");
	ts = srv->cur_ts - srv->startup_ts;
	buffer_append_long(b, ts);
	BUFFER_APPEND_STRING_CONST(b, "\n");

	/* output busy servers */
	BUFFER_APPEND_STRING_CONST(b, "BusyServers: ");
	buffer_append_long(b, srv->conns->used);
	BUFFER_APPEND_STRING_CONST(b, "\n");

	/* set text/plain output */

	response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain"));

	return 0;
}
Пример #5
0
/**
 * move the content of chunk to another chunkqueue. return total bytes copied/stolen.
 */
off_t chunkqueue_steal_chunk(chunkqueue *cq, chunk *c) {
    /* we are copying the whole buffer, just steal it */
    off_t total = 0;
    buffer *b, btmp;

    if (!cq) return 0;
    if (chunk_is_done(c)) return 0;

    switch (c->type) {
    case MEM_CHUNK:
        total = c->mem->used - c->offset - 1;
        if (c->offset == 0) {
            b = chunkqueue_get_append_buffer(cq);
            btmp = *b;
            *b = *(c->mem);
            *(c->mem) = btmp;
        } else {
            chunkqueue_append_mem(cq, c->mem->ptr + c->offset, total);
            chunk_set_done(c);
        }
        break;
    case FILE_CHUNK:
        total = c->file.length - c->offset;

        if (c->file.is_temp) {
            chunkqueue_steal_tempfile(cq, c);
        } else {
            chunkqueue_append_file(cq, c->file.name, c->file.start + c->offset, c->file.length - c->offset);
            chunk_set_done(c);
        }

        break;
    case UNUSED_CHUNK:
        return 0;
    }

    return total;
}
Пример #6
0
static int connection_handle_write_prepare(server *srv, connection *con) {
	if (con->mode == DIRECT) {
		/* static files */
		switch(con->request.http_method) {
		case HTTP_METHOD_GET:
		case HTTP_METHOD_POST:
		case HTTP_METHOD_HEAD:
		case HTTP_METHOD_PUT:
		case HTTP_METHOD_MKCOL:
		case HTTP_METHOD_DELETE:
		case HTTP_METHOD_COPY:
		case HTTP_METHOD_MOVE:
		case HTTP_METHOD_PROPFIND:
		case HTTP_METHOD_PROPPATCH:
		case HTTP_METHOD_LOCK:
		case HTTP_METHOD_UNLOCK:
		case HTTP_METHOD_BITS_POST:
			break;
		case HTTP_METHOD_OPTIONS:
			/*
			 * 400 is coming from the request-parser BEFORE uri.path is set
			 * 403 is from the response handler when noone else catched it
			 *
			 * */
			if ((!con->http_status || con->http_status == 200) && con->uri.path->used &&
			    con->uri.path->ptr[0] != '*') {
				response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST"));

				con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED;
				con->parsed_response &= ~HTTP_CONTENT_LENGTH;

				con->http_status = 200;
				con->file_finished = 1;

				chunkqueue_reset(con->write_queue);
			}
			break;
		default:
			switch(con->http_status) {
			case 400: /* bad request */
			case 414: /* overload request header */
			case 416: /* Range-Not-Satisfiable */
			  //log_error_write(srv, __FILE__, __LINE__, "s", " 416 resetting read-queue");
			         //Reset the queue as we will no longer deal with the remaining packets
	  			chunkqueue_reset(con->read_queue);
			case 505: /* unknown protocol */
			case 207: /* this was webdav */
				break;
			default:
				con->http_status = 501;
				break;
			}
			break;
		}
	}

	if (con->http_status == 0) {
		con->http_status = 403;
	}

	switch(con->http_status) {
	case 204: /* class: header only */
	case 205:
	case 304:
		/* disable chunked encoding again as we have no body */
		con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED;
		con->parsed_response &= ~HTTP_CONTENT_LENGTH;
		chunkqueue_reset(con->write_queue);

		con->file_finished = 1;
		break;
	default: /* class: header + body */
		if (con->mode != DIRECT) break;

		/* only custom body for 4xx and 5xx */
		if (con->http_status < 400 || con->http_status >= 600) break;

		con->file_finished = 0;

		buffer_reset(con->physical.path);

		/* try to send static errorfile */
		if (!buffer_is_empty(con->conf.errorfile_prefix)) {
			stat_cache_entry *sce = NULL;

			buffer_copy_string_buffer(con->physical.path, con->conf.errorfile_prefix);
			buffer_append_long(con->physical.path, con->http_status);
			buffer_append_string_len(con->physical.path, CONST_STR_LEN(".html"));

			if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
				con->file_finished = 1;

				http_chunk_append_file(srv, con, con->physical.path, 0, sce->st.st_size);
				response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type));
			}
		}

		if (!con->file_finished) {
			con->file_finished = 1;

			if (con->conf.default_errorpages) {
				buffer *b;
				buffer_reset(con->physical.path);
				b = chunkqueue_get_append_buffer(con->write_queue);

				/* build default error-page */
				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>"));
				buffer_append_long(b, con->http_status);
				buffer_append_string_len(b, CONST_STR_LEN(" - "));
				buffer_append_string(b, get_http_status_name(con->http_status));

				buffer_append_string_len(b, CONST_STR_LEN(
						"</title>\n"
						" </head>\n"
						" <body>\n"
						"  <h1>"));
				buffer_append_long(b, con->http_status);
				buffer_append_string_len(b, CONST_STR_LEN(" - "));
				buffer_append_string(b, get_http_status_name(con->http_status));

				buffer_append_string_len(b, CONST_STR_LEN("</h1>\n"
						" </body>\n"
						"</html>\n"
						));

				response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
			}
		}
		break;
	}

	if (con->file_finished) {
		/* we have all the content and chunked encoding is not used, set a content-length */

		if ((!(con->parsed_response & HTTP_CONTENT_LENGTH)) &&
		    (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0) {
			off_t qlen = chunkqueue_length(con->write_queue);

			/**
			 * The Content-Length header only can be sent if we have content:
			 * - HEAD doesn't have a content-body (but have a content-length)
			 * - 1xx, 204 and 304 don't have a content-body (RFC 2616 Section 4.3)
			 *
			 * Otherwise generate a Content-Length header as chunked encoding is not
			 * available
			 */
			if ((con->http_status >= 100 && con->http_status < 200) ||
			    con->http_status == 204 ||
			    con->http_status == 304) {
				data_string *ds;
				/* no Content-Body, no Content-Length */
				if (NULL != (ds = (data_string*) array_get_element(con->response.headers, "Content-Length"))) {
					buffer_reset(ds->value); /* Headers with empty values are ignored for output */
				}
			} else if (qlen > 0 || con->request.http_method != HTTP_METHOD_HEAD) {
				/* qlen = 0 is important for Redirects (301, ...) as they MAY have
				 * a content. Browsers are waiting for a Content otherwise
				 */
				buffer_copy_off_t(srv->tmp_buf, qlen);

				response_header_overwrite(srv, con, CONST_STR_LEN("Content-Length"), CONST_BUF_LEN(srv->tmp_buf));
			}
		}
	} else {
		/**
		 * the file isn't finished yet, but we have all headers
		 *
		 * to get keep-alive we either need:
		 * - Content-Length: ... (HTTP/1.0 and HTTP/1.0) or
		 * - Transfer-Encoding: chunked (HTTP/1.1)
		 */

		if (((con->parsed_response & HTTP_CONTENT_LENGTH) == 0) &&
		    ((con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0)) {
			con->keep_alive = 0;
		}

		/**
		 * if the backend sent a Connection: close, follow the wish
		 *
		 * NOTE: if the backend sent Connection: Keep-Alive, but no Content-Length, we
		 * will close the connection. That's fine. We can always decide the close
		 * the connection
		 *
		 * FIXME: to be nice we should remove the Connection: ...
		 */
		if (con->parsed_response & HTTP_CONNECTION) {
			/* a subrequest disable keep-alive although the client wanted it */
			if (con->keep_alive && !con->response.keep_alive) {
				con->keep_alive = 0;
			}
		}
	}

	if (con->request.http_method == HTTP_METHOD_HEAD) {
		/**
		 * a HEAD request has the same as a GET
		 * without the content
		 */
		con->file_finished = 1;

		chunkqueue_reset(con->write_queue);
		con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED;
	}

	http_response_write_header(srv, con);

	return 0;
}
Пример #7
0
/*
 * copy/steal max_len bytes from chunk chain.  return total bytes copied/stolen.
 *
 */
off_t chunkqueue_steal_chunks_len(chunkqueue *out, chunk *c, off_t max_len) {
    off_t total = 0;
    off_t we_have = 0, we_want = 0;
    buffer *b;

    if (!out || !c) return 0;

    /* copy/steal chunks */
    for (; c && max_len > 0; c = c->next) {
        switch (c->type) {
        case FILE_CHUNK:
            we_have = c->file.length - c->offset;

            if (we_have == 0) break;

            if (we_have > max_len) we_have = max_len;

            chunkqueue_append_file(out, c->file.name, c->offset, we_have);

            c->offset += we_have;
            max_len -= we_have;
            total += we_have;

            /* steal the tempfile
             *
             * This is tricky:
             * - we reference the tempfile from the in-queue several times
             *   if the chunk is larger than max_len
             * - we can't simply cleanup the in-queue as soon as possible
             *   as it would remove the tempfiles
             * - the idea is to 'steal' the tempfiles and attach the is_temp flag to the last
             *   referencing chunk of the fastcgi-write-queue
             *
             */

            if (c->offset == c->file.length) {
                chunk *out_c;

                out_c = out->last;

                /* the last of the out-queue should be a FILE_CHUNK (we just created it)
                 * and the incoming side should have given use a temp-file-chunk */
                assert(out_c->type == FILE_CHUNK);
                assert(c->file.is_temp == 1);

                out_c->file.is_temp = 1;
                c->file.is_temp = 0;
            }

            break;
        case MEM_CHUNK:
            /* skip empty chunks */
            if (c->mem->used == 0) break;

            we_have = c->mem->used - c->offset - 1;
            if (we_have == 0) break;

            we_want = we_have < max_len ? we_have : max_len;

            if (we_have == we_want) {
                /* steal whole chunk */
                chunkqueue_steal_chunk(out, c);
            } else {
                /* copy unused data from chunk */
                b = chunkqueue_get_append_buffer(out);
                buffer_copy_string_len(b, c->mem->ptr + c->offset, we_want);
                c->offset += we_want;
            }
            total += we_want;
            max_len -= we_want;

            break;
        default:
            break;
        }
    }
    return total;
}
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;
#ifdef HAVE_XATTR
	char attrval[128];
	int attrlen;
#endif
#ifdef HAVE_LOCALTIME_R
	struct tm tm;
#endif

	if (dir->used == 0) return -1;

	i = dir->used - 1;

#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(dir->used + name_max);
	assert(path);
	strcpy(path, dir->ptr);
	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);
	assert(dirs.ent);
	dirs.size  = DIRLIST_BLOB_SIZE;
	dirs.used  = 0;
	files.ent  = (dirls_entry_t**) malloc(sizeof(dirls_entry_t*) * DIRLIST_BLOB_SIZE);
	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) {
			if (strcmp(dent->d_name, "README.txt") == 0)
				continue;
		}
		if (p->conf.hide_header_file) {
			if (strcmp(dent->d_name, "HEADER.txt") == 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);

					return -1;
				}
			}
			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);
			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 = chunkqueue_get_append_buffer(con->write_queue);
	buffer_copy_string_len(out, CONST_STR_LEN("<?xml version=\"1.0\" encoding=\""));
	if (buffer_is_empty(p->conf.encoding)) {
		buffer_append_string_len(out, CONST_STR_LEN("iso-8859-1"));
	} else {
		buffer_append_string_buffer(out, p->conf.encoding);
	}
	buffer_append_string_len(out, CONST_STR_LEN("\"?>\n"));
	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><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\">- &nbsp;</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;
#ifdef 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, "Content-Type", attrval, &attrlen, 0) == 0) {
				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 (ds->key->used == 0)
					continue;

				ct_len = ds->key->used - 1;
				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, 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_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;

	return 0;
}
Пример #9
0
static int proxy_create_env(server *srv, handler_ctx *hctx) {
	size_t i;

	connection *con   = hctx->remote_conn;
	buffer *b;

	/* build header */

	b = chunkqueue_get_append_buffer(hctx->wb);

	/* 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 (con->request.http_host &&
	    !buffer_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->conf.is_ssl ? "https" : "http");

	/* request header */
	for (i = 0; i < con->request.headers->used; i++) {
		data_string *ds;

		ds = (data_string *)con->request.headers->data[i];

		if (ds->value->used && ds->key->used) {
			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 += b->used - 1;
	/* body */

	if (con->request.content_length) {
		chunkqueue *req_cq = con->request_content_queue;
		chunk *req_c;
		off_t offset;

		/* something to send ? */
		for (offset = 0, req_c = req_cq->first; offset != req_cq->bytes_in; req_c = req_c->next) {
			off_t weWant = req_cq->bytes_in - offset;
			off_t weHave = 0;

			/* we announce toWrite octects
			 * now take all the request_content chunk that we need to fill this request
			 * */

			switch (req_c->type) {
			case FILE_CHUNK:
				weHave = req_c->file.length - req_c->offset;

				if (weHave > weWant) weHave = weWant;

				chunkqueue_append_file(hctx->wb, req_c->file.name, req_c->offset, weHave);

				req_c->offset += weHave;
				req_cq->bytes_out += weHave;

				hctx->wb->bytes_in += weHave;

				break;
			case MEM_CHUNK:
				/* append to the buffer */
				weHave = req_c->mem->used - 1 - req_c->offset;

				if (weHave > weWant) weHave = weWant;

				b = chunkqueue_get_append_buffer(hctx->wb);
				buffer_append_memory(b, req_c->mem->ptr + req_c->offset, weHave);
				b->used++; /* add virtual \0 */

				req_c->offset += weHave;
				req_cq->bytes_out += weHave;

				hctx->wb->bytes_in += weHave;

				break;
			default:
				break;
			}

			offset += weHave;
		}

	}

	return 0;
}
Пример #10
0
static int connection_handle_read_ssl(server *srv, connection *con) {
#ifdef USE_OPENSSL
	int r, ssl_err, len, count = 0, read_offset, toread;
	buffer *b = NULL;

	if (!con->conf.is_ssl) return -1;

	ERR_clear_error();
	do {
		if (NULL != con->read_queue->last) {
			b = con->read_queue->last->mem;
		}

		if (NULL == b || b->size - b->used < 1024) {
			b = chunkqueue_get_append_buffer(con->read_queue);
			len = SSL_pending(con->ssl);
			if (len < 4*1024) len = 4*1024; /* always alloc >= 4k buffer */
			buffer_prepare_copy(b, len + 1);

			/* overwrite everything with 0 */
			memset(b->ptr, 0, b->size);
		}

		read_offset = (b->used > 0) ? b->used - 1 : 0;
		toread = b->size - 1 - read_offset;

		len = SSL_read(con->ssl, b->ptr + read_offset, toread);

		if (con->renegotiations > 1 && con->conf.ssl_disable_client_renegotiation) {
			connection_set_state(srv, con, CON_STATE_ERROR);
			log_error_write(srv, __FILE__, __LINE__, "s", "SSL: renegotiation initiated by client");
			return -1;
		}

		if (len > 0) {
			if (b->used > 0) b->used--;
			b->used += len;
			b->ptr[b->used++] = '\0';

			con->bytes_read += len;

			count += len;
		}
	} while (len == toread && count < MAX_READ_LIMIT);


	if (len < 0) {
		int oerrno = errno;
		switch ((r = SSL_get_error(con->ssl, len))) {
		case SSL_ERROR_WANT_READ:
		case SSL_ERROR_WANT_WRITE:
			con->is_readable = 0;

			/* the manual says we have to call SSL_read with the same arguments next time.
			 * we ignore this restriction; no one has complained about it in 1.5 yet, so it probably works anyway.
			 */

			return 0;
		case SSL_ERROR_SYSCALL:
			/**
			 * man SSL_get_error()
			 *
			 * SSL_ERROR_SYSCALL
			 *   Some I/O error occurred.  The OpenSSL error queue may contain more
			 *   information on the error.  If the error queue is empty (i.e.
			 *   ERR_get_error() returns 0), ret can be used to find out more about
			 *   the error: If ret == 0, an EOF was observed that violates the
			 *   protocol.  If ret == -1, the underlying BIO reported an I/O error
			 *   (for socket I/O on Unix systems, consult errno for details).
			 *
			 */
			while((ssl_err = ERR_get_error())) {
				/* get all errors from the error-queue */
				log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:",
						r, ERR_error_string(ssl_err, NULL));
			}

			switch(oerrno) {
			default:
				log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL:",
						len, r, oerrno,
						strerror(oerrno));
				break;
			}

			break;
		case SSL_ERROR_ZERO_RETURN:
			/* clean shutdown on the remote side */

			if (r == 0) {
				/* FIXME: later */
			}

			/* fall thourgh */
		default:
			while((ssl_err = ERR_get_error())) {
				switch (ERR_GET_REASON(ssl_err)) {
				case SSL_R_SSL_HANDSHAKE_FAILURE:
				case SSL_R_TLSV1_ALERT_UNKNOWN_CA:
				case SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN:
				case SSL_R_SSLV3_ALERT_BAD_CERTIFICATE:
					if (!con->conf.log_ssl_noise) continue;
					break;
				default:
					break;
				}
				/* get all errors from the error-queue */
				log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:",
				                r, ERR_error_string(ssl_err, NULL));
			}
			break;
		}

		connection_set_state(srv, con, CON_STATE_ERROR);

		return -1;
	} else if (len == 0) {
		con->is_readable = 0;
		/* the other end close the connection -> KEEP-ALIVE */

		return -2;
	} else {
		joblist_append(srv, con);
	}

	return 0;
#else
	UNUSED(srv);
	UNUSED(con);
	return -1;
#endif
}
static int http_response_parse_range(server *srv, connection *con, plugin_data *p) {
	int multipart = 0;
	char *boundary = "fkj49sn38dcn3";
	data_string *ds;
	stat_cache_entry *sce = NULL;
	buffer *content_type = NULL;
	buffer *range = NULL;
	http_req_range *ranges, *r;

	if (NULL != (ds = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("Range")))) {
		range = ds->value;
	} else {
		/* we don't have a Range header */

		return -1;
	}

	if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
		SEGFAULT("stat_cache_get_entry(%s) returned %d", SAFE_BUF_STR(con->physical.path), HANDLER_ERROR);
	}

	con->response.content_length = 0;

	if (NULL != (ds = (data_string *)array_get_element(con->response.headers, CONST_STR_LEN("Content-Type")))) {
		content_type = ds->value;
	}

	/* start the range-header parser
	 * bytes=<num>  */

	ranges = p->ranges;
	http_request_range_reset(ranges);
	switch (http_request_range_parse(range, ranges)) {
	case PARSE_ERROR:
		return -1; /* no range valid Range Header */
	case PARSE_SUCCESS:
		break;
	default:
		TRACE("%s", "foobar");
		return -1;
	}

	if (ranges->next) {
		multipart = 1;
	}

	/* patch the '-1' */
	for (r = ranges; r; r = r->next) {
		if (r->start == -1) {
			/* -<end>
			 *
			 * the last <end> bytes  */
			r->start = sce->st.st_size - r->end;
			r->end = sce->st.st_size - 1;
		}
		if (r->end == -1) {
			/* <start>-
			 * all but the first <start> bytes */

			r->end = sce->st.st_size - 1;
		}

		if (r->end > sce->st.st_size - 1) {
			/* RFC 2616 - 14.35.1
			 *
			 * if last-byte-pos not present or > size-of-file
			 * take the size-of-file
			 *
			 *  */
			r->end = sce->st.st_size - 1;
		}

		if (r->start > sce->st.st_size - 1) {
			/* RFC 2616 - 14.35.1
			 *
			 * if first-byte-pos > file-size, 416
			 */

			con->http_status = 416;
			return -1;
		}

		if (r->start > r->end) {
			/* RFC 2616 - 14.35.1
			 *
			 * if last-byte-pos is present, it has to be >= first-byte-pos
			 *
			 * invalid ranges have to be handle as no Range specified
			 *  */

			return -1;
		}
	}

	if (r) {
		/* we ran into an range violation */
		return -1;
	}

	if (multipart) {
		buffer *b;
		for (r = ranges; r; r = r->next) {
			/* write boundary-header */

			b = chunkqueue_get_append_buffer(con->send);

			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_off_t(b, r->start);
			buffer_append_string_len(b, CONST_STR_LEN("-"));
			buffer_append_off_t(b, r->end);
			buffer_append_string_len(b, CONST_STR_LEN("/"));
			buffer_append_off_t(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 += b->used - 1;
			con->send->bytes_in += b->used - 1;

			chunkqueue_append_file(con->send, con->physical.path, r->start, r->end - r->start + 1);
			con->response.content_length += r->end - r->start + 1;
			con->send->bytes_in += r->end - r->start + 1;
		}

		/* add boundary end */
		b = chunkqueue_get_append_buffer(con->send);

		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 += b->used - 1;
		con->send->bytes_in += b->used - 1;

		/* 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 {
		r = ranges;

		chunkqueue_append_file(con->send, con->physical.path, r->start, r->end - r->start + 1);
		con->response.content_length += r->end - r->start + 1;
		con->send->bytes_in += r->end - r->start + 1;

		buffer_copy_string_len(p->range_buf, CONST_STR_LEN("bytes "));
		buffer_append_off_t(p->range_buf, r->start);
		buffer_append_string_len(p->range_buf, CONST_STR_LEN("-"));
		buffer_append_off_t(p->range_buf, r->end);
		buffer_append_string_len(p->range_buf, CONST_STR_LEN("/"));
		buffer_append_off_t(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;
}
Пример #12
0
static handler_t mod_status_handle_server_status_html(server *srv, connection *con, void *p_d) {
	plugin_data *p = p_d;
	buffer *b;
	size_t j;
	double avg;
	char multiplier = '\0';
	char buf[32];
	time_t ts;

	int days, hours, mins, seconds;

	b = chunkqueue_get_append_buffer(con->write_queue);

	BUFFER_COPY_STRING_CONST(b,
				 "<?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");

	BUFFER_APPEND_STRING_CONST(b,
				   "  <style type=\"text/css\">\n"
				   "    table.status { border: black solid thin; }\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_CONST(b,
					   "<script type=\"text/javascript\">\n"
					   "// <!--\n"
					   "var sort_column;\n"
					   "var prev_span = null;\n");

		BUFFER_APPEND_STRING_CONST(b,
					   "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");

		BUFFER_APPEND_STRING_CONST(b,
					   "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");

		BUFFER_APPEND_STRING_CONST(b,
					   "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");

		BUFFER_APPEND_STRING_CONST(b,
					   " if (prev_span != null) prev_span.innerHTML = '';\n"
					   " if (span.getAttribute('sortdir')=='down') {\n"
					   "  span.innerHTML = '&uarr;';\n"
					   "  span.setAttribute('sortdir','up');\n"
					   "  rows.reverse();\n"
					   " } else {\n"
					   "  span.innerHTML = '&darr;';\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_CONST(b,
				 " </head>\n"
				 " <body>\n");



	/* connection listing */
	BUFFER_APPEND_STRING_CONST(b, "<h1>Server-Status</h1>");

	BUFFER_APPEND_STRING_CONST(b, "<table summary=\"status\" class=\"status\">");
	BUFFER_APPEND_STRING_CONST(b, "<tr><td>Hostname</td><td class=\"string\">");
	buffer_append_string_buffer(b, con->uri.authority);
	BUFFER_APPEND_STRING_CONST(b, " (");
	buffer_append_string_buffer(b, con->server_name);
	BUFFER_APPEND_STRING_CONST(b, ")</td></tr>\n");
	BUFFER_APPEND_STRING_CONST(b, "<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_long(b, days);
		BUFFER_APPEND_STRING_CONST(b, " days ");
	}

	if (hours) {
		buffer_append_long(b, hours);
		BUFFER_APPEND_STRING_CONST(b, " hours ");
	}

	if (mins) {
		buffer_append_long(b, mins);
		BUFFER_APPEND_STRING_CONST(b, " min ");
	}

	buffer_append_long(b, seconds);
	BUFFER_APPEND_STRING_CONST(b, " s");

	BUFFER_APPEND_STRING_CONST(b, "</td></tr>\n");
	BUFFER_APPEND_STRING_CONST(b, "<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_CONST(b, "</td></tr>\n");


	BUFFER_APPEND_STRING_CONST(b, "<tr><th colspan=\"2\">absolute (since start)</th></tr>\n");

	BUFFER_APPEND_STRING_CONST(b, "<tr><td>Requests</td><td class=\"string\">");
	avg = p->abs_requests;

	mod_status_get_multiplier(&avg, &multiplier, 1000);

	buffer_append_long(b, avg);
	BUFFER_APPEND_STRING_CONST(b, " ");
	if (multiplier)	buffer_append_string_len(b, &multiplier, 1);
	BUFFER_APPEND_STRING_CONST(b, "req</td></tr>\n");

	BUFFER_APPEND_STRING_CONST(b, "<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_CONST(b, " ");
	if (multiplier)	buffer_append_string_len(b, &multiplier, 1);
	BUFFER_APPEND_STRING_CONST(b, "byte</td></tr>\n");



	BUFFER_APPEND_STRING_CONST(b, "<tr><th colspan=\"2\">average (since start)</th></tr>\n");

	BUFFER_APPEND_STRING_CONST(b, "<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_long(b, avg);
	BUFFER_APPEND_STRING_CONST(b, " ");
	if (multiplier)	buffer_append_string_len(b, &multiplier, 1);
	BUFFER_APPEND_STRING_CONST(b, "req/s</td></tr>\n");

	BUFFER_APPEND_STRING_CONST(b, "<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_CONST(b, " ");
	if (multiplier)	buffer_append_string_len(b, &multiplier, 1);
	BUFFER_APPEND_STRING_CONST(b, "byte/s</td></tr>\n");



	BUFFER_APPEND_STRING_CONST(b, "<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_CONST(b, "<tr><td>Requests</td><td class=\"string\">");

	mod_status_get_multiplier(&avg, &multiplier, 1000);

	buffer_append_long(b, avg);
	BUFFER_APPEND_STRING_CONST(b, " ");
	if (multiplier)	buffer_append_string_len(b, &multiplier, 1);

	BUFFER_APPEND_STRING_CONST(b, "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_CONST(b, "<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_CONST(b, " ");
	if (multiplier)	buffer_append_string_len(b, &multiplier, 1);
	BUFFER_APPEND_STRING_CONST(b, "byte/s</td></tr>\n");

	BUFFER_APPEND_STRING_CONST(b, "</table>\n");


	BUFFER_APPEND_STRING_CONST(b, "<hr />\n<pre><b>legend</b>\n");
	BUFFER_APPEND_STRING_CONST(b, ". = connect, C = close, E = hard error\n");
	BUFFER_APPEND_STRING_CONST(b, "r = read, R = read-POST, W = write, h = handle-request\n");
	BUFFER_APPEND_STRING_CONST(b, "q = request-start,  Q = request-end\n");
	BUFFER_APPEND_STRING_CONST(b, "s = response-start, S = response-end\n");

	BUFFER_APPEND_STRING_CONST(b, "<b>");
	buffer_append_long(b, srv->conns->used);
	BUFFER_APPEND_STRING_CONST(b, " connections</b>\n");

	for (j = 0; j < srv->conns->used; j++) {
		connection *c = srv->conns->ptr[j];
		const char *state = connection_get_short_state(c->state);

		buffer_append_string_len(b, state, 1);

		if (((j + 1) % 50) == 0) {
			BUFFER_APPEND_STRING_CONST(b, "\n");
		}
	}

	BUFFER_APPEND_STRING_CONST(b, "\n</pre><hr />\n<h2>Connections</h2>\n");

	BUFFER_APPEND_STRING_CONST(b, "<table summary=\"status\" class=\"status\">\n");
	BUFFER_APPEND_STRING_CONST(b, "<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_CONST(b, "</tr>\n");

	for (j = 0; j < srv->conns->used; j++) {
		connection *c = srv->conns->ptr[j];

		BUFFER_APPEND_STRING_CONST(b, "<tr><td class=\"string\">");

		buffer_append_string(b, inet_ntop_cache_get_ip(srv, &(c->dst_addr)));

		BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"int\">");

		if (con->request.content_length) {
			buffer_append_long(b, c->request_content_queue->bytes_in);
			BUFFER_APPEND_STRING_CONST(b, "/");
			buffer_append_long(b, c->request.content_length);
		} else {
			BUFFER_APPEND_STRING_CONST(b, "0/0");
		}

		BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"int\">");

		buffer_append_off_t(b, chunkqueue_written(c->write_queue));
		BUFFER_APPEND_STRING_CONST(b, "/");
		buffer_append_off_t(b, chunkqueue_length(c->write_queue));

		BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"string\">");

		buffer_append_string(b, connection_get_state(c->state));

		BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"int\">");

		buffer_append_long(b, srv->cur_ts - c->request_start);

		BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"string\">");

		if (buffer_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_CONST(b, "</td><td class=\"string\">");

		if (!buffer_is_empty(c->uri.path)) {
			buffer_append_string_encoded(b, CONST_BUF_LEN(c->uri.path), ENCODING_HTML);
		}

		BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"string\">");

		buffer_append_string_buffer(b, c->physical.path);

		BUFFER_APPEND_STRING_CONST(b, "</td></tr>\n");
	}


	BUFFER_APPEND_STRING_CONST(b,
		      "</table>\n");


	BUFFER_APPEND_STRING_CONST(b,
		      " </body>\n"
		      "</html>\n"
		      );

	response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));

	return 0;
}
Пример #13
0
static handler_t mod_status_handle_server_config(server *srv, connection *con, void *p_d) {
	plugin_data *p = p_d;
	buffer *b, *m = p->module_list;
	size_t i;

	struct ev_map { fdevent_handler_t et; const char *name; } event_handlers[] =
	{
		/* - poll is most reliable
		 * - select works everywhere
		 * - linux-* are experimental
		 */
#ifdef USE_POLL
		{ FDEVENT_HANDLER_POLL,           "poll" },
#endif
#ifdef USE_SELECT
		{ FDEVENT_HANDLER_SELECT,         "select" },
#endif
#ifdef USE_LINUX_EPOLL
		{ FDEVENT_HANDLER_LINUX_SYSEPOLL, "linux-sysepoll" },
#endif
#ifdef USE_LINUX_SIGIO
		{ FDEVENT_HANDLER_LINUX_RTSIG,    "linux-rtsig" },
#endif
#ifdef USE_SOLARIS_DEVPOLL
		{ FDEVENT_HANDLER_SOLARIS_DEVPOLL,"solaris-devpoll" },
#endif
#ifdef USE_FREEBSD_KQUEUE
		{ FDEVENT_HANDLER_FREEBSD_KQUEUE, "freebsd-kqueue" },
#endif
		{ FDEVENT_HANDLER_UNSET,          NULL }
	};

	b = chunkqueue_get_append_buffer(con->write_queue);

	BUFFER_COPY_STRING_CONST(b,
			   "<?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_NAME " " PACKAGE_VERSION "</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_string_buffer(m, pl->name);
		} else {
			BUFFER_APPEND_STRING_CONST(m, "<br />");
			buffer_append_string_buffer(m, pl->name);
		}
	}

	mod_status_row_append(b, "Loaded Modules", m->ptr);

	BUFFER_APPEND_STRING_CONST(b, "  </table>\n");

	BUFFER_APPEND_STRING_CONST(b,
		      " </body>\n"
		      "</html>\n"
		      );

	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;
}
Пример #14
0
static int connection_handle_read(server *srv, connection *con) {
	int len;
	buffer *b;
	int toread;

	if (con->conf.is_ssl) {
		return connection_handle_read_ssl(srv, con);
	}

#if defined(__WIN32)
	b = chunkqueue_get_append_buffer(con->read_queue);
	buffer_prepare_copy(b, 4 * 1024);
	len = recv(con->fd, b->ptr, b->size - 1, 0);
#else
	if (ioctl(con->fd, FIONREAD, &toread)) {
		log_error_write(srv, __FILE__, __LINE__, "sd",
				"unexpected end-of-file:",
				con->fd);
		return -1;
	}
	b = chunkqueue_get_append_buffer(con->read_queue);
	buffer_prepare_copy(b, toread + 1);

	len = read(con->fd, b->ptr, b->size - 1);
#endif

	if (len < 0) {
		con->is_readable = 0;

		if (errno == EAGAIN) return 0;
		if (errno == EINTR) {
			/* we have been interrupted before we could read */
			con->is_readable = 1;
			return 0;
		}

		if (errno != ECONNRESET) {
			/* expected for keep-alive */
			log_error_write(srv, __FILE__, __LINE__, "ssd", "connection closed - read failed: ", strerror(errno), errno);
		}

		connection_set_state(srv, con, CON_STATE_ERROR);

		return -1;
	} else if (len == 0) {
		con->is_readable = 0;
		/* the other end close the connection -> KEEP-ALIVE */

		/* pipelining */

		return -2;
	} else if ((size_t)len < b->size - 1) {
		/* we got less then expected, wait for the next fd-event */

		con->is_readable = 0;
	}

	b->used = len;
	b->ptr[b->used++] = '\0';

	con->bytes_read += len;
#if 0
	dump_packet(b->ptr, len);
#endif

	return 0;
}
Пример #15
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;

				b = chunkqueue_get_append_buffer(con->write_queue);

				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_off_t(b, start);
				buffer_append_string_len(b, CONST_STR_LEN("-"));
				buffer_append_off_t(b, end);
				buffer_append_string_len(b, CONST_STR_LEN("/"));
				buffer_append_off_t(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 += b->used - 1;

			}

			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;

		b = chunkqueue_get_append_buffer(con->write_queue);

		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 += b->used - 1;

		/* 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_off_t(p->range_buf, start);
		buffer_append_string_len(p->range_buf, CONST_STR_LEN("-"));
		buffer_append_off_t(p->range_buf, end);
		buffer_append_string_len(p->range_buf, CONST_STR_LEN("/"));
		buffer_append_off_t(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;
}
Пример #16
0
static int
nlua_eval(FCGX_Request *request)
{
    return make_error(request, "Unimplemented", ENOSYS);
#if 0
    char *program;
    int prog_ptr = 0;
    lua_State *lua;
    int streamer[2];
    int in, out;
    int content_length;
    char *tmp = FCGX_GetParam("CONTENT_LENGTH", request->envp);

    close(0);
    content_length = strtoul(content_length, 0, NULL);

    /* If there's no program, there's nothing to do */
    if (content_length <= 0) {
        FCGX_FPrintF(request, "Content-Type: text/plain\r\n\r\n");
        return 0;
    }

    program = malloc(con->request.content_length+1);
    if (!program)
        return make_error(request, "Program size to large", ENOMEM);

    program[content_length] = '\0';

    /* there is content to eval */
    for (c = cq->first; c; c = cq->first) {
        int r = 0;

        /* copy all chunks */
        switch(c->type) {
        case FILE_CHUNK:

            if (c->file.mmap.start == MAP_FAILED) {
                if (-1 == c->file.fd &&  /* open the file if not already open */
                    -1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) {
                    //log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno));

                    free(program);
                    return -1;
                }

                c->file.mmap.length = c->file.length;

                if (MAP_FAILED == (c->file.mmap.start = mmap(0,  c->file.mmap.length, PROT_READ, MAP_SHARED, c->file.fd, 0))) {
                    //log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed: ", strerror(errno), c->file.name,  c->file.fd);

                    free(program);
                    return -1;
                }

                close(c->file.fd);
                c->file.fd = -1;

                /* chunk_reset() or chunk_free() will cleanup for us */
            }

            memcpy(program+prog_ptr, c->file.mmap.start+c->offset,
                    c->file.length - c->offset);
            r = c->file.length - c->offset;
            break;
        case MEM_CHUNK:
            memcpy(program+prog_ptr,
                    c->mem->ptr + c->offset,
                    c->mem->used - c->offset - 1);
            r = c->mem->used - c->offset - 1;
            break;
        case UNUSED_CHUNK:
            break;
        }

        c->offset += r;
        cq->bytes_out += r;
        prog_ptr += r;
        chunkqueue_remove_finished_chunks(cq);
    }

    lua = lua_open();
    if (!lua)
        return make_error(con, "Unable to open lua", errno);

    luaL_openlibs(lua);

    pipe(streamer);
    out = dup2(streamer[1], 0);
    in = streamer[0];

    if (streamer[1] != out)
        close(streamer[1]);

    if (luaL_dostring(lua, program)) {
        char errmsg[2048];
        snprintf(errmsg, sizeof(errmsg)-1,
                "LUA program \"%s\" encountered an error: %s", program, lua_tostring(lua, 1));
        make_error(con, errmsg, 1);
        con->http_status = 200;
    }
    else {
        char data[4096];
        int len;
        bzero(data, sizeof(data));
        len = read(in, data, sizeof(data));
        b = chunkqueue_get_append_buffer(con->write_queue);
        buffer_copy_string_len(b, data, len);
        con->http_status = 200;
    }
    lua_close(lua);
    free(program);

    close(streamer[1]);
    close(streamer[0]);
    close(out);
    close(in);

    return HANDLER_FINISHED;
#endif
}
Пример #17
0
/**
 * handle all header and content read
 *
 * we get called by the state-engine and by the fdevent-handler
 */
static int connection_handle_read_state(server *srv, connection *con)  {
	connection_state_t ostate = con->state;
	chunk *c, *last_chunk;
	off_t last_offset;
	chunkqueue *cq = con->read_queue;
	chunkqueue *dst_cq = con->request_content_queue;
	int is_closed = 0; /* the connection got closed, if we don't have a complete header, -> error */

	if (con->is_readable) {
		con->read_idle_ts = srv->cur_ts;

		switch(connection_handle_read(srv, con)) {
		case -1:
			return -1;
		case -2:
			is_closed = 1;
			break;
		default:
			break;
		}
	}

	/* the last chunk might be empty */
	for (c = cq->first; c;) {
		if (cq->first == c && c->mem->used == 0) {
			/* the first node is empty */
			/* ... and it is empty, move it to unused */

			cq->first = c->next;
			if (cq->first == NULL) cq->last = NULL;

			c->next = cq->unused;
			cq->unused = c;
			cq->unused_chunks++;

			c = cq->first;
		} else if (c->next && c->next->mem->used == 0) {
			chunk *fc;
			/* next node is the last one */
			/* ... and it is empty, move it to unused */

			fc = c->next;
			c->next = fc->next;

			fc->next = cq->unused;
			cq->unused = fc;
			cq->unused_chunks++;

			/* the last node was empty */
			if (c->next == NULL) {
				cq->last = c;
			}

			c = c->next;
		} else {
			c = c->next;
		}
	}

	/* we might have got several packets at once
	 */

	switch(ostate) {
	case CON_STATE_READ:
		/* if there is a \r\n\r\n in the chunkqueue
		 *
		 * scan the chunk-queue twice
		 * 1. to find the \r\n\r\n
		 * 2. to copy the header-packet
		 *
		 */

		last_chunk = NULL;
		last_offset = 0;

		for (c = cq->first; c; c = c->next) {
			buffer b;
			size_t i;

			b.ptr = c->mem->ptr + c->offset;
			b.used = c->mem->used - c->offset;
			if (b.used > 0) b.used--; /* buffer "used" includes terminating zero */

			for (i = 0; i < b.used; i++) {
				char ch = b.ptr[i];

				if ('\r' == ch) {
					/* chec if \n\r\n follows */
					size_t j = i+1;
					chunk *cc = c;
					const char header_end[] = "\r\n\r\n";
					int header_end_match_pos = 1;

					for ( ; cc; cc = cc->next, j = 0 ) {
						buffer bb;
						bb.ptr = cc->mem->ptr + cc->offset;
						bb.used = cc->mem->used - cc->offset;
						if (bb.used > 0) bb.used--; /* buffer "used" includes terminating zero */

						for ( ; j < bb.used; j++) {
							ch = bb.ptr[j];

							if (ch == header_end[header_end_match_pos]) {
								header_end_match_pos++;
								if (4 == header_end_match_pos) {
									last_chunk = cc;
									last_offset = j+1;
									goto found_header_end;
								}
							} else {
								goto reset_search;
							}
						}
					}
				}
reset_search: ;
			}
		}
found_header_end:

		/* found */
		if (last_chunk) {
			buffer_reset(con->request.request);

			for (c = cq->first; c; c = c->next) {
				buffer b;

				b.ptr = c->mem->ptr + c->offset;
				b.used = c->mem->used - c->offset;

				if (c == last_chunk) {
					b.used = last_offset + 1;
				}

				buffer_append_string_buffer(con->request.request, &b);

				if (c == last_chunk) {
					c->offset += last_offset;

					break;
				} else {
					/* the whole packet was copied */
					c->offset = c->mem->used - 1;
				}
			}

			connection_set_state(srv, con, CON_STATE_REQUEST_END);
		} else if (chunkqueue_length(cq) > 64 * 1024) {
			log_error_write(srv, __FILE__, __LINE__, "s", "oversized request-header -> sending Status 414");

			con->http_status = 414; /* Request-URI too large */
			con->keep_alive = 0;
			connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
		}
		break;
	case CON_STATE_READ_POST:
		// xb ready to mod
		for (c = cq->first; c && (dst_cq->bytes_in != (off_t)con->request.content_length); c = c->next) {
			off_t weWant, weHave, toRead;

			weWant = con->request.content_length - dst_cq->bytes_in;

			assert(c->mem->used);

			weHave = c->mem->used - c->offset - 1;

			toRead = weHave > weWant ? weWant : weHave;

			data_string *ds_pi = (data_string *)array_get_element(con->request.headers, "X-Pi-Upload");
			if (ds_pi) {
				float id;
				sscanf(ds_pi->value->ptr, "%f", &id);
				if (id != con->upload_id) {
					log_error_write(srv, __FILE__, __LINE__, "s", "newfile");
					char path[256];
					sprintf(path, "/tmp/upload_vid/%f", id);
					int fd = open(path, O_CREAT | O_WRONLY, 0777);
					con->upload_fd = fd;
					con->upload_id = id;
				}
				log_error_write(srv, __FILE__, __LINE__, "sboood", 
						"pi-upload:", ds_pi->value, weHave, 
						(off_t)dst_cq->bytes_in, (off_t)con->request.content_length,
						(int)ds_pi);
				write(con->upload_fd, c->mem->ptr + c->offset, toRead);
				/*
				dst_cq->bytes_in += toRead;
				if (dst_cq->bytes_in == (off_t)con->request.content_length) {
					connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
					break;
				}
				continue;
				*/
			}

			/* the new way, copy everything into a chunkqueue whcih might use tempfiles */
			if (con->request.content_length > 64 * 1024) {
				chunk *dst_c = NULL;
				/* copy everything to max 1Mb sized tempfiles */

				/*
				 * if the last chunk is
				 * - smaller than 1Mb (size < 1Mb)
				 * - not read yet (offset == 0)
				 * -> append to it
				 * otherwise
				 * -> create a new chunk
				 *
				 * */

				// xb mod: my upload file

				if (dst_cq->last &&
				    dst_cq->last->type == FILE_CHUNK &&
				    dst_cq->last->file.is_temp &&
				    dst_cq->last->offset == 0) {
					/* ok, take the last chunk for our job */

					// xb mod: my upload file
				
					if (0) {

						/*
						dst_c = chunkqueue_get_append_tempfile(dst_cq);
						close(dst_c->file.fd);
						buffer *path = buffer_init_string("/var/cache/lighttpd/uploads/my-upload-");
						buffer_append_string_buffer(path, ds->value);
						dst_c->file.fd = open(path->ptr, O_WRONLY | O_CREAT, 0777);
						log_error_write(srv, __FILE__, __LINE__, "sb", "pi-upload: path", path);
#ifdef FD_CLOEXEC
						fcntl(dst_c->file.fd, F_SETFD, FD_CLOEXEC);
#endif
						buffer_free(path);
						*/

					} else {

						if (dst_cq->last->file.length < 1 * 1024 * 1024) {
							dst_c = dst_cq->last;

							if (dst_c->file.fd == -1) {
								/* this should not happen as we cache the fd, but you never know */
								dst_c->file.fd = open(dst_c->file.name->ptr, O_WRONLY | O_APPEND);
#ifdef FD_CLOEXEC
								fcntl(dst_c->file.fd, F_SETFD, FD_CLOEXEC);
#endif
							}
						} else {
							/* the chunk is too large now, close it */
							dst_c = dst_cq->last;

							if (dst_c->file.fd != -1) {
								close(dst_c->file.fd);
								dst_c->file.fd = -1;
							}
							dst_c = chunkqueue_get_append_tempfile(dst_cq);
						}
					}
				} else {
					dst_c = chunkqueue_get_append_tempfile(dst_cq);
				}

				/* we have a chunk, let's write to it */

				if (dst_c->file.fd == -1) {
					/* we don't have file to write to,
					 * EACCES might be one reason.
					 *
					 * Instead of sending 500 we send 413 and say the request is too large
					 *  */

					log_error_write(srv, __FILE__, __LINE__, "sbs",
							"denying upload as opening to temp-file for upload failed:",
							dst_c->file.name, strerror(errno));

					con->http_status = 413; /* Request-Entity too large */
					con->keep_alive = 0;
					connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);

					break;
				}

				// xb mod
				//if (toRead != write(dst_c->file.fd, c->mem->ptr + c->offset, toRead)) {
				if (!ds_pi && toRead != write(dst_c->file.fd, c->mem->ptr + c->offset, toRead)) {
					/* write failed for some reason ... disk full ? */
					log_error_write(srv, __FILE__, __LINE__, "sbs",
							"denying upload as writing to file failed:",
							dst_c->file.name, strerror(errno));

					con->http_status = 413; /* Request-Entity too large */
					con->keep_alive = 0;
					connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);

					close(dst_c->file.fd);
					dst_c->file.fd = -1;

					break;
				}

				dst_c->file.length += toRead;

				if (dst_cq->bytes_in + toRead == (off_t)con->request.content_length) {
					/* we read everything, close the chunk */
					close(dst_c->file.fd);
					dst_c->file.fd = -1;
				}
			} else {
				buffer *b;

				if (dst_cq->last &&
				    dst_cq->last->type == MEM_CHUNK) {
					b = dst_cq->last->mem;
				} else {
					b = chunkqueue_get_append_buffer(dst_cq);
					/* prepare buffer size for remaining POST data; is < 64kb */
					buffer_prepare_copy(b, con->request.content_length - dst_cq->bytes_in + 1);
				}
				buffer_append_string_len(b, c->mem->ptr + c->offset, toRead);
			}

			c->offset += toRead;
			dst_cq->bytes_in += toRead;
		}

		/* Content is ready */
		if (dst_cq->bytes_in == (off_t)con->request.content_length) {
			connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
			log_error_write(srv, __FILE__, __LINE__, "s", "endfile: ok");
		}

		break;
	default: break;
	}

	/* the connection got closed and we didn't got enough data to leave one of the READ states
	 * the only way is to leave here */
	if (is_closed && ostate == con->state) {
		connection_set_state(srv, con, CON_STATE_ERROR);
		log_error_write(srv, __FILE__, __LINE__, "s", "endfile: error");
	}


	chunkqueue_remove_finished_chunks(cq);

	return 0;
}
Пример #18
0
/* 0: everything ok, -1: error, -2: con closed */
static int connection_handle_read(server *srv, connection *con) {
	int len;
	buffer *b;
	int toread, read_offset;

	if (con->conf.is_ssl) {
		return connection_handle_read_ssl(srv, con);
	}

	b = (NULL != con->read_queue->last) ? con->read_queue->last->mem : NULL;

	/* default size for chunks is 4kb; only use bigger chunks if FIONREAD tells
	 *  us more than 4kb is available
	 * if FIONREAD doesn't signal a big chunk we fill the previous buffer
	 *  if it has >= 1kb free
	 */
#if defined(__WIN32)
	if (NULL == b || b->size - b->used < 1024) {
		b = chunkqueue_get_append_buffer(con->read_queue);
		buffer_prepare_copy(b, 4 * 1024);
	}

	read_offset = (b->used == 0) ? 0 : b->used - 1;
	len = recv(con->fd, b->ptr + read_offset, b->size - 1 - read_offset, 0);
#else
	if (ioctl(con->fd, FIONREAD, &toread) || toread == 0 || toread <= 4*1024) {
		if (NULL == b || b->size - b->used < 1024) {
			b = chunkqueue_get_append_buffer(con->read_queue);
			buffer_prepare_copy(b, 4 * 1024);
		}
	} else {
		if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT;
		b = chunkqueue_get_append_buffer(con->read_queue);
		buffer_prepare_copy(b, toread + 1);
	}

	read_offset = (b->used == 0) ? 0 : b->used - 1;
	len = read(con->fd, b->ptr + read_offset, b->size - 1 - read_offset);
#endif

	if (len < 0) {
		con->is_readable = 0;

		if (errno == EAGAIN) return 0;
		if (errno == EINTR) {
			/* we have been interrupted before we could read */
			con->is_readable = 1;
			return 0;
		}

		if (errno != ECONNRESET) {
			/* expected for keep-alive */
			log_error_write(srv, __FILE__, __LINE__, "ssd", "connection closed - read failed: ", strerror(errno), errno);
		}

		connection_set_state(srv, con, CON_STATE_ERROR);

		return -1;
	} else if (len == 0) {
		con->is_readable = 0;
		/* the other end close the connection -> KEEP-ALIVE */

		/* pipelining */

		return -2;
	} else if ((size_t)len < b->size - 1) {
		/* we got less then expected, wait for the next fd-event */

		con->is_readable = 0;
	}

	if (b->used > 0) b->used--;
	b->used += len;
	b->ptr[b->used++] = '\0';

	con->bytes_read += len;
#if 0
	dump_packet(b->ptr, len);
#endif

	return 0;
}
Пример #19
0
/**
 * handle all header and content read
 *
 * we get called by the state-engine and by the fdevent-handler
 */
int connection_handle_read_state(server *srv, connection *con)  {

	/**
	 * Recording the connection activity to be used for the purpose of timeout calc
	 * See '/sbin/timeout'
	 * Touching the file '/var/last_tcp_connection_time'
	 */

	system("touch /var/last_tcp_connection_time");

	connection_state_t ostate = con->state;
	chunk *c, *last_chunk;
	off_t last_offset;
	chunkqueue *cq = con->read_queue;
	chunkqueue *dst_cq = con->request_content_queue;
	int is_closed = 0; /* the connection got closed, if we don't have a complete header, -> error */

	if (con->is_readable) {
		con->read_idle_ts = srv->cur_ts;

		switch(connection_handle_read(srv, con)) {
		case -1:
			return -1;
		case -2:
			is_closed = 1;
			break;
		default:
			break;
		}
	}
	//log_error_write(srv, __FILE__, __LINE__, "sd", "http_status", con->http_status);
	switch(con->http_status) {
	   case 416:
	           return 0;
	   default:
	           break;
	}

	/* the last chunk might be empty */
	for (c = cq->first; c;) {
		if (cq->first == c && c->mem->used == 0) {
			/* the first node is empty */
			/* ... and it is empty, move it to unused */

			cq->first = c->next;
			if (cq->first == NULL) cq->last = NULL;

			c->next = cq->unused;
			cq->unused = c;
			cq->unused_chunks++;

			c = cq->first;
		} else if (c->next && c->next->mem->used == 0) {
			chunk *fc;
			/* next node is the last one */
			/* ... and it is empty, move it to unused */

			fc = c->next;
			c->next = fc->next;

			fc->next = cq->unused;
			cq->unused = fc;
			cq->unused_chunks++;

			/* the last node was empty */
			if (c->next == NULL) {
				cq->last = c;
			}

			c = c->next;
		} else {
			c = c->next;
		}
	}

	/* we might have got several packets at once
	 */

	switch(ostate) {
	case CON_STATE_READ:
		/* if there is a \r\n\r\n in the chunkqueue
		 *
		 * scan the chunk-queue twice
		 * 1. to find the \r\n\r\n
		 * 2. to copy the header-packet
		 *
		 */

		last_chunk = NULL;
		last_offset = 0;

		for (c = cq->first; !last_chunk && c; c = c->next) {
			buffer b;
			size_t i;

			b.ptr = c->mem->ptr + c->offset;
			b.used = c->mem->used - c->offset;

			for (i = 0; !last_chunk && i < b.used; i++) {
				char ch = b.ptr[i];
				size_t have_chars = 0;

				switch (ch) {
				case '\r':
					/* we have to do a 4 char lookup */
					have_chars = b.used - i - 1;

					if (have_chars >= 4) {
						/* all chars are in this buffer */

						if (0 == strncmp(b.ptr + i, "\r\n\r\n", 4)) {
							/* found */
							last_chunk = c;
							last_offset = i + 4;

							break;
						}
					} else {
						chunk *lookahead_chunk = c->next;
						size_t missing_chars;
						/* looks like the following chars are not in the same chunk */

						missing_chars = 4 - have_chars;

						if (lookahead_chunk && lookahead_chunk->type == MEM_CHUNK) {
							/* is the chunk long enough to contain the other chars ? */

							if (lookahead_chunk->mem->used > missing_chars) {
								if (0 == strncmp(b.ptr + i, "\r\n\r\n", have_chars) &&
								    0 == strncmp(lookahead_chunk->mem->ptr, "\r\n\r\n" + have_chars, missing_chars)) {

									last_chunk = lookahead_chunk;
									last_offset = missing_chars;

									break;
								}
							} else {
								/* a splited \r \n */
								break;
							}
						}
					}

					break;
				}
			}
		}

		/* found */
		if (last_chunk) {
			buffer_reset(con->request.request);

			for (c = cq->first; c; c = c->next) {
				buffer b;

				b.ptr = c->mem->ptr + c->offset;
				b.used = c->mem->used - c->offset;

				if (c == last_chunk) {
					b.used = last_offset + 1;
				}

				buffer_append_string_buffer(con->request.request, &b);

				if (c == last_chunk) {
					c->offset += last_offset;

					break;
				} else {
					/* the whole packet was copied */
					c->offset = c->mem->used - 1;
				}
			}

			connection_set_state(srv, con, CON_STATE_REQUEST_END);
		} else if (chunkqueue_length(cq) > 64 * 1024) {
		  log_error_write(srv, __FILE__, __LINE__, "sdd", "oversized request-header", con->http_status, con->file_finished);

			con->http_status = 414; /* Request-URI too large */
			con->keep_alive = 0;
			connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
		}
		break;
	case CON_STATE_READ_POST:
		for (c = cq->first; c && (dst_cq->bytes_in != (off_t)con->request.content_length); c = c->next) {
			off_t weWant, weHave, toRead;

			weWant = con->request.content_length - dst_cq->bytes_in;

			assert(c->mem->used);

			weHave = c->mem->used - c->offset - 1;

			toRead = weHave > weWant ? weWant : weHave;

			buffer *b;

			b = chunkqueue_get_append_buffer(dst_cq);
			buffer_copy_string_len(b, c->mem->ptr + c->offset, toRead);
			c->offset += toRead;
			dst_cq->bytes_in += toRead;
		}
		/* Content is ready */
		if (dst_cq->bytes_in == (off_t)con->request.content_length ||
                    dst_cq->bytes_in - dst_cq->bytes_out > srv->srvconf.max_request_size * 1024) {
                    connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
		}

		break;
	default: break;
	}

	/* the connection got closed and we didn't got enough data to leave one of the READ states
	 * the only way is to leave here */
	if (is_closed && ostate == con->state) {
	        log_error_write(srv, __FILE__, __LINE__, "sdd", "Connection got closed, not enough data to leave one of the READ states", is_closed, ostate, con->state);
		connection_set_state(srv, con, CON_STATE_ERROR);
	}

	chunkqueue_remove_finished_chunks(cq);

	return 0;
}
Пример #20
0
static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, const char **l, size_t n, stat_cache_entry *sce) {
	size_t i, ssicmd = 0;
	char buf[255];
	buffer *b = NULL;

	struct {
		const char *var;
		enum { SSI_UNSET, SSI_ECHO, SSI_FSIZE, SSI_INCLUDE, SSI_FLASTMOD,
				SSI_CONFIG, SSI_PRINTENV, SSI_SET, SSI_IF, SSI_ELIF,
				SSI_ELSE, SSI_ENDIF, SSI_EXEC } type;
	} ssicmds[] = {
		{ "echo",     SSI_ECHO },
		{ "include",  SSI_INCLUDE },
		{ "flastmod", SSI_FLASTMOD },
		{ "fsize",    SSI_FSIZE },
		{ "config",   SSI_CONFIG },
		{ "printenv", SSI_PRINTENV },
		{ "set",      SSI_SET },
		{ "if",       SSI_IF },
		{ "elif",     SSI_ELIF },
		{ "endif",    SSI_ENDIF },
		{ "else",     SSI_ELSE },
		{ "exec",     SSI_EXEC },

		{ NULL, SSI_UNSET }
	};

	for (i = 0; ssicmds[i].var; i++) {
		if (0 == strcmp(l[1], ssicmds[i].var)) {
			ssicmd = ssicmds[i].type;
			break;
		}
	}

	switch(ssicmd) {
	case SSI_ECHO: {
		/* echo */
		int var = 0;
		/* int enc = 0; */
		const char *var_val = NULL;

		struct {
			const char *var;
			enum { SSI_ECHO_UNSET, SSI_ECHO_DATE_GMT, SSI_ECHO_DATE_LOCAL, SSI_ECHO_DOCUMENT_NAME, SSI_ECHO_DOCUMENT_URI,
					SSI_ECHO_LAST_MODIFIED, SSI_ECHO_USER_NAME } type;
		} echovars[] = {
			{ "DATE_GMT",      SSI_ECHO_DATE_GMT },
			{ "DATE_LOCAL",    SSI_ECHO_DATE_LOCAL },
			{ "DOCUMENT_NAME", SSI_ECHO_DOCUMENT_NAME },
			{ "DOCUMENT_URI",  SSI_ECHO_DOCUMENT_URI },
			{ "LAST_MODIFIED", SSI_ECHO_LAST_MODIFIED },
			{ "USER_NAME",     SSI_ECHO_USER_NAME },

			{ NULL, SSI_ECHO_UNSET }
		};

/*
		struct {
			const char *var;
			enum { SSI_ENC_UNSET, SSI_ENC_URL, SSI_ENC_NONE, SSI_ENC_ENTITY } type;
		} encvars[] = {
			{ "url",          SSI_ENC_URL },
			{ "none",         SSI_ENC_NONE },
			{ "entity",       SSI_ENC_ENTITY },

			{ NULL, SSI_ENC_UNSET }
		};
*/

		for (i = 2; i < n; i += 2) {
			if (0 == strcmp(l[i], "var")) {
				int j;

				var_val = l[i+1];

				for (j = 0; echovars[j].var; j++) {
					if (0 == strcmp(l[i+1], echovars[j].var)) {
						var = echovars[j].type;
						break;
					}
				}
			} else if (0 == strcmp(l[i], "encoding")) {
/*
				int j;

				for (j = 0; encvars[j].var; j++) {
					if (0 == strcmp(l[i+1], encvars[j].var)) {
						enc = encvars[j].type;
						break;
					}
				}
*/
			} else {
				log_error_write(srv, __FILE__, __LINE__, "sss",
						"ssi: unknow attribute for ",
						l[1], l[i]);
			}
		}

		if (p->if_is_false) break;

		if (!var_val) {
			log_error_write(srv, __FILE__, __LINE__, "sss",
					"ssi: ",
					l[1], "var is missing");
			break;
		}

		switch(var) {
		case SSI_ECHO_USER_NAME: {
			struct passwd *pw;

			b = chunkqueue_get_append_buffer(con->write_queue);
#ifdef HAVE_PWD_H
			if (NULL == (pw = getpwuid(sce->st.st_uid))) {
				buffer_copy_long(b, sce->st.st_uid);
			} else {
				buffer_copy_string(b, pw->pw_name);
			}
#else
			buffer_copy_long(b, sce->st.st_uid);
#endif
			break;
		}
		case SSI_ECHO_LAST_MODIFIED:	{
			time_t t = sce->st.st_mtime;

			b = chunkqueue_get_append_buffer(con->write_queue);
			if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) {
				buffer_copy_string_len(b, CONST_STR_LEN("(none)"));
			} else {
				buffer_copy_string(b, buf);
			}
			break;
		}
		case SSI_ECHO_DATE_LOCAL: {
			time_t t = time(NULL);

			b = chunkqueue_get_append_buffer(con->write_queue);
			if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) {
				buffer_copy_string_len(b, CONST_STR_LEN("(none)"));
			} else {
				buffer_copy_string(b, buf);
			}
			break;
		}
		case SSI_ECHO_DATE_GMT: {
			time_t t = time(NULL);

			b = chunkqueue_get_append_buffer(con->write_queue);
			if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, gmtime(&t))) {
				buffer_copy_string_len(b, CONST_STR_LEN("(none)"));
			} else {
				buffer_copy_string(b, buf);
			}
			break;
		}
		case SSI_ECHO_DOCUMENT_NAME: {
			char *sl;

			b = chunkqueue_get_append_buffer(con->write_queue);
			if (NULL == (sl = strrchr(con->physical.path->ptr, '/'))) {
				buffer_copy_string_buffer(b, con->physical.path);
			} else {
				buffer_copy_string(b, sl + 1);
			}
			break;
		}
		case SSI_ECHO_DOCUMENT_URI: {
			b = chunkqueue_get_append_buffer(con->write_queue);
			buffer_copy_string_buffer(b, con->uri.path);
			break;
		}
		default: {
			data_string *ds;
			/* check if it is a cgi-var */

			b = chunkqueue_get_append_buffer(con->write_queue);

			if (NULL != (ds = (data_string *)array_get_element(p->ssi_cgi_env, var_val))) {
				buffer_copy_string_buffer(b, ds->value);
			} else {
				buffer_copy_string_len(b, CONST_STR_LEN("(none)"));
			}

			break;
		}
		}
		break;
	}
	case SSI_INCLUDE:
	case SSI_FLASTMOD:
	case SSI_FSIZE: {
		const char * file_path = NULL, *virt_path = NULL;
		struct stat st;
		char *sl;

		for (i = 2; i < n; i += 2) {
			if (0 == strcmp(l[i], "file")) {
				file_path = l[i+1];
			} else if (0 == strcmp(l[i], "virtual")) {
				virt_path = l[i+1];
			} else {
				log_error_write(srv, __FILE__, __LINE__, "sss",
						"ssi: unknow attribute for ",
						l[1], l[i]);
			}
		}

		if (!file_path && !virt_path) {
			log_error_write(srv, __FILE__, __LINE__, "sss",
					"ssi: ",
					l[1], "file or virtual are missing");
			break;
		}

		if (file_path && virt_path) {
			log_error_write(srv, __FILE__, __LINE__, "sss",
					"ssi: ",
					l[1], "only one of file and virtual is allowed here");
			break;
		}


		if (p->if_is_false) break;

		if (file_path) {
			/* current doc-root */
			if (NULL == (sl = strrchr(con->physical.path->ptr, '/'))) {
				buffer_copy_string_len(p->stat_fn, CONST_STR_LEN("/"));
			} else {
				buffer_copy_string_len(p->stat_fn, con->physical.path->ptr, sl - con->physical.path->ptr + 1);
			}

			buffer_copy_string(srv->tmp_buf, file_path);
			buffer_urldecode_path(srv->tmp_buf);
			buffer_path_simplify(srv->tmp_buf, srv->tmp_buf);
			buffer_append_string_buffer(p->stat_fn, srv->tmp_buf);
		} else {
			/* virtual */

			if (virt_path[0] == '/') {
				buffer_copy_string(p->stat_fn, virt_path);
			} else {
				/* there is always a / */
				sl = strrchr(con->uri.path->ptr, '/');

				buffer_copy_string_len(p->stat_fn, con->uri.path->ptr, sl - con->uri.path->ptr + 1);
				buffer_append_string(p->stat_fn, virt_path);
			}

			buffer_urldecode_path(p->stat_fn);
			buffer_path_simplify(srv->tmp_buf, p->stat_fn);

			/* we have an uri */

			buffer_copy_string_buffer(p->stat_fn, con->physical.doc_root);
			buffer_append_string_buffer(p->stat_fn, srv->tmp_buf);
		}

		if (0 == stat(p->stat_fn->ptr, &st)) {
			time_t t = st.st_mtime;

			switch (ssicmd) {
			case SSI_FSIZE:
				b = chunkqueue_get_append_buffer(con->write_queue);
				if (p->sizefmt) {
					int j = 0;
					const char *abr[] = { " B", " kB", " MB", " GB", " TB", NULL };

					off_t s = st.st_size;

					for (j = 0; s > 1024 && abr[j+1]; s /= 1024, j++);

					buffer_copy_off_t(b, s);
					buffer_append_string(b, abr[j]);
				} else {
					buffer_copy_off_t(b, st.st_size);
				}
				break;
			case SSI_FLASTMOD:
				b = chunkqueue_get_append_buffer(con->write_queue);
				if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) {
					buffer_copy_string_len(b, CONST_STR_LEN("(none)"));
				} else {
					buffer_copy_string(b, buf);
				}
				break;
			case SSI_INCLUDE:
				chunkqueue_append_file(con->write_queue, p->stat_fn, 0, st.st_size);

				/* Keep the newest mtime of included files */
				if (st.st_mtime > include_file_last_mtime)
				  include_file_last_mtime = st.st_mtime;

				break;
			}
		} else {
			log_error_write(srv, __FILE__, __LINE__, "sbs",
					"ssi: stating failed ",
					p->stat_fn, strerror(errno));
		}
		break;
	}
	case SSI_SET: {
		const char *key = NULL, *val = NULL;
		for (i = 2; i < n; i += 2) {
			if (0 == strcmp(l[i], "var")) {
				key = l[i+1];
			} else if (0 == strcmp(l[i], "value")) {
				val = l[i+1];
			} else {
				log_error_write(srv, __FILE__, __LINE__, "sss",
						"ssi: unknow attribute for ",
						l[1], l[i]);
			}
		}

		if (p->if_is_false) break;

		if (key && val) {
			data_string *ds;

			if (NULL == (ds = (data_string *)array_get_unused_element(p->ssi_vars, TYPE_STRING))) {
				ds = data_string_init();
			}
			buffer_copy_string(ds->key,   key);
			buffer_copy_string(ds->value, val);

			array_insert_unique(p->ssi_vars, (data_unset *)ds);
		} else {
			log_error_write(srv, __FILE__, __LINE__, "sss",
					"ssi: var and value have to be set in",
					l[0], l[1]);
		}
		break;
	}
	case SSI_CONFIG:
		if (p->if_is_false) break;

		for (i = 2; i < n; i += 2) {
			if (0 == strcmp(l[i], "timefmt")) {
				buffer_copy_string(p->timefmt, l[i+1]);
			} else if (0 == strcmp(l[i], "sizefmt")) {
				if (0 == strcmp(l[i+1], "abbrev")) {
					p->sizefmt = 1;
				} else if (0 == strcmp(l[i+1], "abbrev")) {
					p->sizefmt = 0;
				} else {
					log_error_write(srv, __FILE__, __LINE__, "sssss",
							"ssi: unknow value for attribute '",
							l[i],
							"' for ",
							l[1], l[i+1]);
				}
			} else {
				log_error_write(srv, __FILE__, __LINE__, "sss",
						"ssi: unknow attribute for ",
						l[1], l[i]);
			}
		}
		break;
	case SSI_PRINTENV:
		if (p->if_is_false) break;

		b = chunkqueue_get_append_buffer(con->write_queue);
		for (i = 0; i < p->ssi_vars->used; i++) {
			data_string *ds = (data_string *)p->ssi_vars->data[p->ssi_vars->sorted[i]];

			buffer_append_string_buffer(b, ds->key);
			buffer_append_string_len(b, CONST_STR_LEN("="));
			buffer_append_string_encoded(b, CONST_BUF_LEN(ds->value), ENCODING_MINIMAL_XML);
			buffer_append_string_len(b, CONST_STR_LEN("\n"));
		}
		for (i = 0; i < p->ssi_cgi_env->used; i++) {
			data_string *ds = (data_string *)p->ssi_cgi_env->data[p->ssi_cgi_env->sorted[i]];

			buffer_append_string_buffer(b, ds->key);
			buffer_append_string_len(b, CONST_STR_LEN("="));
			buffer_append_string_encoded(b, CONST_BUF_LEN(ds->value), ENCODING_MINIMAL_XML);
			buffer_append_string_len(b, CONST_STR_LEN("\n"));
		}

		break;
	case SSI_EXEC: {
		const char *cmd = NULL;
		pid_t pid;
		int from_exec_fds[2];

		for (i = 2; i < n; i += 2) {
			if (0 == strcmp(l[i], "cmd")) {
				cmd = l[i+1];
			} else {
				log_error_write(srv, __FILE__, __LINE__, "sss",
						"ssi: unknow attribute for ",
						l[1], l[i]);
			}
		}

		if (p->if_is_false) break;

		/* create a return pipe and send output to the html-page
		 *
		 * as exec is assumed evil it is implemented synchronously
		 */

		if (!cmd) break;
#ifdef HAVE_FORK
		if (pipe(from_exec_fds)) {
			log_error_write(srv, __FILE__, __LINE__, "ss",
					"pipe failed: ", strerror(errno));
			return -1;
		}

		/* fork, execve */
		switch (pid = fork()) {
		case 0: {
			/* move stdout to from_rrdtool_fd[1] */
			close(STDOUT_FILENO);
			dup2(from_exec_fds[1], STDOUT_FILENO);
			close(from_exec_fds[1]);
			/* not needed */
			close(from_exec_fds[0]);

			/* close stdin */
			close(STDIN_FILENO);

			execl("/bin/sh", "sh", "-c", cmd, (char *)NULL);

			log_error_write(srv, __FILE__, __LINE__, "sss", "spawing exec failed:", strerror(errno), cmd);

			/* */
			SEGFAULT();
			break;
		}
		case -1:
			/* error */
			log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno));
			break;
		default: {
			/* father */
			int status;
			ssize_t r;
			int was_interrupted = 0;

			close(from_exec_fds[1]);

			/* wait for the client to end */

			/*
			 * OpenBSD and Solaris send a EINTR on SIGCHILD even if we ignore it
			 */
			do {
				if (-1 == waitpid(pid, &status, 0)) {
					if (errno == EINTR) {
						was_interrupted++;
					} else {
						was_interrupted = 0;
						log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed:", strerror(errno));
					}
				} else if (WIFEXITED(status)) {
					int toread;
					/* read everything from client and paste it into the output */
					was_interrupted = 0;
	
					while(1) {
						if (ioctl(from_exec_fds[0], FIONREAD, &toread)) {
							log_error_write(srv, __FILE__, __LINE__, "s",
								"unexpected end-of-file (perhaps the ssi-exec process died)");
							return -1;
						}
	
						if (toread > 0) {
							b = chunkqueue_get_append_buffer(con->write_queue);
	
							buffer_prepare_copy(b, toread + 1);
	
							if ((r = read(from_exec_fds[0], b->ptr, b->size - 1)) < 0) {
								/* read failed */
								break;
							} else {
								b->used = r;
								b->ptr[b->used++] = '\0';
							}
						} else {
							break;
						}
					}
				} else {
					was_interrupted = 0;
					log_error_write(srv, __FILE__, __LINE__, "s", "process exited abnormally");
				}
			} while (was_interrupted > 0 && was_interrupted < 4); /* if waitpid() gets interrupted, retry, but max 4 times */

			close(from_exec_fds[0]);

			break;
		}
		}
#else

		return -1;
#endif

		break;
	}
	case SSI_IF: {
		const char *expr = NULL;

		for (i = 2; i < n; i += 2) {
			if (0 == strcmp(l[i], "expr")) {
				expr = l[i+1];
			} else {
				log_error_write(srv, __FILE__, __LINE__, "sss",
						"ssi: unknow attribute for ",
						l[1], l[i]);
			}
		}

		if (!expr) {
			log_error_write(srv, __FILE__, __LINE__, "sss",
					"ssi: ",
					l[1], "expr missing");
			break;
		}

		if ((!p->if_is_false) &&
		    ((p->if_is_false_level == 0) ||
		     (p->if_level < p->if_is_false_level))) {
			switch (ssi_eval_expr(srv, con, p, expr)) {
			case -1:
			case 0:
				p->if_is_false = 1;
				p->if_is_false_level = p->if_level;
				break;
			case 1:
				p->if_is_false = 0;
				break;
			}
		}

		p->if_level++;

		break;
	}
	case SSI_ELSE:
		p->if_level--;

		if (p->if_is_false) {
			if ((p->if_level == p->if_is_false_level) &&
			    (p->if_is_false_endif == 0)) {
				p->if_is_false = 0;
			}
		} else {
			p->if_is_false = 1;

			p->if_is_false_level = p->if_level;
		}
		p->if_level++;

		break;
	case SSI_ELIF: {
		const char *expr = NULL;
		for (i = 2; i < n; i += 2) {
			if (0 == strcmp(l[i], "expr")) {
				expr = l[i+1];
			} else {
				log_error_write(srv, __FILE__, __LINE__, "sss",
						"ssi: unknow attribute for ",
						l[1], l[i]);
			}
		}

		if (!expr) {
			log_error_write(srv, __FILE__, __LINE__, "sss",
					"ssi: ",
					l[1], "expr missing");
			break;
		}

		p->if_level--;

		if (p->if_level == p->if_is_false_level) {
			if ((p->if_is_false) &&
			    (p->if_is_false_endif == 0)) {
				switch (ssi_eval_expr(srv, con, p, expr)) {
				case -1:
				case 0:
					p->if_is_false = 1;
					p->if_is_false_level = p->if_level;
					break;
				case 1:
					p->if_is_false = 0;
					break;
				}
			} else {
				p->if_is_false = 1;
				p->if_is_false_level = p->if_level;
				p->if_is_false_endif = 1;
			}
		}

		p->if_level++;

		break;
	}
	case SSI_ENDIF:
		p->if_level--;

		if (p->if_level == p->if_is_false_level) {
			p->if_is_false = 0;
			p->if_is_false_endif = 0;
		}

		break;
	default:
		log_error_write(srv, __FILE__, __LINE__, "ss",
				"ssi: unknow ssi-command:",
				l[1]);
		break;
	}

	return 0;

}
Пример #21
0
static handler_t proxy_http_parse_chunked_stream(server *srv, proxy_session *sess, chunkqueue *in, chunkqueue *out) {
	protocol_state_data *data = (protocol_state_data *)sess->proxy_con->protocol_data;
	char *err = NULL;
	off_t we_have = 0, we_want = 0;
	off_t chunk_len = 0;
	off_t offset = 0;
	buffer *b;
	chunk *c;
	char ch = '\0';
	int finished = 0;

	UNUSED(srv);

	for (c = in->first; c && !finished;) {
		if(c->mem->used == 0) {
			c = c->next;
			continue;
		}
		switch(data->chunk_parse_state) {
		case HTTP_CHUNK_LEN:
			/* parse chunk len. */
			for(offset = c->offset; (size_t)(offset) < (c->mem->used - 1) ; offset++) {
				ch = c->mem->ptr[offset];
				if(!light_isxdigit(ch)) break;
			}
			if(offset > c->offset) {
				buffer_append_string_len(data->buf, (c->mem->ptr + c->offset), offset - c->offset);
				in->bytes_out += (offset - c->offset);
				c->offset = offset;
			}
			if (!(ch == ' ' || ch == '\r' || ch == ';')) {
				if (ch == '\0') {
					/* get next chunk from queue */
					break;
				}
				/* protocol error.  bad http-chunk len */
				return HANDLER_ERROR;
			}
			data->chunk_len = strtol(BUF_STR(data->buf), &err, 16);
			data->chunk_offset = 0;
			buffer_reset(data->buf);
			data->chunk_parse_state = HTTP_CHUNK_EXTENSION;
		case HTTP_CHUNK_EXTENSION:
			/* find CRLF.  discard chunk-extension */
			for(ch = 0; (size_t)(c->offset) < (c->mem->used - 1) && ch != '\n' ;) {
				ch = c->mem->ptr[c->offset];
				c->offset++;
				in->bytes_out++;
			}
			if(ch != '\n') {
				/* get next chunk from queue */
				break;
			}
			if(data->chunk_len > 0) {
				data->chunk_parse_state = HTTP_CHUNK_DATA;
			} else {
				data->chunk_parse_state = HTTP_CHUNK_END;
			}
		case HTTP_CHUNK_DATA:
			chunk_len = data->chunk_len - data->chunk_offset;
			/* copy chunk_len bytes from in queue to out queue. */
			we_have = c->mem->used - c->offset - 1;
			we_want = chunk_len > we_have ? we_have : chunk_len;

			if (c->offset == 0 && we_want == we_have) {
				/* we are copying the whole buffer, just steal it */
				chunkqueue_steal_chunk(out, c);
				/* c is an empty chunk now */
			} else {
				b = chunkqueue_get_append_buffer(out);
				buffer_copy_string_len(b, c->mem->ptr + c->offset, we_want);
				c->offset += we_want;
			}

			chunk_len -= we_want;
			out->bytes_in += we_want;
			in->bytes_out += we_want;
			data->chunk_offset += we_want;
			if(chunk_len > 0) {
				/* get next chunk from queue */
				break;
			}
			data->chunk_offset = 0;
			data->chunk_parse_state = HTTP_CHUNK_END;
		case HTTP_CHUNK_END:
			/* discard CRLF.*/
			for(ch = 0; c->mem->used > 0 && (size_t)(c->offset) < (c->mem->used - 1) && ch != '\n' ;) {
				ch = c->mem->ptr[c->offset];
				c->offset++;
				in->bytes_out++;
			}
			if(ch != '\n') {
				/* get next chunk from queue */
				break;
			}
			/* final chunk */
			if(data->chunk_len == 0) {
				finished = 1;
			}
			/* finished http-chunk.  reset and parse next chunk. */
			protocol_state_data_reset(data);
			break;
		}
		if((size_t)(c->offset) == c->mem->used - 1) {
			c = c->next;
		}
	}
	chunkqueue_remove_finished_chunks(in);
	if (finished) {
		sess->is_request_finished = 1;
		return HANDLER_FINISHED;
	}
	/* ran out of data. */
	return HANDLER_GO_ON;
}
Пример #22
0
/* 0: everything ok, -1: error, -2: con closed */
static int connection_handle_read(server *srv, connection *con) {
	int len;
	buffer *b;
	int toread, read_offset;

	if (con->conf.is_ssl) {
		return connection_handle_read_ssl(srv, con);
	}

	b = (NULL != con->read_queue->last) ? con->read_queue->last->mem : NULL;

	/* default size for chunks is 4kb; only use bigger chunks if FIONREAD tells
	 *  us more than 4kb is available
	 * if FIONREAD doesn't signal a big chunk we fill the previous buffer
	 *  if it has >= 1kb free
	 */
#if defined(__WIN32)
	if (NULL == b || b->size - b->used < 1024) {
		b = chunkqueue_get_append_buffer(con->read_queue);
		buffer_prepare_copy(b, 4 * 1024);
	}

	read_offset = (b->used == 0) ? 0 : b->used - 1;
	len = recv(con->fd, b->ptr + read_offset, b->size - 1 - read_offset, 0);
#else
#ifdef HAVE_LIBMTCP
	/* toread = MAX_READ_LIMIT; */
	if (mtcp_socket_ioctl(srv->mctx, con->fd, FIONREAD, &toread) 
	    || toread == 0 || toread <= 4*1024) {
#else
	if (ioctl(con->fd, FIONREAD, &toread) || toread == 0 || toread <= 4*1024) {
#endif
		if (NULL == b || b->size - b->used < 1024) {
			b = chunkqueue_get_append_buffer(con->read_queue);
			buffer_prepare_copy(b, 4 * 1024);
		}
	} else {
		if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT;
		b = chunkqueue_get_append_buffer(con->read_queue);
		buffer_prepare_copy(b, toread + 1);
	}

	read_offset = (b->used == 0) ? 0 : b->used - 1;
#ifdef HAVE_LIBMTCP
	len = mtcp_read(srv->mctx, con->fd, b->ptr + read_offset, 
			b->size - 1 - read_offset);
#else
	len = read(con->fd, b->ptr + read_offset, b->size - 1 - read_offset);
#endif
#endif

	if (len < 0) {
		con->is_readable = 0;

		if (errno == EAGAIN) return 0;
		if (errno == EINTR) {
			/* we have been interrupted before we could read */
			con->is_readable = 1;
			return 0;
		}

		if (errno != ECONNRESET) {
			/* expected for keep-alive */
			log_error_write(srv, __FILE__, __LINE__, "ssd", "connection closed - read failed: ", strerror(errno), errno);
		}

		connection_set_state(srv, con, CON_STATE_ERROR);

		return -1;
	} else if (len == 0) {
		con->is_readable = 0;
		/* the other end close the connection -> KEEP-ALIVE */

		/* pipelining */

		return -2;
	} else if ((size_t)len < b->size - 1) {
		/* we got less then expected, wait for the next fd-event */

		con->is_readable = 0;
	}

	if (b->used > 0) b->used--;
	b->used += len;
	b->ptr[b->used++] = '\0';

	con->bytes_read += len;
#if 0
	dump_packet(b->ptr, len);
#endif

	return 0;
}

static int connection_handle_write_prepare(server *srv, connection *con) {
	if (con->mode == DIRECT) {
		/* static files */
		switch(con->request.http_method) {
		case HTTP_METHOD_GET:
		case HTTP_METHOD_POST:
		case HTTP_METHOD_HEAD:
		case HTTP_METHOD_PUT:
		case HTTP_METHOD_PATCH:
		case HTTP_METHOD_MKCOL:
		case HTTP_METHOD_DELETE:
		case HTTP_METHOD_COPY:
		case HTTP_METHOD_MOVE:
		case HTTP_METHOD_PROPFIND:
		case HTTP_METHOD_PROPPATCH:
		case HTTP_METHOD_LOCK:
		case HTTP_METHOD_UNLOCK:
			break;
		case HTTP_METHOD_OPTIONS:
			/*
			 * 400 is coming from the request-parser BEFORE uri.path is set
			 * 403 is from the response handler when noone else catched it
			 *
			 * */
			if ((!con->http_status || con->http_status == 200) && con->uri.path->used &&
			    con->uri.path->ptr[0] != '*') {
				response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST"));

				con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED;
				con->parsed_response &= ~HTTP_CONTENT_LENGTH;

				con->http_status = 200;
				con->file_finished = 1;

				chunkqueue_reset(con->write_queue);
			}
			break;
		default:
			switch(con->http_status) {
			case 400: /* bad request */
			case 401: /* authorization required */
			case 414: /* overload request header */
			case 505: /* unknown protocol */
			case 207: /* this was webdav */
				break;
			default:
				con->http_status = 501;
				break;
			}
			break;
		}
	}

	if (con->http_status == 0) {
		con->http_status = 403;
	}

	switch(con->http_status) {
	case 204: /* class: header only */
	case 205:
	case 304:
		/* disable chunked encoding again as we have no body */
		con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED;
		con->parsed_response &= ~HTTP_CONTENT_LENGTH;
		chunkqueue_reset(con->write_queue);

		con->file_finished = 1;
		break;
	default: /* class: header + body */
		if (con->mode != DIRECT) break;

		/* only custom body for 4xx and 5xx */
		if (con->http_status < 400 || con->http_status >= 600) break;

		con->file_finished = 0;

		buffer_reset(con->physical.path);

		/* try to send static errorfile */
		if (!buffer_is_empty(con->conf.errorfile_prefix)) {
			stat_cache_entry *sce = NULL;

			buffer_copy_string_buffer(con->physical.path, con->conf.errorfile_prefix);
			buffer_append_long(con->physical.path, con->http_status);
			buffer_append_string_len(con->physical.path, CONST_STR_LEN(".html"));

			if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
				con->file_finished = 1;

				http_chunk_append_file(srv, con, con->physical.path, 0, sce->st.st_size);
				response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type));
			}
		}

		if (!con->file_finished) {
			buffer *b;

			buffer_reset(con->physical.path);

			con->file_finished = 1;
			b = chunkqueue_get_append_buffer(con->write_queue);

			/* build default error-page */
			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>"));
			buffer_append_long(b, con->http_status);
			buffer_append_string_len(b, CONST_STR_LEN(" - "));
			buffer_append_string(b, get_http_status_name(con->http_status));

			buffer_append_string_len(b, CONST_STR_LEN(
					     "</title>\n"
					     " </head>\n"
					     " <body>\n"
					     "  <h1>"));
			buffer_append_long(b, con->http_status);
			buffer_append_string_len(b, CONST_STR_LEN(" - "));
			buffer_append_string(b, get_http_status_name(con->http_status));

			buffer_append_string_len(b, CONST_STR_LEN("</h1>\n"
					     " </body>\n"
					     "</html>\n"
					     ));

			response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
		}
		break;
	}

	if (con->file_finished) {
		/* we have all the content and chunked encoding is not used, set a content-length */

		if ((!(con->parsed_response & HTTP_CONTENT_LENGTH)) &&
		    (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0) {
			off_t qlen = chunkqueue_length(con->write_queue);

			/**
			 * The Content-Length header only can be sent if we have content:
			 * - HEAD doesn't have a content-body (but have a content-length)
			 * - 1xx, 204 and 304 don't have a content-body (RFC 2616 Section 4.3)
			 *
			 * Otherwise generate a Content-Length header as chunked encoding is not 
			 * available
			 */
			if ((con->http_status >= 100 && con->http_status < 200) ||
			    con->http_status == 204 ||
			    con->http_status == 304) {
				data_string *ds;
				/* no Content-Body, no Content-Length */
				if (NULL != (ds = (data_string*) array_get_element(con->response.headers, "Content-Length"))) {
					buffer_reset(ds->value); /* Headers with empty values are ignored for output */
				}
			} else if (qlen > 0 || con->request.http_method != HTTP_METHOD_HEAD) {
				/* qlen = 0 is important for Redirects (301, ...) as they MAY have
				 * a content. Browsers are waiting for a Content otherwise
				 */
				buffer_copy_off_t(srv->tmp_buf, qlen);

				response_header_overwrite(srv, con, CONST_STR_LEN("Content-Length"), CONST_BUF_LEN(srv->tmp_buf));
			}
		}
	} else {
		/**
		 * the file isn't finished yet, but we have all headers
		 *
		 * to get keep-alive we either need:
		 * - Content-Length: ... (HTTP/1.0 and HTTP/1.0) or
		 * - Transfer-Encoding: chunked (HTTP/1.1)
		 */

		if (((con->parsed_response & HTTP_CONTENT_LENGTH) == 0) &&
		    ((con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0)) {
			con->keep_alive = 0;
		}

		/**
		 * if the backend sent a Connection: close, follow the wish
		 *
		 * NOTE: if the backend sent Connection: Keep-Alive, but no Content-Length, we
		 * will close the connection. That's fine. We can always decide the close 
		 * the connection
		 *
		 * FIXME: to be nice we should remove the Connection: ... 
		 */
		if (con->parsed_response & HTTP_CONNECTION) {
			/* a subrequest disable keep-alive although the client wanted it */
			if (con->keep_alive && !con->response.keep_alive) {
				con->keep_alive = 0;
			}
		}
	}

	if (con->request.http_method == HTTP_METHOD_HEAD) {
		/**
		 * a HEAD request has the same as a GET 
		 * without the content
		 */
		con->file_finished = 1;

		chunkqueue_reset(con->write_queue);
		con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED;
	}

	http_response_write_header(srv, con);

	return 0;
}

static int connection_handle_write(server *srv, connection *con) {
	switch(network_write_chunkqueue(srv, con, con->write_queue, MAX_WRITE_LIMIT)) {
	case 0:
		con->write_request_ts = srv->cur_ts;
		if (con->file_finished) {
			connection_set_state(srv, con, CON_STATE_RESPONSE_END);
			joblist_append(srv, con);
		}
		break;
	case -1: /* error on our side */
		log_error_write(srv, __FILE__, __LINE__, "sd",
				"connection closed: write failed on fd", con->fd);
		connection_set_state(srv, con, CON_STATE_ERROR);
		joblist_append(srv, con);
		break;
	case -2: /* remote close */
		connection_set_state(srv, con, CON_STATE_ERROR);
		joblist_append(srv, con);
		break;
	case 1:
		con->write_request_ts = srv->cur_ts;
		con->is_writable = 0;

		/* not finished yet -> WRITE */
		break;
	}

	return 0;
}



connection *connection_init(server *srv) {
	connection *con;

	UNUSED(srv);

	con = calloc(1, sizeof(*con));

	con->fd = 0;
	con->ndx = -1;
	con->fde_ndx = -1;
	con->bytes_written = 0;
	con->bytes_read = 0;
	con->bytes_header = 0;
	con->loops_per_request = 0;

#define CLEAN(x) \
	con->x = buffer_init();

	CLEAN(request.uri);
	CLEAN(request.request_line);
	CLEAN(request.request);
	CLEAN(request.pathinfo);

	CLEAN(request.orig_uri);

	CLEAN(uri.scheme);
	CLEAN(uri.authority);
	CLEAN(uri.path);
	CLEAN(uri.path_raw);
	CLEAN(uri.query);

	CLEAN(physical.doc_root);
	CLEAN(physical.path);
	CLEAN(physical.basedir);
	CLEAN(physical.rel_path);
	CLEAN(physical.etag);
	CLEAN(parse_request);

	CLEAN(authed_user);
	CLEAN(server_name);
	CLEAN(error_handler);
	CLEAN(dst_addr_buf);
#if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT
	CLEAN(tlsext_server_name);
#endif

#undef CLEAN
	con->write_queue = chunkqueue_init();
	con->read_queue = chunkqueue_init();
	con->request_content_queue = chunkqueue_init();
	chunkqueue_set_tempdirs(con->request_content_queue, srv->srvconf.upload_tempdirs);

	con->request.headers      = array_init();
	con->response.headers     = array_init();
	con->environment     = array_init();

	/* init plugin specific connection structures */

	con->plugin_ctx = calloc(1, (srv->plugins.used + 1) * sizeof(void *));

	con->cond_cache = calloc(srv->config_context->used, sizeof(cond_cache_t));
	config_setup_connection(srv, con);

	return con;
}