store the GSource in priv, don't ref the session. Otherwise the session
[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                 debug_printf (1, "  error: next chunk requested before last one freed!\n");
41                 errors++;
42         }
43
44         if (ptd->next < G_N_ELEMENTS (ptd->chunks)) {
45                 soup_message_body_append_buffer (msg->request_body,
46                                                  ptd->chunks[ptd->next]);
47                 soup_buffer_free (ptd->chunks[ptd->next]);
48                 ptd->next++;
49         } else
50                 soup_message_body_complete (msg->request_body);
51         soup_session_unpause_message (ptd->session, msg);
52 }
53
54 static void
55 wrote_body_data (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
56 {
57         PutTestData *ptd = user_data;
58
59         debug_printf (2, "  wrote_body_data, %d bytes\n",
60                       (int)chunk->length);
61         ptd->nwrote += chunk->length;
62 }
63
64 static void
65 clear_buffer_ptr (gpointer data)
66 {
67         SoupBuffer **buffer_ptr = data;
68
69         debug_printf (2, "  clearing chunk\n");
70         if (*buffer_ptr) {
71                 (*buffer_ptr)->length = 0;
72                 *buffer_ptr = NULL;
73         } else {
74                 debug_printf (2, "  chunk is already clear!\n");
75                 errors++;
76         }
77 }
78
79 /* Put a chunk containing @text into *@buffer, set up so that it will
80  * clear out *@buffer when the chunk is freed, allowing us to make sure
81  * the set_accumulate(FALSE) is working.
82  */
83 static void
84 make_put_chunk (SoupBuffer **buffer, const char *text)
85 {
86         *buffer = soup_buffer_new_with_owner (text, strlen (text),
87                                               buffer, clear_buffer_ptr);
88 }
89
90 static void
91 do_request_test (SoupSession *session, SoupURI *base_uri)
92 {
93         PutTestData ptd;
94         SoupMessage *msg;
95         const char *client_md5, *server_md5;
96         GChecksum *check;
97         int i, length;
98
99         debug_printf (1, "PUT\n");
100
101         ptd.session = session;
102         make_put_chunk (&ptd.chunks[0], "one\r\n");
103         make_put_chunk (&ptd.chunks[1], "two\r\n");
104         make_put_chunk (&ptd.chunks[2], "three\r\n");
105         ptd.next = ptd.nwrote = 0;
106
107         check = g_checksum_new (G_CHECKSUM_MD5);
108         length = 0;
109         for (i = 0; i < 3; i++) {
110                 g_checksum_update (check, (guchar *)ptd.chunks[i]->data,
111                                    ptd.chunks[i]->length);
112                 length += ptd.chunks[i]->length;
113         }
114         client_md5 = g_checksum_get_string (check);
115
116         msg = soup_message_new_from_uri ("PUT", base_uri);
117         soup_message_headers_set_encoding (msg->request_headers, SOUP_ENCODING_CHUNKED);
118         soup_message_body_set_accumulate (msg->request_body, FALSE);
119         soup_message_set_chunk_allocator (msg, error_chunk_allocator, NULL, NULL);
120         g_signal_connect (msg, "wrote_headers",
121                           G_CALLBACK (write_next_chunk), &ptd);
122         g_signal_connect (msg, "wrote_chunk",
123                           G_CALLBACK (write_next_chunk), &ptd);
124         g_signal_connect (msg, "wrote_body_data",
125                           G_CALLBACK (wrote_body_data), &ptd);
126         soup_session_send_message (session, msg);
127
128         if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
129                 debug_printf (1, "  message failed: %d %s\n",
130                               msg->status_code, msg->reason_phrase);
131                 errors++;
132         }
133
134         if (msg->request_body->data) {
135                 debug_printf (1, "  msg->request_body set!\n");
136                 errors++;
137         }
138         if (msg->request_body->length != length || length != ptd.nwrote) {
139                 debug_printf (1, "  sent length mismatch: %d vs %d vs %d\n",
140                               (int)msg->request_body->length, length, ptd.nwrote);
141                 errors++;
142         }
143
144         server_md5 = soup_message_headers_get (msg->response_headers, "Content-MD5");
145         if (!server_md5 || strcmp (client_md5, server_md5) != 0) {
146                 debug_printf (1, "  client/server data mismatch: %s vs %s\n",
147                               client_md5, server_md5 ? server_md5 : "(null)");
148                 errors++;
149         }
150
151         g_object_unref (msg);
152         g_checksum_free (check);
153 }
154
155 typedef struct {
156         SoupBuffer *current_chunk;
157         GChecksum *check;
158         int length;
159 } GetTestData;
160
161 static SoupBuffer *
162 chunk_allocator (SoupMessage *msg, gsize max_len, gpointer user_data)
163 {
164         GetTestData *gtd = user_data;
165
166         debug_printf (2, "  allocating chunk\n");
167
168         if (gtd->current_chunk) {
169                 debug_printf (1, "  error: next chunk allocated before last one freed!\n");
170                 errors++;
171         }
172         gtd->current_chunk = soup_buffer_new_with_owner (g_malloc (6), 6,
173                                                          &gtd->current_chunk,
174                                                          clear_buffer_ptr);
175         return gtd->current_chunk;
176 }
177
178 static void
179 got_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
180 {
181         GetTestData *gtd = user_data;
182
183         debug_printf (2, "  got chunk, %d bytes\n",
184                       (int)chunk->length);
185         if (chunk != gtd->current_chunk) {
186                 debug_printf (1, "chunk mismatch! %p vs %p\n",
187                               chunk, gtd->current_chunk);
188         }
189
190         g_checksum_update (gtd->check, (guchar *)chunk->data, chunk->length);
191         gtd->length += chunk->length;
192 }
193
194 static void
195 do_response_test (SoupSession *session, SoupURI *base_uri)
196 {
197         GetTestData gtd;
198         SoupMessage *msg;
199         const char *client_md5, *server_md5;
200
201         debug_printf (1, "GET\n");
202
203         gtd.current_chunk = NULL;
204         gtd.length = 0;
205         gtd.check = g_checksum_new (G_CHECKSUM_MD5);
206
207         msg = soup_message_new_from_uri ("GET", base_uri);
208         soup_message_body_set_accumulate (msg->response_body, FALSE);
209         soup_message_set_chunk_allocator (msg, chunk_allocator, &gtd, NULL);
210         g_signal_connect (msg, "got_chunk",
211                           G_CALLBACK (got_chunk), &gtd);
212         soup_session_send_message (session, msg);
213
214         if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
215                 debug_printf (1, "  message failed: %d %s\n",
216                               msg->status_code, msg->reason_phrase);
217                 errors++;
218         }
219
220         if (msg->response_body->data) {
221                 debug_printf (1, "  msg->response_body set!\n");
222                 errors++;
223         }
224         if (soup_message_headers_get_content_length (msg->response_headers) != gtd.length) {
225                 debug_printf (1, "  received length mismatch: %d vs %d\n",
226                               (int)soup_message_headers_get_content_length (msg->response_headers), gtd.length);
227                 errors++;
228         }
229
230         client_md5 = g_checksum_get_string (gtd.check);
231         server_md5 = soup_message_headers_get (msg->response_headers, "Content-MD5");
232         if (!server_md5 || strcmp (client_md5, server_md5) != 0) {
233                 debug_printf (1, "  client/server data mismatch: %s vs %s\n",
234                               client_md5, server_md5 ? server_md5 : "(null)");
235                 errors++;
236         }
237
238         g_object_unref (msg);
239         g_checksum_free (gtd.check);
240 }
241
242 static void
243 do_chunk_tests (SoupURI *base_uri)
244 {
245         SoupSession *session;
246
247         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
248         do_request_test (session, base_uri);
249         debug_printf (2, "\n\n");
250         do_response_test (session, base_uri);
251         soup_test_session_abort_unref (session);
252 }
253
254 static void
255 server_callback (SoupServer *server, SoupMessage *msg,
256                  const char *path, GHashTable *query,
257                  SoupClientContext *context, gpointer data)
258 {
259         SoupMessageBody *md5_body;
260         char *md5;
261
262         if (msg->method == SOUP_METHOD_GET) {
263                 soup_message_set_response (msg, "text/plain",
264                                            SOUP_MEMORY_STATIC,
265                                            "three\r\ntwo\r\none\r\n",
266                                            strlen ("three\r\ntwo\r\none\r\n"));
267                 soup_buffer_free (soup_message_body_flatten (msg->response_body));
268                 md5_body = msg->response_body;
269                 soup_message_set_status (msg, SOUP_STATUS_OK);
270         } else if (msg->method == SOUP_METHOD_PUT) {
271                 soup_message_set_status (msg, SOUP_STATUS_CREATED);
272                 md5_body = msg->request_body;
273         } else {
274                 soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED);
275                 return;
276         }
277
278         md5 = g_compute_checksum_for_data (G_CHECKSUM_MD5,
279                                            (guchar *)md5_body->data,
280                                            md5_body->length);
281         soup_message_headers_append (msg->response_headers,
282                                      "Content-MD5", md5);
283         g_free (md5);
284 }
285
286 int
287 main (int argc, char **argv)
288 {
289         GMainLoop *loop;
290         SoupServer *server;
291         guint port;
292         SoupURI *base_uri;
293
294         test_init (argc, argv, NULL);
295
296         server = soup_test_server_new (TRUE);
297         soup_server_add_handler (server, NULL,
298                                  server_callback, NULL, NULL);
299         port =  soup_server_get_port (server);
300
301         loop = g_main_loop_new (NULL, TRUE);
302
303         base_uri = soup_uri_new ("http://localhost");
304         soup_uri_set_port (base_uri, port);
305         do_chunk_tests (base_uri);
306         soup_uri_free (base_uri);
307
308         g_main_loop_unref (loop);
309
310         test_cleanup ();
311         return errors != 0;
312 }