예제 #1
0
liHandlerResult li_chunk_parser_next(liChunkParserCtx *ctx, char **p, char **pe, GError **err) {
	off_t l;
	liHandlerResult res;

	g_return_val_if_fail (err == NULL || *err == NULL, LI_HANDLER_ERROR);

	if (NULL == ctx->curi.element) return LI_HANDLER_WAIT_FOR_EVENT;

	while (ctx->start >= (l = li_chunkiter_length(ctx->curi))) {
		liChunkIter i = ctx->curi;
		 /* Wait at the end of the last chunk if it gets extended */
		if (!li_chunkiter_next(&i)) return LI_HANDLER_WAIT_FOR_EVENT;
		ctx->curi = i;
		ctx->start -= l;
	}

	if (NULL == ctx->curi.element) return LI_HANDLER_WAIT_FOR_EVENT;

	if (LI_HANDLER_GO_ON != (res = li_chunkiter_read(ctx->curi, ctx->start, l - ctx->start, &ctx->buf, &ctx->length, err))) {
		return res;
	}

	*p = ctx->buf;
	*pe = ctx->buf + ctx->length;
	return LI_HANDLER_GO_ON;
}
예제 #2
0
gboolean li_chunkqueue_extract_to_bytearr(liVRequest *vr, liChunkQueue *cq, goffset len, GByteArray *dest) {
	liChunkIter ci;
	goffset coff, clen;
	g_byte_array_set_size(dest, 0);
	if (len > cq->length) return FALSE;

	g_byte_array_set_size(dest, len);
	g_byte_array_set_size(dest, 0);

	ci = li_chunkqueue_iter(cq);

	while (len > 0) {
		coff = 0;
		clen = li_chunkiter_length(ci);
		while (coff < clen) {
			gchar *buf;
			off_t we_have;
			if (LI_HANDLER_GO_ON != li_chunkiter_read(vr, ci, coff, len, &buf, &we_have)) goto error;
			g_byte_array_append(dest, (guint8*) buf, we_have);
			coff += we_have;
			len -= we_have;
			if (len <= 0) return TRUE;
		}
		li_chunkiter_next(&ci);
	}

	return TRUE;

error:
	g_byte_array_set_size(dest, 0);
	return FALSE;
}
예제 #3
0
gboolean li_chunk_extract_to(liChunkParserMark from, liChunkParserMark to, GString *dest, GError **err) {
	liChunkParserMark i;

	g_return_val_if_fail (err == NULL || *err == NULL, FALSE);

	g_string_set_size(dest, to.abs_pos - from.abs_pos);
	li_g_string_clear(dest);

	for ( i = from; i.ci.element != to.ci.element; li_chunkiter_next(&i.ci) ) {
		goffset len = li_chunkiter_length(i.ci);
		while (i.pos < len) {
			char *buf;
			off_t we_have;
			if (LI_HANDLER_GO_ON != li_chunkiter_read(i.ci, i.pos, len - i.pos, &buf, &we_have, err)) goto error;
			if (dest->len + we_have < dest->allocated_len) {
				/* "fast" append */
				memcpy(dest->str + dest->len, buf, we_have);
				dest->len += we_have;
				dest->str[dest->len] = '\0';
			} else {
				g_string_append_len(dest, buf, we_have);
			}
			i.pos += we_have;
		}
		i.pos = 0;
	}
	while (i.pos < to.pos) {
		char *buf;
		off_t we_have;
		if (LI_HANDLER_GO_ON != li_chunkiter_read(i.ci, i.pos, to.pos - i.pos, &buf, &we_have, err)) goto error;
		if (dest->len + we_have < dest->allocated_len) {
			/* "fast" append */
			memcpy(dest->str + dest->len, buf, we_have);
			dest->len += we_have;
			dest->str[dest->len] = '\0';
		} else {
			g_string_append_len(dest, buf, we_have);
		}
		i.pos += we_have;
	}

	return TRUE;

error:
	li_g_string_clear(dest);
	return FALSE;
}
예제 #4
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;
}
예제 #5
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;
}
예제 #6
0
liHandlerResult li_chunk_parser_next(liVRequest *vr, liChunkParserCtx *ctx, char **p, char **pe) {
	off_t l;
	liHandlerResult res;

	if (NULL == ctx->curi.element) return LI_HANDLER_WAIT_FOR_EVENT;

	while (ctx->start >= (l = li_chunkiter_length(ctx->curi))) {
		liChunkIter i = ctx->curi;
		 /* Wait at the end of the last chunk if it gets extended */
		if (!li_chunkiter_next(&i)) return LI_HANDLER_WAIT_FOR_EVENT;
		ctx->curi = i;
		ctx->start -= l;
	}

	if (NULL == ctx->curi.element) return LI_HANDLER_WAIT_FOR_EVENT;

	if (LI_HANDLER_GO_ON != (res = li_chunkiter_read(vr, ctx->curi, ctx->start, l - ctx->start, &ctx->buf, &ctx->length))) {
		return res;
	}

	*p = ctx->buf;
	*pe = ctx->buf + ctx->length;
	return LI_HANDLER_GO_ON;
}
예제 #7
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;
}
예제 #8
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);
}
예제 #9
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 */
	}
}
예제 #10
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;
}
예제 #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;
}