コード例 #1
0
static liHandlerResult cache_etag_filter_miss(liVRequest *vr, liFilter *f) {
	cache_etag_file *cfile = (cache_etag_file*) f->param;
	ssize_t res;
	gchar *buf;
	off_t buflen;
	liChunkIter citer = li_chunkqueue_iter(f->in);
	UNUSED(vr);

	if (0 == f->in->length) return LI_HANDLER_GO_ON;

	if (!cfile) { /* somehow we lost the file */
		li_chunkqueue_steal_all(f->out, f->in);
		if (f->in->is_closed) f->out->is_closed = TRUE;
		return LI_HANDLER_GO_ON;
	}

	if (LI_HANDLER_GO_ON != li_chunkiter_read(vr, citer, 0, 64*1024, &buf, &buflen)) {
		VR_ERROR(vr, "%s", "Couldn't read data from chunkqueue");
		cache_etag_file_free(cfile);
		f->param = NULL;
		li_chunkqueue_steal_all(f->out, f->in);
		if (f->in->is_closed) f->out->is_closed = TRUE;
		return LI_HANDLER_GO_ON;
	}

	res = write(cfile->fd, buf, buflen);
	if (res < 0) {
		switch (errno) {
		case EINTR:
		case EAGAIN:
			break; /* come back later */
		default:
			VR_ERROR(vr, "Couldn't write to temporary cache file '%s': %s",
				cfile->tmpfilename->str, g_strerror(errno));
			cache_etag_file_free(cfile);
			f->param = NULL;
			li_chunkqueue_steal_all(f->out, f->in);
			if (f->in->is_closed) f->out->is_closed = TRUE;
			return LI_HANDLER_GO_ON;
		}
	} else {
		li_chunkqueue_steal_len(f->out, f->in, res);
		if (f->in->length == 0 && f->in->is_closed) {
			cache_etag_file_finish(vr, cfile);
			f->param = NULL;
			f->out->is_closed = TRUE;
			return LI_HANDLER_GO_ON;
		}
	}

	return f->in->length ? LI_HANDLER_COMEBACK : LI_HANDLER_GO_ON;
}
コード例 #2
0
ファイル: mod_fastcgi.c プロジェクト: soarpenguin/lighttpd2
static void stream_send_chunks(liChunkQueue *out, guint8 type, guint16 requestid, liChunkQueue *in) {
	while (in->length > 0) {
		guint16 tosend = (in->length > G_MAXUINT16) ? G_MAXUINT16 : in->length;
		guint8 padlen = stream_send_fcgi_record(out, type, requestid, tosend);
		li_chunkqueue_steal_len(out, in, tosend);
		li_chunkqueue_append_mem(out, __padding, padlen);
	}

	if (in->is_closed && !out->is_closed) {
		out->is_closed = TRUE;
		stream_send_fcgi_record(out, type, requestid, 0);
	}
}
コード例 #3
0
ファイル: mod_fastcgi.c プロジェクト: soarpenguin/lighttpd2
static gboolean fastcgi_parse_response(fastcgi_connection *fcon) {
	liVRequest *vr = fcon->vr;
	liPlugin *p = fcon->ctx->plugin;
	gint len;
	while (fastcgi_get_packet(fcon)) {
		if (fcon->fcgi_in_record.version != FCGI_VERSION_1) {
			VR_ERROR(vr, "(%s) Unknown fastcgi protocol version %i", fcon->ctx->socket_str->str, (gint) fcon->fcgi_in_record.version);
			close(fcon->fd);
			fcon->fd = -1;
			li_vrequest_error(vr);
			return FALSE;
		}
		switch (fcon->fcgi_in_record.type) {
		case FCGI_END_REQUEST:
			li_chunkqueue_skip(fcon->fcgi_in, fastcgi_available(fcon));
			fcon->stdout->is_closed = TRUE;
			break;
		case FCGI_STDOUT:
			if (0 == fcon->fcgi_in_record.contentLength) {
				fcon->stdout->is_closed = TRUE;
			} else {
				li_chunkqueue_steal_len(fcon->stdout, fcon->fcgi_in, fastcgi_available(fcon));
			}
			break;
		case FCGI_STDERR:
			len = fastcgi_available(fcon);
			li_chunkqueue_extract_to(vr, fcon->fcgi_in, len, vr->wrk->tmp_str);
			if (OPTION(FASTCGI_OPTION_LOG_PLAIN_ERRORS).boolean) {
				li_log_split_lines(vr->wrk->srv, vr, LI_LOG_LEVEL_BACKEND, 0, vr->wrk->tmp_str->str, "");
			} else {
				VR_BACKEND_LINES(vr, vr->wrk->tmp_str->str, "(fcgi-stderr %s) ", fcon->ctx->socket_str->str);
			}
			li_chunkqueue_skip(fcon->fcgi_in, len);
			break;
		default:
			if (fcon->fcgi_in_record.first) VR_WARNING(vr, "(%s) Unhandled fastcgi record type %i", fcon->ctx->socket_str->str, (gint) fcon->fcgi_in_record.type);
			li_chunkqueue_skip(fcon->fcgi_in, fastcgi_available(fcon));
			break;
		}
		fcon->fcgi_in_record.first = FALSE;
	}
	return TRUE;
}
コード例 #4
0
ファイル: connection.c プロジェクト: angel1991521/lighttpd2
/* tcp/ssl -> http "parser" */
static void _connection_http_in_cb(liStream *stream, liStreamEvent event) {
	liConnection *con = LI_CONTAINER_OF(stream, liConnection, in);
	liChunkQueue *raw_in, *in;
	liVRequest *vr = con->mainvr;

	switch (event) {
	case LI_STREAM_NEW_DATA:
		/* handle below */
		break;
	case LI_STREAM_DISCONNECTED_SOURCE:
		connection_close(con);
		return;
	case LI_STREAM_DESTROY:
		con->info.req = NULL;
		li_job_later(&con->wrk->loop.jobqueue, &con->job_reset);
		return;
	default:
		return;
	}

	if (NULL == stream->source) return;

	/* raw_in never gets closed normally - if we receive EOF from the client it means it cancelled the request */
	raw_in = stream->source->out;
	if (raw_in->is_closed) {
		connection_close(con);
		return;
	}

	/* always close "in" after request body end. reopen it on keep-alive */
	in = con->in.out;

	if (0 == raw_in->length) return; /* no (new) data */

	if (LI_CON_STATE_UPGRADED == con->state) {
		li_chunkqueue_steal_all(in, raw_in);
		li_stream_notify(stream);
		return;
	}

	if (con->state == LI_CON_STATE_KEEP_ALIVE) {
		/* stop keep alive timeout watchers */
		if (con->keep_alive_data.link) {
			g_queue_delete_link(&con->wrk->keep_alive_queue, con->keep_alive_data.link);
			con->keep_alive_data.link = NULL;
		}
		con->keep_alive_data.timeout = 0;
		li_event_stop(&con->keep_alive_data.watcher);

		con->keep_alive_requests++;
		/* disable keep alive if limit is reached */
		if (con->keep_alive_requests == CORE_OPTION(LI_CORE_OPTION_MAX_KEEP_ALIVE_REQUESTS).number)
			con->info.keep_alive = FALSE;

		/* reopen stream for request body */
		li_chunkqueue_reset(in);
		/* reset stuff from keep-alive and record timestamp */
		li_vrequest_start(con->mainvr);

		con->state = LI_CON_STATE_READ_REQUEST_HEADER;

		/* put back in io timeout queue */
		li_connection_update_io_wait(con);
	} else if (con->state == LI_CON_STATE_REQUEST_START) {
		con->state = LI_CON_STATE_READ_REQUEST_HEADER;
		li_connection_update_io_wait(con);
	}

	if (con->state == LI_CON_STATE_READ_REQUEST_HEADER) {
		liHandlerResult res;

		if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
			VR_DEBUG(vr, "%s", "reading request header");
		}

		res = li_http_request_parse(vr, &con->req_parser_ctx);

		/* max uri length 8 kilobytes */
		/* TODO: check this and similar in request_parse and response_parse */
		if (vr->request.uri.raw->len > 8*1024) {
			VR_INFO(vr,
				"request uri too large. limit: 8kb, received: %s",
				li_counter_format(vr->request.uri.raw->len, COUNTER_BYTES, vr->wrk->tmp_str)->str
			);

			con->info.keep_alive = FALSE;
			vr->response.http_status = 414; /* Request-URI Too Large */
			con->state = LI_CON_STATE_WRITE;
			li_connection_update_io_wait(con);
			li_stream_again(&con->out);
			return;
		}

		switch(res) {
		case LI_HANDLER_GO_ON:
			break; /* go on */
		case LI_HANDLER_WAIT_FOR_EVENT:
			return;
		case LI_HANDLER_ERROR:
		case LI_HANDLER_COMEBACK: /* unexpected */
			/* unparsable header */
			if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
				VR_DEBUG(vr, "%s", "parsing header failed");
			}

			con->wrk->stats.requests++;
			con->info.keep_alive = FALSE;
			/* set status 400 if not already set to e.g. 413 */
			if (vr->response.http_status == 0)
				vr->response.http_status = 400;
			con->state = LI_CON_STATE_WRITE;
			li_connection_update_io_wait(con);
			li_stream_again(&con->out);
			return;
		}

		con->wrk->stats.requests++;

		/* headers ready */
		if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
			VR_DEBUG(vr, "%s", "validating request header");
		}
		if (!li_request_validate_header(con)) {
			/* set status 400 if not already set */
			if (vr->response.http_status == 0)
				vr->response.http_status = 400;
			con->state = LI_CON_STATE_WRITE;
			con->info.keep_alive = FALSE;
			li_connection_update_io_wait(con);
			li_stream_again(&con->out);
			return;
		}

		/* When does a client ask for 100 Continue? probably not while trying to ddos us
		 * as post content probably goes to a dynamic backend anyway, we don't
		 * care about the rare cases we could determine that we don't want a request at all
		 * before sending it to a backend - so just send the stupid header
		 */
		if (con->expect_100_cont) {
			if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
				VR_DEBUG(vr, "%s", "send 100 Continue");
			}
			li_chunkqueue_append_mem(con->out.out, CONST_STR_LEN("HTTP/1.1 100 Continue\r\n\r\n"));
			con->expect_100_cont = FALSE;

			li_stream_notify(&con->out);
		}

		con->state = LI_CON_STATE_HANDLE_MAINVR;
		li_connection_update_io_wait(con);
		li_action_enter(vr, con->srv->mainaction);

		li_vrequest_handle_request_headers(vr);
	}

	if (con->state != LI_CON_STATE_READ_REQUEST_HEADER && !in->is_closed) {
		goffset newbytes = 0;

		if (-1 == vr->request.content_length) {
			if (!in->is_closed) {
				if (!li_filter_chunked_decode(vr, in, raw_in, &con->in_chunked_decode_state)) {
					if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
						VR_DEBUG(vr, "%s", "failed decoding chunked request body");
					}
					li_connection_error(con);
					return;
				}
				if (in->is_closed) vr->request.content_length = in->bytes_in;
				newbytes = 1; /* always notify */
			}
		} else {
			if (in->bytes_in < vr->request.content_length) {
				newbytes = li_chunkqueue_steal_len(in, raw_in, vr->request.content_length - in->bytes_in);
			}
			if (in->bytes_in == vr->request.content_length) {
				in->is_closed = TRUE;
			}
		}
		if (newbytes > 0 || in->is_closed) {
			li_stream_notify(&con->in);
		}
	}
}
コード例 #5
0
ファイル: mod_memcached.c プロジェクト: Aivaras/lighttpd2
static liHandlerResult memcache_store_filter(liVRequest *vr, liFilter *f) {
	memcache_filter *mf = (memcache_filter*) f->param;

	if (NULL == f->in) {
		memcache_store_filter_free(vr, f);
		/* didn't handle f->in->is_closed? abort forwarding */
		if (!f->out->is_closed) li_stream_reset(&f->stream);
		return LI_HANDLER_GO_ON;
	}

	if (NULL == mf) goto forward;

	if (f->in->is_closed && 0 == f->in->length && f->out->is_closed) {
		/* nothing to do anymore */
		return LI_HANDLER_GO_ON;
	}

	/* check if size still fits into buffer */
	if ((gssize) (f->in->length + mf->buf->used) > (gssize) mf->ctx->maxsize) {
		/* response too big, switch to "forward" mode */
		memcache_store_filter_free(vr, f);
		goto forward;
	}

	while (0 < f->in->length) {
		char *data;
		off_t len;
		liChunkIter ci;
		liHandlerResult res;
		GError *err = NULL;

		ci = li_chunkqueue_iter(f->in);

		if (LI_HANDLER_GO_ON != (res = li_chunkiter_read(ci, 0, 16*1024, &data, &len, &err))) {
			if (NULL != err) {
				VR_ERROR(vr, "Couldn't read data from chunkqueue: %s", err->message);
				g_error_free(err);
			}
			return res;
		}

		if ((gssize) (len + mf->buf->used) > (gssize) mf->ctx->maxsize) {
			/* response too big, switch to "forward" mode */
			memcache_store_filter_free(vr, f);
			goto forward;
		}

		memcpy(mf->buf->addr + mf->buf->used, data, len);
		mf->buf->used += len;

		if (!f->out->is_closed) {
			li_chunkqueue_steal_len(f->out, f->in, len);
		} else {
			li_chunkqueue_skip(f->in, len);
		}
	}

	if (f->in->is_closed) {
		/* finally: store response in memcached */

		liMemcachedCon *con;
		GError *err = NULL;
		liMemcachedRequest *req;
		memcached_ctx *ctx = mf->ctx;

		assert(0 == f->in->length);

		f->out->is_closed = TRUE;

		con = mc_ctx_prepare(ctx, vr->wrk);
		mc_ctx_build_key(vr->wrk->tmp_str, ctx, vr);

		if (NULL != vr && CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
			VR_DEBUG(vr, "memcached.store: storing response for key '%s'", vr->wrk->tmp_str->str);
		}

		req = li_memcached_set(con, vr->wrk->tmp_str, ctx->flags, ctx->ttl, mf->buf, NULL, NULL, &err);
		memcache_store_filter_free(vr, f);

		if (NULL == req) {
			if (NULL != err) {
				if (NULL != vr && LI_MEMCACHED_DISABLED != err->code) {
					VR_ERROR(vr, "memcached.store: set failed: %s", err->message);
				}
				g_clear_error(&err);
			} else if (NULL != vr) {
				VR_ERROR(vr, "memcached.store: set failed: %s", "Unkown error");
			}
		}
	}

	return LI_HANDLER_GO_ON;

forward:
	if (f->out->is_closed) {
		li_chunkqueue_skip_all(f->in);
		li_stream_disconnect(&f->stream);
	} else {
		li_chunkqueue_steal_all(f->out, f->in);
		if (f->in->is_closed) f->out->is_closed = f->in->is_closed;
	}
	return LI_HANDLER_GO_ON;
}
コード例 #6
0
ファイル: filter_chunked.c プロジェクト: Aivaras/lighttpd2
gboolean li_filter_chunked_decode(liVRequest *vr, liChunkQueue *out, liChunkQueue *in, liFilterChunkedDecodeState *state) {
	liChunkParserCtx ctx;
	gchar *p = NULL, *pe = NULL;
	gchar c;
	int digit;

	li_chunk_parser_init(&ctx, in);
	li_chunk_parser_prepare(&ctx);


	for (;;) {
		 /* 0: start new chunklen, 1: reading chunklen, 2: found \r, 3: copying content, 4: found \r,
		  * 10: wait for \r\n\r\n, 11: wait for \n\r\n, 12: wait for \r\n, 13: wait for \n, 14: eof,
		  * 20: error
		  */
		switch (state->parse_state) {
		case 0:
			state->cur_chunklen = -1;
			li_chunk_parser_prepare(&ctx);
			state->parse_state = 1;
			break;
		case 1:
			read_char(c);
			li_chunk_parser_done(&ctx, 1);
			digit = -1;
			if (c >= '0' && c <= '9') {
				digit = c - '0';
			} else if (c >= 'a' && c <= 'f') {
				digit = c - 'a' + 10;
			} else if (c >= 'A' && c >= 'F') {
				digit = c - 'A' + 10;
			} else if (c == '\r') {
				if (state->cur_chunklen == -1) {
					state->parse_state = 20;
				} else {
					state->parse_state = 2;
				}
			} else {
				state->parse_state = 20;
			}
			if (digit >= 0) {
				if (state->cur_chunklen < 0) {
					state->cur_chunklen = digit;
				} else {
					if ((G_MAXINT64 - digit) / 16 < state->cur_chunklen) {
						state->parse_state = 20; /* overflow */
					} else {
						state->cur_chunklen = 16 * state->cur_chunklen + digit;
					}
				}
			}
			break;
		case 2:
			read_char(c);
			li_chunk_parser_done(&ctx, 1);
			if (c == '\n') {
				li_chunkqueue_skip(in, ctx.bytes_in);
				li_chunk_parser_reset(&ctx); p = NULL;
				if (state->cur_chunklen > 0) {
					state->parse_state = 3;
				} else {
					li_chunk_parser_prepare(&ctx);
					state->parse_state = 12;
				}
			} else {
				state->parse_state = 20;
			}
			break;
		case 3:
			if (state->cur_chunklen != 0) {
				state->cur_chunklen -= li_chunkqueue_steal_len(out, in, state->cur_chunklen);
			}
			if (state->cur_chunklen == 0) {
				li_chunk_parser_prepare(&ctx);
				read_char(c);
				li_chunk_parser_done(&ctx, 1);
				if (c == '\r') {
					state->parse_state = 4;
				} else {
					state->parse_state = 20;
				}
			} else {
				/* wait for more data for the current chunk */
				return TRUE;
			}
			break;
		case 4:
			read_char(c);
			li_chunk_parser_done(&ctx, 1);
			if (c == '\n') {
				li_chunkqueue_skip(in, ctx.bytes_in);
				li_chunk_parser_reset(&ctx); p = NULL;
				state->parse_state = 0;
			} else {
				state->parse_state = 20;
			}
			break;
		case 10: /* \r\n\r\n */
			read_char(c);
			li_chunk_parser_done(&ctx, 1);
			state->parse_state = (c == '\r') ? 11 : 10;
			break;
		case 11: /* \n\r\n */
			read_char(c);
			li_chunk_parser_done(&ctx, 1);
			state->parse_state = (c == '\n') ? 12 : 10;
			break;
		case 12: /* \r\n */
			read_char(c);
			li_chunk_parser_done(&ctx, 1);
			state->parse_state = (c == '\r') ? 13 : 10;
			break;
		case 13: /* \n */
			read_char(c);
			li_chunk_parser_done(&ctx, 1);
			state->parse_state = (c == '\n') ? 14 : 10;
			break;
		case 14:
			out->is_closed = TRUE;
			goto leave;
		case 20:
			goto error;
		}
	}

leave:
	li_chunkqueue_skip(in, ctx.bytes_in);
	return TRUE;

error:
	out->is_closed = TRUE;
	li_chunkqueue_skip_all(in);
	state->parse_state = 20;
	return FALSE;
}
コード例 #7
0
static liHandlerResult cache_etag_filter_miss(liVRequest *vr, liFilter *f) {
	cache_etag_file *cfile = (cache_etag_file*) f->param;
	ssize_t res;
	gchar *buf;
	off_t buflen;
	liChunkIter citer;
	GError *err = NULL;

	if (NULL == f->in) {
		cache_etag_filter_free(vr, f);
		/* didn't handle f->in->is_closed? abort forwarding */
		if (!f->out->is_closed) li_stream_reset(&f->stream);
		return LI_HANDLER_GO_ON;
	}

	if (NULL == cfile) goto forward;

	if (f->in->length > 0) {
		citer = li_chunkqueue_iter(f->in);
		if (LI_HANDLER_GO_ON != li_chunkiter_read(citer, 0, 64*1024, &buf, &buflen, &err)) {
			if (NULL != err) {
				if (NULL != vr) VR_ERROR(vr, "Couldn't read data from chunkqueue: %s", err->message);
				g_error_free(err);
			} else {
				if (NULL != vr) VR_ERROR(vr, "%s", "Couldn't read data from chunkqueue");
			}
			cache_etag_filter_free(vr, f);
			goto forward;
		}

		res = write(cfile->fd, buf, buflen);
		if (res < 0) {
			switch (errno) {
			case EINTR:
			case EAGAIN:
				return LI_HANDLER_COMEBACK;
			default:
				if (NULL != vr) VR_ERROR(vr, "Couldn't write to temporary cache file '%s': %s",
					cfile->tmpfilename->str, g_strerror(errno));
				cache_etag_filter_free(vr, f);
				goto forward;
			}
		} else {
			if (!f->out->is_closed) {
				li_chunkqueue_steal_len(f->out, f->in, res);
			} else {
				li_chunkqueue_skip(f->in, res);
			}
		}
	}

	if (0 == f->in->length && f->in->is_closed) {
		f->out->is_closed = TRUE;
		f->param = NULL;
		cache_etag_file_finish(vr, cfile);
		return LI_HANDLER_GO_ON;
	}

	return LI_HANDLER_GO_ON;

forward:
	if (f->out->is_closed) {
		li_chunkqueue_skip_all(f->in);
		li_stream_disconnect(&f->stream);
	} else {
		li_chunkqueue_steal_all(f->out, f->in);
		if (f->in->is_closed) f->out->is_closed = f->in->is_closed;
	}
	return LI_HANDLER_GO_ON;
}