--- a/src/server/test/httpclient.c Tue Mar 17 21:01:57 2026 +0100 +++ b/src/server/test/httpclient.c Wed Mar 18 21:42:35 2026 +0100 @@ -43,6 +43,9 @@ static pthread_cond_t test_cond; static volatile int test_finished; +static size_t response_body_write_fail_after = 0; +static int last_http_client_error = 0; + void http_client_tests_init(void) { pthread_mutex_init(&test_mutex, NULL); pthread_cond_init(&test_cond, NULL); @@ -62,13 +65,18 @@ pthread_mutex_lock(&test_mutex); pthread_cond_signal(&test_cond); pthread_mutex_unlock(&test_mutex); + last_http_client_error = client->error; http_client_free(client); test_finished = 1; } static ssize_t test_response_body_write(HttpClient *client, void *buf, size_t nbytes, void *userdata) { char *str = buf; - return cxBufferWrite(str, 1, nbytes, userdata); + CxBuffer *buffer = userdata; + if(response_body_write_fail_after > 0 && buffer->pos > response_body_write_fail_after) { + return HTTP_CLIENT_CALLBACK_ERROR; + } + return cxBufferWrite(str, 1, nbytes, buffer); } static cxstring request_body_str; @@ -133,7 +141,9 @@ size_t nbytes = response.length - response_pos; const char *str = response.ptr + response_pos; ssize_t w = write(fds[1], str, nbytes); - CX_TEST_ASSERT(w >= 0); + if(response_body_write_fail_after == 0) { + CX_TEST_ASSERT(w >= 0); + } response_pos += w; } } @@ -332,3 +342,23 @@ cxBufferFree(out); } } + +CX_TEST(test_http_client_response_io_error) { + CX_TEST_DO { + size_t content_length = 1024*1024*16; + char *content = malloc(content_length); + memset(content, 'f', content_length); + + cxmutstr response_m = cx_strcat(CX_NULLSTR, 2, cx_str("HTTP/1.1 200 OK\r\nCOntent-length: 16777216\r\n\r\n"), cx_strn(content, content_length)); + cxstring response = cx_strcast(response_m); + + CxBuffer *out = cxBufferCreate(NULL, NULL, content_length, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS); + + response_body_write_fail_after = 2048; + CX_TEST_CALL_SUBROUTINE(test_httpclient, cx_str(NULL), FALSE, &response, 1, out); + CX_TEST_ASSERT(last_http_client_error == 1); + cxBufferFree(out); + free(content); + } + response_body_write_fail_after = 0; +}