soup-multipart-input-stream: belatedly add .h file to soup.h
[platform/upstream/libsoup.git] / tests / misc-test.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright 2007-2012 Red Hat, Inc.
4  */
5
6 #include "test-utils.h"
7
8 SoupServer *server, *ssl_server;
9 SoupURI *base_uri, *ssl_base_uri;
10
11 static gboolean
12 auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
13                const char *username, const char *password, gpointer data)
14 {
15         return !strcmp (username, "user") && !strcmp (password, "password");
16 }
17
18 static gboolean
19 timeout_finish_message (gpointer msg)
20 {
21         SoupServer *server = g_object_get_data (G_OBJECT (msg), "server");
22
23         soup_server_unpause_message (server, msg);
24         return FALSE;
25 }
26
27 static void
28 server_callback (SoupServer *server, SoupMessage *msg,
29                  const char *path, GHashTable *query,
30                  SoupClientContext *context, gpointer data)
31 {
32         SoupURI *uri = soup_message_get_uri (msg);
33         const char *server_protocol = data;
34
35         soup_message_headers_append (msg->response_headers,
36                                      "X-Handled-By", "server_callback");
37
38         if (!strcmp (path, "*")) {
39                 debug_printf (1, "    default server_callback got request for '*'!\n");
40                 errors++;
41                 soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
42                 return;
43         }
44
45         if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_POST) {
46                 soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
47                 return;
48         }
49
50         if (!strcmp (path, "/redirect")) {
51                 soup_message_set_redirect (msg, SOUP_STATUS_FOUND, "/");
52                 return;
53         }
54
55         if (!strcmp (path, "/alias-redirect")) {
56                 SoupURI *redirect_uri;
57                 char *redirect_string;
58                 const char *redirect_protocol;
59
60                 redirect_protocol = soup_message_headers_get_one (msg->request_headers, "X-Redirect-Protocol");
61
62                 redirect_uri = soup_uri_copy (uri);
63                 soup_uri_set_scheme (redirect_uri, "foo");
64                 if (!g_strcmp0 (redirect_protocol, "https"))
65                         soup_uri_set_port (redirect_uri, ssl_base_uri->port);
66                 else
67                         soup_uri_set_port (redirect_uri, base_uri->port);
68                 soup_uri_set_path (redirect_uri, "/alias-redirected");
69                 redirect_string = soup_uri_to_string (redirect_uri, FALSE);
70
71                 soup_message_set_redirect (msg, SOUP_STATUS_FOUND, redirect_string);
72                 g_free (redirect_string);
73                 soup_uri_free (redirect_uri);
74                 return;
75         } else if (!strcmp (path, "/alias-redirected")) {
76                 soup_message_set_status (msg, SOUP_STATUS_OK);
77                 soup_message_headers_append (msg->response_headers,
78                                              "X-Redirected-Protocol",
79                                              server_protocol);
80                 return;
81         }
82
83         if (!strcmp (path, "/slow")) {
84                 soup_server_pause_message (server, msg);
85                 g_object_set_data (G_OBJECT (msg), "server", server);
86                 soup_add_timeout (soup_server_get_async_context (server),
87                                   1000, timeout_finish_message, msg);
88         }
89
90         soup_message_set_status (msg, SOUP_STATUS_OK);
91         if (!strcmp (uri->host, "foo")) {
92                 soup_message_set_response (msg, "text/plain",
93                                            SOUP_MEMORY_STATIC, "foo-index", 9);
94                 return;
95         } else {
96                 soup_message_set_response (msg, "text/plain",
97                                            SOUP_MEMORY_STATIC, "index", 5);
98                 return;
99         }
100 }
101
102 static void
103 server_star_callback (SoupServer *server, SoupMessage *msg,
104                       const char *path, GHashTable *query,
105                       SoupClientContext *context, gpointer data)
106 {
107         soup_message_headers_append (msg->response_headers,
108                                      "X-Handled-By", "star_callback");
109
110         if (strcmp (path, "*") != 0) {
111                 debug_printf (1, "    server_star_callback got request for '%s'!\n", path);
112                 errors++;
113                 soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
114                 return;
115         }
116
117         if (msg->method != SOUP_METHOD_OPTIONS) {
118                 soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED);
119                 return;
120         }
121
122         soup_message_set_status (msg, SOUP_STATUS_OK);
123 }
124
125 /* Host header handling: client must be able to override the default
126  * value, server must be able to recognize different Host values.
127  * #539803.
128  */
129 static void
130 do_host_test (void)
131 {
132         SoupSession *session;
133         SoupMessage *one, *two;
134
135         debug_printf (1, "Host handling\n");
136
137         session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
138
139         one = soup_message_new_from_uri ("GET", base_uri);
140         two = soup_message_new_from_uri ("GET", base_uri);
141         soup_message_headers_replace (two->request_headers, "Host", "foo");
142
143         soup_session_send_message (session, one);
144         soup_session_send_message (session, two);
145
146         soup_test_session_abort_unref (session);
147
148         if (!SOUP_STATUS_IS_SUCCESSFUL (one->status_code)) {
149                 debug_printf (1, "  Message 1 failed: %d %s\n",
150                               one->status_code, one->reason_phrase);
151                 errors++;
152         } else if (strcmp (one->response_body->data, "index") != 0) {
153                 debug_printf (1, "  Unexpected response to message 1: '%s'\n",
154                               one->response_body->data);
155                 errors++;
156         }
157         g_object_unref (one);
158
159         if (!SOUP_STATUS_IS_SUCCESSFUL (two->status_code)) {
160                 debug_printf (1, "  Message 2 failed: %d %s\n",
161                               two->status_code, two->reason_phrase);
162                 errors++;
163         } else if (strcmp (two->response_body->data, "foo-index") != 0) {
164                 debug_printf (1, "  Unexpected response to message 2: '%s'\n",
165                               two->response_body->data);
166                 errors++;
167         }
168         g_object_unref (two);
169 }
170
171 /* Dropping the application's ref on the session from a callback
172  * should not cause the session to be freed at an incorrect time.
173  * (This test will crash if it fails.) #533473
174  */
175 static void
176 cu_one_completed (SoupSession *session, SoupMessage *msg, gpointer loop)
177 {
178         debug_printf (2, "  Message 1 completed\n");
179         if (msg->status_code != SOUP_STATUS_CANT_CONNECT) {
180                 debug_printf (1, "  Unexpected status on Message 1: %d %s\n",
181                               msg->status_code, msg->reason_phrase);
182                 errors++;
183         }
184         g_object_unref (session);
185 }
186
187 static gboolean
188 cu_idle_quit (gpointer loop)
189 {
190         g_main_loop_quit (loop);
191         return FALSE;
192 }
193
194 static void
195 cu_two_completed (SoupSession *session, SoupMessage *msg, gpointer loop)
196 {
197         debug_printf (2, "  Message 2 completed\n");
198         if (msg->status_code != SOUP_STATUS_CANT_CONNECT) {
199                 debug_printf (1, "  Unexpected status on Message 2: %d %s\n",
200                               msg->status_code, msg->reason_phrase);
201                 errors++;
202         }
203         g_idle_add (cu_idle_quit, loop); 
204 }
205
206 static void
207 do_callback_unref_test (void)
208 {
209         SoupServer *bad_server;
210         SoupAddress *addr;
211         SoupSession *session;
212         SoupMessage *one, *two;
213         GMainLoop *loop;
214         char *bad_uri;
215
216         debug_printf (1, "\nCallback unref handling (msg api)\n");
217
218         /* Get a guaranteed-bad URI */
219         addr = soup_address_new ("127.0.0.1", SOUP_ADDRESS_ANY_PORT);
220         soup_address_resolve_sync (addr, NULL);
221         bad_server = soup_server_new (SOUP_SERVER_INTERFACE, addr,
222                                       NULL);
223         g_object_unref (addr);
224
225         bad_uri = g_strdup_printf ("http://127.0.0.1:%u/",
226                                    soup_server_get_port (bad_server));
227         g_object_unref (bad_server);
228
229         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
230         g_object_add_weak_pointer (G_OBJECT (session), (gpointer *)&session);
231
232         loop = g_main_loop_new (NULL, TRUE);
233
234         one = soup_message_new ("GET", bad_uri);
235         g_object_add_weak_pointer (G_OBJECT (one), (gpointer *)&one);
236         two = soup_message_new ("GET", bad_uri);
237         g_object_add_weak_pointer (G_OBJECT (two), (gpointer *)&two);
238         g_free (bad_uri);
239
240         soup_session_queue_message (session, one, cu_one_completed, loop);
241         soup_session_queue_message (session, two, cu_two_completed, loop);
242
243         g_main_loop_run (loop);
244         g_main_loop_unref (loop);
245
246         if (session) {
247                 g_object_remove_weak_pointer (G_OBJECT (session), (gpointer *)&session);
248                 debug_printf (1, "  Session not destroyed?\n");
249                 errors++;
250                 g_object_unref (session);
251         }
252         if (one) {
253                 g_object_remove_weak_pointer (G_OBJECT (one), (gpointer *)&one);
254                 debug_printf (1, "  Message 1 not destroyed?\n");
255                 errors++;
256                 g_object_unref (one);
257         }
258         if (two) {
259                 g_object_remove_weak_pointer (G_OBJECT (two), (gpointer *)&two);
260                 debug_printf (1, "  Message 2 not destroyed?\n");
261                 errors++;
262                 g_object_unref (two);
263         }
264
265         /* Otherwise, if we haven't crashed, we're ok. */
266 }
267
268 static void
269 cur_one_completed (GObject *source, GAsyncResult *result, gpointer session)
270 {
271         SoupRequest *one = SOUP_REQUEST (source);
272         GError *error = NULL;
273
274         debug_printf (2, "  Request 1 completed\n");
275         if (soup_request_send_finish (one, result, &error)) {
276                 debug_printf (1, "  Request 1 succeeded?\n");
277                 errors++;
278         } else if (!g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_CANT_CONNECT)) {
279                 debug_printf (1, "  Unexpected error on Request 1: %s\n",
280                               error->message);
281                 errors++;
282         }
283         g_clear_error (&error);
284
285         g_object_unref (session);
286 }
287
288 static gboolean
289 cur_idle_quit (gpointer loop)
290 {
291         g_main_loop_quit (loop);
292         return FALSE;
293 }
294
295 static void
296 cur_two_completed (GObject *source, GAsyncResult *result, gpointer loop)
297 {
298         SoupRequest *two = SOUP_REQUEST (source);
299         GError *error = NULL;
300
301         debug_printf (2, "  Request 2 completed\n");
302         if (soup_request_send_finish (two, result, &error)) {
303                 debug_printf (1, "  Request 2 succeeded?\n");
304                 errors++;
305         } else if (!g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_CANT_CONNECT)) {
306                 debug_printf (1, "  Unexpected error on Request 2: %s\n",
307                               error->message);
308                 errors++;
309         }
310         g_clear_error (&error);
311
312         g_idle_add (cur_idle_quit, loop); 
313 }
314
315 static void
316 do_callback_unref_req_test (void)
317 {
318         SoupServer *bad_server;
319         SoupAddress *addr;
320         SoupSession *session;
321         SoupRequester *requester;
322         SoupRequest *one, *two;
323         GMainLoop *loop;
324         char *bad_uri;
325
326         debug_printf (1, "\nCallback unref handling (request api)\n");
327
328         /* Get a guaranteed-bad URI */
329         addr = soup_address_new ("127.0.0.1", SOUP_ADDRESS_ANY_PORT);
330         soup_address_resolve_sync (addr, NULL);
331         bad_server = soup_server_new (SOUP_SERVER_INTERFACE, addr,
332                                       NULL);
333         g_object_unref (addr);
334
335         bad_uri = g_strdup_printf ("http://127.0.0.1:%u/",
336                                    soup_server_get_port (bad_server));
337         g_object_unref (bad_server);
338
339         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
340                                          SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
341                                          SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
342                                          NULL);
343         g_object_add_weak_pointer (G_OBJECT (session), (gpointer *)&session);
344
345         requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
346
347         loop = g_main_loop_new (NULL, TRUE);
348
349         one = soup_requester_request (requester, bad_uri, NULL);
350         g_object_add_weak_pointer (G_OBJECT (one), (gpointer *)&one);
351         two = soup_requester_request (requester, bad_uri, NULL);
352         g_object_add_weak_pointer (G_OBJECT (two), (gpointer *)&two);
353         g_free (bad_uri);
354
355         soup_request_send_async (one, NULL, cur_one_completed, session);
356         g_object_unref (one);
357         soup_request_send_async (two, NULL, cur_two_completed, loop);
358         g_object_unref (two);
359
360         g_main_loop_run (loop);
361         g_main_loop_unref (loop);
362
363         if (session) {
364                 g_object_remove_weak_pointer (G_OBJECT (session), (gpointer *)&session);
365                 debug_printf (1, "  Session not destroyed?\n");
366                 errors++;
367                 g_object_unref (session);
368         }
369         if (one) {
370                 g_object_remove_weak_pointer (G_OBJECT (one), (gpointer *)&one);
371                 debug_printf (1, "  Request 1 not destroyed?\n");
372                 errors++;
373                 g_object_unref (one);
374         }
375         if (two) {
376                 g_object_remove_weak_pointer (G_OBJECT (two), (gpointer *)&two);
377                 debug_printf (1, "  Request 2 not destroyed?\n");
378                 errors++;
379                 g_object_unref (two);
380         }
381
382         /* Otherwise, if we haven't crashed, we're ok. */
383 }
384
385 /* SoupSession should clean up all signal handlers on a message after
386  * it is finished, allowing the message to be reused if desired.
387  * #559054
388  */
389 static void
390 ensure_no_signal_handlers (SoupMessage *msg, guint *signal_ids, guint n_signal_ids)
391 {
392         int i;
393
394         for (i = 0; i < n_signal_ids; i++) {
395                 if (g_signal_handler_find (msg, G_SIGNAL_MATCH_ID, signal_ids[i],
396                                            0, NULL, NULL, NULL)) {
397                         debug_printf (1, "    Message has handler for '%s'\n",
398                                       g_signal_name (signal_ids[i]));
399                         errors++;
400                 }
401         }
402 }
403
404 static void
405 reuse_test_authenticate (SoupSession *session, SoupMessage *msg,
406                          SoupAuth *auth, gboolean retrying)
407 {
408         /* Get it wrong the first time, then succeed */
409         if (!retrying)
410                 soup_auth_authenticate (auth, "user", "wrong password");
411         else
412                 soup_auth_authenticate (auth, "user", "password");
413 }
414
415 static void
416 do_msg_reuse_test (void)
417 {
418         SoupSession *session;
419         SoupMessage *msg;
420         SoupURI *uri;
421         guint *signal_ids, n_signal_ids;
422
423         debug_printf (1, "\nSoupMessage reuse\n");
424
425         signal_ids = g_signal_list_ids (SOUP_TYPE_MESSAGE, &n_signal_ids);
426
427         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
428         g_signal_connect (session, "authenticate",
429                           G_CALLBACK (reuse_test_authenticate), NULL);
430
431         debug_printf (1, "  First message\n");
432         msg = soup_message_new_from_uri ("GET", base_uri);
433         soup_session_send_message (session, msg);
434         ensure_no_signal_handlers (msg, signal_ids, n_signal_ids);
435
436         debug_printf (1, "  Redirect message\n");
437         uri = soup_uri_new_with_base (base_uri, "/redirect");
438         soup_message_set_uri (msg, uri);
439         soup_uri_free (uri);
440         soup_session_send_message (session, msg);
441         if (!soup_uri_equal (soup_message_get_uri (msg), base_uri)) {
442                 debug_printf (1, "    Message did not get redirected!\n");
443                 errors++;
444         }
445         ensure_no_signal_handlers (msg, signal_ids, n_signal_ids);
446
447         debug_printf (1, "  Auth message\n");
448         uri = soup_uri_new_with_base (base_uri, "/auth");
449         soup_message_set_uri (msg, uri);
450         soup_uri_free (uri);
451         soup_session_send_message (session, msg);
452         if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
453                 debug_printf (1, "    Message did not get authenticated!\n");
454                 errors++;
455         }
456         ensure_no_signal_handlers (msg, signal_ids, n_signal_ids);
457
458         /* One last try to make sure the auth stuff got cleaned up */
459         debug_printf (1, "  Last message\n");
460         soup_message_set_uri (msg, base_uri);
461         soup_session_send_message (session, msg);
462         ensure_no_signal_handlers (msg, signal_ids, n_signal_ids);
463
464         soup_test_session_abort_unref (session);
465         g_object_unref (msg);
466         g_free (signal_ids);
467 }
468
469 /* Server handlers for "*" work but are separate from handlers for
470  * all other URIs. #590751
471  */
472 static void
473 do_star_test (void)
474 {
475         SoupSession *session;
476         SoupMessage *msg;
477         SoupURI *star_uri;
478         const char *handled_by;
479
480         debug_printf (1, "\nOPTIONS *\n");
481
482         session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
483         star_uri = soup_uri_copy (base_uri);
484         soup_uri_set_path (star_uri, "*");
485
486         debug_printf (1, "  Testing with no handler\n");
487         msg = soup_message_new_from_uri ("OPTIONS", star_uri);
488         soup_session_send_message (session, msg);
489
490         if (msg->status_code != SOUP_STATUS_NOT_FOUND) {
491                 debug_printf (1, "    Unexpected response: %d %s\n",
492                               msg->status_code, msg->reason_phrase);
493                 errors++;
494         }
495         handled_by = soup_message_headers_get_one (msg->response_headers,
496                                                    "X-Handled-By");
497         if (handled_by) {
498                 /* Should have been rejected by SoupServer directly */
499                 debug_printf (1, "    Message reached handler '%s'\n",
500                               handled_by);
501                 errors++;
502         }
503         g_object_unref (msg);
504
505         soup_server_add_handler (server, "*", server_star_callback, NULL, NULL);
506
507         debug_printf (1, "  Testing with handler\n");
508         msg = soup_message_new_from_uri ("OPTIONS", star_uri);
509         soup_session_send_message (session, msg);
510
511         if (msg->status_code != SOUP_STATUS_OK) {
512                 debug_printf (1, "    Unexpected response: %d %s\n",
513                               msg->status_code, msg->reason_phrase);
514                 errors++;
515         }
516         handled_by = soup_message_headers_get_one (msg->response_headers,
517                                                    "X-Handled-By");
518         if (!handled_by) {
519                 debug_printf (1, "    Message did not reach handler!\n");
520                 errors++;
521         } else if (strcmp (handled_by, "star_callback") != 0) {
522                 debug_printf (1, "    Message reached incorrect handler '%s'\n",
523                               handled_by);
524                 errors++;
525         }
526         g_object_unref (msg);
527
528         soup_test_session_abort_unref (session);
529         soup_uri_free (star_uri);
530 }
531
532 /* Handle unexpectedly-early aborts. #596074, #618641 */
533 static void
534 ea_msg_completed_one (SoupSession *session, SoupMessage *msg, gpointer loop)
535 {
536         debug_printf (2, "  Message 1 completed\n");
537         if (msg->status_code != SOUP_STATUS_CANCELLED) {
538                 debug_printf (1, "  Unexpected status on Message 1: %d %s\n",
539                               msg->status_code, msg->reason_phrase);
540                 errors++;
541         }
542         g_main_loop_quit (loop);
543 }
544
545 static gboolean
546 ea_abort_session (gpointer session)
547 {
548         soup_session_abort (session);
549         return FALSE;
550 }
551
552 static void
553 ea_connection_state_changed (GObject *conn, GParamSpec *pspec, gpointer session)
554 {
555         SoupConnectionState state;
556
557         g_object_get (conn, "state", &state, NULL);
558         if (state == SOUP_CONNECTION_CONNECTING) {
559                 g_idle_add_full (G_PRIORITY_HIGH,
560                                  ea_abort_session,
561                                  session, NULL);
562                 g_signal_handlers_disconnect_by_func (conn, ea_connection_state_changed, session);
563         }
564 }               
565
566 static void
567 ea_connection_created (SoupSession *session, GObject *conn, gpointer user_data)
568 {
569         g_signal_connect (conn, "notify::state",
570                           G_CALLBACK (ea_connection_state_changed), session);
571         g_signal_handlers_disconnect_by_func (session, ea_connection_created, user_data);
572 }
573
574 static void
575 ea_request_started (SoupSession *session, SoupMessage *msg, SoupSocket *socket, gpointer user_data)
576 {
577         soup_session_cancel_message (session, msg, SOUP_STATUS_CANCELLED);
578 }
579
580 static void
581 do_early_abort_test (void)
582 {
583         SoupSession *session;
584         SoupMessage *msg;
585         GMainContext *context;
586         GMainLoop *loop;
587
588         debug_printf (1, "\nAbort with pending connection (msg api)\n");
589
590         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
591         msg = soup_message_new_from_uri ("GET", base_uri);
592
593         context = g_main_context_default ();
594         loop = g_main_loop_new (context, TRUE);
595         soup_session_queue_message (session, msg, ea_msg_completed_one, loop);
596         g_main_context_iteration (context, FALSE);
597
598         soup_session_abort (session);
599         while (g_main_context_pending (context))
600                 g_main_context_iteration (context, FALSE);
601         g_main_loop_unref (loop);
602         soup_test_session_abort_unref (session);
603
604         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
605         msg = soup_message_new_from_uri ("GET", base_uri);
606
607         g_signal_connect (session, "connection-created",
608                           G_CALLBACK (ea_connection_created), NULL);
609         soup_session_send_message (session, msg);
610         debug_printf (2, "  Message 2 completed\n");
611
612         if (msg->status_code != SOUP_STATUS_CANCELLED) {
613                 debug_printf (1, "    Unexpected response: %d %s\n",
614                               msg->status_code, msg->reason_phrase);
615                 errors++;
616         }
617         g_object_unref (msg);
618
619         while (g_main_context_pending (context))
620                 g_main_context_iteration (context, FALSE);
621
622         soup_test_session_abort_unref (session);
623
624         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
625         msg = soup_message_new_from_uri ("GET", base_uri);
626
627         g_signal_connect (session, "request-started",
628                           G_CALLBACK (ea_request_started), NULL);
629         soup_session_send_message (session, msg);
630         debug_printf (2, "  Message 3 completed\n");
631
632         if (msg->status_code != SOUP_STATUS_CANCELLED) {
633                 debug_printf (1, "    Unexpected response: %d %s\n",
634                               msg->status_code, msg->reason_phrase);
635                 errors++;
636         }
637         g_object_unref (msg);
638
639         while (g_main_context_pending (context))
640                 g_main_context_iteration (context, FALSE);
641
642         soup_test_session_abort_unref (session);
643 }
644
645 static void
646 ear_one_completed (GObject *source, GAsyncResult *result, gpointer user_data)
647 {
648         GError *error = NULL;
649
650         debug_printf (2, "  Request 1 completed\n");
651         if (soup_request_send_finish (SOUP_REQUEST (source), result, &error)) {
652                 debug_printf (1, "  Request 1 succeeded?\n");
653                 errors++;
654         } else if (!g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_CANCELLED)) {
655                 debug_printf (1, "  Unexpected error on Request 1: %s\n",
656                               error->message);
657                 errors++;
658         }
659         g_clear_error (&error);
660 }
661
662 static void
663 ear_two_completed (GObject *source, GAsyncResult *result, gpointer loop)
664 {
665         GError *error = NULL;
666
667         debug_printf (2, "  Request 2 completed\n");
668         if (soup_request_send_finish (SOUP_REQUEST (source), result, &error)) {
669                 debug_printf (1, "  Request 2 succeeded?\n");
670                 errors++;
671         } else if (!g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_CANCELLED)) {
672                 debug_printf (1, "  Unexpected error on Request 2: %s\n",
673                               error->message);
674                 errors++;
675         }
676         g_clear_error (&error);
677
678         g_main_loop_quit (loop);
679 }
680
681 static void
682 ear_three_completed (GObject *source, GAsyncResult *result, gpointer loop)
683 {
684         GError *error = NULL;
685
686         debug_printf (2, "  Request 3 completed\n");
687         if (soup_request_send_finish (SOUP_REQUEST (source), result, &error)) {
688                 debug_printf (1, "  Request 3 succeeded?\n");
689                 errors++;
690         } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
691                 debug_printf (1, "  Unexpected error on Request 3: %s\n",
692                               error->message);
693                 errors++;
694         }
695         g_clear_error (&error);
696
697         g_main_loop_quit (loop);
698 }
699
700 static void
701 ear_request_started (SoupSession *session, SoupMessage *msg,
702                      SoupSocket *socket, gpointer cancellable)
703 {
704         g_cancellable_cancel (cancellable);
705 }
706
707 static void
708 do_early_abort_req_test (void)
709 {
710         SoupSession *session;
711         SoupRequester *requester;
712         SoupRequest *req;
713         GMainContext *context;
714         GMainLoop *loop;
715         GCancellable *cancellable;
716
717         debug_printf (1, "\nAbort with pending connection (request api)\n");
718
719         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
720                                          SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
721                                          SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
722                                          NULL);
723         requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
724         req = soup_requester_request_uri (requester, base_uri, NULL);
725
726         context = g_main_context_default ();
727         loop = g_main_loop_new (context, TRUE);
728         soup_request_send_async (req, NULL, ear_one_completed, NULL);
729         g_object_unref (req);
730         g_main_context_iteration (context, FALSE);
731
732         soup_session_abort (session);
733         while (g_main_context_pending (context))
734                 g_main_context_iteration (context, FALSE);
735         soup_test_session_abort_unref (session);
736
737         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
738                                          SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
739                                          SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
740                                          NULL);
741         requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
742         req = soup_requester_request_uri (requester, base_uri, NULL);
743
744         g_signal_connect (session, "connection-created",
745                           G_CALLBACK (ea_connection_created), NULL);
746         soup_request_send_async (req, NULL, ear_two_completed, loop);
747         g_main_loop_run (loop);
748         g_object_unref (req);
749
750         while (g_main_context_pending (context))
751                 g_main_context_iteration (context, FALSE);
752
753         soup_test_session_abort_unref (session);
754
755         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
756                                          SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
757                                          SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
758                                          NULL);
759         requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
760         req = soup_requester_request_uri (requester, base_uri, NULL);
761
762         cancellable = g_cancellable_new ();
763         g_signal_connect (session, "request-started",
764                           G_CALLBACK (ear_request_started), cancellable);
765         soup_request_send_async (req, cancellable, ear_three_completed, loop);
766         g_main_loop_run (loop);
767         g_object_unref (req);
768         g_object_unref (cancellable);
769
770         while (g_main_context_pending (context))
771                 g_main_context_iteration (context, FALSE);
772
773         soup_test_session_abort_unref (session);
774         g_main_loop_unref (loop);
775 }
776
777 static void
778 do_one_accept_language_test (const char *language, const char *expected_header)
779 {
780         SoupSession *session;
781         SoupMessage *msg;
782         const char *val;
783
784         debug_printf (1, "  LANGUAGE=%s\n", language);
785         g_setenv ("LANGUAGE", language, TRUE);
786         session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC,
787                                          SOUP_SESSION_ACCEPT_LANGUAGE_AUTO, TRUE,
788                                          NULL);
789         msg = soup_message_new_from_uri ("GET", base_uri);
790         soup_session_send_message (session, msg);
791         soup_test_session_abort_unref (session);
792
793         if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
794                 debug_printf (1, "    Message failed? %d %s\n",
795                               msg->status_code, msg->reason_phrase);
796                 errors++;
797         }
798         val = soup_message_headers_get_list (msg->request_headers,
799                                              "Accept-Language");
800         if (!val) {
801                 debug_printf (1, "    No Accept-Language set!\n");
802                 errors++;
803         } else if (strcmp (val, expected_header) != 0) {
804                 debug_printf (1, "    Wrong Accept-Language: expected '%s', got '%s'\n",
805                               expected_header, val);
806                 errors++;
807         }
808
809         g_object_unref (msg);
810 }
811
812 static void
813 do_accept_language_test (void)
814 {
815         const char *orig_language;
816
817         debug_printf (1, "\nAutomatic Accept-Language processing\n");
818
819         orig_language = g_getenv ("LANGUAGE");
820         do_one_accept_language_test ("C", "en");
821         do_one_accept_language_test ("fr_FR", "fr-fr, fr;q=0.9");
822         do_one_accept_language_test ("fr_FR:de:en_US", "fr-fr, fr;q=0.9, de;q=0.8, en-us;q=0.7, en;q=0.6");
823
824         if (orig_language)
825                 g_setenv ("LANGUAGE", orig_language, TRUE);
826         else
827                 g_unsetenv ("LANGUAGE");
828 }
829
830 static gboolean
831 cancel_message_timeout (gpointer msg)
832 {
833         SoupSession *session = g_object_get_data (G_OBJECT (msg), "session");
834
835         soup_session_cancel_message (session, msg, SOUP_STATUS_CANCELLED);
836         g_object_unref (msg);
837         g_object_unref (session);
838         return FALSE;
839 }
840
841 static gpointer
842 cancel_message_thread (gpointer msg)
843 {
844         SoupSession *session = g_object_get_data (G_OBJECT (msg), "session");
845
846         g_usleep (100000); /* .1s */
847         soup_session_cancel_message (session, msg, SOUP_STATUS_CANCELLED);
848         g_object_unref (msg);
849         g_object_unref (session);
850         return NULL;
851 }
852
853 static void
854 set_done (SoupSession *session, SoupMessage *msg, gpointer user_data)
855 {
856         gboolean *done = user_data;
857
858         *done = TRUE;
859 }
860
861 static void
862 do_cancel_while_reading_test_for_session (SoupSession *session)
863 {
864         SoupMessage *msg;
865         GThread *thread = NULL;
866         SoupURI *uri;
867         gboolean done = FALSE;
868
869         uri = soup_uri_new_with_base (base_uri, "/slow");
870         msg = soup_message_new_from_uri ("GET", uri);
871         soup_uri_free (uri);
872
873         g_object_set_data (G_OBJECT (msg), "session", session);
874         g_object_ref (msg);
875         g_object_ref (session);
876         if (SOUP_IS_SESSION_ASYNC (session))
877                 g_timeout_add (100, cancel_message_timeout, msg);
878         else
879                 thread = g_thread_new ("cancel_message_thread", cancel_message_thread, msg);
880
881         /* We intentionally don't use soup_session_send_message() here,
882          * because it holds an extra ref on the SoupMessageQueueItem
883          * relative to soup_session_queue_message().
884          */
885         g_object_ref (msg);
886         soup_session_queue_message (session, msg, set_done, &done);
887         while (!done)
888                 g_main_context_iteration (NULL, TRUE);
889
890         if (msg->status_code != SOUP_STATUS_CANCELLED) {
891                 debug_printf (1, "      FAILED: %d %s (expected Cancelled)\n",
892                               msg->status_code, msg->reason_phrase);
893                 errors++;
894         }
895         g_object_unref (msg);
896
897         if (thread)
898                 g_thread_join (thread);
899 }
900
901 static void
902 do_cancel_while_reading_test (void)
903 {
904         SoupSession *session;
905
906         debug_printf (1, "\nCancelling message while reading response (msg api)\n");
907
908         debug_printf (1, "  Async session\n");
909         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
910         do_cancel_while_reading_test_for_session (session);
911         soup_test_session_abort_unref (session);
912
913         debug_printf (1, "  Sync session\n");
914         session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
915         do_cancel_while_reading_test_for_session (session);
916         soup_test_session_abort_unref (session);
917 }
918
919 static gboolean
920 cancel_request_timeout (gpointer cancellable)
921 {
922         g_cancellable_cancel (cancellable);
923         return FALSE;
924 }
925
926 static gpointer
927 cancel_request_thread (gpointer cancellable)
928 {
929         g_usleep (100000); /* .1s */
930         g_cancellable_cancel (cancellable);
931         g_object_unref (cancellable);
932         return NULL;
933 }
934
935 static void
936 do_cancel_while_reading_req_test_for_session (SoupRequester *requester)
937 {
938         SoupRequest *req;
939         SoupURI *uri;
940         GCancellable *cancellable;
941         GError *error = NULL;
942
943         uri = soup_uri_new_with_base (base_uri, "/slow");
944         req = soup_requester_request_uri (requester, uri, NULL);
945         soup_uri_free (uri);
946
947         cancellable = g_cancellable_new ();
948
949         if (SOUP_IS_SESSION_ASYNC (soup_request_get_session (req))) {
950                 g_timeout_add (100, cancel_request_timeout, cancellable);
951                 soup_test_request_send (req, cancellable, &error);
952         } else {
953                 GThread *thread;
954
955                 thread = g_thread_new ("cancel_request_thread", cancel_request_thread, g_object_ref (cancellable));
956                 soup_test_request_send (req, cancellable, &error);
957                 g_thread_unref (thread);
958         }
959
960         if (!error) {
961                 debug_printf (1, "  Request succeeded?\n");
962                 errors++;
963         } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
964                 debug_printf (1, "  Unexpected error: %s\n",
965                               error->message);
966                 errors++;
967         }
968         g_clear_error (&error);
969
970         g_object_unref (req);
971         g_object_unref (cancellable);
972 }
973
974 static void
975 do_cancel_while_reading_req_test (void)
976 {
977         SoupSession *session;
978         SoupRequester *requester;
979
980         debug_printf (1, "\nCancelling message while reading response (request api)\n");
981
982         debug_printf (1, "  Async session\n");
983         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
984                                          SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
985                                          SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
986                                          NULL);
987         requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
988         do_cancel_while_reading_req_test_for_session (requester);
989         soup_test_session_abort_unref (session);
990
991         debug_printf (1, "  Sync session\n");
992         session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC,
993                                          SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
994                                          NULL);
995         requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
996         do_cancel_while_reading_req_test_for_session (requester);
997         soup_test_session_abort_unref (session);
998 }
999
1000 static void
1001 do_aliases_test_for_session (SoupSession *session,
1002                              const char *redirect_protocol)
1003 {
1004         SoupMessage *msg;
1005         SoupURI *uri;
1006         const char *redirected_protocol;
1007
1008         uri = soup_uri_new_with_base (base_uri, "/alias-redirect");
1009         msg = soup_message_new_from_uri ("GET", uri);
1010         if (redirect_protocol)
1011                 soup_message_headers_append (msg->request_headers, "X-Redirect-Protocol", redirect_protocol);
1012         soup_uri_free (uri);
1013         soup_session_send_message (session, msg);
1014
1015         redirected_protocol = soup_message_headers_get_one (msg->response_headers, "X-Redirected-Protocol");
1016
1017         if (g_strcmp0 (redirect_protocol, redirected_protocol)) {
1018                 debug_printf (1, "    redirect went to %s, should have gone to %s!\n",
1019                               redirected_protocol ? redirected_protocol : "(none)",
1020                               redirect_protocol ? redirect_protocol : "(none)");
1021                 errors++;
1022         } else if (redirect_protocol && !SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
1023                 debug_printf (1, "    msg failed? (%d %s)\n",
1024                               msg->status_code, msg->reason_phrase);
1025                 errors++;
1026         } else if (!redirect_protocol && SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
1027                 debug_printf (1, "    msg succeeded? (%d %s)\n",
1028                               msg->status_code, msg->reason_phrase);
1029                 errors++;
1030         }
1031
1032         g_object_unref (msg);
1033 }
1034
1035 static void
1036 do_aliases_test (void)
1037 {
1038         SoupSession *session;
1039         char *aliases[] = { "foo", NULL };
1040
1041         debug_printf (1, "\nhttp-aliases / https-aliases\n");
1042
1043         debug_printf (1, "  Default behavior\n");
1044         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
1045         do_aliases_test_for_session (session, "http");
1046         soup_test_session_abort_unref (session);
1047
1048         debug_printf (1, "  foo-means-https\n");
1049         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
1050                                          SOUP_SESSION_HTTPS_ALIASES, aliases,
1051                                          NULL);
1052         do_aliases_test_for_session (session, "https");
1053         soup_test_session_abort_unref (session);
1054
1055         debug_printf (1, "  foo-means-nothing\n");
1056         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
1057                                          SOUP_SESSION_HTTP_ALIASES, NULL,
1058                                          NULL);
1059         do_aliases_test_for_session (session, NULL);
1060         soup_test_session_abort_unref (session);
1061 }
1062
1063 static void
1064 do_dot_dot_test (void)
1065 {
1066         SoupSession *session;
1067         SoupMessage *msg;
1068         SoupURI *uri;
1069
1070         debug_printf (1, "\n'..' smuggling test\n");
1071
1072         session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
1073
1074         uri = soup_uri_new_with_base (base_uri, "/..%2ftest");
1075         msg = soup_message_new_from_uri ("GET", uri);
1076         soup_uri_free (uri);
1077
1078         soup_session_send_message (session, msg);
1079
1080         if (msg->status_code != SOUP_STATUS_BAD_REQUEST) {
1081                 debug_printf (1, "      FAILED: %d %s (expected Bad Request)\n",
1082                               msg->status_code, msg->reason_phrase);
1083                 errors++;
1084         }
1085         g_object_unref (msg);
1086
1087         soup_test_session_abort_unref (session);
1088 }
1089
1090 static void
1091 do_ipv6_test (void)
1092 {
1093         SoupServer *ipv6_server;
1094         SoupURI *ipv6_uri;
1095         SoupAddress *ipv6_addr;
1096         SoupSession *session;
1097         SoupMessage *msg;
1098
1099         debug_printf (1, "\nIPv6 server test\n");
1100
1101         ipv6_addr = soup_address_new ("::1", SOUP_ADDRESS_ANY_PORT);
1102         soup_address_resolve_sync (ipv6_addr, NULL);
1103         ipv6_server = soup_server_new (SOUP_SERVER_INTERFACE, ipv6_addr,
1104                                        NULL);
1105         g_object_unref (ipv6_addr);
1106         soup_server_add_handler (ipv6_server, NULL, server_callback, NULL, NULL);
1107         soup_server_run_async (ipv6_server);
1108
1109         ipv6_uri = soup_uri_new ("http://[::1]/");
1110         soup_uri_set_port (ipv6_uri, soup_server_get_port (ipv6_server));
1111
1112         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
1113
1114         debug_printf (1, "  HTTP/1.1\n");
1115         msg = soup_message_new_from_uri ("GET", ipv6_uri);
1116         soup_session_send_message (session, msg);
1117         if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
1118                 debug_printf (1, "    request failed: %d %s\n",
1119                               msg->status_code, msg->reason_phrase);
1120                 errors++;
1121         }
1122         g_object_unref (msg);
1123
1124         debug_printf (1, "  HTTP/1.0\n");
1125         msg = soup_message_new_from_uri ("GET", ipv6_uri);
1126         soup_message_set_http_version (msg, SOUP_HTTP_1_0);
1127         soup_session_send_message (session, msg);
1128         if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
1129                 debug_printf (1, "    request failed: %d %s\n",
1130                               msg->status_code, msg->reason_phrase);
1131                 errors++;
1132         }
1133         g_object_unref (msg);
1134
1135         soup_uri_free (ipv6_uri);
1136         soup_test_session_abort_unref (session);
1137         soup_test_server_quit_unref (ipv6_server);
1138 }
1139
1140 static void
1141 do_idle_on_dispose_test (void)
1142 {
1143         SoupSession *session;
1144         SoupMessage *msg;
1145         GMainContext *async_context;
1146
1147         debug_printf (1, "\nTesting SoupSessionAsync dispose behavior\n");
1148
1149         async_context = g_main_context_new ();
1150         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
1151                                          SOUP_SESSION_ASYNC_CONTEXT, async_context,
1152                                          NULL);
1153
1154         msg = soup_message_new_from_uri ("GET", base_uri);
1155         soup_session_send_message (session, msg);
1156         g_object_unref (msg);
1157
1158         while (g_main_context_iteration (async_context, FALSE))
1159                 ;
1160
1161         g_object_run_dispose (G_OBJECT (session));
1162
1163         if (g_main_context_iteration (async_context, FALSE)) {
1164                 debug_printf (1, "  idle was queued!\n");
1165                 errors++;
1166         }
1167
1168         g_object_unref (session);
1169         g_main_context_unref (async_context);
1170 }
1171
1172 static void
1173 do_pause_abort_test (void)
1174 {
1175         SoupSession *session;
1176         SoupMessage *msg;
1177         gpointer ptr;
1178
1179         debug_printf (1, "\nTesting paused messages don't get leaked on abort\n");
1180
1181         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
1182
1183         msg = soup_message_new_from_uri ("GET", base_uri);
1184         soup_session_queue_message (session, msg, NULL, NULL);
1185         soup_session_pause_message (session, msg);
1186
1187         g_object_add_weak_pointer (G_OBJECT (msg), &ptr);
1188         soup_test_session_abort_unref (session);
1189
1190         if (ptr) {
1191                 debug_printf (1, "  msg was leaked\n");
1192                 errors++;
1193         }
1194 }
1195
1196 int
1197 main (int argc, char **argv)
1198 {
1199         SoupAuthDomain *auth_domain;
1200
1201         test_init (argc, argv, NULL);
1202
1203         server = soup_test_server_new (TRUE);
1204         soup_server_add_handler (server, NULL, server_callback, "http", NULL);
1205         base_uri = soup_uri_new ("http://127.0.0.1/");
1206         soup_uri_set_port (base_uri, soup_server_get_port (server));
1207
1208         auth_domain = soup_auth_domain_basic_new (
1209                 SOUP_AUTH_DOMAIN_REALM, "misc-test",
1210                 SOUP_AUTH_DOMAIN_ADD_PATH, "/auth",
1211                 SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, auth_callback,
1212                 NULL);
1213         soup_server_add_auth_domain (server, auth_domain);
1214         g_object_unref (auth_domain);
1215
1216         ssl_server = soup_test_server_new_ssl (TRUE);
1217         soup_server_add_handler (ssl_server, NULL, server_callback, "https", NULL);
1218         ssl_base_uri = soup_uri_new ("https://127.0.0.1/");
1219         soup_uri_set_port (ssl_base_uri, soup_server_get_port (ssl_server));
1220
1221         do_host_test ();
1222         do_callback_unref_test ();
1223         do_callback_unref_req_test ();
1224         do_msg_reuse_test ();
1225         do_star_test ();
1226         do_early_abort_test ();
1227         do_early_abort_req_test ();
1228         do_accept_language_test ();
1229         do_cancel_while_reading_test ();
1230         do_cancel_while_reading_req_test ();
1231         do_aliases_test ();
1232         do_dot_dot_test ();
1233         do_ipv6_test ();
1234         do_idle_on_dispose_test ();
1235         do_pause_abort_test ();
1236
1237         soup_uri_free (base_uri);
1238         soup_uri_free (ssl_base_uri);
1239         soup_test_server_quit_unref (server);
1240         soup_test_server_quit_unref (ssl_server);
1241
1242         test_cleanup ();
1243         return errors != 0;
1244 }