Fix leaks in test programs, update libsoup.supp
[platform/upstream/libsoup.git] / tests / chunk-test.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2008 Red Hat, Inc.
4  */
5
6 #include "config.h"
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11
12 #include <glib.h>
13 #include <libsoup/soup.h>
14
15 #include "test-utils.h"
16
17 typedef struct {
18         SoupSession *session;
19         SoupBuffer *chunks[3];
20         int next, nwrote;
21 } PutTestData;
22
23 static SoupBuffer *
24 error_chunk_allocator (SoupMessage *msg, gsize max_len, gpointer user_data)
25 {
26         /* This should never be called, because there is no response body. */
27         debug_printf (1, "  error_chunk_allocator called!\n");
28         errors++;
29         return soup_buffer_new (SOUP_MEMORY_TAKE, g_malloc (100), 100);
30 }
31
32 static void
33 write_next_chunk (SoupMessage *msg, gpointer user_data)
34 {
35         PutTestData *ptd = user_data;
36
37         debug_printf (2, "  writing chunk\n");
38
39         if (ptd->next > 0 && ptd->chunks[ptd->next - 1]) {
40 #ifdef FIXME
41                 debug_printf (1, "  error: next chunk requested before last one freed!\n");
42                 errors++;
43 #else
44                 debug_printf (0, "  ignoring bug in test case... FIXME!\n");
45 #endif
46         }
47
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]);
52                 ptd->next++;
53         } else
54                 soup_message_body_complete (msg->request_body);
55         soup_session_unpause_message (ptd->session, msg);
56 }
57
58 static void
59 wrote_body_data (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
60 {
61         PutTestData *ptd = user_data;
62
63         debug_printf (2, "  wrote_body_data, %d bytes\n",
64                       (int)chunk->length);
65         ptd->nwrote += chunk->length;
66 }
67
68 static void
69 clear_buffer_ptr (gpointer data)
70 {
71         SoupBuffer **buffer_ptr = data;
72
73         debug_printf (2, "  clearing chunk\n");
74         if (*buffer_ptr) {
75                 (*buffer_ptr)->length = 0;
76                 g_free ((char *)(*buffer_ptr)->data);
77                 *buffer_ptr = NULL;
78         } else {
79                 debug_printf (2, "  chunk is already clear!\n");
80                 errors++;
81         }
82 }
83
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.
87  */
88 static void
89 make_put_chunk (SoupBuffer **buffer, const char *text)
90 {
91         *buffer = soup_buffer_new_with_owner (g_strdup (text), strlen (text),
92                                               buffer, clear_buffer_ptr);
93 }
94
95 static void
96 do_request_test (SoupSession *session, SoupURI *base_uri)
97 {
98         PutTestData ptd;
99         SoupMessage *msg;
100         const char *client_md5, *server_md5;
101         GChecksum *check;
102         int i, length;
103
104         debug_printf (1, "PUT\n");
105
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;
111
112         check = g_checksum_new (G_CHECKSUM_MD5);
113         length = 0;
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;
118         }
119         client_md5 = g_checksum_get_string (check);
120
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);
132
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);
136                 errors++;
137         }
138
139         if (msg->request_body->data) {
140                 debug_printf (1, "  msg->request_body set!\n");
141                 errors++;
142         }
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);
146                 errors++;
147         }
148
149         server_md5 = soup_message_headers_get_one (msg->response_headers,
150                                                    "Content-MD5");
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)");
154                 errors++;
155         }
156
157         g_object_unref (msg);
158         g_checksum_free (check);
159 }
160
161 typedef struct {
162         SoupBuffer *current_chunk;
163         GChecksum *check;
164         int length;
165 } GetTestData;
166
167 static SoupBuffer *
168 chunk_allocator (SoupMessage *msg, gsize max_len, gpointer user_data)
169 {
170         GetTestData *gtd = user_data;
171
172         debug_printf (2, "  allocating chunk\n");
173
174         if (gtd->current_chunk) {
175                 debug_printf (1, "  error: next chunk allocated before last one freed!\n");
176                 errors++;
177         }
178         gtd->current_chunk = soup_buffer_new_with_owner (g_malloc (6), 6,
179                                                          &gtd->current_chunk,
180                                                          clear_buffer_ptr);
181         return gtd->current_chunk;
182 }
183
184 static void
185 got_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
186 {
187         GetTestData *gtd = user_data;
188
189         debug_printf (2, "  got chunk, %d bytes\n",
190                       (int)chunk->length);
191         if (chunk != gtd->current_chunk) {
192                 debug_printf (1, "chunk mismatch! %p vs %p\n",
193                               chunk, gtd->current_chunk);
194         }
195
196         g_checksum_update (gtd->check, (guchar *)chunk->data, chunk->length);
197         gtd->length += chunk->length;
198 }
199
200 static void
201 do_response_test (SoupSession *session, SoupURI *base_uri)
202 {
203         GetTestData gtd;
204         SoupMessage *msg;
205         const char *client_md5, *server_md5;
206
207         debug_printf (1, "GET\n");
208
209         gtd.current_chunk = NULL;
210         gtd.length = 0;
211         gtd.check = g_checksum_new (G_CHECKSUM_MD5);
212
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, &gtd, NULL);
216         g_signal_connect (msg, "got_chunk",
217                           G_CALLBACK (got_chunk), &gtd);
218         soup_session_send_message (session, msg);
219
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);
223                 errors++;
224         }
225
226         if (msg->response_body->data) {
227                 debug_printf (1, "  msg->response_body set!\n");
228                 errors++;
229         }
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);
233                 errors++;
234         }
235
236         client_md5 = g_checksum_get_string (gtd.check);
237         server_md5 = soup_message_headers_get_one (msg->response_headers,
238                                                    "Content-MD5");
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)");
242                 errors++;
243         }
244
245         g_object_unref (msg);
246         g_checksum_free (gtd.check);
247 }
248
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
251  */
252
253 static void
254 temp_test_wrote_chunk (SoupMessage *msg, gpointer session)
255 {
256         SoupBuffer *chunk;
257
258         chunk = soup_message_body_get_chunk (msg->request_body, 5);
259
260         /* When the bug is present, the second chunk will also be
261          * discarded after the first is written, which will cause
262          * the I/O to stall since soup-message-io will think it's
263          * done, but it hasn't written Content-Length bytes yet.
264          */
265         if (!chunk) {
266                 debug_printf (1, "  Lost second chunk!\n");
267                 errors++;
268                 soup_session_abort (session);
269         } else
270                 soup_buffer_free (chunk);
271
272         g_signal_handlers_disconnect_by_func (msg, temp_test_wrote_chunk, session);
273 }
274
275 static void
276 do_temporary_test (SoupSession *session, SoupURI *base_uri)
277 {
278         SoupMessage *msg;
279         char *client_md5;
280         const char *server_md5;
281
282         debug_printf (1, "PUT w/ temporary buffers\n");
283
284         msg = soup_message_new_from_uri ("PUT", base_uri);
285         soup_message_body_append (msg->request_body, SOUP_MEMORY_TEMPORARY,
286                                   "one\r\n", 5);
287         soup_message_body_append (msg->request_body, SOUP_MEMORY_STATIC,
288                                   "two\r\n", 5);
289         soup_message_body_set_accumulate (msg->request_body, FALSE);
290
291         client_md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5,
292                                                     "one\r\ntwo\r\n", 10);
293         g_signal_connect (msg, "wrote_chunk",
294                           G_CALLBACK (temp_test_wrote_chunk), session);
295         soup_session_send_message (session, msg);
296
297         if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
298                 debug_printf (1, "  message failed: %d %s\n",
299                               msg->status_code, msg->reason_phrase);
300                 errors++;
301         }
302
303         server_md5 = soup_message_headers_get_one (msg->response_headers,
304                                                    "Content-MD5");
305         if (!server_md5 || strcmp (client_md5, server_md5) != 0) {
306                 debug_printf (1, "  client/server data mismatch: %s vs %s\n",
307                               client_md5, server_md5 ? server_md5 : "(null)");
308                 errors++;
309         }
310
311         g_free (client_md5);
312         g_object_unref (msg);
313 }
314
315 static void
316 do_chunk_tests (SoupURI *base_uri)
317 {
318         SoupSession *session;
319
320         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
321         do_request_test (session, base_uri);
322         debug_printf (2, "\n\n");
323         do_response_test (session, base_uri);
324         debug_printf (2, "\n\n");
325         do_temporary_test (session, base_uri);
326         soup_test_session_abort_unref (session);
327 }
328
329 static void
330 server_callback (SoupServer *server, SoupMessage *msg,
331                  const char *path, GHashTable *query,
332                  SoupClientContext *context, gpointer data)
333 {
334         SoupMessageBody *md5_body;
335         char *md5;
336
337         if (msg->method == SOUP_METHOD_GET) {
338                 soup_message_set_response (msg, "text/plain",
339                                            SOUP_MEMORY_STATIC,
340                                            "three\r\ntwo\r\none\r\n",
341                                            strlen ("three\r\ntwo\r\none\r\n"));
342                 soup_buffer_free (soup_message_body_flatten (msg->response_body));
343                 md5_body = msg->response_body;
344                 soup_message_set_status (msg, SOUP_STATUS_OK);
345         } else if (msg->method == SOUP_METHOD_PUT) {
346                 soup_message_set_status (msg, SOUP_STATUS_CREATED);
347                 md5_body = msg->request_body;
348         } else {
349                 soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED);
350                 return;
351         }
352
353         md5 = g_compute_checksum_for_data (G_CHECKSUM_MD5,
354                                            (guchar *)md5_body->data,
355                                            md5_body->length);
356         soup_message_headers_append (msg->response_headers,
357                                      "Content-MD5", md5);
358         g_free (md5);
359 }
360
361 int
362 main (int argc, char **argv)
363 {
364         GMainLoop *loop;
365         SoupServer *server;
366         guint port;
367         SoupURI *base_uri;
368
369         test_init (argc, argv, NULL);
370
371         server = soup_test_server_new (TRUE);
372         soup_server_add_handler (server, NULL,
373                                  server_callback, NULL, NULL);
374         port =  soup_server_get_port (server);
375
376         loop = g_main_loop_new (NULL, TRUE);
377
378         base_uri = soup_uri_new ("http://127.0.0.1");
379         soup_uri_set_port (base_uri, port);
380         do_chunk_tests (base_uri);
381         soup_uri_free (base_uri);
382
383         g_main_loop_unref (loop);
384
385         test_cleanup ();
386         return errors != 0;
387 }