vessel/web/httpbody.c
Arija A. 4f0bf80e5f
refact: Remove mem.h
Signed-off-by: Arija A. <ari@ari.lt>
2025-06-21 23:43:31 +03:00

536 lines
13 KiB
C

#include "include/conf.h"
#include "include/http.h"
#include "include/httpbody.h"
#include <stdio.h>
#include <stdarg.h>
bool vw_HTTPBody_init(vw_HTTPBody *body, vs_Stream *stream, size_t datasz, bool chunked) {
if (!body || !stream) {
return false;
}
body->stream = stream;
body->datasz = datasz;
body->chunked = chunked;
body->finished = false;
return true;
}
bool vw_HTTPBody_read1(vw_HTTPBody *body, void *buf) {
if (!body || body->finished) {
return false;
}
if (body->datasz == 0) {
if (body->chunked) {
bool found = false;
char hex[20];
if (vs_Stream_ignore_err(vs_Stream_readb(body->stream,
hex,
sizeof(hex),
VW_HTTP_CRLF,
VW_HTTP_CRLF_LENGTH,
&found,
true)) == 0 ||
!found) {
return false;
}
body->datasz = vs_hex2usize(hex);
if (body->datasz == 0) {
body->finished = true;
vs_Stream_read(body->stream, NULL, VW_HTTP_CRLF_LENGTH);
return false;
}
} else {
body->finished = true;
return false;
}
}
const bool ret = vs_Stream_read1(body->stream, buf);
if (ret) {
--body->datasz;
if (body->datasz == 0 &&
vs_Stream_read(body->stream, NULL, VW_HTTP_CRLF_LENGTH) != VW_HTTP_CRLF_LENGTH) {
return false;
}
}
return ret;
}
size_t vw_HTTPBody_read(vw_HTTPBody *body, void *buf, size_t count) {
if (!body) {
return VS_STREAM_ERROR;
}
if (body->finished) {
return 0;
}
size_t total_read = 0;
do {
if (body->datasz == 0) {
if (body->chunked) {
bool found = false;
char hex[20];
if (vs_Stream_ignore_err(vs_Stream_readb(body->stream,
hex,
sizeof(hex),
VW_HTTP_CRLF,
VW_HTTP_CRLF_LENGTH,
&found,
true)) == 0) {
return false;
}
if (!found) {
return false;
}
body->datasz = vs_hex2usize(hex);
if (body->datasz == 0) {
body->finished = true;
return vs_Stream_read(body->stream, NULL, VW_HTTP_CRLF_LENGTH)
? total_read
: VS_STREAM_ERROR;
}
} else {
body->finished = true;
return total_read;
}
}
const size_t now_read = vs_Stream_read(body->stream,
buf ? ((uint8_t *)buf + total_read) : NULL,
VS_MIN(body->datasz, count - total_read));
if (vs_Stream_ignore_err(now_read) == 0) {
break;
}
body->datasz -= now_read;
total_read += now_read;
if (body->datasz == 0) {
if (body->chunked) {
/* End of chunk */
if (vs_Stream_read(body->stream, NULL, VW_HTTP_CRLF_LENGTH) !=
VW_HTTP_CRLF_LENGTH) {
break;
}
} else {
/* End of data */
body->finished = true;
break;
}
}
} while (total_read < count);
return total_read;
}
size_t vw_HTTPBody_readb(vw_HTTPBody *body,
void *buf,
size_t buf_size,
const void *boundary,
size_t boundary_size,
bool *found,
bool is_string) {
if (found) {
*found = false;
}
if (!body || !boundary) {
return VS_STREAM_ERROR;
}
if (body->finished) {
return 0;
}
size_t total_read = 0;
size_t matched_bytes = 0;
const size_t full_read_size = buf_size - is_string;
do {
if (body->datasz == 0) {
if (body->chunked) {
bool read_found = false;
char hex[20];
if (vs_Stream_ignore_err(vs_Stream_readb(body->stream,
hex,
sizeof(hex),
VW_HTTP_CRLF,
VW_HTTP_CRLF_LENGTH,
&read_found,
true)) == 0) {
return false;
}
if (!read_found) {
return false;
}
body->datasz = vs_hex2usize(hex);
if (body->datasz == 0) {
body->finished = true;
return vs_Stream_read(body->stream, NULL, VW_HTTP_CRLF_LENGTH)
? total_read
: VS_STREAM_ERROR;
}
} else {
body->finished = true;
return total_read;
}
}
uint8_t byte = 0;
if (!vs_Stream_read1(body->stream, &byte)) {
break;
}
--body->datasz;
if (is_string && byte == '\0') {
return total_read;
}
if (byte == *((const uint8_t *)boundary + matched_bytes)) {
++matched_bytes;
if (matched_bytes == boundary_size) {
const size_t total = total_read - matched_bytes + 1;
if (found) {
*found = true;
}
if (is_string && buf) {
*((uint8_t *)buf + total) = '\0';
}
return total;
}
} else {
matched_bytes = 0;
}
if (buf) {
*((uint8_t *)buf + total_read) = byte;
}
++total_read;
if (body->datasz == 0) {
if (body->chunked) {
/* End of chunk */
if (vs_Stream_read(body->stream, NULL, VW_HTTP_CRLF_LENGTH) !=
VW_HTTP_CRLF_LENGTH) {
break;
}
} else {
/* End of data */
body->finished = true;
break;
}
}
} while (total_read < full_read_size);
if (is_string) {
if (buf) {
*((uint8_t *)buf + total_read) = '\0';
}
++total_read;
}
return total_read;
}
size_t vw_HTTPBody_readbf(vw_HTTPBody *body,
void *buf,
size_t buf_size,
bool *found,
bool is_string,
const char *boundary_fmt,
...) {
if (!body) {
return VS_STREAM_ERROR;
}
if (body->finished || !boundary_fmt) {
return 0;
}
va_list args;
char *temp_buf = NULL;
size_t size = 0;
ssize_t needed = 0;
va_start(args, boundary_fmt);
/* Determine the size needed for the buffer */
va_list args_copy;
va_copy(args_copy, args);
needed = vsnprintf(NULL, 0, boundary_fmt, args_copy) + 1; /* +1 for null terminator */
va_end(args_copy);
if (needed <= 0) {
va_end(args);
return 0;
}
size = (size_t)needed;
/* Alocate a dynamic temporary buffer */
temp_buf = VS_MALLOC(size);
if (!temp_buf) {
va_end(args);
return VS_STREAM_ERROR;
}
/* Format the string into the allocated buffer */
needed = vsnprintf(temp_buf, size, boundary_fmt, args);
va_end(args);
if (needed < 0) {
VS_FREE(temp_buf);
return VS_STREAM_ERROR;
}
size = (size_t)needed;
/* Use readb() abstraction */
size = vw_HTTPBody_readb(body, buf, buf_size, temp_buf, size, found, is_string);
VS_FREE(temp_buf);
return size;
}
size_t vw_HTTPBody_write(vw_HTTPBody *body, const void *buf, size_t count) {
if (!body) {
return VS_STREAM_ERROR;
}
if (!buf || count == 0) {
return 0;
}
return vs_Stream_write(body->stream, buf, count);
}
size_t vw_HTTPBody_write_chunk(vw_HTTPBody *body, const void *buf, size_t count) {
if (!body) {
return VS_STREAM_ERROR;
}
if (!buf || count == 0) {
return 0;
}
if (vs_Stream_ignore_err(vs_Stream_writef(body->stream, "%zX" VW_HTTP_CRLF, count)) <
VW_HTTP_CRLF_LENGTH + 1) {
return 0;
}
const size_t written = vs_Stream_write(body->stream, buf, count);
if (vs_Stream_write(body->stream, VW_HTTP_CRLF, VW_HTTP_CRLF_LENGTH) != VW_HTTP_CRLF_LENGTH) {
return VS_STREAM_ERROR;
}
return written;
}
size_t vw_HTTPBody_writef(vw_HTTPBody *body, const char *fmt, ...) {
if (!body) {
return VS_STREAM_ERROR;
}
if (!fmt || !*fmt) {
return 0;
}
va_list args;
char *temp_buf = NULL;
size_t size = 0;
ssize_t needed = 0;
va_start(args, fmt);
/* Determine the size needed for the buffer */
va_list args_copy;
va_copy(args_copy, args);
needed = vsnprintf(NULL, 0, fmt, args_copy) + 1; /* +1 for null terminator */
va_end(args_copy);
if (needed <= 0) {
va_end(args);
return 0;
}
size = (size_t)needed;
/* Alocate a dynamic temporary buffer */
temp_buf = VS_MALLOC(size);
if (!temp_buf) {
va_end(args);
return VS_STREAM_ERROR;
}
/* Format the string into the allocated buffer */
needed = vsnprintf(temp_buf, size, fmt, args);
if (needed < 0) {
VS_FREE(temp_buf);
va_end(args);
return VS_STREAM_ERROR;
}
va_end(args);
size = (size_t)needed;
/* Now, just use the write abstraction */
size = vw_HTTPBody_write(body, temp_buf, size);
VS_FREE(temp_buf);
return size;
}
size_t vw_HTTPBody_writef_chunk(vw_HTTPBody *body, const char *fmt, ...) {
if (!body) {
return VS_STREAM_ERROR;
}
if (!fmt) {
return 0;
}
va_list args;
char *temp_buf = NULL;
size_t size = 0;
ssize_t needed = 0;
va_start(args, fmt);
/* Determine the size needed for the buffer */
va_list args_copy;
va_copy(args_copy, args);
needed = vsnprintf(NULL, 0, fmt, args_copy) + 1; /* +1 for null terminator */
va_end(args_copy);
if (needed <= 0) {
va_end(args);
return 0;
}
size = (size_t)needed;
/* Alocate a dynamic temporary buffer */
temp_buf = VS_MALLOC(size);
if (!temp_buf) {
va_end(args);
return VS_STREAM_ERROR;
}
/* Format the string into the allocated buffer */
needed = vsnprintf(temp_buf, size, fmt, args);
if (needed < 0) {
VS_FREE(temp_buf);
va_end(args);
return VS_STREAM_ERROR;
}
va_end(args);
size = (size_t)needed;
/* Now, just use the write abstraction */
if (vs_Stream_ignore_err(vw_HTTPBody_writef(body, "%zX" VW_HTTP_CRLF, size)) <
VW_HTTP_CRLF_LENGTH + 1) {
size = VS_STREAM_ERROR;
} else {
size = vw_HTTPBody_write(body, temp_buf, size);
if (vs_Stream_ignore_err(size) != 0 &&
vs_Stream_write(body->stream, VW_HTTP_CRLF, VW_HTTP_CRLF_LENGTH) !=
VW_HTTP_CRLF_LENGTH) {
size = VS_STREAM_ERROR;
}
}
VS_FREE(temp_buf);
return size;
}
size_t vw_HTTPBody_skip_content(vw_HTTPBody *body) {
if (!body || body->finished) {
return 0;
}
size_t now_read = 0;
size_t total_read = 0;
while (!body->finished) {
now_read = vw_HTTPBody_read(body, NULL, body->datasz);
if (now_read == VS_STREAM_ERROR) {
return VS_STREAM_ERROR;
}
total_read += now_read;
}
return total_read;
}
bool vw_HTTPBody_adjust_chunkiness(vw_HTTPBody *body, bool chunked, size_t datasz) {
if (!body || vw_HTTPBody_skip_content(body) == VS_STREAM_ERROR) {
return false;
}
body->chunked = chunked;
body->finished = false;
body->datasz = datasz;
return true;
}
bool vw_HTTPBody_destroy(vw_HTTPBody *body) {
if (!body) {
return false;
}
body->stream = NULL;
body->datasz = 0;
body->chunked = false;
body->finished = false;
return true;
}