1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2008 Red Hat, Inc.
6 #include "test-utils.h"
10 SoupBuffer *chunks[3];
11 int next, nwrote, nfreed;
16 write_next_chunk (SoupMessage *msg, gpointer user_data)
18 PutTestData *ptd = user_data;
20 debug_printf (2, " writing chunk %d\n", ptd->next);
22 if (ptd->streaming && ptd->next > 0 && ptd->chunks[ptd->next - 1]) {
23 debug_printf (1, " error: next chunk requested before last one freed!\n");
27 if (ptd->next < G_N_ELEMENTS (ptd->chunks)) {
28 soup_message_body_append_buffer (msg->request_body,
29 ptd->chunks[ptd->next]);
30 soup_buffer_free (ptd->chunks[ptd->next]);
33 soup_message_body_complete (msg->request_body);
34 soup_session_unpause_message (ptd->session, msg);
37 /* This is not a supported part of the API. Use SOUP_MESSAGE_CAN_REBUILD
41 write_next_chunk_streaming_hack (SoupMessage *msg, gpointer user_data)
43 PutTestData *ptd = user_data;
46 debug_printf (2, " freeing chunk at %d\n", ptd->nfreed);
47 chunk = soup_message_body_get_chunk (msg->request_body, ptd->nfreed);
49 ptd->nfreed += chunk->length;
50 soup_message_body_wrote_chunk (msg->request_body, chunk);
51 soup_buffer_free (chunk);
53 debug_printf (1, " error: written chunk does not exist!\n");
56 write_next_chunk (msg, user_data);
60 wrote_body_data (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
62 PutTestData *ptd = user_data;
64 debug_printf (2, " wrote_body_data, %d bytes\n",
66 ptd->nwrote += chunk->length;
70 clear_buffer_ptr (gpointer data)
72 SoupBuffer **buffer_ptr = data;
74 debug_printf (2, " clearing chunk\n");
76 (*buffer_ptr)->length = 0;
77 g_free ((char *)(*buffer_ptr)->data);
80 debug_printf (2, " chunk is already clear!\n");
85 /* Put a chunk containing @text into *@buffer, set up so that it will
86 * clear out *@buffer when the chunk is freed, allowing us to make sure
87 * the set_accumulate(FALSE) is working.
90 make_put_chunk (SoupBuffer **buffer, const char *text)
92 *buffer = soup_buffer_new_with_owner (g_strdup (text), strlen (text),
93 buffer, clear_buffer_ptr);
97 setup_request_body (PutTestData *ptd)
99 make_put_chunk (&ptd->chunks[0], "one\r\n");
100 make_put_chunk (&ptd->chunks[1], "two\r\n");
101 make_put_chunk (&ptd->chunks[2], "three\r\n");
102 ptd->next = ptd->nwrote = ptd->nfreed = 0;
106 restarted_streaming (SoupMessage *msg, gpointer user_data)
108 PutTestData *ptd = user_data;
110 debug_printf (2, " --restarting--\n");
112 /* We're streaming, and we had to restart. So the data need
115 setup_request_body (ptd);
117 /* The 302 redirect will turn it into a GET request and
118 * reset the body encoding back to "NONE". Fix that.
120 soup_message_headers_set_encoding (msg->request_headers,
121 SOUP_ENCODING_CHUNKED);
122 msg->method = SOUP_METHOD_PUT;
126 restarted_streaming_hack (SoupMessage *msg, gpointer user_data)
128 restarted_streaming (msg, user_data);
129 soup_message_body_truncate (msg->request_body);
133 HACKY_STREAMING = (1 << 0),
134 PROPER_STREAMING = (1 << 1),
139 do_request_test (SoupSession *session, SoupURI *base_uri, RequestTestFlags flags)
141 SoupURI *uri = base_uri;
144 const char *client_md5, *server_md5;
148 debug_printf (1, "PUT");
149 if (flags & HACKY_STREAMING)
150 debug_printf (1, " w/ hacky streaming");
151 else if (flags & PROPER_STREAMING)
152 debug_printf (1, " w/ proper streaming");
153 if (flags & RESTART) {
154 debug_printf (1, " and restart");
155 uri = soup_uri_copy (base_uri);
156 soup_uri_set_path (uri, "/redirect");
158 debug_printf (1, "\n");
160 ptd.session = session;
161 setup_request_body (&ptd);
162 ptd.streaming = flags & (HACKY_STREAMING | PROPER_STREAMING);
164 check = g_checksum_new (G_CHECKSUM_MD5);
166 for (i = 0; i < 3; i++) {
167 g_checksum_update (check, (guchar *)ptd.chunks[i]->data,
168 ptd.chunks[i]->length);
169 length += ptd.chunks[i]->length;
171 client_md5 = g_checksum_get_string (check);
173 msg = soup_message_new_from_uri ("PUT", uri);
174 soup_message_headers_set_encoding (msg->request_headers, SOUP_ENCODING_CHUNKED);
175 soup_message_body_set_accumulate (msg->request_body, FALSE);
176 if (flags & HACKY_STREAMING) {
177 g_signal_connect (msg, "wrote_chunk",
178 G_CALLBACK (write_next_chunk_streaming_hack), &ptd);
179 if (flags & RESTART) {
180 g_signal_connect (msg, "restarted",
181 G_CALLBACK (restarted_streaming_hack), &ptd);
184 g_signal_connect (msg, "wrote_chunk",
185 G_CALLBACK (write_next_chunk), &ptd);
188 if (flags & PROPER_STREAMING) {
189 soup_message_set_flags (msg, SOUP_MESSAGE_CAN_REBUILD);
190 if (flags & RESTART) {
191 g_signal_connect (msg, "restarted",
192 G_CALLBACK (restarted_streaming), &ptd);
196 g_signal_connect (msg, "wrote_headers",
197 G_CALLBACK (write_next_chunk), &ptd);
198 g_signal_connect (msg, "wrote_body_data",
199 G_CALLBACK (wrote_body_data), &ptd);
200 soup_session_send_message (session, msg);
202 if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
203 debug_printf (1, " message failed: %d %s\n",
204 msg->status_code, msg->reason_phrase);
208 if (msg->request_body->data) {
209 debug_printf (1, " msg->request_body set!\n");
212 if (msg->request_body->length != length || length != ptd.nwrote) {
213 debug_printf (1, " sent length mismatch: %d vs %d vs %d\n",
214 (int)msg->request_body->length, length, ptd.nwrote);
218 server_md5 = soup_message_headers_get_one (msg->response_headers,
220 if (!server_md5 || strcmp (client_md5, server_md5) != 0) {
221 debug_printf (1, " client/server data mismatch: %s vs %s\n",
222 client_md5, server_md5 ? server_md5 : "(null)");
226 g_object_unref (msg);
227 g_checksum_free (check);
234 SoupBuffer *current_chunk;
240 chunk_allocator (SoupMessage *msg, gsize max_len, gpointer user_data)
242 GetTestData *gtd = user_data;
244 debug_printf (2, " allocating chunk\n");
246 if (gtd->current_chunk) {
247 debug_printf (1, " error: next chunk allocated before last one freed!\n");
250 gtd->current_chunk = soup_buffer_new_with_owner (g_malloc (6), 6,
253 return gtd->current_chunk;
257 got_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
259 GetTestData *gtd = user_data;
261 debug_printf (2, " got chunk, %d bytes\n",
263 if (chunk != gtd->current_chunk) {
264 debug_printf (1, "chunk mismatch! %p vs %p\n",
265 chunk, gtd->current_chunk);
268 g_checksum_update (gtd->check, (guchar *)chunk->data, chunk->length);
269 gtd->length += chunk->length;
273 do_response_test (SoupSession *session, SoupURI *base_uri)
277 const char *client_md5, *server_md5;
279 debug_printf (1, "GET\n");
281 gtd.current_chunk = NULL;
283 gtd.check = g_checksum_new (G_CHECKSUM_MD5);
285 msg = soup_message_new_from_uri ("GET", base_uri);
286 soup_message_body_set_accumulate (msg->response_body, FALSE);
287 soup_message_set_chunk_allocator (msg, chunk_allocator, >d, NULL);
288 g_signal_connect (msg, "got_chunk",
289 G_CALLBACK (got_chunk), >d);
290 soup_session_send_message (session, msg);
292 if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
293 debug_printf (1, " message failed: %d %s\n",
294 msg->status_code, msg->reason_phrase);
298 if (msg->response_body->data) {
299 debug_printf (1, " msg->response_body set!\n");
302 if (soup_message_headers_get_content_length (msg->response_headers) != gtd.length) {
303 debug_printf (1, " received length mismatch: %d vs %d\n",
304 (int)soup_message_headers_get_content_length (msg->response_headers), gtd.length);
308 client_md5 = g_checksum_get_string (gtd.check);
309 server_md5 = soup_message_headers_get_one (msg->response_headers,
311 if (!server_md5 || strcmp (client_md5, server_md5) != 0) {
312 debug_printf (1, " client/server data mismatch: %s vs %s\n",
313 client_md5, server_md5 ? server_md5 : "(null)");
317 g_object_unref (msg);
318 g_checksum_free (gtd.check);
321 /* Make sure TEMPORARY buffers are handled properly with non-accumulating
322 * message bodies. Part of https://bugs.webkit.org/show_bug.cgi?id=18343
326 temp_test_wrote_chunk (SoupMessage *msg, gpointer session)
330 chunk = soup_message_body_get_chunk (msg->request_body, 5);
332 /* When the bug is present, the second chunk will also be
333 * discarded after the first is written, which will cause
334 * the I/O to stall since soup-message-io will think it's
335 * done, but it hasn't written Content-Length bytes yet.
338 debug_printf (1, " Lost second chunk!\n");
340 soup_session_abort (session);
342 soup_buffer_free (chunk);
344 g_signal_handlers_disconnect_by_func (msg, temp_test_wrote_chunk, session);
348 do_temporary_test (SoupSession *session, SoupURI *base_uri)
352 const char *server_md5;
354 debug_printf (1, "PUT w/ temporary buffers\n");
356 msg = soup_message_new_from_uri ("PUT", base_uri);
357 soup_message_body_append (msg->request_body, SOUP_MEMORY_TEMPORARY,
359 soup_message_body_append (msg->request_body, SOUP_MEMORY_STATIC,
361 soup_message_body_set_accumulate (msg->request_body, FALSE);
363 client_md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5,
364 "one\r\ntwo\r\n", 10);
365 g_signal_connect (msg, "wrote_chunk",
366 G_CALLBACK (temp_test_wrote_chunk), session);
367 soup_session_send_message (session, msg);
369 if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
370 debug_printf (1, " message failed: %d %s\n",
371 msg->status_code, msg->reason_phrase);
375 server_md5 = soup_message_headers_get_one (msg->response_headers,
377 if (!server_md5 || strcmp (client_md5, server_md5) != 0) {
378 debug_printf (1, " client/server data mismatch: %s vs %s\n",
379 client_md5, server_md5 ? server_md5 : "(null)");
384 g_object_unref (msg);
387 #define LARGE_CHUNK_SIZE 1000000
395 large_wrote_body_data (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
397 LargeChunkData *lcd = user_data;
399 if (memcmp (chunk->data, lcd->buf->data + lcd->offset, chunk->length) != 0) {
400 debug_printf (1, " chunk data mismatch at %ld\n", (long)lcd->offset);
403 debug_printf (2, " chunk data match at %ld\n", (long)lcd->offset);
404 lcd->offset += chunk->length;
408 do_large_chunk_test (SoupSession *session, SoupURI *base_uri)
415 debug_printf (1, "PUT w/ large chunk\n");
417 msg = soup_message_new_from_uri ("PUT", base_uri);
419 buf_data = g_malloc0 (LARGE_CHUNK_SIZE);
420 for (i = 0; i < LARGE_CHUNK_SIZE; i++)
421 buf_data[i] = i & 0xFF;
422 lcd.buf = soup_buffer_new (SOUP_MEMORY_TAKE, buf_data, LARGE_CHUNK_SIZE);
424 soup_message_body_append_buffer (msg->request_body, lcd.buf);
425 soup_message_body_set_accumulate (msg->request_body, FALSE);
427 g_signal_connect (msg, "wrote_body_data",
428 G_CALLBACK (large_wrote_body_data), &lcd);
429 soup_session_send_message (session, msg);
431 if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
432 debug_printf (1, " message failed: %d %s\n",
433 msg->status_code, msg->reason_phrase);
437 soup_buffer_free (lcd.buf);
438 g_object_unref (msg);
442 do_chunk_tests (SoupURI *base_uri)
444 SoupSession *session;
446 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
447 do_request_test (session, base_uri, 0);
448 debug_printf (2, "\n\n");
449 do_request_test (session, base_uri, PROPER_STREAMING);
450 debug_printf (2, "\n\n");
451 do_request_test (session, base_uri, PROPER_STREAMING | RESTART);
452 debug_printf (2, "\n\n");
453 do_request_test (session, base_uri, HACKY_STREAMING);
454 debug_printf (2, "\n\n");
455 do_request_test (session, base_uri, HACKY_STREAMING | RESTART);
456 debug_printf (2, "\n\n");
457 do_response_test (session, base_uri);
458 debug_printf (2, "\n\n");
459 do_temporary_test (session, base_uri);
460 debug_printf (2, "\n\n");
461 do_large_chunk_test (session, base_uri);
462 soup_test_session_abort_unref (session);
466 server_callback (SoupServer *server, SoupMessage *msg,
467 const char *path, GHashTable *query,
468 SoupClientContext *context, gpointer data)
470 SoupMessageBody *md5_body;
473 if (g_str_has_prefix (path, "/redirect")) {
474 soup_message_set_redirect (msg, SOUP_STATUS_FOUND, "/");
478 if (msg->method == SOUP_METHOD_GET) {
479 soup_message_set_response (msg, "text/plain",
481 "three\r\ntwo\r\none\r\n",
482 strlen ("three\r\ntwo\r\none\r\n"));
483 soup_buffer_free (soup_message_body_flatten (msg->response_body));
484 md5_body = msg->response_body;
485 soup_message_set_status (msg, SOUP_STATUS_OK);
486 } else if (msg->method == SOUP_METHOD_PUT) {
487 soup_message_set_status (msg, SOUP_STATUS_CREATED);
488 md5_body = msg->request_body;
490 soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED);
494 md5 = g_compute_checksum_for_data (G_CHECKSUM_MD5,
495 (guchar *)md5_body->data,
497 soup_message_headers_append (msg->response_headers,
503 main (int argc, char **argv)
510 test_init (argc, argv, NULL);
512 server = soup_test_server_new (TRUE);
513 soup_server_add_handler (server, NULL,
514 server_callback, NULL, NULL);
515 port = soup_server_get_port (server);
517 loop = g_main_loop_new (NULL, TRUE);
519 base_uri = soup_uri_new ("http://127.0.0.1");
520 soup_uri_set_port (base_uri, port);
521 do_chunk_tests (base_uri);
522 soup_uri_free (base_uri);
524 g_main_loop_unref (loop);
525 soup_test_server_quit_unref (server);