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; }
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; }
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; }
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; }
/* 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; }