add missing slash in %configure
[platform/upstream/libsoup.git] / tests / connection-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 #include <gio/gnetworking.h>
9
10 SoupServer *server;
11 SoupURI *base_uri;
12 GMutex server_mutex;
13
14 static void
15 forget_close (SoupMessage *msg, gpointer user_data)
16 {
17         soup_message_headers_remove (msg->response_headers, "Connection");
18 }
19
20 static void
21 close_socket (SoupMessage *msg, gpointer user_data)
22 {
23         SoupSocket *sock = user_data;
24         int sockfd;
25
26         /* Actually calling soup_socket_disconnect() here would cause
27          * us to leak memory, so just shutdown the socket instead.
28          */
29         sockfd = soup_socket_get_fd (sock);
30 #ifdef G_OS_WIN32
31         shutdown (sockfd, SD_SEND);
32 #else
33         shutdown (sockfd, SHUT_WR);
34 #endif
35
36         /* Then add the missing data to the message now, so SoupServer
37          * can clean up after itself properly.
38          */
39         soup_message_body_append (msg->response_body, SOUP_MEMORY_STATIC,
40                                   "foo", 3);
41 }
42
43 static void
44 timeout_socket (SoupSocket *sock, gpointer user_data)
45 {
46         soup_socket_disconnect (sock);
47 }
48
49 static void
50 timeout_request_started (SoupServer *server, SoupMessage *msg,
51                          SoupClientContext *client, gpointer user_data)
52 {
53         SoupSocket *sock;
54         GMainContext *context = soup_server_get_async_context (server);
55         guint readable;
56
57         sock = soup_client_context_get_socket (client);
58         readable = g_signal_connect (sock, "readable",
59                                     G_CALLBACK (timeout_socket), NULL);
60         while (soup_socket_is_connected (sock))
61                 g_main_context_iteration (context, TRUE);
62         g_signal_handler_disconnect (sock, readable);
63         g_signal_handlers_disconnect_by_func (server, timeout_request_started, NULL);
64 }
65
66 static void
67 setup_timeout_persistent (SoupServer *server, SoupSocket *sock)
68 {
69         char buf[1];
70         gsize nread;
71
72         /* In order for the test to work correctly, we have to
73          * close the connection *after* the client side writes
74          * the request. To ensure that this happens reliably,
75          * regardless of thread scheduling, we:
76          *
77          *   1. Try to read off the socket now, knowing it will
78          *      fail (since the client is waiting for us to
79          *      return a response). This will cause it to
80          *      emit "readable" later.
81          *   2. Connect to the server's request-started signal.
82          *   3. Run an inner main loop from that signal handler
83          *      until the socket emits "readable". (If we don't
84          *      do this then it's possible the client's next
85          *      request would be ready before we returned to
86          *      the main loop, and so the signal would never be
87          *      emitted.)
88          *   4. Close the socket.
89          */
90
91         soup_socket_read (sock, buf, 1, &nread, NULL, NULL);
92         g_signal_connect (server, "request-started",
93                           G_CALLBACK (timeout_request_started), NULL);
94 }
95
96 static void
97 server_callback (SoupServer *server, SoupMessage *msg,
98                  const char *path, GHashTable *query,
99                  SoupClientContext *context, gpointer data)
100 {
101         /* The way this gets used in the tests, we don't actually
102          * need to hold it through the whole function, so it's simpler
103          * to just release it right away.
104          */
105         g_mutex_lock (&server_mutex);
106         g_mutex_unlock (&server_mutex);
107
108         if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_POST) {
109                 soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
110                 return;
111         }
112
113         if (g_str_has_prefix (path, "/content-length/")) {
114                 gboolean too_long = strcmp (path, "/content-length/long") == 0;
115                 gboolean no_close = strcmp (path, "/content-length/noclose") == 0;
116
117                 soup_message_set_status (msg, SOUP_STATUS_OK);
118                 soup_message_set_response (msg, "text/plain",
119                                            SOUP_MEMORY_STATIC, "foobar", 6);
120                 if (too_long)
121                         soup_message_headers_set_content_length (msg->response_headers, 9);
122                 soup_message_headers_append (msg->response_headers,
123                                              "Connection", "close");
124
125                 if (too_long) {
126                         SoupSocket *sock;
127
128                         /* soup-message-io will wait for us to add
129                          * another chunk after the first, to fill out
130                          * the declared Content-Length. Instead, we
131                          * forcibly close the socket at that point.
132                          */
133                         sock = soup_client_context_get_socket (context);
134                         g_signal_connect (msg, "wrote-chunk",
135                                           G_CALLBACK (close_socket), sock);
136                 } else if (no_close) {
137                         /* Remove the 'Connection: close' after writing
138                          * the headers, so that when we check it after
139                          * writing the body, we'll think we aren't
140                          * supposed to close it.
141                          */
142                         g_signal_connect (msg, "wrote-headers",
143                                           G_CALLBACK (forget_close), NULL);
144                 }
145                 return;
146         }
147
148         if (!strcmp (path, "/timeout-persistent")) {
149                 SoupSocket *sock;
150
151                 sock = soup_client_context_get_socket (context);
152                 setup_timeout_persistent (server, sock);
153         }
154
155         soup_message_set_status (msg, SOUP_STATUS_OK);
156         soup_message_set_response (msg, "text/plain",
157                                    SOUP_MEMORY_STATIC, "index", 5);
158         return;
159 }
160
161 static void
162 do_content_length_framing_test (void)
163 {
164         SoupSession *session;
165         SoupMessage *msg;
166         SoupURI *request_uri;
167         goffset declared_length;
168
169         g_test_bug ("611481");
170
171         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
172
173         debug_printf (1, "  Content-Length larger than message body length\n");
174         request_uri = soup_uri_new_with_base (base_uri, "/content-length/long");
175         msg = soup_message_new_from_uri ("GET", request_uri);
176         soup_session_send_message (session, msg);
177
178         soup_test_assert_message_status (msg, SOUP_STATUS_OK);
179
180         declared_length = soup_message_headers_get_content_length (msg->response_headers);
181         debug_printf (2, "    Content-Length: %lu, body: %s\n",
182                       (gulong)declared_length, msg->response_body->data);
183         g_assert_cmpint (msg->response_body->length, <, declared_length);
184
185         soup_uri_free (request_uri);
186         g_object_unref (msg);
187
188         debug_printf (1, "  Server claims 'Connection: close' but doesn't\n");
189         request_uri = soup_uri_new_with_base (base_uri, "/content-length/noclose");
190         msg = soup_message_new_from_uri ("GET", request_uri);
191         soup_session_send_message (session, msg);
192
193         soup_test_assert_message_status (msg, SOUP_STATUS_OK);
194
195         declared_length = soup_message_headers_get_content_length (msg->response_headers);
196         g_assert_cmpint (msg->response_body->length, ==, declared_length);
197
198         soup_uri_free (request_uri);
199         g_object_unref (msg);
200
201         soup_test_session_abort_unref (session);
202 }
203
204 static void
205 request_started_socket_collector (SoupSession *session, SoupMessage *msg,
206                                   SoupSocket *socket, gpointer user_data)
207 {
208         SoupSocket **sockets = user_data;
209         int i;
210
211         debug_printf (2, "      msg %p => socket %p\n", msg, socket);
212         for (i = 0; i < 4; i++) {
213                 if (!sockets[i]) {
214                         /* We ref the socket to make sure that even if
215                          * it gets disconnected, it doesn't get freed,
216                          * since our checks would get messed up if the
217                          * slice allocator reused the same address for
218                          * two consecutive sockets.
219                          */
220                         sockets[i] = g_object_ref (socket);
221                         break;
222                 }
223         }
224
225         soup_test_assert (i < 4, "socket queue overflowed");
226 }
227
228 static void
229 do_timeout_test_for_session (SoupSession *session)
230 {
231         SoupMessage *msg;
232         SoupSocket *sockets[4] = { NULL, NULL, NULL, NULL };
233         SoupURI *timeout_uri;
234         int i;
235
236         g_signal_connect (session, "request-started",
237                           G_CALLBACK (request_started_socket_collector),
238                           &sockets);
239
240         debug_printf (1, "    First message\n");
241         timeout_uri = soup_uri_new_with_base (base_uri, "/timeout-persistent");
242         msg = soup_message_new_from_uri ("GET", timeout_uri);
243         soup_uri_free (timeout_uri);
244         soup_session_send_message (session, msg);
245         soup_test_assert_message_status (msg, SOUP_STATUS_OK);
246
247         if (sockets[1]) {
248                 soup_test_assert (sockets[1] == NULL, "Message was retried");
249                 sockets[1] = sockets[2] = sockets[3] = NULL;
250         }
251         g_object_unref (msg);
252
253         debug_printf (1, "    Second message\n");
254         msg = soup_message_new_from_uri ("GET", base_uri);
255         soup_session_send_message (session, msg);
256         soup_test_assert_message_status (msg, SOUP_STATUS_OK);
257
258         soup_test_assert (sockets[1] == sockets[0],
259                           "Message was not retried on existing connection");
260         soup_test_assert (sockets[2] != NULL,
261                           "Message was not retried after disconnect");
262         soup_test_assert (sockets[2] != sockets[1],
263                           "Message was retried on closed connection");
264         soup_test_assert (sockets[3] == NULL,
265                           "Message was retried again");
266         g_object_unref (msg);
267
268         for (i = 0; sockets[i]; i++)
269                 g_object_unref (sockets[i]);
270 }
271
272 static void
273 do_timeout_req_test_for_session (SoupSession *session)
274 {
275         SoupRequest *req;
276         SoupMessage *msg;
277         GInputStream *stream;
278         SoupSocket *sockets[4] = { NULL, NULL, NULL, NULL };
279         SoupURI *timeout_uri;
280         GError *error = NULL;
281         int i;
282
283         g_signal_connect (session, "request-started",
284                           G_CALLBACK (request_started_socket_collector),
285                           &sockets);
286
287         debug_printf (1, "    First request\n");
288         timeout_uri = soup_uri_new_with_base (base_uri, "/timeout-persistent");
289         req = soup_session_request_uri (session, timeout_uri, NULL);
290         soup_uri_free (timeout_uri);
291
292         stream = soup_test_request_send (req, NULL, 0, &error);
293         if (error) {
294                 g_assert_no_error (error);
295                 g_clear_error (&error);
296         } else {
297                 soup_test_request_read_all (req, stream, NULL, &error);
298                 if (error) {
299                         g_assert_no_error (error);
300                         g_clear_error (&error);
301                 }
302
303                 soup_test_request_close_stream (req, stream, NULL, &error);
304                 if (error) {
305                         g_assert_no_error (error);
306                         g_clear_error (&error);
307                 }
308                 g_object_unref (stream);
309         }
310
311         if (sockets[1]) {
312                 soup_test_assert (sockets[1] == NULL, "Message was retried");
313                 sockets[1] = sockets[2] = sockets[3] = NULL;
314         }
315         g_object_unref (req);
316
317         debug_printf (1, "    Second request\n");
318         req = soup_session_request_uri (session, base_uri, NULL);
319
320         stream = soup_test_request_send (req, NULL, 0, &error);
321         if (error) {
322                 g_assert_no_error (error);
323                 g_clear_error (&error);
324         } else {
325                 soup_test_request_close_stream (req, stream, NULL, &error);
326                 if (error) {
327                         g_assert_no_error (error);
328                         g_clear_error (&error);
329                 }
330                 g_object_unref (stream);
331         }
332
333         msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req));
334         soup_test_assert_message_status (msg, SOUP_STATUS_OK);
335
336         soup_test_assert (sockets[1] == sockets[0],
337                           "Message was not retried on existing connection");
338         soup_test_assert (sockets[2] != NULL,
339                           "Message was not retried after disconnect");
340         soup_test_assert (sockets[2] != sockets[1],
341                           "Message was retried on closed connection");
342         soup_test_assert (sockets[3] == NULL,
343                           "Message was retried again");
344         g_object_unref (msg);
345         g_object_unref (req);
346
347         for (i = 0; sockets[i]; i++)
348                 g_object_unref (sockets[i]);
349 }
350
351 static void
352 do_persistent_connection_timeout_test (void)
353 {
354         SoupSession *session;
355
356         g_test_bug ("631525");
357
358         debug_printf (1, "  Async session, message API\n");
359         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
360         do_timeout_test_for_session (session);
361         soup_test_session_abort_unref (session);
362
363         debug_printf (1, "  Async session, request API\n");
364         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
365                                          SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
366                                          NULL);
367         do_timeout_req_test_for_session (session);
368         soup_test_session_abort_unref (session);
369
370         debug_printf (1, "  Sync session, message API\n");
371         session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
372         do_timeout_test_for_session (session);
373         soup_test_session_abort_unref (session);
374
375         debug_printf (1, "  Sync session, request API\n");
376         session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
377         do_timeout_req_test_for_session (session);
378         soup_test_session_abort_unref (session);
379 }
380
381 static GMainLoop *max_conns_loop;
382 static int msgs_done;
383 static guint quit_loop_timeout;
384 #define MAX_CONNS 2
385 #define TEST_CONNS (MAX_CONNS * 2)
386
387 static gboolean
388 idle_start_server (gpointer data)
389 {
390         g_mutex_unlock (&server_mutex);
391         return FALSE;
392 }
393
394 static gboolean
395 quit_loop (gpointer data)
396 {
397         quit_loop_timeout = 0;
398         g_main_loop_quit (max_conns_loop);
399         return FALSE;
400 }
401
402 static void
403 max_conns_request_started (SoupSession *session, SoupMessage *msg,
404                            SoupSocket *socket, gpointer user_data)
405 {
406         if (++msgs_done == MAX_CONNS) {
407                 if (quit_loop_timeout)
408                         g_source_remove (quit_loop_timeout);
409                 quit_loop_timeout = g_timeout_add (100, quit_loop, NULL);
410         }
411 }
412
413 static void
414 max_conns_message_complete (SoupSession *session, SoupMessage *msg, gpointer user_data)
415 {
416         if (++msgs_done == TEST_CONNS)
417                 g_main_loop_quit (max_conns_loop);
418 }
419
420 static void
421 do_max_conns_test_for_session (SoupSession *session)
422 {
423         SoupMessage *msgs[TEST_CONNS];
424         int i;
425
426         max_conns_loop = g_main_loop_new (NULL, TRUE);
427
428         g_mutex_lock (&server_mutex);
429
430         g_signal_connect (session, "request-started",
431                           G_CALLBACK (max_conns_request_started), NULL);
432         msgs_done = 0;
433         for (i = 0; i < TEST_CONNS; i++) {
434                 msgs[i] = soup_message_new_from_uri ("GET", base_uri);
435                 g_object_ref (msgs[i]);
436                 soup_session_queue_message (session, msgs[i],
437                                             max_conns_message_complete, NULL);
438         }
439
440         g_main_loop_run (max_conns_loop);
441         g_assert_cmpint (msgs_done, ==, MAX_CONNS);
442         g_signal_handlers_disconnect_by_func (session, max_conns_request_started, NULL);
443
444         msgs_done = 0;
445         g_idle_add (idle_start_server, NULL);
446         if (quit_loop_timeout)
447                 g_source_remove (quit_loop_timeout);
448         quit_loop_timeout = g_timeout_add (1000, quit_loop, NULL);
449         g_main_loop_run (max_conns_loop);
450
451         for (i = 0; i < TEST_CONNS; i++)
452                 soup_test_assert_message_status (msgs[i], SOUP_STATUS_OK);
453
454         if (msgs_done != TEST_CONNS) {
455                 /* Clean up so we don't get a spurious "Leaked
456                  * session" error.
457                  */
458                 for (i = 0; i < TEST_CONNS; i++)
459                         soup_session_cancel_message (session, msgs[i], SOUP_STATUS_CANCELLED);
460                 g_main_loop_run (max_conns_loop);
461         }
462
463         g_main_loop_unref (max_conns_loop);
464         if (quit_loop_timeout) {
465                 g_source_remove (quit_loop_timeout);
466                 quit_loop_timeout = 0;
467         }
468
469         for (i = 0; i < TEST_CONNS; i++)
470                 g_object_unref (msgs[i]);
471 }
472
473 static void
474 do_max_conns_test (void)
475 {
476         SoupSession *session;
477
478         g_test_bug ("634422");
479
480         debug_printf (1, "  Async session\n");
481         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
482                                          SOUP_SESSION_MAX_CONNS, MAX_CONNS,
483                                          NULL);
484         do_max_conns_test_for_session (session);
485         soup_test_session_abort_unref (session);
486
487         debug_printf (1, "  Sync session\n");
488         session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC,
489                                          SOUP_SESSION_MAX_CONNS, MAX_CONNS,
490                                          NULL);
491         do_max_conns_test_for_session (session);
492         soup_test_session_abort_unref (session);
493 }
494
495 static void
496 np_request_started (SoupSession *session, SoupMessage *msg,
497                     SoupSocket *socket, gpointer user_data)
498 {
499         SoupSocket **save_socket = user_data;
500
501         *save_socket = g_object_ref (socket);
502 }
503
504 static void
505 np_request_unqueued (SoupSession *session, SoupMessage *msg,
506                      gpointer user_data)
507 {
508         SoupSocket *socket = *(SoupSocket **)user_data;
509
510         g_assert_false (soup_socket_is_connected (socket));
511 }
512
513 static void
514 np_request_finished (SoupSession *session, SoupMessage *msg,
515                      gpointer user_data)
516 {
517         GMainLoop *loop = user_data;
518
519         g_main_loop_quit (loop);
520 }
521
522 static void
523 do_non_persistent_test_for_session (SoupSession *session)
524 {
525         SoupMessage *msg;
526         SoupSocket *socket = NULL;
527         GMainLoop *loop;
528
529         loop = g_main_loop_new (NULL, FALSE);
530
531         g_signal_connect (session, "request-started",
532                           G_CALLBACK (np_request_started),
533                           &socket);
534         g_signal_connect (session, "request-unqueued",
535                           G_CALLBACK (np_request_unqueued),
536                           &socket);
537
538         msg = soup_message_new_from_uri ("GET", base_uri);
539         soup_message_headers_append (msg->request_headers, "Connection", "close");
540         g_object_ref (msg);
541         soup_session_queue_message (session, msg,
542                                     np_request_finished, loop);
543         g_main_loop_run (loop);
544         g_main_loop_unref (loop);
545
546         soup_test_assert_message_status (msg, SOUP_STATUS_OK);
547
548         g_object_unref (msg);
549         g_object_unref (socket);
550 }
551
552 static void
553 do_non_persistent_connection_test (void)
554 {
555         SoupSession *session;
556
557         g_test_bug ("578990");
558
559         debug_printf (1, "  Async session\n");
560         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
561         do_non_persistent_test_for_session (session);
562         soup_test_session_abort_unref (session);
563
564         debug_printf (1, "  Sync session\n");
565         session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
566         do_non_persistent_test_for_session (session);
567         soup_test_session_abort_unref (session);
568 }
569
570 static void
571 do_non_idempotent_test_for_session (SoupSession *session)
572 {
573         SoupMessage *msg;
574         SoupSocket *sockets[4] = { NULL, NULL, NULL, NULL };
575         int i;
576
577         g_signal_connect (session, "request-started",
578                           G_CALLBACK (request_started_socket_collector),
579                           &sockets);
580
581         debug_printf (2, "    GET\n");
582         msg = soup_message_new_from_uri ("GET", base_uri);
583         soup_session_send_message (session, msg);
584         soup_test_assert_message_status (msg, SOUP_STATUS_OK);
585         if (sockets[1]) {
586                 soup_test_assert (sockets[1] == NULL, "Message was retried");
587                 sockets[1] = sockets[2] = sockets[3] = NULL;
588         }
589         g_object_unref (msg);
590
591         debug_printf (2, "    POST\n");
592         msg = soup_message_new_from_uri ("POST", base_uri);
593         soup_session_send_message (session, msg);
594         soup_test_assert_message_status (msg, SOUP_STATUS_OK);
595         soup_test_assert (sockets[1] != sockets[0],
596                           "Message was sent on existing connection");
597         soup_test_assert (sockets[2] == NULL,
598                           "Too many connections used");
599
600         g_object_unref (msg);
601
602         for (i = 0; sockets[i]; i++)
603                 g_object_unref (sockets[i]);
604 }
605
606 static void
607 do_non_idempotent_connection_test (void)
608 {
609         SoupSession *session;
610
611         debug_printf (1, "  Async session\n");
612         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
613         do_non_idempotent_test_for_session (session);
614         soup_test_session_abort_unref (session);
615
616         debug_printf (1, "  Sync session\n");
617         session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
618         do_non_idempotent_test_for_session (session);
619         soup_test_session_abort_unref (session);
620 }
621
622 #define HTTP_SERVER  "http://127.0.0.1:47524"
623 #define HTTPS_SERVER "https://127.0.0.1:47525"
624 #define HTTP_PROXY   "http://127.0.0.1:47526"
625
626 static SoupConnectionState state_transitions[] = {
627         /* NEW -> */        SOUP_CONNECTION_CONNECTING,
628         /* CONNECTING -> */ SOUP_CONNECTION_IN_USE,
629         /* IDLE -> */       SOUP_CONNECTION_DISCONNECTED,
630         /* IN_USE -> */     SOUP_CONNECTION_IDLE,
631
632         /* REMOTE_DISCONNECTED */ -1,
633         /* DISCONNECTED */        -1,
634 };
635
636 static const char *state_names[] = {
637         "NEW", "CONNECTING", "IDLE", "IN_USE",
638         "REMOTE_DISCONNECTED", "DISCONNECTED"
639 };
640
641 static void
642 connection_state_changed (GObject *object, GParamSpec *param,
643                           gpointer user_data)
644 {
645         SoupConnectionState *state = user_data;
646         SoupConnectionState new_state;
647
648         g_object_get (object, "state", &new_state, NULL);
649         debug_printf (2, "      %s -> %s\n",
650                       state_names[*state], state_names[new_state]);
651         soup_test_assert (state_transitions[*state] == new_state,
652                           "Unexpected transition: %s -> %s\n",
653                           state_names[*state], state_names[new_state]);
654         *state = new_state;
655 }
656
657 static void
658 connection_created (SoupSession *session, GObject *conn,
659                     gpointer user_data)
660 {
661         SoupConnectionState *state = user_data;
662
663         g_object_get (conn, "state", state, NULL);
664         g_assert_cmpint (*state, ==, SOUP_CONNECTION_NEW);
665
666         g_signal_connect (conn, "notify::state",
667                           G_CALLBACK (connection_state_changed),
668                           state);
669 }
670
671 static void
672 do_one_connection_state_test (SoupSession *session, const char *uri)
673 {
674         SoupMessage *msg;
675
676         msg = soup_message_new ("GET", uri);
677         soup_session_send_message (session, msg);
678         soup_test_assert_message_status (msg, SOUP_STATUS_OK);
679         g_object_unref (msg);
680         soup_session_abort (session);
681 }
682
683 static void
684 do_connection_state_test_for_session (SoupSession *session)
685 {
686         SoupConnectionState state;
687         SoupURI *proxy_uri;
688
689         g_signal_connect (session, "connection-created",
690                           G_CALLBACK (connection_created),
691                           &state);
692
693         debug_printf (1, "    http\n");
694         do_one_connection_state_test (session, HTTP_SERVER);
695
696         if (tls_available) {
697                 debug_printf (1, "    https\n");
698                 do_one_connection_state_test (session, HTTPS_SERVER);
699         } else
700                 debug_printf (1, "    https -- SKIPPING\n");
701
702         proxy_uri = soup_uri_new (HTTP_PROXY);
703         g_object_set (G_OBJECT (session),
704                       SOUP_SESSION_PROXY_URI, proxy_uri,
705                       NULL);
706         soup_uri_free (proxy_uri);
707
708         debug_printf (1, "    http with proxy\n");
709         do_one_connection_state_test (session, HTTP_SERVER);
710
711         if (tls_available) {
712                 debug_printf (1, "    https with proxy\n");
713                 do_one_connection_state_test (session, HTTPS_SERVER);
714         } else
715                 debug_printf (1, "    https with proxy -- SKIPPING\n");
716 }
717
718 static void
719 do_connection_state_test (void)
720 {
721         SoupSession *session;
722
723         SOUP_TEST_SKIP_IF_NO_APACHE;
724
725         debug_printf (1, "  Async session\n");
726         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
727         do_connection_state_test_for_session (session);
728         soup_test_session_abort_unref (session);
729
730         debug_printf (1, "  Sync session\n");
731         session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
732         do_connection_state_test_for_session (session);
733         soup_test_session_abort_unref (session);
734 }
735
736
737 static const char *event_names[] = {
738         "RESOLVING", "RESOLVED", "CONNECTING", "CONNECTED",
739         "PROXY_NEGOTIATING", "PROXY_NEGOTIATED",
740         "TLS_HANDSHAKING", "TLS_HANDSHAKED", "COMPLETE"
741 };
742
743 static const char event_abbrevs[] = {
744         'r', 'R', 'c', 'C', 'p', 'P', 't', 'T', 'x', '\0'
745 };
746
747 static const char *
748 event_name_from_abbrev (char abbrev)
749 {
750         int evt;
751
752         for (evt = 0; event_abbrevs[evt]; evt++) {
753                 if (event_abbrevs[evt] == abbrev)
754                         return event_names[evt];
755         }
756         return "???";
757 }
758
759 static void
760 network_event (SoupMessage *msg, GSocketClientEvent event,
761                GIOStream *connection, gpointer user_data)
762 {
763         const char **events = user_data;
764
765         debug_printf (2, "      %s\n", event_name_from_abbrev (**events));
766         soup_test_assert (**events == event_abbrevs[event],
767                           "Unexpected event: %s (expected %s)\n",
768                           event_names[event],
769                           event_name_from_abbrev (**events));
770         *events = *events + 1;
771 }
772
773 static void
774 do_one_connection_event_test (SoupSession *session, const char *uri,
775                               const char *events)
776 {
777         SoupMessage *msg;
778
779         msg = soup_message_new ("GET", uri);
780         g_signal_connect (msg, "network-event",
781                           G_CALLBACK (network_event),
782                           &events);
783         soup_session_send_message (session, msg);
784         soup_test_assert_message_status (msg, SOUP_STATUS_OK);
785         while (*events) {
786                 soup_test_assert (!*events,
787                                   "Expected %s",
788                                   event_name_from_abbrev (*events));
789                 events++;
790         }
791
792         g_object_unref (msg);
793         soup_session_abort (session);
794 }
795
796 static void
797 do_connection_event_test_for_session (SoupSession *session)
798 {
799         SoupURI *proxy_uri;
800
801         debug_printf (1, "    http\n");
802         do_one_connection_event_test (session, HTTP_SERVER, "rRcCx");
803
804         if (tls_available) {
805                 debug_printf (1, "    https\n");
806                 do_one_connection_event_test (session, HTTPS_SERVER, "rRcCtTx");
807         } else
808                 debug_printf (1, "    https -- SKIPPING\n");
809
810         proxy_uri = soup_uri_new (HTTP_PROXY);
811         g_object_set (G_OBJECT (session),
812                       SOUP_SESSION_PROXY_URI, proxy_uri,
813                       NULL);
814         soup_uri_free (proxy_uri);
815
816         debug_printf (1, "    http with proxy\n");
817         do_one_connection_event_test (session, HTTP_SERVER, "rRcCx");
818
819         if (tls_available) {
820                 debug_printf (1, "    https with proxy\n");
821                 do_one_connection_event_test (session, HTTPS_SERVER, "rRcCpPtTx");
822         } else
823                 debug_printf (1, "    https with proxy -- SKIPPING\n");
824 }
825
826 static void
827 do_connection_event_test (void)
828 {
829         SoupSession *session;
830
831         SOUP_TEST_SKIP_IF_NO_APACHE;
832
833         debug_printf (1, "  Async session\n");
834         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
835         do_connection_event_test_for_session (session);
836         soup_test_session_abort_unref (session);
837
838         debug_printf (1, "  Sync session\n");
839         session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
840         do_connection_event_test_for_session (session);
841         soup_test_session_abort_unref (session);
842 }
843
844 int
845 main (int argc, char **argv)
846 {
847         int ret;
848
849         test_init (argc, argv, NULL);
850         apache_init ();
851
852         server = soup_test_server_new (TRUE);
853         soup_server_add_handler (server, NULL, server_callback, "http", NULL);
854         base_uri = soup_uri_new ("http://127.0.0.1/");
855         soup_uri_set_port (base_uri, soup_server_get_port (server));
856
857         g_test_add_func ("/connection/content-length-framing", do_content_length_framing_test);
858         g_test_add_func ("/connection/persistent-connection-timeout", do_persistent_connection_timeout_test);
859         g_test_add_func ("/connection/max-conns", do_max_conns_test);
860         g_test_add_func ("/connection/non-persistent", do_non_persistent_connection_test);
861         g_test_add_func ("/connection/non-idempotent", do_non_idempotent_connection_test);
862         g_test_add_func ("/connection/state", do_connection_state_test);
863         g_test_add_func ("/connection/event", do_connection_event_test);
864
865         ret = g_test_run ();
866
867         soup_uri_free (base_uri);
868         soup_test_server_quit_unref (server);
869
870         test_cleanup ();
871         return ret;
872 }