From b9a1c0663ff8ab6e79715db4b35b54f560416ddd Mon Sep 17 00:00:00 2001
From: Carlos Garcia Campos <cgarcia@igalia.com>
Date: Thu, 29 Jan 2026 13:28:55 +0100
Subject: [PATCH] Use CRLF as line boundary when parsing chunked enconding data
Closes #481
Conflict: move libsoup/http1/soup-body-input-stream.c to libsoup/soup-body-input-stream.c
context adapt and adapt the server-test.c
Reference:https://gitlab.gnome.org/GNOME/libsoup/-/commit/b9a1c0663ff8ab6e79715db4b35b54f560416ddd
libsoup/soup-body-input-stream.c | 18 +++++----
tests/server-test.c | 67 ++++++++++++++++++++++++++++++++
2 files changed, 78 insertions(+), 7 deletions(-)
@@ -159,11 +159,13 @@ soup_body_input_stream_read_chunked (SoupBodyInputStream *bistream,
again:
switch (bistream->priv->chunked_state) {
case SOUP_BODY_INPUT_STREAM_STATE_CHUNK_SIZE:
- nread = soup_filter_input_stream_read_line (
- fstream, metabuf, sizeof (metabuf), blocking,
+ nread = soup_filter_input_stream_read_until (
+ fstream, metabuf, sizeof (metabuf),
+ "\r\n", 2, blocking, TRUE,
&got_line, cancellable, error);
if (nread <= 0)
return nread;
+
if (!got_line) {
g_set_error_literal (error, G_IO_ERROR,
G_IO_ERROR_PARTIAL_INPUT,
@@ -191,9 +193,10 @@ again:
return nread;
case SOUP_BODY_INPUT_STREAM_STATE_CHUNK_END:
- nread = soup_filter_input_stream_read_line (
+ nread = soup_filter_input_stream_read_until (
SOUP_FILTER_INPUT_STREAM (bistream->priv->base_stream),
- metabuf, sizeof (metabuf), blocking,
+ metabuf, sizeof (metabuf),
+ "\r\n", 2, blocking, TRUE,
&got_line, cancellable, error);
if (nread <= 0)
return nread;
@@ -208,13 +211,14 @@ again:
break;
case SOUP_BODY_INPUT_STREAM_STATE_TRAILERS:
- nread = soup_filter_input_stream_read_line (
- fstream, metabuf, sizeof (metabuf), blocking,
+ nread = soup_filter_input_stream_read_until (
+ fstream, metabuf, sizeof (metabuf),
+ "\r\n", 2, blocking, TRUE,
&got_line, cancellable, error);
if (nread <= 0)
return nread;
- if ((nread == 2 && strncmp (metabuf, "\r\n", nread) == 0) || (nread == 1 && strncmp (metabuf, "\n", nread) == 0)) {
+ if (nread == 2 && strncmp (metabuf, "\r\n", nread) == 0) {
bistream->priv->chunked_state = SOUP_BODY_INPUT_STREAM_STATE_DONE;
bistream->priv->eof = TRUE;
}
@@ -1373,6 +1373,71 @@ do_steal_connect_test (ServerData *sd, gconstpointer test_data)
soup_uri_free (proxy_uri);
}
+static void
+server_chunked_hundler (SoupServer *server,
+ SoupMessage *msg,
+ const char *path,
+ GHashTable *query,
+ gpointer data)
+{
+ g_assert_true (msg->method == SOUP_METHOD_POST);
+ g_assert_cmpstr (path, ==, "/valid");
+
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+ soup_message_set_response (msg, "text/plain", SOUP_MEMORY_STATIC, "index", 5);
+}
+
+#define CHUNKED_FORMAT_REQUEST "POST /valid HTTP/1.1\r\nHost: 127.0.0.1\r\n%sGET /invalid HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n"
+
+static void
+do_chunked_test (ServerData *sd, gconstpointer test_data)
+{
+ gint i;
+ struct {
+ const char *description;
+ const char *test;
+ } tests[] = {
+ { "Lone LF", "Transfer-Encoding: chunked\r\n\r\n5;ext\n data\r\n0\r\n\r\n" },
+ };
+
+ sd->server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
+ sd->base_uri = soup_test_server_get_uri (sd->server, "http", NULL);
+ server_add_handler (sd, NULL, server_chunked_hundler, NULL, NULL);
+
+ for (i = 0; i < G_N_ELEMENTS (tests); i++) {
+ GSocketClient *client;
+ GSocketConnection *conn;
+ GInputStream *input;
+ GOutputStream *output;
+ char *request;
+ char buffer[4096];
+ gssize nread;
+ GError *error = NULL;
+
+ debug_printf (1, " %s\n", tests[i].description);
+
+ client = g_socket_client_new ();
+ conn = g_socket_client_connect_to_host (client, sd->base_uri->host, sd->base_uri->port, NULL, &error);
+ g_assert_no_error (error);
+
+ request = g_strdup_printf (CHUNKED_FORMAT_REQUEST, tests[i].test);
+
+ output = g_io_stream_get_output_stream (G_IO_STREAM (conn));
+ g_output_stream_write_all (output, request, strlen (request), NULL, NULL, NULL);
+ g_output_stream_close (output, NULL, NULL);
+ g_socket_shutdown (g_socket_connection_get_socket (G_SOCKET_CONNECTION (conn)), FALSE, TRUE, &error);
+
+ input = g_io_stream_get_input_stream (G_IO_STREAM (conn));
+ do {
+ nread = g_input_stream_read (input, buffer, sizeof(buffer), NULL, NULL);
+ } while (nread > 0);
+
+ g_free (request);
+ g_object_unref (conn);
+ g_object_unref (client);
+ }
+}
+
int
main (int argc, char **argv)
{
@@ -1411,6 +1476,8 @@ main (int argc, char **argv)
server_setup_nohandler, do_early_multi_test, server_teardown);
g_test_add ("/server/steal/CONNECT", ServerData, NULL,
server_setup, do_steal_connect_test, server_teardown);
+ g_test_add ("/server/chunked", ServerData, NULL,
+ NULL, do_chunked_test, server_teardown);
ret = g_test_run ();
--
2.33.0