soup-message-io: use gio streams rather than SoupSocket
[platform/upstream/libsoup.git] / tests / chunk-test.c
index e6bc1e8..c3eecc4 100644 (file)
 typedef struct {
        SoupSession *session;
        SoupBuffer *chunks[3];
-       int next, nwrote;
+       int next, nwrote, nfreed;
+       gboolean streaming;
 } PutTestData;
 
-static SoupBuffer *
-error_chunk_allocator (SoupMessage *msg, gsize max_len, gpointer user_data)
-{
-       /* This should never be called, because there is no response body. */
-       debug_printf (1, "  error_chunk_allocator called!\n");
-       errors++;
-       return soup_buffer_new (SOUP_MEMORY_TAKE, g_malloc (100), 100);
-}
-
 static void
 write_next_chunk (SoupMessage *msg, gpointer user_data)
 {
        PutTestData *ptd = user_data;
 
-       debug_printf (2, "  writing chunk\n");
+       debug_printf (2, "  writing chunk %d\n", ptd->next);
 
-       if (ptd->next > 0 && ptd->chunks[ptd->next - 1]) {
+       if (ptd->streaming && ptd->next > 0 && ptd->chunks[ptd->next - 1]) {
                debug_printf (1, "  error: next chunk requested before last one freed!\n");
                errors++;
        }
@@ -51,6 +43,28 @@ write_next_chunk (SoupMessage *msg, gpointer user_data)
        soup_session_unpause_message (ptd->session, msg);
 }
 
+/* This is not a supported part of the API. Use SOUP_MESSAGE_CAN_REBUILD
+ * instead.
+ */
+static void
+write_next_chunk_streaming_hack (SoupMessage *msg, gpointer user_data)
+{
+       PutTestData *ptd = user_data;
+       SoupBuffer *chunk;
+
+       debug_printf (2, "  freeing chunk at %d\n", ptd->nfreed);
+       chunk = soup_message_body_get_chunk (msg->request_body, ptd->nfreed);
+       if (chunk) {
+               ptd->nfreed += chunk->length;
+               soup_message_body_wrote_chunk (msg->request_body, chunk);
+               soup_buffer_free (chunk);
+       } else {
+               debug_printf (1, "  error: written chunk does not exist!\n");
+               errors++;
+       }
+       write_next_chunk (msg, user_data);
+}
+
 static void
 wrote_body_data (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
 {
@@ -89,21 +103,72 @@ make_put_chunk (SoupBuffer **buffer, const char *text)
 }
 
 static void
-do_request_test (SoupSession *session, SoupURI *base_uri)
+setup_request_body (PutTestData *ptd)
+{
+       make_put_chunk (&ptd->chunks[0], "one\r\n");
+       make_put_chunk (&ptd->chunks[1], "two\r\n");
+       make_put_chunk (&ptd->chunks[2], "three\r\n");
+       ptd->next = ptd->nwrote = ptd->nfreed = 0;
+}
+
+static void
+restarted_streaming (SoupMessage *msg, gpointer user_data)
 {
+       PutTestData *ptd = user_data;
+
+       debug_printf (2, "  --restarting--\n");
+
+       /* We're streaming, and we had to restart. So the data need
+        * to be regenerated.
+        */
+       setup_request_body (ptd);
+
+       /* The 302 redirect will turn it into a GET request and
+        * reset the body encoding back to "NONE". Fix that.
+        */
+       soup_message_headers_set_encoding (msg->request_headers,
+                                          SOUP_ENCODING_CHUNKED);
+       msg->method = SOUP_METHOD_PUT;
+}
+
+static void
+restarted_streaming_hack (SoupMessage *msg, gpointer user_data)
+{
+       restarted_streaming (msg, user_data);
+       soup_message_body_truncate (msg->request_body);
+}
+
+typedef enum {
+       HACKY_STREAMING  = (1 << 0),
+       PROPER_STREAMING = (1 << 1),
+       RESTART          = (1 << 2)
+} RequestTestFlags;
+
+static void
+do_request_test (SoupSession *session, SoupURI *base_uri, RequestTestFlags flags)
+{
+       SoupURI *uri = base_uri;
        PutTestData ptd;
        SoupMessage *msg;
        const char *client_md5, *server_md5;
        GChecksum *check;
        int i, length;
 
-       debug_printf (1, "PUT\n");
+       debug_printf (1, "PUT");
+       if (flags & HACKY_STREAMING)
+               debug_printf (1, " w/ hacky streaming");
+       else if (flags & PROPER_STREAMING)
+               debug_printf (1, " w/ proper streaming");
+       if (flags & RESTART) {
+               debug_printf (1, " and restart");
+               uri = soup_uri_copy (base_uri);
+               soup_uri_set_path (uri, "/redirect");
+       }
+       debug_printf (1, "\n");
 
        ptd.session = session;
-       make_put_chunk (&ptd.chunks[0], "one\r\n");
-       make_put_chunk (&ptd.chunks[1], "two\r\n");
-       make_put_chunk (&ptd.chunks[2], "three\r\n");
-       ptd.next = ptd.nwrote = 0;
+       setup_request_body (&ptd);
+       ptd.streaming = flags & (HACKY_STREAMING | PROPER_STREAMING);
 
        check = g_checksum_new (G_CHECKSUM_MD5);
        length = 0;
@@ -114,14 +179,31 @@ do_request_test (SoupSession *session, SoupURI *base_uri)
        }
        client_md5 = g_checksum_get_string (check);
 
-       msg = soup_message_new_from_uri ("PUT", base_uri);
+       msg = soup_message_new_from_uri ("PUT", uri);
        soup_message_headers_set_encoding (msg->request_headers, SOUP_ENCODING_CHUNKED);
        soup_message_body_set_accumulate (msg->request_body, FALSE);
-       soup_message_set_chunk_allocator (msg, error_chunk_allocator, NULL, NULL);
+       if (flags & HACKY_STREAMING) {
+               g_signal_connect (msg, "wrote_chunk",
+                                 G_CALLBACK (write_next_chunk_streaming_hack), &ptd);
+               if (flags & RESTART) {
+                       g_signal_connect (msg, "restarted",
+                                         G_CALLBACK (restarted_streaming_hack), &ptd);
+               }
+       } else {
+               g_signal_connect (msg, "wrote_chunk",
+                                 G_CALLBACK (write_next_chunk), &ptd);
+       }
+
+       if (flags & PROPER_STREAMING) {
+               soup_message_set_flags (msg, SOUP_MESSAGE_CAN_REBUILD);
+               if (flags & RESTART) {
+                       g_signal_connect (msg, "restarted",
+                                         G_CALLBACK (restarted_streaming), &ptd);
+               }
+       }
+
        g_signal_connect (msg, "wrote_headers",
                          G_CALLBACK (write_next_chunk), &ptd);
-       g_signal_connect (msg, "wrote_chunk",
-                         G_CALLBACK (write_next_chunk), &ptd);
        g_signal_connect (msg, "wrote_body_data",
                          G_CALLBACK (wrote_body_data), &ptd);
        soup_session_send_message (session, msg);
@@ -142,7 +224,8 @@ do_request_test (SoupSession *session, SoupURI *base_uri)
                errors++;
        }
 
-       server_md5 = soup_message_headers_get (msg->response_headers, "Content-MD5");
+       server_md5 = soup_message_headers_get_one (msg->response_headers,
+                                                  "Content-MD5");
        if (!server_md5 || strcmp (client_md5, server_md5) != 0) {
                debug_printf (1, "  client/server data mismatch: %s vs %s\n",
                              client_md5, server_md5 ? server_md5 : "(null)");
@@ -151,6 +234,9 @@ do_request_test (SoupSession *session, SoupURI *base_uri)
 
        g_object_unref (msg);
        g_checksum_free (check);
+
+       if (uri != base_uri)
+               soup_uri_free (uri);
 }
 
 typedef struct {
@@ -229,7 +315,8 @@ do_response_test (SoupSession *session, SoupURI *base_uri)
        }
 
        client_md5 = g_checksum_get_string (gtd.check);
-       server_md5 = soup_message_headers_get (msg->response_headers, "Content-MD5");
+       server_md5 = soup_message_headers_get_one (msg->response_headers,
+                                                  "Content-MD5");
        if (!server_md5 || strcmp (client_md5, server_md5) != 0) {
                debug_printf (1, "  client/server data mismatch: %s vs %s\n",
                              client_md5, server_md5 ? server_md5 : "(null)");
@@ -247,18 +334,21 @@ do_response_test (SoupSession *session, SoupURI *base_uri)
 static void
 temp_test_wrote_chunk (SoupMessage *msg, gpointer session)
 {
+       SoupBuffer *chunk;
+
+       chunk = soup_message_body_get_chunk (msg->request_body, 5);
+
        /* When the bug is present, the second chunk will also be
         * discarded after the first is written, which will cause
         * the I/O to stall since soup-message-io will think it's
         * done, but it hasn't written Content-Length bytes yet.
-        * So add in another chunk to keep it going.
         */
-       if (!soup_message_body_get_chunk (msg->request_body,
-                                         5)) {
+       if (!chunk) {
                debug_printf (1, "  Lost second chunk!\n");
                errors++;
                soup_session_abort (session);
-       }
+       } else
+               soup_buffer_free (chunk);
 
        g_signal_handlers_disconnect_by_func (msg, temp_test_wrote_chunk, session);
 }
@@ -267,7 +357,8 @@ static void
 do_temporary_test (SoupSession *session, SoupURI *base_uri)
 {
        SoupMessage *msg;
-       const char *client_md5, *server_md5;
+       char *client_md5;
+       const char *server_md5;
 
        debug_printf (1, "PUT w/ temporary buffers\n");
 
@@ -290,13 +381,69 @@ do_temporary_test (SoupSession *session, SoupURI *base_uri)
                errors++;
        }
 
-       server_md5 = soup_message_headers_get (msg->response_headers, "Content-MD5");
+       server_md5 = soup_message_headers_get_one (msg->response_headers,
+                                                  "Content-MD5");
        if (!server_md5 || strcmp (client_md5, server_md5) != 0) {
                debug_printf (1, "  client/server data mismatch: %s vs %s\n",
                              client_md5, server_md5 ? server_md5 : "(null)");
                errors++;
        }
 
+       g_free (client_md5);
+       g_object_unref (msg);
+}
+
+#define LARGE_CHUNK_SIZE 1000000
+
+typedef struct {
+       SoupBuffer *buf;
+       gsize offset;
+} LargeChunkData;
+
+static void
+large_wrote_body_data (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
+{
+       LargeChunkData *lcd = user_data;
+
+       if (memcmp (chunk->data, lcd->buf->data + lcd->offset, chunk->length) != 0) {
+               debug_printf (1, "  chunk data mismatch at %ld\n", (long)lcd->offset);
+               errors++;
+       } else
+               debug_printf (2, "  chunk data match at %ld\n", (long)lcd->offset);
+       lcd->offset += chunk->length;
+}
+
+static void
+do_large_chunk_test (SoupSession *session, SoupURI *base_uri)
+{
+       SoupMessage *msg;
+       char *buf_data;
+       int i;
+       LargeChunkData lcd;
+
+       debug_printf (1, "PUT w/ large chunk\n");
+
+       msg = soup_message_new_from_uri ("PUT", base_uri);
+
+       buf_data = g_malloc0 (LARGE_CHUNK_SIZE);
+       for (i = 0; i < LARGE_CHUNK_SIZE; i++)
+               buf_data[i] = i & 0xFF;
+       lcd.buf = soup_buffer_new (SOUP_MEMORY_TAKE, buf_data, LARGE_CHUNK_SIZE);
+       lcd.offset = 0;
+       soup_message_body_append_buffer (msg->request_body, lcd.buf);
+       soup_message_body_set_accumulate (msg->request_body, FALSE);
+
+       g_signal_connect (msg, "wrote_body_data",
+                         G_CALLBACK (large_wrote_body_data), &lcd);
+       soup_session_send_message (session, msg);
+
+       if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+               debug_printf (1, "  message failed: %d %s\n",
+                             msg->status_code, msg->reason_phrase);
+               errors++;
+       }
+
+       soup_buffer_free (lcd.buf);
        g_object_unref (msg);
 }
 
@@ -306,11 +453,21 @@ do_chunk_tests (SoupURI *base_uri)
        SoupSession *session;
 
        session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
-       do_request_test (session, base_uri);
+       do_request_test (session, base_uri, 0);
+       debug_printf (2, "\n\n");
+       do_request_test (session, base_uri, PROPER_STREAMING);
+       debug_printf (2, "\n\n");
+       do_request_test (session, base_uri, PROPER_STREAMING | RESTART);
+       debug_printf (2, "\n\n");
+       do_request_test (session, base_uri, HACKY_STREAMING);
+       debug_printf (2, "\n\n");
+       do_request_test (session, base_uri, HACKY_STREAMING | RESTART);
        debug_printf (2, "\n\n");
        do_response_test (session, base_uri);
        debug_printf (2, "\n\n");
        do_temporary_test (session, base_uri);
+       debug_printf (2, "\n\n");
+       do_large_chunk_test (session, base_uri);
        soup_test_session_abort_unref (session);
 }
 
@@ -322,6 +479,11 @@ server_callback (SoupServer *server, SoupMessage *msg,
        SoupMessageBody *md5_body;
        char *md5;
 
+       if (g_str_has_prefix (path, "/redirect")) {
+               soup_message_set_redirect (msg, SOUP_STATUS_FOUND, "/");
+               return;
+       }
+
        if (msg->method == SOUP_METHOD_GET) {
                soup_message_set_response (msg, "text/plain",
                                           SOUP_MEMORY_STATIC,
@@ -369,6 +531,7 @@ main (int argc, char **argv)
        soup_uri_free (base_uri);
 
        g_main_loop_unref (loop);
+       soup_test_server_quit_unref (server);
 
        test_cleanup ();
        return errors != 0;