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++;
}
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)
{
}
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;
}
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);
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)");
g_object_unref (msg);
g_checksum_free (check);
+
+ if (uri != base_uri)
+ soup_uri_free (uri);
}
typedef struct {
}
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)");
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);
}
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");
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);
}
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);
}
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,
soup_uri_free (base_uri);
g_main_loop_unref (loop);
+ soup_test_server_quit_unref (server);
test_cleanup ();
return errors != 0;