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; }
static void send_queue_push_buffer(GQueue *queue, liBuffer *buf, gsize start, gsize len) { send_item *i; if (!buf || !len) return; g_assert(start+len <= buf->used); li_buffer_acquire(buf); i = g_slice_new0(send_item); i->buf = buf; i->pos = start; i->len = len; g_queue_push_tail(queue, i); }
liMemcachedRequest* li_memcached_set(liMemcachedCon *con, GString *key, guint32 flags, li_tstamp ttl, liBuffer *data, liMemcachedCB callback, gpointer cb_data, GError **err) { int_request* req; if (!li_memcached_is_key_valid(key)) { g_set_error(err, LI_MEMCACHED_ERROR, LI_MEMCACHED_BAD_KEY, "Invalid key: '%s'", key->str); return NULL; } if (-1 == con->fd) memcached_connect(con); if (-1 == con->fd) { if (NULL == con->err) { g_set_error(err, LI_MEMCACHED_ERROR, LI_MEMCACHED_DISABLED, "Not connected"); } else if (err) { *err = g_error_copy(con->err); } return NULL; } req = g_slice_new0(int_request); req->req.callback = callback; req->req.cb_data = cb_data; req->type = REQ_SET; req->key = g_string_new_len(GSTR_LEN(key)); req->flags = flags; req->ttl = ttl; if (NULL != data) { li_buffer_acquire(data); req->data = data; } if (!push_request(con, req, err)) { free_request(con, req); return NULL; } return &req->req; }
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); }
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; }
/* steal up to length bytes from in and put them into out, return number of bytes stolen */ goffset li_chunkqueue_steal_len(liChunkQueue *out, liChunkQueue *in, goffset length) { liChunk *c, *cnew; GList* l; goffset bytes = 0, meminbytes = 0, memoutbytes = 0; goffset we_have; while ( (NULL != (c = li_chunkqueue_first_chunk(in))) && length > 0 ) { we_have = li_chunk_length(c); if (!we_have) { /* remove empty chunks */ if (c->type == STRING_CHUNK) meminbytes -= c->data.str->len; else if (c->type == MEM_CHUNK) meminbytes -= c->mem->len; else if (c->type == BUFFER_CHUNK) meminbytes -= c->data.buffer.length; chunk_free(in, c); continue; } if (we_have <= length) { /* move complete chunk */ l = g_queue_pop_head_link(&in->queue); g_queue_push_tail_link(&out->queue, l); bytes += we_have; if (c->type == STRING_CHUNK) { meminbytes -= c->data.str->len; memoutbytes += c->data.str->len; } else if (c->type == MEM_CHUNK) { meminbytes -= c->mem->len; memoutbytes += c->mem->len; } else if (c->type == BUFFER_CHUNK) { meminbytes -= c->data.buffer.length; memoutbytes += c->data.buffer.length; } length -= we_have; } else { /* copy first part of a chunk */ cnew = chunk_new(); switch (c->type) { case UNUSED_CHUNK: /* impossible, has length 0 */ /* remove "empty" chunks */ chunk_free(in, c); chunk_free(NULL, cnew); continue; case STRING_CHUNK: /* change type to MEM_CHUNK, as we copy it anyway */ cnew->type = MEM_CHUNK; cnew->mem = g_byte_array_sized_new(length); g_byte_array_append(cnew->mem, (guint8*) c->data.str->str + c->offset, length); memoutbytes += length; break; case MEM_CHUNK: cnew->type = MEM_CHUNK; cnew->mem = g_byte_array_sized_new(length); g_byte_array_append(cnew->mem, (guint8*) c->mem->data + c->offset, length); memoutbytes += length; break; case FILE_CHUNK: cnew->type = FILE_CHUNK; li_chunkfile_acquire(c->data.file.file); cnew->data.file.file = c->data.file.file; cnew->data.file.start = c->data.file.start + c->offset; cnew->data.file.length = length; break; case BUFFER_CHUNK: cnew->type = BUFFER_CHUNK; li_buffer_acquire(c->data.buffer.buffer); cnew->data.buffer.buffer = c->data.buffer.buffer; cnew->data.buffer.offset = c->data.buffer.offset + c->offset; cnew->data.buffer.length = length; memoutbytes += length; break; } c->offset += length; bytes += length; length = 0; g_queue_push_tail_link(&out->queue, &cnew->cq_link); } } in->bytes_out += bytes; in->length -= bytes; out->bytes_in += bytes; out->length += bytes; cqlimit_update(out, memoutbytes); cqlimit_update(in, meminbytes); return bytes; }