Example #1
0
static void li_memcached_con_free(liMemcachedCon* con) {
	if (!con) return;

	if (-1 != li_event_io_fd(&con->con_watcher)) {
		close(li_event_io_fd(&con->con_watcher));
		li_event_clear(&con->con_watcher);
		con->fd = -1;
	}

	send_queue_reset(&con->out);
	cancel_all_requests(con);

	li_buffer_release(con->buf);
	li_buffer_release(con->line);
	li_buffer_release(con->remaining);
	li_buffer_release(con->data);
	reset_item(&con->curitem);

	li_sockaddr_clear(&con->addr);
	g_string_free(con->tmpstr, TRUE);

	g_clear_error(&con->err);

	g_slice_free(liMemcachedCon, con);
}
Example #2
0
static void li_memcached_con_free(liMemcachedCon* con) {
	if (!con) return;

	if (-1 != con->con_watcher.fd) {
		close(con->con_watcher.fd);
		/* as io has a reference on con, we don't need to stop it here */
		ev_io_set(&con->con_watcher, -1, 0);
		con->fd = -1;
	}

	send_queue_reset(&con->out);
	cancel_all_requests(con);

	li_buffer_release(con->buf);
	li_buffer_release(con->line);
	li_buffer_release(con->remaining);
	li_buffer_release(con->data);
	reset_item(&con->curitem);

	li_sockaddr_clear(&con->addr);
	g_string_free(con->tmpstr, TRUE);

	g_clear_error(&con->err);

	g_slice_free(liMemcachedCon, con);
}
Example #3
0
static ssize_t stream_pushv(gnutls_transport_ptr_t trans, const li_iovec_t * iov, int iovcnt) {
	const ssize_t blocksize = 16*1024; /* 16k */
	liGnuTLSFilter *f = (liGnuTLSFilter*) trans;
	liChunkQueue *cq;
	int i;
	liBuffer *buf;
	gboolean cq_buf_append;
	ssize_t written = 0;

	errno = ECONNRESET;

	if (NULL == f || NULL == f->crypt_source.out) return -1;
	cq = f->crypt_source.out;
	if (cq->is_closed) return -1;

	buf = f->raw_out_buffer;
	cq_buf_append = (buf != NULL && buf == li_chunkqueue_get_last_buffer(cq, 1024));
	for (i = 0; i < iovcnt; ++i) {
		const char *data = iov[i].iov_base;
		size_t len = iov[i].iov_len;

		while (len > 0) {
			size_t bufsize, do_write;
			if (NULL == buf) buf = li_buffer_new(blocksize);

			bufsize = buf->alloc_size - buf->used;
			do_write = (bufsize > len) ? len : bufsize;
			memcpy(buf->addr + buf->used, data, do_write);
			len -= do_write;
			data += do_write;
			if (cq_buf_append) {
				/* also updates buf->used */
				li_chunkqueue_update_last_buffer_size(cq, do_write);
			} else {
				gsize offset = buf->used;
				buf->used += do_write;
				li_buffer_acquire(buf);
				li_chunkqueue_append_buffer2(cq, buf, offset, do_write);
				cq_buf_append = TRUE;
			}
			if (buf->used == buf->alloc_size) {
				li_buffer_release(buf);
				buf = NULL;
				cq_buf_append = FALSE;
			}
			written += do_write;
		}
	}
	if (NULL != buf && buf->alloc_size - buf->used < 1024) {
		li_buffer_release(buf);
		f->raw_out_buffer = buf = NULL;
	} else {
		f->raw_out_buffer = buf;
	}

	li_stream_notify_later(&f->crypt_source);

	errno = 0;
	return written;
}
Example #4
0
static void send_queue_reset(GQueue *queue) {
	send_item *i;
	while (NULL != (i = g_queue_pop_head(queue))) {
		li_buffer_release(i->buf);
		g_slice_free(send_item, i);
	}
}
Example #5
0
static void f_release(liGnuTLSFilter *f) {
	LI_FORCE_ASSERT(f->refcount > 0);
	if (0 == --f->refcount) {
		f->refcount = 1;
		f_close_gnutls(f);
		if (NULL != f->raw_in_buffer) {
			li_buffer_release(f->raw_in_buffer);
			f->raw_in_buffer = NULL;
		}
		if (NULL != f->raw_out_buffer) {
			li_buffer_release(f->raw_out_buffer);
			f->raw_out_buffer = NULL;
		}

		g_slice_free(liGnuTLSFilter, f);
	}
}
Example #6
0
static void send_queue_clean(GQueue *queue) {
	send_item *i;
	while (NULL != (i = g_queue_peek_head(queue))) {
		if (i->len != 0) return;
		g_queue_pop_head(queue);
		li_buffer_release(i->buf);
		g_slice_free(send_item, i);
	}
}
Example #7
0
static void add_remaining(liMemcachedCon *con, gchar *addr, gsize len) {
	liBuffer *rem = con->remaining;
	if (!rem) rem = con->remaining = li_buffer_new_slice(MAX(BUFFER_CHUNK_SIZE, len));
	if (rem->used + len > rem->alloc_size) {
		rem = li_buffer_new_slice(MAX(BUFFER_CHUNK_SIZE, rem->used + len));
		memcpy(rem->addr, con->remaining->addr, (rem->used = con->remaining->used));
		li_buffer_release(con->remaining);
		con->remaining = rem;
	}

	memcpy(rem->addr + rem->used, addr, len);
	rem->used += len;
}
Example #8
0
static void reset_item(liMemcachedItem *item) {
	if (item->key) {
		g_string_free(item->key, TRUE);
		item->key = NULL;
	}
	item->flags = 0;
	item->ttl = 0;
	item->cas = 0;
	if (item->data) {
		li_buffer_release(item->data);
		item->data = NULL;
	}
}
Example #9
0
static void memcache_store_filter_free(liVRequest *vr, liFilter *f) {
	memcache_filter *mf = (memcache_filter*) f->param;
	UNUSED(vr);

	if (NULL == f->param) return;

	f->param = NULL;

	mc_ctx_release(NULL, mf->ctx);
	li_buffer_release(mf->buf);
	mf->buf = NULL;

	g_slice_free(memcache_filter, mf);
}
Example #10
0
static liHandlerResult mc_lookup_handle_free(liVRequest *vr, gpointer param, gpointer context) {
	memcache_request *req = context;
	UNUSED(vr);
	UNUSED(param);

	if (NULL == req->req) {
		li_buffer_release(req->buffer);
		g_slice_free(memcache_request, req);
	} else {
		req->vr = NULL;
	}

	return LI_HANDLER_GO_ON;
}
Example #11
0
 /* pass ownership of one buffer reference to chunkqueue
  * if the length is NULL, reference is released immediately
  */
void li_chunkqueue_append_buffer(liChunkQueue *cq, liBuffer *buffer) {
	liChunk *c;
	if (!buffer->used) {
		li_buffer_release(buffer);
		return;
	}
	c = chunk_new();
	c->type = BUFFER_CHUNK;
	c->data.buffer.buffer = buffer;
	c->data.buffer.offset = 0;
	c->data.buffer.length = buffer->used;
	g_queue_push_tail_link(&cq->queue, &c->cq_link);
	cq->length += buffer->used;
	cq->bytes_in += buffer->used;
	cqlimit_update(cq, buffer->used);
}
Example #12
0
void li_chunkqueue_append_buffer2(liChunkQueue *cq, liBuffer *buffer, gsize offset, gsize length) {
	liChunk *c;
	if (length == 0) {
		li_buffer_release(buffer);
		return;
	}
	assert(offset + length <= buffer->used);
	c = chunk_new();
	c->type = BUFFER_CHUNK;
	c->data.buffer.buffer = buffer;
	c->data.buffer.offset = offset;
	c->data.buffer.length = length;
	g_queue_push_tail_link(&cq->queue, &c->cq_link);
	cq->length += length;
	cq->bytes_in += length;
	cqlimit_update(cq, length);
}
Example #13
0
static void send_queue_push_gstring(GQueue *queue, GString *str, liBuffer **pbuf) {
	liBuffer *buf = *pbuf;
	gsize pos;

	if (NULL != buf && (1 == buf->refcount)) {
		buf->used = 0;
	}
	if (NULL == buf || (buf->alloc_size - buf->used < str->len)) {
		li_buffer_release(buf);
		buf = li_buffer_new_slice(BUFFER_CHUNK_SIZE > str->len ? BUFFER_CHUNK_SIZE : str->len);
		*pbuf = buf;
	}

	pos = buf->used;
	memcpy(buf->addr + pos, str->str, str->len);
	buf->used += str->len;
	send_queue_push_buffer(queue, buf, pos, str->len);
}
Example #14
0
static void proxy_connection_free(proxy_connection *pcon) {
	liVRequest *vr;
	if (!pcon) return;

	vr = pcon->vr;
	ev_io_stop(vr->wrk->loop, &pcon->fd_watcher);
	proxy_context_release(pcon->ctx);
	if (pcon->fd != -1) close(pcon->fd);
	li_vrequest_backend_finished(vr);

	li_chunkqueue_free(pcon->proxy_in);
	li_chunkqueue_free(pcon->proxy_out);
	li_buffer_release(pcon->proxy_in_buffer);

	li_http_response_parser_clear(&pcon->parse_response_ctx);

	g_slice_free(proxy_connection, pcon);
}
Example #15
0
static void fastcgi_connection_free(fastcgi_connection *fcon) {
	liVRequest *vr;
	if (!fcon) return;

	vr = fcon->vr;
	ev_io_stop(vr->wrk->loop, &fcon->fd_watcher);
	fastcgi_context_release(fcon->ctx);
	if (fcon->fd != -1) close(fcon->fd);
	li_vrequest_backend_finished(vr);

	li_chunkqueue_free(fcon->fcgi_in);
	li_chunkqueue_free(fcon->fcgi_out);
	li_chunkqueue_free(fcon->stdout);
	li_buffer_release(fcon->fcgi_in_buffer);
	g_byte_array_free(fcon->buf_in_record, TRUE);

	li_http_response_parser_clear(&fcon->parse_response_ctx);

	g_slice_free(fastcgi_connection, fcon);
}
Example #16
0
static void chunk_free(liChunkQueue *cq, liChunk *c) {
	if (!c) return;
	if (cq) {
		g_queue_unlink(&cq->queue, &c->cq_link);
	}
	switch (c->type) {
	case UNUSED_CHUNK:
		break;
	case STRING_CHUNK:
		g_string_free(c->data.str, TRUE);
		c->data.str = NULL;
		break;
	case MEM_CHUNK:
		/* mem is handled extra below */
		break;
	case FILE_CHUNK:
		if (c->data.file.file) {
			li_chunkfile_release(c->data.file.file);
			c->data.file.file = NULL;
		}
		if (c->data.file.mmap.data != MAP_FAILED) {
			munmap(c->data.file.mmap.data, c->data.file.mmap.length);
			c->data.file.mmap.data = MAP_FAILED;
		}
		break;
	case BUFFER_CHUNK:
		li_buffer_release(c->data.buffer.buffer);
		break;
	}
	c->type = UNUSED_CHUNK;
	if (c->mem) {
		g_byte_array_free(c->mem, TRUE);
		c->mem = NULL;
	}
	g_slice_free(liChunk, c);
}
Example #17
0
static void free_request(liMemcachedCon *con, int_request *req) {
	if (!req) return;

	li_memcached_con_release(con);

	if (NULL != req->iter.data) {
		req->iter.data = NULL;
		g_queue_unlink(&con->req_queue, &req->iter);
	}

	switch (req->type) {
	case REQ_GET:
		break;
	case REQ_SET:
		li_buffer_release(req->data);
		req->data = NULL;
		break;
	}

	g_string_free(req->key, TRUE);
	req->key = NULL;

	g_slice_free(int_request, req);
}
Example #18
0
static liHandlerResult mc_handle_lookup(liVRequest *vr, gpointer param, gpointer *context) {
	memcached_ctx *ctx = param;
	memcache_request *req = *context;

	if (req) {
		static const GString default_mime_str = { CONST_STR_LEN("application/octet-stream"), 0 };

		liBuffer *buf = req->buffer;
		const GString *mime_str;

		if (NULL != req->req) return LI_HANDLER_WAIT_FOR_EVENT; /* not done yet */

		g_slice_free(memcache_request, req);
		*context = NULL;

		if (NULL == buf) {
			/* miss */
			if (ctx->act_miss) li_action_enter(vr, ctx->act_miss);
			return LI_HANDLER_GO_ON;
		}

		if (!li_vrequest_handle_direct(vr)) {
			if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
				VR_DEBUG(vr, "%s", "memcached.lookup: request already handled");
			}
			li_buffer_release(buf);
			return LI_HANDLER_GO_ON;
		}

		if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
			VR_DEBUG(vr, "%s", "memcached.lookup: key found, handling request");
		}

		li_chunkqueue_append_buffer(vr->direct_out, buf);

		vr->response.http_status = 200;

		mime_str = li_mimetype_get(vr, vr->request.uri.path);
		if (!mime_str) mime_str = &default_mime_str;
		li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Type"), GSTR_LEN(mime_str));

		/* hit */
		if (ctx->act_found) li_action_enter(vr, ctx->act_found);
		return LI_HANDLER_GO_ON;
	} else {
		liMemcachedCon *con;
		GError *err = NULL;

		if (li_vrequest_is_handled(vr)) {
			if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
				VR_DEBUG(vr, "%s", "memcached.lookup: request already handled");
			}
			return LI_HANDLER_GO_ON;
		}

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

		if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
			VR_DEBUG(vr, "memcached.lookup: looking up key '%s'", vr->wrk->tmp_str->str);
		}

		req = g_slice_new0(memcache_request);
		req->req = li_memcached_get(con, vr->wrk->tmp_str, memcache_callback, req, &err);

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

			/* miss */
			if (ctx->act_miss) li_action_enter(vr, ctx->act_miss);

			return LI_HANDLER_GO_ON;
		}
		req->vr = vr;

		*context = req;

		return LI_HANDLER_WAIT_FOR_EVENT;
	}
}
Example #19
0
static void do_gnutls_read(liGnuTLSFilter *f) {
	const ssize_t blocksize = 16*1024; /* 16k */
	off_t max_read = 4 * blocksize; /* 64k */
	ssize_t r;
	off_t len = 0;
	liChunkQueue *cq = f->plain_source.out;

	f_acquire(f);

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

	do {
		liBuffer *buf;
		gboolean cq_buf_append;

		buf = li_chunkqueue_get_last_buffer(cq, 1024);
		cq_buf_append = (buf != NULL);

		if (buf != NULL) {
			/* use last buffer as raw_in_buffer; they should be the same anyway */
			if (G_UNLIKELY(buf != f->raw_in_buffer)) {
				li_buffer_acquire(buf);
				li_buffer_release(f->raw_in_buffer);
				f->raw_in_buffer = buf;
			}
		} else {
			buf = f->raw_in_buffer;
			if (buf != NULL && buf->alloc_size - buf->used < 1024) {
				/* release *buffer */
				li_buffer_release(buf);
				f->raw_in_buffer = buf = NULL;
			}
			if (buf == NULL) {
				f->raw_in_buffer = buf = li_buffer_new(blocksize);
			}
		}
		LI_FORCE_ASSERT(f->raw_in_buffer == buf);

		r = gnutls_record_recv(f->session, buf->addr + buf->used, buf->alloc_size - buf->used);
		if (r < 0) {
			do_handle_error(f, "gnutls_record_recv", r, FALSE);
			goto out;
		} else if (r == 0) {
			/* clean shutdown? */
			f->plain_source.out->is_closed = TRUE;
			f->plain_drain.out->is_closed = TRUE;
			f->crypt_source.out->is_closed = TRUE;
			f->crypt_drain.out->is_closed = TRUE;
			li_stream_disconnect(&f->crypt_drain); /* io -> crypt in */
			li_stream_disconnect_dest(&f->crypt_source); /* crypt out -> io */
			li_stream_disconnect(&f->crypt_source); /* plain in -> crypt out */
			f_close_gnutls(f);
			goto out;
		}

		if (cq_buf_append) {
			li_chunkqueue_update_last_buffer_size(cq, r);
		} else {
			gsize offset;

			li_buffer_acquire(buf);

			offset = buf->used;
			buf->used += r;
			li_chunkqueue_append_buffer2(cq, buf, offset, r);
		}
		if (buf->alloc_size - buf->used < 1024) {
			/* release *buffer */
			li_buffer_release(buf);
			f->raw_in_buffer = buf = NULL;
		}
		len += r;
	} while (len < max_read);

out:
	f_release(f);
}
Example #20
0
static gboolean try_read_data(liMemcachedCon *con, gsize datalen) {
	liBuffer *data;
	ssize_t r;

	datalen += 2; /* \r\n */

	/* if we have remaining data use it for a new line */
	if ((!con->data || con->data->used == 0) && con->remaining && con->remaining->used > 0) {
		liBuffer *tmp = con->remaining; con->remaining = con->data; con->data = tmp;
	}

	if (!con->data) con->data = li_buffer_new_slice(MAX(BUFFER_CHUNK_SIZE, datalen));

	if (con->data->alloc_size < datalen) {
		data = li_buffer_new_slice(MAX(BUFFER_CHUNK_SIZE, datalen));
		memcpy(data->addr, con->data->addr, (data->used = con->data->used));
		li_buffer_release(con->data);
		con->data = data;
	}

	g_assert(NULL == con->remaining || 0 == con->remaining->used); /* there shouldn't be any data in remaining while we fill con->data */

	data = con->data;

	if (data->used < datalen) {
		/* read more data */
		r = net_read(con->fd, data->addr + data->used, data->alloc_size - data->used);
		if (r == 0) {
			/* EOF */
			g_clear_error(&con->err);
			g_set_error(&con->err, LI_MEMCACHED_ERROR, LI_MEMCACHED_CONNECTION, "Connection closed by peer");
			close_con(con);
			return FALSE;
		} else if (r < 0) {
			switch (errno) {
			case EAGAIN:
#if EWOULDBLOCK != EAGAIN
			case EWOULDBLOCK:
#endif
				break;
			default:
				g_clear_error(&con->err);
				g_set_error(&con->err, LI_MEMCACHED_ERROR, LI_MEMCACHED_CONNECTION, "Connection closed: %s", g_strerror(errno));
				close_con(con);
				break;
			}
			return FALSE;
		}

		data->used += r;
	}

	if (data->used >= datalen) {
		if (data->addr[datalen-2] != '\r' || data->addr[datalen-1] != '\n') {
			/* Protocol error: data block not terminated with \r\n */
			g_clear_error(&con->err);
			g_set_error(&con->err, LI_MEMCACHED_ERROR, LI_MEMCACHED_CONNECTION, "Protocol error: data block not terminated with \\r\\n");
			close_con(con);
			return FALSE;
		}
		add_remaining(con, data->addr + datalen, data->used - datalen);
		data->used = datalen - 2;
		data->addr[datalen-2] = '\0';
		return TRUE;
	}

	return FALSE;
}
Example #21
0
liNetworkStatus li_network_read(liVRequest *vr, int fd, liChunkQueue *cq, liBuffer **buffer) {
	const ssize_t blocksize = 16*1024; /* 16k */
	off_t max_read = 16 * blocksize; /* 256k */
	ssize_t r;
	off_t len = 0;

	if (cq->limit && cq->limit->limit > 0) {
		if (max_read > cq->limit->limit - cq->limit->current) {
			max_read = cq->limit->limit - cq->limit->current;
			if (max_read <= 0) {
				max_read = 0; /* we still have to read something */
				VR_ERROR(vr, "%s", "li_network_read: fd should be disabled as chunkqueue is already full");
			}
		}
	}

	do {
		liBuffer *buf = NULL;
		gboolean cq_buf_append;

		buf = li_chunkqueue_get_last_buffer(cq, 1024);
		cq_buf_append = (buf != NULL);

		if (NULL != buffer) {
			if (buf != NULL) {
				/* use last buffer as *buffer; they should be the same anyway */
				if (G_UNLIKELY(buf != *buffer)) {
					li_buffer_acquire(buf);
					li_buffer_release(*buffer);
					*buffer = buf;
				}
			} else {
				buf = *buffer;
				if (buf != NULL) {
					/* if *buffer is the only reference, we can reset the buffer */
					if (g_atomic_int_get(&buf->refcount) == 1) {
						buf->used = 0;
					}

					if (buf->alloc_size - buf->used < 1024) {
						/* release *buffer */
						li_buffer_release(buf);
						*buffer = buf = NULL;
					}
				}
				if (buf == NULL) {
					*buffer = buf = li_buffer_new(blocksize);
				}
			}
			assert(*buffer == buf);
		} else {
			if (buf == NULL) {
				buf = li_buffer_new(blocksize);
			}
		}

		if (-1 == (r = li_net_read(fd, buf->addr + buf->used, buf->alloc_size - buf->used))) {
			if (buffer == NULL && !cq_buf_append) li_buffer_release(buf);
			switch (errno) {
			case EAGAIN:
#if EWOULDBLOCK != EAGAIN
			case EWOULDBLOCK:
#endif
				return LI_NETWORK_STATUS_WAIT_FOR_EVENT;
			case ECONNRESET:
			case ETIMEDOUT:
				return LI_NETWORK_STATUS_CONNECTION_CLOSE;
			default:
				VR_ERROR(vr, "oops, read from fd=%d failed: %s", fd, g_strerror(errno) );
				return LI_NETWORK_STATUS_FATAL_ERROR;
			}
		} else if (0 == r) {
			if (buffer == NULL && !cq_buf_append) li_buffer_release(buf);
			return LI_NETWORK_STATUS_CONNECTION_CLOSE;
		}
		if (cq_buf_append) {
			li_chunkqueue_update_last_buffer_size(cq, r);
		} else {
			gsize offset;

			if (buffer != NULL) li_buffer_acquire(buf);

			offset = buf->used;
			buf->used += r;
			li_chunkqueue_append_buffer2(cq, buf, offset, r);
		}
		if (NULL != buffer) {
			if (buf->alloc_size - buf->used < 1024) {
				/* release *buffer */
				li_buffer_release(buf);
				*buffer = buf = NULL;
			}
		}
		len += r;
	} while (r == blocksize && len < max_read);

	return LI_NETWORK_STATUS_SUCCESS;
}
Example #22
0
static void send_queue_item_free(send_item *i) {
	if (!i) return;
	li_buffer_release(i->buf);
	g_slice_free(send_item, i);
}