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;
}
Beispiel #2
0
static handler_t mod_status_handle_server_statistics(server *srv, connection *con, void *p_d) {
	buffer *b;
	size_t i;
	array *st = srv->status;
	UNUSED(p_d);

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

		return HANDLER_FINISHED;
	}

	b = buffer_init();
	for (i = 0; i < st->used; i++) {
		size_t ndx = st->sorted[i];

		buffer_append_string_buffer(b, st->data[ndx]->key);
		buffer_append_string_len(b, CONST_STR_LEN(": "));
		buffer_append_int(b, ((data_integer *)(st->data[ndx]))->value);
		buffer_append_string_len(b, CONST_STR_LEN("\n"));
	}

	chunkqueue_append_buffer(con->write_queue, b);
	buffer_free(b);

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

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

	return HANDLER_FINISHED;
}
Beispiel #3
0
static handler_t mod_status_handle_server_status_text(server *srv, connection *con, void *p_d) {
	plugin_data *p = p_d;
	buffer *b = buffer_init();
	double avg;
	time_t ts;
	char buf[32];
	unsigned int k;
	unsigned int l;

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

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

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

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

	buffer_append_string_len(b, CONST_STR_LEN("IdleServers: "));
	buffer_append_int(b, srv->conns->size - srv->conns->used);
	buffer_append_string_len(b, CONST_STR_LEN("\n"));

	/* output scoreboard */
	buffer_append_string_len(b, CONST_STR_LEN("Scoreboard: "));
	for (k = 0; k < srv->conns->used; k++) {
		connection *c = srv->conns->ptr[k];
		const char *state = connection_get_short_state(c->state);
		buffer_append_string_len(b, state, 1);
	}
	for (l = 0; l < srv->conns->size - srv->conns->used; l++) {
		buffer_append_string_len(b, CONST_STR_LEN("_"));
	}
	buffer_append_string_len(b, CONST_STR_LEN("\n"));

	chunkqueue_append_buffer(con->write_queue, b);
	buffer_free(b);

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

	return 0;
}
Beispiel #4
0
static int
magnet_copy_response_header(server * srv, connection * con,
							plugin_data * p, lua_State * L)
{
	UNUSED(p);
	/**
	 * get the environment of the function
	 */

	lua_getfenv(L, -1);			/* -1 is the function */

	/*
	 * lighty.header 
	 */

	lua_getfield(L, -1, "lighty");	/* lighty.* from the env */
	assert(lua_istable(L, -1));

	lua_getfield(L, -1, "header");	/* lighty.header */
	if (lua_istable(L, -1))
	{
		/*
		 * header is found, and is a table 
		 */

		lua_pushnil(L);
		while (lua_next(L, -2) != 0)
		{
			if (lua_isstring(L, -1) && lua_isstring(L, -2))
			{
				const char *key, *val;
				size_t key_len, val_len;

				key = lua_tolstring(L, -2, &key_len);
				val = lua_tolstring(L, -1, &val_len);

				response_header_overwrite(srv, con, key, key_len, val, val_len);
			}

			lua_pop(L, 1);
		}
	}

	lua_pop(L, 1);				/* pop the header-table */
	lua_pop(L, 1);				/* pop the lighty-env */
	lua_pop(L, 1);				/* pop the function env */

	return 0;
}
Beispiel #5
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;
}
Beispiel #6
0
static int magnet_copy_response_header(server *srv, connection *con, lua_State *L, int lighty_table_ndx) {
	force_assert(lua_istable(L, lighty_table_ndx));

	lua_getfield(L, lighty_table_ndx, "header"); /* lighty.header */
	if (lua_istable(L, -1)) {
		/* header is found, and is a table */

		lua_pushnil(L);
		while (lua_next(L, -2) != 0) {
			if (lua_isstring(L, -1) && lua_isstring(L, -2)) {
				const_buffer key = magnet_checkconstbuffer(L, -2);
				const_buffer val = magnet_checkconstbuffer(L, -1);

				response_header_overwrite(srv, con, key.ptr, key.len, val.ptr, val.len);
			}

			lua_pop(L, 1);
		}
	}
	lua_pop(L, 1); /* pop lighty.header */

	return 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;
}
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;
}
Beispiel #9
0
int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) {
	lua_State *L;
	int ret = -1;
	buffer *b;

	b = buffer_init();
	/* push the lua file to the interpreter and see what happends */
	L = luaL_newstate();
	luaL_openlibs(L);

	/* register functions */
	lua_register(L, "md5", f_crypto_md5);
	lua_register(L, "file_mtime", f_file_mtime);
	lua_register(L, "file_isreg", f_file_isreg);
	lua_register(L, "file_isdir", f_file_isreg);
	lua_register(L, "dir_files", f_dir_files);

#ifdef USE_MEMCACHED
	lua_pushlightuserdata(L, p->conf.memc);
	lua_pushcclosure(L, f_memcache_get_long, 1);
	lua_setglobal(L, "memcache_get_long");

	lua_pushlightuserdata(L, p->conf.memc);
	lua_pushcclosure(L, f_memcache_get_string, 1);
	lua_setglobal(L, "memcache_get_string");

	lua_pushlightuserdata(L, p->conf.memc);
	lua_pushcclosure(L, f_memcache_exists, 1);
	lua_setglobal(L, "memcache_exists");
#endif

	/* register CGI environment */
	lua_newtable(L);
	{
		int header_tbl = lua_gettop(L);

		c_to_lua_push(L, header_tbl, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
		c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
		c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path));
		c_to_lua_push(L, header_tbl, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir));
		if (!buffer_string_is_empty(con->request.pathinfo)) {
			c_to_lua_push(L, header_tbl, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
		}

		c_to_lua_push(L, header_tbl, CONST_STR_LEN("CWD"), CONST_BUF_LEN(p->basedir));
		c_to_lua_push(L, header_tbl, CONST_STR_LEN("BASEURL"), CONST_BUF_LEN(p->baseurl));
	}
	lua_setglobal(L, "request");

	/* register GET parameter */
	lua_newtable(L);
	{
		int get_tbl = lua_gettop(L);

		buffer_copy_buffer(b, con->uri.query);
		cache_export_get_params(L, get_tbl, b);
		buffer_reset(b);
	}
	lua_setglobal(L, "get");

	/* 2 default constants */
	lua_pushinteger(L, 0);
	lua_setglobal(L, "CACHE_HIT");

	lua_pushinteger(L, 1);
	lua_setglobal(L, "CACHE_MISS");

	/* load lua program */
	ret = luaL_loadfile(L, fn->ptr);
	if (0 != ret) {
		log_error_write(srv, __FILE__, __LINE__, "sbsS",
			"failed loading cml_lua script",
			fn,
			":",
			lua_tostring(L, -1));
		goto error;
	}

	if (lua_pcall(L, 0, 1, 0)) {
		log_error_write(srv, __FILE__, __LINE__, "sbsS",
			"failed running cml_lua script",
			fn,
			":",
			lua_tostring(L, -1));
		goto error;
	}

	/* get return value */
	ret = (int)lua_tointeger(L, -1);
	lua_pop(L, 1);

	/* fetch the data from lua */
	lua_to_c_get_string(L, "trigger_handler", p->trigger_handler);

	if (0 == lua_to_c_get_string(L, "output_contenttype", b)) {
		response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(b));
	}

	if (ret == 0) {
		/* up to now it is a cache-hit, check if all files exist */

		int curelem;
		time_t mtime = 0;

		if (!lua_to_c_is_table(L, "output_include")) {
			log_error_write(srv, __FILE__, __LINE__, "s",
				"output_include is missing or not a table");
			ret = -1;

			goto error;
		}

		lua_getglobal(L, "output_include");
		curelem = lua_gettop(L);

		/* HOW-TO build a etag ?
		 * as we don't just have one file we have to take the stat()
		 * from all base files, merge them and build the etag from
		 * it later.
		 *
		 * The mtime of the content is the mtime of the freshest base file
		 *
		 * */

		lua_pushnil(L);  /* first key */
		while (lua_next(L, curelem) != 0) {
			/* key' is at index -2 and value' at index -1 */

			if (lua_isstring(L, -1)) {
				const char *s = lua_tostring(L, -1);
				struct stat st;
				int fd;

				/* the file is relative, make it absolute */
				if (s[0] != '/') {
					buffer_copy_buffer(b, p->basedir);
					buffer_append_string(b, lua_tostring(L, -1));
				} else {
					buffer_copy_string(b, lua_tostring(L, -1));
				}

				fd = stat_cache_open_rdonly_fstat(srv, con, b, &st);
				if (fd < 0) {
					/* stat failed */

					switch(errno) {
					case ENOENT:
						/* a file is missing, call the handler to generate it */
						if (!buffer_string_is_empty(p->trigger_handler)) {
							ret = 1; /* cache-miss */

							log_error_write(srv, __FILE__, __LINE__, "s",
									"a file is missing, calling handler");

							break;
						} else {
							/* handler not set -> 500 */
							ret = -1;

							log_error_write(srv, __FILE__, __LINE__, "s",
									"a file missing and no handler set");

							break;
						}
						break;
					default:
						break;
					}
				} else {
					chunkqueue_append_file_fd(con->write_queue, b, fd, 0, st.st_size);
					if (st.st_mtime > mtime) mtime = st.st_mtime;
				}
			} else {
				/* not a string */
				ret = -1;
				log_error_write(srv, __FILE__, __LINE__, "s",
						"not a string");
				break;
			}

			lua_pop(L, 1);  /* removes value'; keeps key' for next iteration */
		}

		lua_settop(L, curelem - 1);

		if (ret == 0) {
			data_string *ds;
			char timebuf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];

			con->file_finished = 1;

			ds = (data_string *)array_get_element(con->response.headers, "Last-Modified");
			if (0 == mtime) mtime = time(NULL); /* default last-modified to now */

			/* no Last-Modified specified */
			if (NULL == ds) {

				strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime));

				response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), timebuf, sizeof(timebuf) - 1);
				ds = (data_string *)array_get_element(con->response.headers, "Last-Modified");
				force_assert(NULL != ds);
			}

			if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, ds->value)) {
				/* ok, the client already has our content,
				 * no need to send it again */

				chunkqueue_reset(con->write_queue);
				ret = 0; /* cache-hit */
			}
		} else {
			chunkqueue_reset(con->write_queue);
		}
	}

	if (ret == 1 && !buffer_string_is_empty(p->trigger_handler)) {
		/* cache-miss */
		buffer_copy_buffer(con->uri.path, p->baseurl);
		buffer_append_string_buffer(con->uri.path, p->trigger_handler);

		buffer_copy_buffer(con->physical.path, p->basedir);
		buffer_append_string_buffer(con->physical.path, p->trigger_handler);

		chunkqueue_reset(con->write_queue);
	}

error:
	lua_close(L);

	buffer_free(b);

	return ret /* cache-error */;
}
Beispiel #10
0
static int http_list_directory(server *srv, connection *con, plugin_data *p, buffer *dir) {
	DIR *dp;
	buffer *out;
	struct dirent *dent;
	struct stat st;
	char *path, *path_file;
	size_t i;
	int hide_dotfiles = p->conf.hide_dot_files;
	dirls_list_t dirs, files, *list;
	dirls_entry_t *tmp;
	char sizebuf[sizeof("999.9K")];
	char datebuf[sizeof("2005-Jan-01 22:23:24")];
	size_t k;
	const char *content_type;
	long name_max;
#if defined(HAVE_XATTR) || defined(HAVE_EXTATTR)
	char attrval[128];
	int attrlen;
#endif
#ifdef HAVE_LOCALTIME_R
	struct tm tm;
#endif

	if (buffer_string_is_empty(dir)) return -1;

	i = buffer_string_length(dir);

#ifdef HAVE_PATHCONF
	if (0 >= (name_max = pathconf(dir->ptr, _PC_NAME_MAX))) {
		/* some broken fs (fuse) return 0 instead of -1 */
#ifdef NAME_MAX
		name_max = NAME_MAX;
#else
		name_max = 255; /* stupid default */
#endif
	}
#elif defined __WIN32
	name_max = FILENAME_MAX;
#else
	name_max = NAME_MAX;
#endif

	path = malloc(i + name_max + 1);
	force_assert(NULL != path);
	memcpy(path, dir->ptr, i+1);
	path_file = path + i;

	if (NULL == (dp = opendir(path))) {
		log_error_write(srv, __FILE__, __LINE__, "sbs",
			"opendir failed:", dir, strerror(errno));

		free(path);
		return -1;
	}

	dirs.ent   = (dirls_entry_t**) malloc(sizeof(dirls_entry_t*) * DIRLIST_BLOB_SIZE);
	force_assert(dirs.ent);
	dirs.size  = DIRLIST_BLOB_SIZE;
	dirs.used  = 0;
	files.ent  = (dirls_entry_t**) malloc(sizeof(dirls_entry_t*) * DIRLIST_BLOB_SIZE);
	force_assert(files.ent);
	files.size = DIRLIST_BLOB_SIZE;
	files.used = 0;

	while ((dent = readdir(dp)) != NULL) {
		unsigned short exclude_match = 0;

		if (dent->d_name[0] == '.') {
			if (hide_dotfiles)
				continue;
			if (dent->d_name[1] == '\0')
				continue;
			if (dent->d_name[1] == '.' && dent->d_name[2] == '\0')
				continue;
		}

		if (p->conf.hide_readme_file && !buffer_string_is_empty(p->conf.show_readme)) {
			if (strcmp(dent->d_name, p->conf.show_readme->ptr) == 0)
				continue;
		}
		if (p->conf.hide_header_file && !buffer_string_is_empty(p->conf.show_header)) {
			if (strcmp(dent->d_name, p->conf.show_header->ptr) == 0)
				continue;
		}

		/* compare d_name against excludes array
		 * elements, skipping any that match.
		 */
#ifdef HAVE_PCRE_H
		for(i = 0; i < p->conf.excludes->used; i++) {
			int n;
#define N 10
			int ovec[N * 3];
			pcre *regex = p->conf.excludes->ptr[i]->regex;

			if ((n = pcre_exec(regex, NULL, dent->d_name,
				    strlen(dent->d_name), 0, 0, ovec, 3 * N)) < 0) {
				if (n != PCRE_ERROR_NOMATCH) {
					log_error_write(srv, __FILE__, __LINE__, "sd",
						"execution error while matching:", n);

					/* aborting would require a lot of manual cleanup here.
					 * skip instead (to not leak names that break pcre matching)
					 */
					exclude_match = 1;
					break;
				}
			}
			else {
				exclude_match = 1;
				break;
			}
		}

		if (exclude_match) {
			continue;
		}
#endif

		i = strlen(dent->d_name);

		/* NOTE: the manual says, d_name is never more than NAME_MAX
		 *       so this should actually not be a buffer-overflow-risk
		 */
		if (i > (size_t)name_max) continue;

		memcpy(path_file, dent->d_name, i + 1);
		if (stat(path, &st) != 0)
			continue;

		list = &files;
		if (S_ISDIR(st.st_mode))
			list = &dirs;

		if (list->used == list->size) {
			list->size += DIRLIST_BLOB_SIZE;
			list->ent   = (dirls_entry_t**) realloc(list->ent, sizeof(dirls_entry_t*) * list->size);
			force_assert(list->ent);
		}

		tmp = (dirls_entry_t*) malloc(sizeof(dirls_entry_t) + 1 + i);
		tmp->mtime = st.st_mtime;
		tmp->size  = st.st_size;
		tmp->namelen = i;
		memcpy(DIRLIST_ENT_NAME(tmp), dent->d_name, i + 1);

		list->ent[list->used++] = tmp;
	}
	closedir(dp);

	if (dirs.used) http_dirls_sort(dirs.ent, dirs.used);

	if (files.used) http_dirls_sort(files.ent, files.used);

	out = buffer_init();
	http_list_directory_header(srv, con, p, out);

	/* directories */
	for (i = 0; i < dirs.used; i++) {
		tmp = dirs.ent[i];

#ifdef HAVE_LOCALTIME_R
		localtime_r(&(tmp->mtime), &tm);
		strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", &tm);
#else
		strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", localtime(&(tmp->mtime)));
#endif

		buffer_append_string_len(out, CONST_STR_LEN("<tr class=\"d\"><td class=\"n\"><a href=\""));
		buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_REL_URI_PART);
		buffer_append_string_len(out, CONST_STR_LEN("/\">"));
		buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_MINIMAL_XML);
		buffer_append_string_len(out, CONST_STR_LEN("</a>/</td><td class=\"m\">"));
		buffer_append_string_len(out, datebuf, sizeof(datebuf) - 1);
		buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"s\">- &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;
#if defined(HAVE_XATTR)
		if (con->conf.use_xattr) {
			memcpy(path_file, DIRLIST_ENT_NAME(tmp), tmp->namelen + 1);
			attrlen = sizeof(attrval) - 1;
			if (attr_get(path, srv->srvconf.xattr_name->ptr, attrval, &attrlen, 0) == 0) {
				attrval[attrlen] = '\0';
				content_type = attrval;
			}
		}
#elif defined(HAVE_EXTATTR)
		if (con->conf.use_xattr) {
			memcpy(path_file, DIRLIST_ENT_NAME(tmp), tmp->namelen + 1);
			if(-1 != (attrlen = extattr_get_file(path, EXTATTR_NAMESPACE_USER, srv->srvconf.xattr_name->ptr, attrval, sizeof(attrval)-1))) {
				attrval[attrlen] = '\0';
				content_type = attrval;
			}
		}
#endif

		if (content_type == NULL) {
			content_type = "application/octet-stream";
			for (k = 0; k < con->conf.mimetypes->used; k++) {
				data_string *ds = (data_string *)con->conf.mimetypes->data[k];
				size_t ct_len;

				if (buffer_is_empty(ds->key))
					continue;

				ct_len = buffer_string_length(ds->key);
				if (tmp->namelen < ct_len)
					continue;

				if (0 == strncasecmp(DIRLIST_ENT_NAME(tmp) + tmp->namelen - ct_len, ds->key->ptr, ct_len)) {
					content_type = ds->value->ptr;
					break;
				}
			}
		}

#ifdef HAVE_LOCALTIME_R
		localtime_r(&(tmp->mtime), &tm);
		strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", &tm);
#else
		strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", localtime(&(tmp->mtime)));
#endif
		http_list_directory_sizefmt(sizebuf, sizeof(sizebuf), tmp->size);

		buffer_append_string_len(out, CONST_STR_LEN("<tr><td class=\"n\"><a href=\""));
		buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_REL_URI_PART);
		buffer_append_string_len(out, CONST_STR_LEN("\">"));
		buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_MINIMAL_XML);
		buffer_append_string_len(out, CONST_STR_LEN("</a></td><td class=\"m\">"));
		buffer_append_string_len(out, datebuf, sizeof(datebuf) - 1);
		buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"s\">"));
		buffer_append_string(out, sizebuf);
		buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"t\">"));
		buffer_append_string(out, content_type);
		buffer_append_string_len(out, CONST_STR_LEN("</td></tr>\n"));

		free(tmp);
	}

	free(files.ent);
	free(dirs.ent);
	free(path);

	http_list_directory_footer(srv, con, p, out);

	/* Insert possible charset to Content-Type */
	if (buffer_string_is_empty(p->conf.encoding)) {
		response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
	} else {
		buffer_copy_string_len(p->content_charset, CONST_STR_LEN("text/html; charset="));
		buffer_append_string_buffer(p->content_charset, p->conf.encoding);
		response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->content_charset));
	}

	con->file_finished = 1;
	chunkqueue_append_buffer(con->write_queue, out);
	buffer_free(out);

	return 0;
}
Beispiel #11
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;
}
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;
}
Beispiel #13
0
static int mod_ssi_handle_request(server *srv, connection *con, plugin_data *p) {
	stream s;
#ifdef  HAVE_PCRE_H
	int i, n;

#define N 10
	int ovec[N * 3];
#endif

	stat_cache_entry *sce = NULL;


	/* get a stream to the file */

	array_reset(p->ssi_vars);
	array_reset(p->ssi_cgi_env);
	buffer_copy_string_len(p->timefmt, CONST_STR_LEN("%a, %d %b %Y %H:%M:%S %Z"));
	p->sizefmt = 0;
	build_ssi_cgi_vars(srv, con, p);
	p->if_is_false = 0;

	/* Reset the modified time of included files */
	include_file_last_mtime = 0;

	if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
		log_error_write(srv, __FILE__, __LINE__,  "SB", "stat_cache_get_entry failed: ", con->physical.path);
		return -1;
	}

	if (-1 == stream_open(&s, con->physical.path)) {
		log_error_write(srv, __FILE__, __LINE__, "sb",
				"stream-open: ", con->physical.path);
		return -1;
	}


	/**
	 * <!--#element attribute=value attribute=value ... -->
	 *
	 * config       DONE
	 *   errmsg     -- missing
	 *   sizefmt    DONE
	 *   timefmt    DONE
	 * echo         DONE
	 *   var        DONE
	 *   encoding   -- missing
	 * exec         DONE
	 *   cgi        -- never
	 *   cmd        DONE
	 * fsize        DONE
	 *   file       DONE
	 *   virtual    DONE
	 * flastmod     DONE
	 *   file       DONE
	 *   virtual    DONE
	 * include      DONE
	 *   file       DONE
	 *   virtual    DONE
	 * printenv     DONE
	 * set          DONE
	 *   var        DONE
	 *   value      DONE
	 *
	 * if           DONE
	 * elif         DONE
	 * else         DONE
	 * endif        DONE
	 *
	 *
	 * expressions
	 * AND, OR      DONE
	 * comp         DONE
	 * ${...}       -- missing
	 * $...         DONE
	 * '...'        DONE
	 * ( ... )      DONE
	 *
	 *
	 *
	 * ** all DONE **
	 * DATE_GMT
	 *   The current date in Greenwich Mean Time.
	 * DATE_LOCAL
	 *   The current date in the local time zone.
	 * DOCUMENT_NAME
	 *   The filename (excluding directories) of the document requested by the user.
	 * DOCUMENT_URI
	 *   The (%-decoded) URL path of the document requested by the user. Note that in the case of nested include files, this is not then URL for the current document.
	 * LAST_MODIFIED
	 *   The last modification date of the document requested by the user.
	 * USER_NAME
	 *   Contains the owner of the file which included it.
	 *
	 */
#ifdef HAVE_PCRE_H
	for (i = 0; (n = pcre_exec(p->ssi_regex, NULL, s.start, s.size, i, 0, ovec, N * 3)) > 0; i = ovec[1]) {
		const char **l;
		/* take everything from last offset to current match pos */

		if (!p->if_is_false) chunkqueue_append_file(con->write_queue, con->physical.path, i, ovec[0] - i);

		pcre_get_substring_list(s.start, ovec, n, &l);
		process_ssi_stmt(srv, con, p, l, n, sce);
		pcre_free_substring_list(l);
	}

	switch(n) {
	case PCRE_ERROR_NOMATCH:
		/* copy everything/the rest */
		chunkqueue_append_file(con->write_queue, con->physical.path, i, s.size - i);

		break;
	default:
		log_error_write(srv, __FILE__, __LINE__, "sd",
				"execution error while matching: ", n);
		break;
	}
#endif


	stream_close(&s);

	con->file_started  = 1;
	con->file_finished = 1;
	con->mode = p->id;

	if (p->conf.content_type->used <= 1) {
		response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
	} else {
		response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->conf.content_type));
	}

	{
		/* Generate "ETag" & "Last-Modified" headers */
		time_t lm_time = 0;
		buffer *mtime = NULL;

		etag_mutate(con->physical.etag, sce->etag);
		response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag));

		if (sce->st.st_mtime > include_file_last_mtime)
			lm_time = sce->st.st_mtime;
		else
			lm_time = include_file_last_mtime;

		mtime = strftime_cache_get(srv, lm_time);
		response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime));
	}

	/* Reset the modified time of included files */
	include_file_last_mtime = 0;

	/* reset physical.path */
	buffer_reset(con->physical.path);

	return 0;
}
Beispiel #14
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;
}
void http_response_send_file (server *srv, connection *con, buffer *path) {
    stat_cache_entry *sce = NULL;
    buffer *mtime = NULL;
    data_string *ds;
    int allow_caching = (0 == con->http_status || 200 == con->http_status);

    if (HANDLER_ERROR == stat_cache_get_entry(srv, con, path, &sce)) {
        con->http_status = (errno == ENOENT) ? 404 : 403;

        log_error_write(srv, __FILE__, __LINE__, "sbsb",
                        "not a regular file:", con->uri.path,
                        "->", path);

        return;
    }

    /* we only handline regular files */
#ifdef HAVE_LSTAT
    if ((sce->is_symlink == 1) && !con->conf.follow_symlink) {
        con->http_status = 403;

        if (con->conf.log_request_handling) {
            log_error_write(srv, __FILE__, __LINE__,  "s",  "-- access denied due symlink restriction");
            log_error_write(srv, __FILE__, __LINE__,  "sb", "Path         :", path);
        }

        return;
    }
#endif
    if (!S_ISREG(sce->st.st_mode)) {
        con->http_status = 403;

        if (con->conf.log_file_not_found) {
            log_error_write(srv, __FILE__, __LINE__, "sbsb",
                            "not a regular file:", con->uri.path,
                            "->", sce->name);
        }

        return;
    }

    /* mod_compress might set several data directly, don't overwrite them */

    /* set response content-type, if not set already */

    if (NULL == array_get_element(con->response.headers, "Content-Type")) {
        if (buffer_string_is_empty(sce->content_type)) {
            /* we are setting application/octet-stream, but also announce that
             * this header field might change in the seconds few requests
             *
             * This should fix the aggressive caching of FF and the script download
             * seen by the first installations
             */
            response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/octet-stream"));

            allow_caching = 0;
        } else {
            response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type));
        }
    }

    if (con->conf.range_requests) {
        response_header_overwrite(srv, con, CONST_STR_LEN("Accept-Ranges"), CONST_STR_LEN("bytes"));
    }

    if (allow_caching) {
        if (con->etag_flags != 0 && !buffer_string_is_empty(sce->etag)) {
            if (NULL == array_get_element(con->response.headers, "ETag")) {
                /* generate e-tag */
                etag_mutate(con->physical.etag, sce->etag);

                response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag));
            }
        }

        /* prepare header */
        if (NULL == (ds = (data_string *)array_get_element(con->response.headers, "Last-Modified"))) {
            mtime = strftime_cache_get(srv, sce->st.st_mtime);
            response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime));
        } else {
            mtime = ds->value;
        }

        if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) {
            return;
        }
    }

    if (con->request.http_range && con->conf.range_requests
            && (200 == con->http_status || 0 == con->http_status)
            && NULL == array_get_element(con->response.headers, "Content-Encoding")) {
        int do_range_request = 1;
        /* check if we have a conditional GET */

        if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "If-Range"))) {
            /* if the value is the same as our ETag, we do a Range-request,
             * otherwise a full 200 */

            if (ds->value->ptr[0] == '"') {
                /**
                 * client wants a ETag
                 */
                if (!con->physical.etag) {
                    do_range_request = 0;
                } else if (!buffer_is_equal(ds->value, con->physical.etag)) {
                    do_range_request = 0;
                }
            } else if (!mtime) {
                /**
                 * we don't have a Last-Modified and can match the If-Range:
                 *
                 * sending all
                 */
                do_range_request = 0;
            } else if (!buffer_is_equal(ds->value, mtime)) {
                do_range_request = 0;
            }
        }

        if (do_range_request) {
            /* content prepared, I'm done */
            con->file_finished = 1;

            if (0 == http_response_parse_range(srv, con, path, sce)) {
                con->http_status = 206;
            }
            return;
        }
    }

    /* if we are still here, prepare body */

    /* we add it here for all requests
     * the HEAD request will drop it afterwards again
     */
    if (0 == sce->st.st_size || 0 == http_chunk_append_file(srv, con, path)) {
        con->http_status = 200;
        con->file_finished = 1;
    } else {
        con->http_status = 403;
    }
}
Beispiel #16
0
int http_response_write_header(server *srv, connection *con) {
	buffer *b;
	size_t i;
	int have_date = 0;
	int have_server = 0;

	b = chunkqueue_get_prepend_buffer(con->write_queue);

	if (con->request.http_version == HTTP_VERSION_1_1) {
		buffer_copy_string_len(b, CONST_STR_LEN("HTTP/1.1 "));
	} else {
		buffer_copy_string_len(b, CONST_STR_LEN("HTTP/1.0 "));
	}
	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));

	/* disable keep-alive if requested */
	if (con->request_count > con->conf.max_keep_alive_requests || 0 == con->conf.max_keep_alive_idle) {
		con->keep_alive = 0;
	} else {
		con->keep_alive_idle = con->conf.max_keep_alive_idle;
	}

	if (con->request.http_version != HTTP_VERSION_1_1 || con->keep_alive == 0) {
		if (con->keep_alive) {
			response_header_overwrite(srv, con, CONST_STR_LEN("Connection"), CONST_STR_LEN("keep-alive"));
		} else {
			response_header_overwrite(srv, con, CONST_STR_LEN("Connection"), CONST_STR_LEN("close"));
		}
	}

	if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) {
		response_header_overwrite(srv, con, CONST_STR_LEN("Transfer-Encoding"), CONST_STR_LEN("chunked"));
	}


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

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

		if (ds->value->used && ds->key->used &&
		    0 != strncasecmp(ds->key->ptr, CONST_STR_LEN("X-LIGHTTPD-")) &&
			0 != strncasecmp(ds->key->ptr, CONST_STR_LEN("X-Sendfile"))) {
			if (0 == strcasecmp(ds->key->ptr, "Date")) have_date = 1;
			if (0 == strcasecmp(ds->key->ptr, "Server")) have_server = 1;
			if (0 == strcasecmp(ds->key->ptr, "Content-Encoding") && 304 == con->http_status) continue;

			buffer_append_string_len(b, CONST_STR_LEN("\r\n"));
			buffer_append_string_buffer(b, ds->key);
			buffer_append_string_len(b, CONST_STR_LEN(": "));
#if 0
			/** 
			 * the value might contain newlines, encode them with at least one white-space
			 */
			buffer_append_string_encoded(b, CONST_BUF_LEN(ds->value), ENCODING_HTTP_HEADER);
#else
			buffer_append_string_buffer(b, ds->value);
#endif
		}
	}

	if (!have_date) {
		/* HTTP/1.1 requires a Date: header */
		buffer_append_string_len(b, CONST_STR_LEN("\r\nDate: "));

		/* cache the generated timestamp */
		if (srv->cur_ts != srv->last_generated_date_ts) {
			buffer_prepare_copy(srv->ts_date_str, 255);

			strftime(srv->ts_date_str->ptr, srv->ts_date_str->size - 1,
				 "%a, %d %b %Y %H:%M:%S GMT", gmtime(&(srv->cur_ts)));

			srv->ts_date_str->used = strlen(srv->ts_date_str->ptr) + 1;

			srv->last_generated_date_ts = srv->cur_ts;
		}

		buffer_append_string_buffer(b, srv->ts_date_str);
	}

	if (!have_server) {
		if (buffer_is_empty(con->conf.server_tag)) {
			buffer_append_string_len(b, CONST_STR_LEN("\r\nServer: " PACKAGE_DESC));
		} else if (con->conf.server_tag->used > 1) {
			buffer_append_string_len(b, CONST_STR_LEN("\r\nServer: "));
			buffer_append_string_encoded(b, CONST_BUF_LEN(con->conf.server_tag), ENCODING_HTTP_HEADER);
		}
	}

	//- Jerry add 20110923
#if EMBEDDED_EANBLE	 
	char * ddns_host_n = nvram_get_ddns_host_name();	
	if(ddns_host_n){
		buffer_append_string_len(b, CONST_STR_LEN("\r\nDDNS: "));
		buffer_append_string(b, ddns_host_n);
	}else
#endif
	buffer_append_string_len(b, CONST_STR_LEN("\r\nDDNS: "));
	
	buffer_append_string_len(b, CONST_STR_LEN("\r\n\r\n"));


	con->bytes_header = b->used - 1;

	if (con->conf.log_response_header) {
		log_error_write(srv, __FILE__, __LINE__, "sSb", "Response-Header:", "\n", b);
	}

	return 0;
}
Beispiel #17
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:
			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) && !buffer_string_is_empty(con->uri.path) &&
			    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:
			if (0 == con->http_status) {
				con->http_status = 501;
			}
			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;

		connection_handle_errdoc_init(srv, con);

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

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

			if (0 == http_chunk_append_file(srv, con, con->physical.path)) {
				con->file_finished = 1;
				if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
					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 = buffer_init();

			/* 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_int(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_int(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"
					     ));

			(void)http_chunk_append_buffer(srv, con, b);
			buffer_free(b);

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

	/* Allow filter plugins to change response headers before they are written. */
	switch(plugins_call_handle_response_start(srv, con)) {
	case HANDLER_GO_ON:
	case HANDLER_FINISHED:
		break;
	default:
		log_error_write(srv, __FILE__, __LINE__, "s", "response_start plugin failed");
		return -1;
	}

	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_int(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)) {
			if (con->request.http_version == HTTP_VERSION_1_1) {
				off_t qlen = chunkqueue_length(con->write_queue);
				con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
				if (qlen) {
					/* create initial Transfer-Encoding: chunked segment */
					buffer *b = srv->tmp_chunk_len;
					buffer_string_set_length(b, 0);
					buffer_append_uint_hex(b, (uintmax_t)qlen);
					buffer_append_string_len(b, CONST_STR_LEN("\r\n"));
					chunkqueue_prepend_buffer(con->write_queue, b);
					chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("\r\n"));
				}
			} else {
				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;
}