1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2008 Red Hat, Inc.
13 #include <libsoup/soup.h>
15 #include "test-utils.h"
19 SoupBuffer *chunks[3];
20 int next, nwrote, nfreed;
25 error_chunk_allocator (SoupMessage *msg, gsize max_len, gpointer user_data)
27 /* This should never be called, because there is no response body. */
28 debug_printf (1, " error_chunk_allocator called!\n");
30 return soup_buffer_new (SOUP_MEMORY_TAKE, g_malloc (100), 100);
34 write_next_chunk (SoupMessage *msg, gpointer user_data)
36 PutTestData *ptd = user_data;
38 debug_printf (2, " writing chunk %d\n", ptd->next);
40 if (ptd->next > 0 && ptd->chunks[ptd->next - 1]) {
42 debug_printf (1, " error: next chunk requested before last one freed!\n");
45 debug_printf (0, " ignoring bug in test case... FIXME!\n");
49 if (ptd->next < G_N_ELEMENTS (ptd->chunks)) {
50 soup_message_body_append_buffer (msg->request_body,
51 ptd->chunks[ptd->next]);
52 soup_buffer_free (ptd->chunks[ptd->next]);
55 soup_message_body_complete (msg->request_body);
56 soup_session_unpause_message (ptd->session, msg);
59 /* This is not a supported part of the API. Don't try this at home */
61 write_next_chunk_streaming_hack (SoupMessage *msg, gpointer user_data)
63 PutTestData *ptd = user_data;
66 debug_printf (2, " freeing chunk at %d\n", ptd->nfreed);
67 chunk = soup_message_body_get_chunk (msg->request_body, ptd->nfreed);
69 ptd->nfreed += chunk->length;
70 soup_message_body_wrote_chunk (msg->request_body, chunk);
71 soup_buffer_free (chunk);
73 debug_printf (1, " error: written chunk does not exist!\n");
76 write_next_chunk (msg, user_data);
80 wrote_body_data (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
82 PutTestData *ptd = user_data;
84 debug_printf (2, " wrote_body_data, %d bytes\n",
86 ptd->nwrote += chunk->length;
90 clear_buffer_ptr (gpointer data)
92 SoupBuffer **buffer_ptr = data;
94 debug_printf (2, " clearing chunk\n");
96 (*buffer_ptr)->length = 0;
97 g_free ((char *)(*buffer_ptr)->data);
100 debug_printf (2, " chunk is already clear!\n");
105 /* Put a chunk containing @text into *@buffer, set up so that it will
106 * clear out *@buffer when the chunk is freed, allowing us to make sure
107 * the set_accumulate(FALSE) is working.
110 make_put_chunk (SoupBuffer **buffer, const char *text)
112 *buffer = soup_buffer_new_with_owner (g_strdup (text), strlen (text),
113 buffer, clear_buffer_ptr);
117 restarted_streaming_hack (SoupMessage *msg, gpointer user_data)
119 PutTestData *ptd = user_data;
121 /* We're streaming, and we had to restart. So the data need
122 to be regenerated. That's the *point* of this test; we don't
123 *want* it to accumulate in the request body */
124 make_put_chunk (&ptd->chunks[0], "one\r\n");
125 make_put_chunk (&ptd->chunks[1], "two\r\n");
126 make_put_chunk (&ptd->chunks[2], "three\r\n");
127 ptd->next = ptd->nwrote = ptd->nfreed = 0;
129 debug_printf (2, " truncating request body on restart\n");
130 soup_message_body_truncate (msg->request_body);
132 /* The redirect will turn it into a GET request. Fix that... */
133 soup_message_headers_set_encoding (msg->request_headers, SOUP_ENCODING_CHUNKED);
134 msg->method = SOUP_METHOD_PUT;
138 do_request_test (SoupSession *session, SoupURI *base_uri, int test)
140 SoupURI *uri = base_uri;
143 const char *client_md5, *server_md5;
146 gboolean streaming = FALSE;
150 debug_printf (1, "PUT\n");
154 debug_printf (1, "PUT w/ streaming\n");
159 debug_printf (1, "PUT w/ streaming and restart\n");
161 uri = soup_uri_copy (base_uri);
162 soup_uri_set_path (uri, "/redirect");
166 ptd.session = session;
167 make_put_chunk (&ptd.chunks[0], "one\r\n");
168 make_put_chunk (&ptd.chunks[1], "two\r\n");
169 make_put_chunk (&ptd.chunks[2], "three\r\n");
170 ptd.next = ptd.nwrote = ptd.nfreed = 0;
171 ptd.streaming = streaming;
173 check = g_checksum_new (G_CHECKSUM_MD5);
175 for (i = 0; i < 3; i++) {
176 g_checksum_update (check, (guchar *)ptd.chunks[i]->data,
177 ptd.chunks[i]->length);
178 length += ptd.chunks[i]->length;
180 client_md5 = g_checksum_get_string (check);
182 msg = soup_message_new_from_uri ("PUT", uri);
183 soup_message_headers_set_encoding (msg->request_headers, SOUP_ENCODING_CHUNKED);
184 soup_message_body_set_accumulate (msg->request_body, FALSE);
185 soup_message_set_chunk_allocator (msg, error_chunk_allocator, NULL, NULL);
187 g_signal_connect (msg, "wrote_chunk",
188 G_CALLBACK (write_next_chunk_streaming_hack), &ptd);
189 g_signal_connect (msg, "restarted",
190 G_CALLBACK (restarted_streaming_hack), &ptd);
192 g_signal_connect (msg, "wrote_chunk",
193 G_CALLBACK (write_next_chunk), &ptd);
195 g_signal_connect (msg, "wrote_headers",
196 G_CALLBACK (write_next_chunk), &ptd);
197 g_signal_connect (msg, "wrote_body_data",
198 G_CALLBACK (wrote_body_data), &ptd);
199 soup_session_send_message (session, msg);
201 if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
202 debug_printf (1, " message failed: %d %s\n",
203 msg->status_code, msg->reason_phrase);
207 if (msg->request_body->data) {
208 debug_printf (1, " msg->request_body set!\n");
211 if (msg->request_body->length != length || length != ptd.nwrote) {
212 debug_printf (1, " sent length mismatch: %d vs %d vs %d\n",
213 (int)msg->request_body->length, length, ptd.nwrote);
217 server_md5 = soup_message_headers_get_one (msg->response_headers,
219 if (!server_md5 || strcmp (client_md5, server_md5) != 0) {
220 debug_printf (1, " client/server data mismatch: %s vs %s\n",
221 client_md5, server_md5 ? server_md5 : "(null)");
225 g_object_unref (msg);
226 g_checksum_free (check);
233 SoupBuffer *current_chunk;
239 chunk_allocator (SoupMessage *msg, gsize max_len, gpointer user_data)
241 GetTestData *gtd = user_data;
243 debug_printf (2, " allocating chunk\n");
245 if (gtd->current_chunk) {
246 debug_printf (1, " error: next chunk allocated before last one freed!\n");
249 gtd->current_chunk = soup_buffer_new_with_owner (g_malloc (6), 6,
252 return gtd->current_chunk;
256 got_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
258 GetTestData *gtd = user_data;
260 debug_printf (2, " got chunk, %d bytes\n",
262 if (chunk != gtd->current_chunk) {
263 debug_printf (1, "chunk mismatch! %p vs %p\n",
264 chunk, gtd->current_chunk);
267 g_checksum_update (gtd->check, (guchar *)chunk->data, chunk->length);
268 gtd->length += chunk->length;
272 do_response_test (SoupSession *session, SoupURI *base_uri)
276 const char *client_md5, *server_md5;
278 debug_printf (1, "GET\n");
280 gtd.current_chunk = NULL;
282 gtd.check = g_checksum_new (G_CHECKSUM_MD5);
284 msg = soup_message_new_from_uri ("GET", base_uri);
285 soup_message_body_set_accumulate (msg->response_body, FALSE);
286 soup_message_set_chunk_allocator (msg, chunk_allocator, >d, NULL);
287 g_signal_connect (msg, "got_chunk",
288 G_CALLBACK (got_chunk), >d);
289 soup_session_send_message (session, msg);
291 if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
292 debug_printf (1, " message failed: %d %s\n",
293 msg->status_code, msg->reason_phrase);
297 if (msg->response_body->data) {
298 debug_printf (1, " msg->response_body set!\n");
301 if (soup_message_headers_get_content_length (msg->response_headers) != gtd.length) {
302 debug_printf (1, " received length mismatch: %d vs %d\n",
303 (int)soup_message_headers_get_content_length (msg->response_headers), gtd.length);
307 client_md5 = g_checksum_get_string (gtd.check);
308 server_md5 = soup_message_headers_get_one (msg->response_headers,
310 if (!server_md5 || strcmp (client_md5, server_md5) != 0) {
311 debug_printf (1, " client/server data mismatch: %s vs %s\n",
312 client_md5, server_md5 ? server_md5 : "(null)");
316 g_object_unref (msg);
317 g_checksum_free (gtd.check);
320 /* Make sure TEMPORARY buffers are handled properly with non-accumulating
321 * message bodies. Part of https://bugs.webkit.org/show_bug.cgi?id=18343
325 temp_test_wrote_chunk (SoupMessage *msg, gpointer session)
329 chunk = soup_message_body_get_chunk (msg->request_body, 5);
331 /* When the bug is present, the second chunk will also be
332 * discarded after the first is written, which will cause
333 * the I/O to stall since soup-message-io will think it's
334 * done, but it hasn't written Content-Length bytes yet.
337 debug_printf (1, " Lost second chunk!\n");
339 soup_session_abort (session);
341 soup_buffer_free (chunk);
343 g_signal_handlers_disconnect_by_func (msg, temp_test_wrote_chunk, session);
347 do_temporary_test (SoupSession *session, SoupURI *base_uri)
351 const char *server_md5;
353 debug_printf (1, "PUT w/ temporary buffers\n");
355 msg = soup_message_new_from_uri ("PUT", base_uri);
356 soup_message_body_append (msg->request_body, SOUP_MEMORY_TEMPORARY,
358 soup_message_body_append (msg->request_body, SOUP_MEMORY_STATIC,
360 soup_message_body_set_accumulate (msg->request_body, FALSE);
362 client_md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5,
363 "one\r\ntwo\r\n", 10);
364 g_signal_connect (msg, "wrote_chunk",
365 G_CALLBACK (temp_test_wrote_chunk), session);
366 soup_session_send_message (session, msg);
368 if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
369 debug_printf (1, " message failed: %d %s\n",
370 msg->status_code, msg->reason_phrase);
374 server_md5 = soup_message_headers_get_one (msg->response_headers,
376 if (!server_md5 || strcmp (client_md5, server_md5) != 0) {
377 debug_printf (1, " client/server data mismatch: %s vs %s\n",
378 client_md5, server_md5 ? server_md5 : "(null)");
383 g_object_unref (msg);
387 do_chunk_tests (SoupURI *base_uri)
389 SoupSession *session;
391 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
392 do_request_test (session, base_uri, 0);
393 debug_printf (2, "\n\n");
394 do_request_test (session, base_uri, 1);
395 debug_printf (2, "\n\n");
396 do_request_test (session, base_uri, 2);
397 debug_printf (2, "\n\n");
398 do_response_test (session, base_uri);
399 debug_printf (2, "\n\n");
400 do_temporary_test (session, base_uri);
401 soup_test_session_abort_unref (session);
405 server_callback (SoupServer *server, SoupMessage *msg,
406 const char *path, GHashTable *query,
407 SoupClientContext *context, gpointer data)
409 SoupMessageBody *md5_body;
412 if (g_str_has_prefix (path, "/redirect")) {
413 soup_message_set_status (msg, SOUP_STATUS_FOUND);
414 soup_message_headers_replace (msg->response_headers,
419 if (msg->method == SOUP_METHOD_GET) {
420 soup_message_set_response (msg, "text/plain",
422 "three\r\ntwo\r\none\r\n",
423 strlen ("three\r\ntwo\r\none\r\n"));
424 soup_buffer_free (soup_message_body_flatten (msg->response_body));
425 md5_body = msg->response_body;
426 soup_message_set_status (msg, SOUP_STATUS_OK);
427 } else if (msg->method == SOUP_METHOD_PUT) {
428 soup_message_set_status (msg, SOUP_STATUS_CREATED);
429 md5_body = msg->request_body;
431 soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED);
435 md5 = g_compute_checksum_for_data (G_CHECKSUM_MD5,
436 (guchar *)md5_body->data,
438 soup_message_headers_append (msg->response_headers,
444 main (int argc, char **argv)
451 test_init (argc, argv, NULL);
453 server = soup_test_server_new (TRUE);
454 soup_server_add_handler (server, NULL,
455 server_callback, NULL, NULL);
456 port = soup_server_get_port (server);
458 loop = g_main_loop_new (NULL, TRUE);
460 base_uri = soup_uri_new ("http://127.0.0.1");
461 soup_uri_set_port (base_uri, port);
462 do_chunk_tests (base_uri);
463 soup_uri_free (base_uri);
465 g_main_loop_unref (loop);
466 soup_test_server_quit_unref (server);