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];
24 error_chunk_allocator (SoupMessage *msg, gsize max_len, gpointer user_data)
26 /* This should never be called, because there is no response body. */
27 debug_printf (1, " error_chunk_allocator called!\n");
29 return soup_buffer_new (SOUP_MEMORY_TAKE, g_malloc (100), 100);
33 write_next_chunk (SoupMessage *msg, gpointer user_data)
35 PutTestData *ptd = user_data;
37 debug_printf (2, " writing chunk\n");
39 if (ptd->next > 0 && ptd->chunks[ptd->next - 1]) {
41 debug_printf (1, " error: next chunk requested before last one freed!\n");
44 debug_printf (0, " ignoring bug in test case... FIXME!\n");
48 if (ptd->next < G_N_ELEMENTS (ptd->chunks)) {
49 soup_message_body_append_buffer (msg->request_body,
50 ptd->chunks[ptd->next]);
51 soup_buffer_free (ptd->chunks[ptd->next]);
54 soup_message_body_complete (msg->request_body);
55 soup_session_unpause_message (ptd->session, msg);
59 wrote_body_data (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
61 PutTestData *ptd = user_data;
63 debug_printf (2, " wrote_body_data, %d bytes\n",
65 ptd->nwrote += chunk->length;
69 clear_buffer_ptr (gpointer data)
71 SoupBuffer **buffer_ptr = data;
73 debug_printf (2, " clearing chunk\n");
75 (*buffer_ptr)->length = 0;
76 g_free ((char *)(*buffer_ptr)->data);
79 debug_printf (2, " chunk is already clear!\n");
84 /* Put a chunk containing @text into *@buffer, set up so that it will
85 * clear out *@buffer when the chunk is freed, allowing us to make sure
86 * the set_accumulate(FALSE) is working.
89 make_put_chunk (SoupBuffer **buffer, const char *text)
91 *buffer = soup_buffer_new_with_owner (g_strdup (text), strlen (text),
92 buffer, clear_buffer_ptr);
96 do_request_test (SoupSession *session, SoupURI *base_uri)
100 const char *client_md5, *server_md5;
104 debug_printf (1, "PUT\n");
106 ptd.session = session;
107 make_put_chunk (&ptd.chunks[0], "one\r\n");
108 make_put_chunk (&ptd.chunks[1], "two\r\n");
109 make_put_chunk (&ptd.chunks[2], "three\r\n");
110 ptd.next = ptd.nwrote = 0;
112 check = g_checksum_new (G_CHECKSUM_MD5);
114 for (i = 0; i < 3; i++) {
115 g_checksum_update (check, (guchar *)ptd.chunks[i]->data,
116 ptd.chunks[i]->length);
117 length += ptd.chunks[i]->length;
119 client_md5 = g_checksum_get_string (check);
121 msg = soup_message_new_from_uri ("PUT", base_uri);
122 soup_message_headers_set_encoding (msg->request_headers, SOUP_ENCODING_CHUNKED);
123 soup_message_body_set_accumulate (msg->request_body, FALSE);
124 soup_message_set_chunk_allocator (msg, error_chunk_allocator, NULL, NULL);
125 g_signal_connect (msg, "wrote_headers",
126 G_CALLBACK (write_next_chunk), &ptd);
127 g_signal_connect (msg, "wrote_chunk",
128 G_CALLBACK (write_next_chunk), &ptd);
129 g_signal_connect (msg, "wrote_body_data",
130 G_CALLBACK (wrote_body_data), &ptd);
131 soup_session_send_message (session, msg);
133 if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
134 debug_printf (1, " message failed: %d %s\n",
135 msg->status_code, msg->reason_phrase);
139 if (msg->request_body->data) {
140 debug_printf (1, " msg->request_body set!\n");
143 if (msg->request_body->length != length || length != ptd.nwrote) {
144 debug_printf (1, " sent length mismatch: %d vs %d vs %d\n",
145 (int)msg->request_body->length, length, ptd.nwrote);
149 server_md5 = soup_message_headers_get_one (msg->response_headers,
151 if (!server_md5 || strcmp (client_md5, server_md5) != 0) {
152 debug_printf (1, " client/server data mismatch: %s vs %s\n",
153 client_md5, server_md5 ? server_md5 : "(null)");
157 g_object_unref (msg);
158 g_checksum_free (check);
162 SoupBuffer *current_chunk;
168 chunk_allocator (SoupMessage *msg, gsize max_len, gpointer user_data)
170 GetTestData *gtd = user_data;
172 debug_printf (2, " allocating chunk\n");
174 if (gtd->current_chunk) {
175 debug_printf (1, " error: next chunk allocated before last one freed!\n");
178 gtd->current_chunk = soup_buffer_new_with_owner (g_malloc (6), 6,
181 return gtd->current_chunk;
185 got_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
187 GetTestData *gtd = user_data;
189 debug_printf (2, " got chunk, %d bytes\n",
191 if (chunk != gtd->current_chunk) {
192 debug_printf (1, "chunk mismatch! %p vs %p\n",
193 chunk, gtd->current_chunk);
196 g_checksum_update (gtd->check, (guchar *)chunk->data, chunk->length);
197 gtd->length += chunk->length;
201 do_response_test (SoupSession *session, SoupURI *base_uri)
205 const char *client_md5, *server_md5;
207 debug_printf (1, "GET\n");
209 gtd.current_chunk = NULL;
211 gtd.check = g_checksum_new (G_CHECKSUM_MD5);
213 msg = soup_message_new_from_uri ("GET", base_uri);
214 soup_message_body_set_accumulate (msg->response_body, FALSE);
215 soup_message_set_chunk_allocator (msg, chunk_allocator, >d, NULL);
216 g_signal_connect (msg, "got_chunk",
217 G_CALLBACK (got_chunk), >d);
218 soup_session_send_message (session, msg);
220 if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
221 debug_printf (1, " message failed: %d %s\n",
222 msg->status_code, msg->reason_phrase);
226 if (msg->response_body->data) {
227 debug_printf (1, " msg->response_body set!\n");
230 if (soup_message_headers_get_content_length (msg->response_headers) != gtd.length) {
231 debug_printf (1, " received length mismatch: %d vs %d\n",
232 (int)soup_message_headers_get_content_length (msg->response_headers), gtd.length);
236 client_md5 = g_checksum_get_string (gtd.check);
237 server_md5 = soup_message_headers_get_one (msg->response_headers,
239 if (!server_md5 || strcmp (client_md5, server_md5) != 0) {
240 debug_printf (1, " client/server data mismatch: %s vs %s\n",
241 client_md5, server_md5 ? server_md5 : "(null)");
245 g_object_unref (msg);
246 g_checksum_free (gtd.check);
249 /* Make sure TEMPORARY buffers are handled properly with non-accumulating
250 * message bodies. Part of https://bugs.webkit.org/show_bug.cgi?id=18343
254 temp_test_wrote_chunk (SoupMessage *msg, gpointer session)
256 /* When the bug is present, the second chunk will also be
257 * discarded after the first is written, which will cause
258 * the I/O to stall since soup-message-io will think it's
259 * done, but it hasn't written Content-Length bytes yet.
260 * So add in another chunk to keep it going.
262 if (!soup_message_body_get_chunk (msg->request_body,
264 debug_printf (1, " Lost second chunk!\n");
266 soup_session_abort (session);
269 g_signal_handlers_disconnect_by_func (msg, temp_test_wrote_chunk, session);
273 do_temporary_test (SoupSession *session, SoupURI *base_uri)
276 const char *client_md5, *server_md5;
278 debug_printf (1, "PUT w/ temporary buffers\n");
280 msg = soup_message_new_from_uri ("PUT", base_uri);
281 soup_message_body_append (msg->request_body, SOUP_MEMORY_TEMPORARY,
283 soup_message_body_append (msg->request_body, SOUP_MEMORY_STATIC,
285 soup_message_body_set_accumulate (msg->request_body, FALSE);
287 client_md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5,
288 "one\r\ntwo\r\n", 10);
289 g_signal_connect (msg, "wrote_chunk",
290 G_CALLBACK (temp_test_wrote_chunk), session);
291 soup_session_send_message (session, msg);
293 if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
294 debug_printf (1, " message failed: %d %s\n",
295 msg->status_code, msg->reason_phrase);
299 server_md5 = soup_message_headers_get_one (msg->response_headers,
301 if (!server_md5 || strcmp (client_md5, server_md5) != 0) {
302 debug_printf (1, " client/server data mismatch: %s vs %s\n",
303 client_md5, server_md5 ? server_md5 : "(null)");
307 g_object_unref (msg);
311 do_chunk_tests (SoupURI *base_uri)
313 SoupSession *session;
315 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
316 do_request_test (session, base_uri);
317 debug_printf (2, "\n\n");
318 do_response_test (session, base_uri);
319 debug_printf (2, "\n\n");
320 do_temporary_test (session, base_uri);
321 soup_test_session_abort_unref (session);
325 server_callback (SoupServer *server, SoupMessage *msg,
326 const char *path, GHashTable *query,
327 SoupClientContext *context, gpointer data)
329 SoupMessageBody *md5_body;
332 if (msg->method == SOUP_METHOD_GET) {
333 soup_message_set_response (msg, "text/plain",
335 "three\r\ntwo\r\none\r\n",
336 strlen ("three\r\ntwo\r\none\r\n"));
337 soup_buffer_free (soup_message_body_flatten (msg->response_body));
338 md5_body = msg->response_body;
339 soup_message_set_status (msg, SOUP_STATUS_OK);
340 } else if (msg->method == SOUP_METHOD_PUT) {
341 soup_message_set_status (msg, SOUP_STATUS_CREATED);
342 md5_body = msg->request_body;
344 soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED);
348 md5 = g_compute_checksum_for_data (G_CHECKSUM_MD5,
349 (guchar *)md5_body->data,
351 soup_message_headers_append (msg->response_headers,
357 main (int argc, char **argv)
364 test_init (argc, argv, NULL);
366 server = soup_test_server_new (TRUE);
367 soup_server_add_handler (server, NULL,
368 server_callback, NULL, NULL);
369 port = soup_server_get_port (server);
371 loop = g_main_loop_new (NULL, TRUE);
373 base_uri = soup_uri_new ("http://127.0.0.1");
374 soup_uri_set_port (base_uri, port);
375 do_chunk_tests (base_uri);
376 soup_uri_free (base_uri);
378 g_main_loop_unref (loop);