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