Exemplo n.º 1
0
static gboolean fastcgi_get_packet(fastcgi_connection *fcon) {
	const unsigned char *data;

	/* already got packet */
	if (fcon->fcgi_in_record.valid) {
		if (0 == fcon->fcgi_in_record.remainingContent) {
			/* wait for padding data ? */
			gint len = fcon->fcgi_in->length;
			if (len > fcon->fcgi_in_record.remainingPadding) len = fcon->fcgi_in_record.remainingPadding;
			li_chunkqueue_skip(fcon->fcgi_in, len);
			fcon->fcgi_in_record.remainingPadding -= len;
			if (0 != fcon->fcgi_in_record.remainingPadding) return FALSE; /* wait for data */
			fcon->fcgi_in_record.valid = FALSE; /* read next packet */
		} else {
			return (fcon->fcgi_in->length > 0); /* wait for/handle more content */
		}
	}

	if (!li_chunkqueue_extract_to_bytearr(fcon->vr, fcon->fcgi_in, FCGI_HEADER_LEN, fcon->buf_in_record)) return FALSE; /* need more data */

	data = (const unsigned char*) fcon->buf_in_record->data;
	fcon->fcgi_in_record.version = data[0];
	fcon->fcgi_in_record.type = data[1];
	fcon->fcgi_in_record.requestID = (data[2] << 8) | (data[3]);
	fcon->fcgi_in_record.contentLength = (data[4] << 8) | (data[5]);
	fcon->fcgi_in_record.paddingLength = data[6];
	fcon->fcgi_in_record.remainingContent = fcon->fcgi_in_record.contentLength;
	fcon->fcgi_in_record.remainingPadding = fcon->fcgi_in_record.paddingLength;
	fcon->fcgi_in_record.valid = TRUE;
	fcon->fcgi_in_record.first = TRUE;

	li_chunkqueue_skip(fcon->fcgi_in, FCGI_HEADER_LEN);

	return TRUE;
}
Exemplo n.º 2
0
static ssize_t stream_pull(gnutls_transport_ptr_t trans, void *buf, size_t len) {
	liGnuTLSFilter *f = (liGnuTLSFilter*) trans;
	liChunkQueue *cq;

	errno = ECONNRESET;
	if (NULL == f || NULL == f->crypt_drain.out) return -1;

	cq = f->crypt_drain.out;

	if (0 == cq->length) {
		if (cq->is_closed) {
			errno = 0;
			return 0;
		} else {
			errno = EAGAIN;
			return -1;
		}
	}

	if (len > (size_t) cq->length) len = cq->length;
	if (!li_chunkqueue_extract_to_memory(cq, len, buf, NULL)) return -1;
	li_chunkqueue_skip(cq, len);

	errno = 0;
	return len;
}
Exemplo n.º 3
0
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;
}
Exemplo n.º 4
0
liNetworkStatus li_network_backend_write(liVRequest *vr, int fd, liChunkQueue *cq, goffset *write_max) {
	const ssize_t blocksize = 16*1024; /* 16k */
	char *block_data;
	off_t block_len;
	ssize_t r;
	gboolean did_write_something = FALSE;
	liChunkIter ci;

	do {
		if (0 == cq->length)
			return did_write_something ? LI_NETWORK_STATUS_SUCCESS : LI_NETWORK_STATUS_FATAL_ERROR;

		ci = li_chunkqueue_iter(cq);
		switch (li_chunkiter_read(vr, ci, 0, blocksize, &block_data, &block_len)) {
		case LI_HANDLER_GO_ON:
			break;
		case LI_HANDLER_ERROR:
		default:
			return LI_NETWORK_STATUS_FATAL_ERROR;
		}

		if (-1 == (r = li_net_write(fd, block_data, block_len))) {
			switch (errno) {
			case EAGAIN:
#if EWOULDBLOCK != EAGAIN
			case EWOULDBLOCK:
#endif
				return did_write_something ? LI_NETWORK_STATUS_SUCCESS : LI_NETWORK_STATUS_WAIT_FOR_EVENT;
			case ECONNRESET:
			case EPIPE:
			case ETIMEDOUT:
				return LI_NETWORK_STATUS_CONNECTION_CLOSE;
			default:
				VR_ERROR(vr, "oops, write to fd=%d failed: %s", fd, g_strerror(errno));
				return LI_NETWORK_STATUS_FATAL_ERROR;
			}
		} else if (0 == r) {
			return did_write_something ? LI_NETWORK_STATUS_SUCCESS : LI_NETWORK_STATUS_WAIT_FOR_EVENT;
		}

		li_chunkqueue_skip(cq, r);
		did_write_something = TRUE;
		*write_max -= r;
	} while (r == block_len && *write_max > 0);

	return LI_NETWORK_STATUS_SUCCESS;
}
Exemplo n.º 5
0
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;
}
Exemplo n.º 6
0
static void do_gnutls_write(liGnuTLSFilter *f) {
	const ssize_t blocksize = 16*1024; /* 16k */
	char *block_data;
	off_t block_len;
	ssize_t r;
	off_t write_max;
#ifdef USE_CORK
	gboolean corked = FALSE;
#endif
	liChunkQueue *cq = f->plain_drain.out;

	f_acquire(f);

	f->write_wants_read = FALSE;

	/* use space in (encrypted) outgoing buffer as amounts of bytes we try to write from (plain) output
	 * don't care if we write a little bit more than the limit allowed */
	write_max = li_chunkqueue_limit_available(f->crypt_source.out);
	LI_FORCE_ASSERT(write_max >= 0); /* we set a limit! */
	if (0 == write_max) goto out;
	/* if we start writing, try to write at least blocksize bytes */
	if (write_max < blocksize) write_max = blocksize;

	if (NULL != f->session && !f->initial_handshaked_finished && !do_gnutls_handshake(f, TRUE)) goto out;
	if (NULL == f->session) {
		f_abort_gnutls(f);
		goto out;
	}

#ifdef USE_CORK
	if (0 != cq->length && cq->queue.length > 1) {
		corked = TRUE;
		gnutls_record_cork(f->session);
	}
#endif

	do {
		GError *err = NULL;
		liChunkIter ci;

		if (0 == cq->length) break;

		ci = li_chunkqueue_iter(cq);
		switch (li_chunkiter_read(ci, 0, blocksize, &block_data, &block_len, &err)) {
		case LI_HANDLER_GO_ON:
			break;
		case LI_HANDLER_ERROR:
			if (NULL != err) {
				_ERROR(f->srv, f->wrk, f->log_context, "Couldn't read data from chunkqueue: %s", err->message);
				g_error_free(err);
			}
			/* fall through */
		default:
			f_abort_gnutls(f);
			goto out;
		}

		r = gnutls_record_send(f->session, block_data, block_len);
		if (r <= 0) {
			do_handle_error(f, "gnutls_record_send", r, TRUE);
			goto out;
		}

		li_chunkqueue_skip(cq, r);
		write_max -= r;
	} while (r == block_len && write_max > 0);

	if (cq->is_closed && 0 == cq->length) {
		r = gnutls_bye(f->session, GNUTLS_SHUT_RDWR);
		switch (r) {
		case GNUTLS_E_SUCCESS:
		case GNUTLS_E_AGAIN:
		case GNUTLS_E_INTERRUPTED:
			f->plain_source.out->is_closed = TRUE;
			f->crypt_source.out->is_closed = TRUE;
			f->crypt_drain.out->is_closed = TRUE;
			li_stream_disconnect(&f->crypt_source); /* plain in -> crypt out */
			f_close_gnutls(f);
			break;
		default:
			do_handle_error(f, "gnutls_bye", r, TRUE);
			f_abort_gnutls(f);
			break;
		}
	} else if (0 < cq->length && 0 != li_chunkqueue_limit_available(f->crypt_source.out)) {
		li_stream_again_later(&f->plain_drain);
	}

out:
#ifdef USE_CORK
	if (NULL != f->session && corked) {
		corked = TRUE;
		gnutls_record_uncork(f->session, 0);
	}
#endif

	f_release(f);
}
Exemplo n.º 7
0
/* first chunk must be a STRING_CHUNK ! */
liNetworkStatus li_network_backend_writev(int fd, liChunkQueue *cq, goffset *write_max, GError **err) {
	off_t we_have;
	ssize_t r;
	gboolean did_write_something = FALSE;
	liChunkIter ci;
	liChunk *c;
	liNetworkStatus res = LI_NETWORK_STATUS_FATAL_ERROR;

	GArray *chunks = g_array_sized_new(FALSE, TRUE, sizeof(struct iovec), UIO_MAXIOV);

	if (0 == cq->length) goto cleanup; /* FATAL ERROR */

	do {
		ci = li_chunkqueue_iter(cq);

		if (STRING_CHUNK != (c = li_chunkiter_chunk(ci))->type && MEM_CHUNK != c->type && BUFFER_CHUNK != c->type) {
			res = did_write_something ? LI_NETWORK_STATUS_SUCCESS : LI_NETWORK_STATUS_FATAL_ERROR;
			goto cleanup;
		}

		we_have = 0;
		do {
			guint i = chunks->len;
			off_t len = li_chunk_length(c);
			struct iovec *v;
			g_array_set_size(chunks, i + 1);
			v = &g_array_index(chunks, struct iovec, i);
			if (c->type == STRING_CHUNK) {
				v->iov_base = c->data.str->str + c->offset;
			} else if (c->type == MEM_CHUNK) {
				v->iov_base = c->mem->data + c->offset;
			} else { /* if (c->type == BUFFER_CHUNK) */
				v->iov_base = c->data.buffer.buffer->addr + c->data.buffer.offset + c->offset;
			}
			if (len > *write_max - we_have) len = *write_max - we_have;
			v->iov_len = len;
			we_have += len;
		} while (we_have < *write_max &&
		         li_chunkiter_next(&ci) &&
		         (STRING_CHUNK == (c = li_chunkiter_chunk(ci))->type || MEM_CHUNK == c->type || BUFFER_CHUNK == c->type) &&
		         chunks->len < UIO_MAXIOV);

		while (-1 == (r = writev(fd, (struct iovec*) chunks->data, chunks->len))) {
			switch (errno) {
			case EAGAIN:
#if EWOULDBLOCK != EAGAIN
			case EWOULDBLOCK:
#endif
				res = LI_NETWORK_STATUS_WAIT_FOR_EVENT;
				goto cleanup;
			case ECONNRESET:
			case EPIPE:
			case ETIMEDOUT:
				res = LI_NETWORK_STATUS_CONNECTION_CLOSE;
				goto cleanup;
			case EINTR:
				break; /* try again */
			default:
				g_set_error(err, LI_NETWORK_ERROR, 0, "li_network_backend_writev: oops, write to fd=%d failed: %s", fd, g_strerror(errno));
				goto cleanup;
			}
		}
		if (0 == r) {
			res = LI_NETWORK_STATUS_WAIT_FOR_EVENT;
			goto cleanup;
		}
		li_chunkqueue_skip(cq, r);
		*write_max -= r;

		if (r != we_have) {
			res = LI_NETWORK_STATUS_WAIT_FOR_EVENT;
			goto cleanup;
		}

		if (0 == cq->length) {
			res = LI_NETWORK_STATUS_SUCCESS;
			goto cleanup;
		}

		did_write_something = TRUE;
		g_array_set_size(chunks, 0);
	} while (*write_max > 0);

	res = LI_NETWORK_STATUS_SUCCESS;

cleanup:
	g_array_free(chunks, TRUE);
	return res;
}
Exemplo n.º 8
0
static void bod_handle_data(bod_state *state) {
	liChunkQueue *out = state->stream.out;
	liChunkQueue *in;

	if (out->is_closed) {
		li_stream_disconnect(&state->stream);
		bod_close(state);
		return;
	}

	in = (state->stream.source != NULL) ? state->stream.source->out : NULL;
	if (NULL == in) goto out;

	if (NULL == state->vr) {
		li_chunkqueue_steal_all(out, in);
		goto out;
	}

	while (in->length > 0) {
		liChunk *c = li_chunkqueue_first_chunk(in);
		liChunkIter ci;
		off_t length, data_len;
		char *data = NULL;
		GError *err;

		assert(UNUSED_CHUNK != c->type);
		switch (c->type) {
		case UNUSED_CHUNK:
			/* shouldn't happen anyway, but stealing it is ok here too */
		case FILE_CHUNK:
			if (state->split_on_file_chunks) {
				bod_close(state);
			} else {
				bod_flush(state);
			}
			li_chunkqueue_steal_chunk(out, in);
			break;
		case STRING_CHUNK:
		case MEM_CHUNK:
		case BUFFER_CHUNK:
			if (!bod_open(state)) return;

			length = li_chunk_length(c);
			ci = li_chunkqueue_iter(in);

			err = NULL;
			if (LI_HANDLER_GO_ON != li_chunkiter_read(ci, 0, length, &data, &data_len, &err)) {
				if (NULL != err) {
					VR_ERROR(state->vr, "%s", err->message);
					g_error_free(err);
				}
				bod_error(state);
				return;
			}

			while ( data_len > 0 ) {
				ssize_t r;

				r = pwrite(state->tempfile->fd, data, data_len, state->write_pos);

				if (r < 0) {
					switch (errno) {
					case EINTR: continue;
					default: break;
					}

					VR_ERROR(state->vr, "pwrite failed: %s", g_strerror(errno));
					bod_stop(state); /* write failures are not critical */
					return;
				}

				data += r;
				data_len -= r;
				state->write_pos += r;
			}

			li_chunkqueue_skip(in, length);

			break;
		}
	}

	bod_autoflush(state);

out:
	if (NULL == in || in->is_closed) {
		out->is_closed = TRUE;
		bod_close(state); /* close/flush ignores out->is_closed */
		li_stream_notify(&state->stream); /* if no flush happened we still notify */
	}
}
Exemplo n.º 9
0
liHandlerResult li_filter_buffer_on_disk(liVRequest *vr, liChunkQueue *out, liChunkQueue *in, bod_state *state) {
	UNUSED(vr);

	if (out->is_closed) {
		in->is_closed = TRUE;
		li_chunkqueue_skip_all(in);
		bod_close(state);
		return LI_HANDLER_GO_ON;
	}

	while (in->length > 0) {
		liChunk *c = li_chunkqueue_first_chunk(in);
		liChunkIter ci;
		off_t length, data_len;
		char *data = NULL;
		GError *err;

		switch (c->type) {
		case UNUSED_CHUNK: return LI_HANDLER_ERROR;
		case FILE_CHUNK:
			bod_flush(out, state);
			if (state->split_on_file_chunks) {
				bod_close(state);
			}
			li_chunkqueue_steal_chunk(out, in);
			break;
		case STRING_CHUNK:
		case MEM_CHUNK:
		case BUFFER_CHUNK:
			if (!bod_open(vr, state)) return LI_HANDLER_ERROR;

			length = li_chunk_length(c);
			ci = li_chunkqueue_iter(in);

			err = NULL;
			if (LI_HANDLER_GO_ON != li_chunkiter_read(ci, 0, length, &data, &data_len, &err)) {
				if (NULL != err) {
					VR_ERROR(vr, "%s", err->message);
					g_error_free(err);
				}
				return LI_HANDLER_ERROR;
			}

			while ( data_len > 0 ) {
				ssize_t r;

				r = pwrite(state->tempfile->fd, data, data_len, state->write_pos);

				if (r < 0) {
					switch (errno) {
					case EINTR: continue;
					default: break;
					}

					VR_ERROR(vr, "pwrite failed: %s", g_strerror(errno));
					return LI_HANDLER_ERROR;
				}

				data += r;
				data_len -= r;
				state->write_pos += r;
			}

			li_chunkqueue_skip(in, length);

			break;
		}
	}

	bod_autoflush(out, state);

	if (in->is_closed) {
		bod_flush(out, state);
		out->is_closed = TRUE;
		bod_close(state);
		return LI_HANDLER_GO_ON;
	}
	return LI_HANDLER_GO_ON;
}
Exemplo n.º 10
0
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;
}
Exemplo n.º 11
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;
}