tests/get: use GProxyResolverDefault
[platform/upstream/libsoup.git] / tests / requester-test.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2011 Red Hat, Inc.
4  */
5
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9
10 #define LIBSOUP_USE_UNSTABLE_REQUEST_API
11 #include <libsoup/soup.h>
12 #include <libsoup/soup-requester.h>
13 #include <libsoup/soup-request-http.h>
14
15 #include "test-utils.h"
16
17 SoupServer *server;
18 GMainLoop *loop;
19 char buf[1024];
20
21 SoupBuffer *response, *auth_response;
22
23 #define REDIRECT_HTML_BODY "<html><body>Try again</body></html>\r\n"
24 #define AUTH_HTML_BODY "<html><body>Unauthorized</body></html>\r\n"
25
26 static void
27 get_index (void)
28 {
29         char *contents;
30         gsize length;
31         GError *error = NULL;
32
33         if (!g_file_get_contents (SRCDIR "/index.txt", &contents, &length, &error)) {
34                 fprintf (stderr, "Could not read index.txt: %s\n",
35                          error->message);
36                 exit (1);
37         }
38
39         response = soup_buffer_new (SOUP_MEMORY_TAKE, contents, length);
40
41         auth_response = soup_buffer_new (SOUP_MEMORY_STATIC,
42                                          AUTH_HTML_BODY,
43                                          strlen (AUTH_HTML_BODY));
44 }
45
46 static void
47 server_callback (SoupServer *server, SoupMessage *msg,
48                  const char *path, GHashTable *query,
49                  SoupClientContext *context, gpointer data)
50 {
51         gboolean chunked = FALSE;
52         int i;
53
54         if (strcmp (path, "/auth") == 0) {
55                 soup_message_set_status (msg, SOUP_STATUS_UNAUTHORIZED);
56                 soup_message_set_response (msg, "text/html",
57                                            SOUP_MEMORY_STATIC,
58                                            AUTH_HTML_BODY,
59                                            strlen (AUTH_HTML_BODY));
60                 soup_message_headers_append (msg->response_headers,
61                                              "WWW-Authenticate",
62                                              "Basic: realm=\"requester-test\"");
63                 return;
64         } else if (strcmp (path, "/foo") == 0) {
65                 soup_message_set_redirect (msg, SOUP_STATUS_FOUND, "/");
66                 /* Make the response HTML so if we sniff that instead of the
67                  * real body, we'll notice.
68                  */
69                 soup_message_set_response (msg, "text/html",
70                                            SOUP_MEMORY_STATIC,
71                                            REDIRECT_HTML_BODY,
72                                            strlen (REDIRECT_HTML_BODY));
73                 return;
74         } else if (strcmp (path, "/chunked") == 0) {
75                 chunked = TRUE;
76         } else if (strcmp (path, "/non-persistent") == 0) {
77                 soup_message_headers_append (msg->response_headers,
78                                              "Connection", "close");
79         }
80
81         soup_message_set_status (msg, SOUP_STATUS_OK);
82
83         if (chunked) {
84                 soup_message_headers_set_encoding (msg->response_headers,
85                                                    SOUP_ENCODING_CHUNKED);
86
87                 for (i = 0; i < response->length; i += 8192) {
88                         SoupBuffer *tmp;
89
90                         tmp = soup_buffer_new_subbuffer (response, i,
91                                                          MIN (8192, response->length - i));
92                         soup_message_body_append_buffer (msg->response_body, tmp);
93                         soup_buffer_free (tmp);
94                 }
95                 soup_message_body_complete (msg->response_body);
96         } else
97                 soup_message_body_append_buffer (msg->response_body, response);
98 }
99
100 static void
101 stream_closed (GObject *source, GAsyncResult *res, gpointer user_data)
102 {
103         GInputStream *stream = G_INPUT_STREAM (source);
104         GError *error = NULL;
105
106         if (!g_input_stream_close_finish (stream, res, &error)) {
107                 debug_printf (1, "    close failed: %s", error->message);
108                 g_error_free (error);
109                 errors++;
110         }
111         g_main_loop_quit (loop);
112         g_object_unref (stream);
113 }
114
115 static void
116 test_read_ready (GObject *source, GAsyncResult *res, gpointer user_data)
117 {
118         GInputStream *stream = G_INPUT_STREAM (source);
119         GString *body = user_data;
120         GError *error = NULL;
121         gsize nread;
122
123         nread = g_input_stream_read_finish (stream, res, &error);
124         if (nread == -1) {
125                 debug_printf (1, "    read_async failed: %s", error->message);
126                 g_error_free (error);
127                 errors++;
128                 g_input_stream_close (stream, NULL, NULL);
129                 g_object_unref (stream);
130                 g_main_loop_quit (loop);
131                 return;
132         } else if (nread == 0) {
133                 g_input_stream_close_async (stream,
134                                             G_PRIORITY_DEFAULT, NULL,
135                                             stream_closed, NULL);
136                 return;
137         }
138
139         g_string_append_len (body, buf, nread);
140         g_input_stream_read_async (stream, buf, sizeof (buf),
141                                    G_PRIORITY_DEFAULT, NULL,
142                                    test_read_ready, body);
143 }
144
145 static void
146 auth_test_sent (GObject *source, GAsyncResult *res, gpointer user_data)
147 {
148         GString *body = user_data;
149         GInputStream *stream;
150         GError *error = NULL;
151         SoupMessage *msg;
152         const char *content_type;
153
154         stream = soup_request_send_finish (SOUP_REQUEST (source), res, &error);
155         if (!stream) {
156                 debug_printf (1, "    send_async failed: %s\n", error->message);
157                 errors++;
158                 g_main_loop_quit (loop);
159                 return;
160         }
161
162         msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (source));
163         if (msg->status_code != SOUP_STATUS_UNAUTHORIZED) {
164                 debug_printf (1, "    GET failed: %d %s\n", msg->status_code,
165                               msg->reason_phrase);
166                 errors++;
167                 g_main_loop_quit (loop);
168                 return;
169         }
170         g_object_unref (msg);
171
172         content_type = soup_request_get_content_type (SOUP_REQUEST (source));
173         if (g_strcmp0 (content_type, "text/html") != 0) {
174                 debug_printf (1, "    failed to sniff Content-Type: got %s\n",
175                               content_type ? content_type : "(NULL)");
176                 errors++;
177         }
178
179         g_input_stream_read_async (stream, buf, sizeof (buf),
180                                    G_PRIORITY_DEFAULT, NULL,
181                                    test_read_ready, body);
182 }
183
184 static void
185 test_sent (GObject *source, GAsyncResult *res, gpointer user_data)
186 {
187         GString *body = user_data;
188         GInputStream *stream;
189         GError *error = NULL;
190         SoupMessage *msg;
191         const char *content_type;
192
193         stream = soup_request_send_finish (SOUP_REQUEST (source), res, &error);
194         if (!stream) {
195                 debug_printf (1, "    send_async failed: %s\n", error->message);
196                 errors++;
197                 g_main_loop_quit (loop);
198                 return;
199         }
200
201         msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (source));
202         if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
203                 debug_printf (1, "    GET failed: %d %s\n", msg->status_code,
204                               msg->reason_phrase);
205                 errors++;
206                 g_main_loop_quit (loop);
207                 return;
208         }
209         g_object_unref (msg);
210
211         content_type = soup_request_get_content_type (SOUP_REQUEST (source));
212         if (g_strcmp0 (content_type, "text/plain") != 0) {
213                 debug_printf (1, "    failed to sniff Content-Type: got %s\n",
214                               content_type ? content_type : "(NULL)");
215                 errors++;
216         }
217
218         g_input_stream_read_async (stream, buf, sizeof (buf),
219                                    G_PRIORITY_DEFAULT, NULL,
220                                    test_read_ready, body);
221 }
222
223 static void
224 request_started (SoupSession *session, SoupMessage *msg,
225                  SoupSocket *socket, gpointer user_data)
226 {
227         SoupSocket **save_socket = user_data;
228
229         *save_socket = g_object_ref (socket);
230 }
231
232 static void
233 do_one_test (SoupSession *session, SoupURI *uri,
234              GAsyncReadyCallback callback, SoupBuffer *expected_response,
235              gboolean persistent)
236 {
237         SoupRequester *requester;
238         SoupRequest *request;
239         GString *body;
240         guint started_id;
241         SoupSocket *socket = NULL;
242
243         requester = SOUP_REQUESTER (soup_session_get_feature (session, SOUP_TYPE_REQUESTER));
244
245         body = g_string_new (NULL);
246         request = soup_requester_request_uri (requester, uri, NULL);
247
248         started_id = g_signal_connect (session, "request-started",
249                                        G_CALLBACK (request_started),
250                                        &socket);
251
252         soup_request_send_async (request, NULL, callback, body);
253         g_object_unref (request);
254
255         loop = g_main_loop_new (soup_session_get_async_context (session), TRUE);
256         g_main_loop_run (loop);
257         g_main_loop_unref (loop);
258
259         g_signal_handler_disconnect (session, started_id);
260
261         if (body->len != expected_response->length) {
262                 debug_printf (1, "    body length mismatch: expected %d, got %d\n",
263                               (int)expected_response->length, (int)body->len);
264                 errors++;
265         } else if (memcmp (body->str, expected_response->data,
266                            expected_response->length) != 0) {
267                 debug_printf (1, "    body data mismatch\n");
268                 errors++;
269         }
270
271         if (persistent) {
272                 if (!soup_socket_is_connected (socket)) {
273                         debug_printf (1, "    socket not still connected!\n");
274                         errors++;
275                 }
276         } else {
277                 if (soup_socket_is_connected (socket)) {
278                         debug_printf (1, "    socket still connected!\n");
279                         errors++;
280                 }
281         }
282         g_object_unref (socket);
283
284         g_string_free (body, TRUE);
285 }
286
287 static void
288 do_test_for_thread_and_context (SoupSession *session, const char *base_uri)
289 {
290         SoupRequester *requester;
291         SoupURI *uri;
292
293         requester = soup_requester_new ();
294         soup_session_add_feature (session, SOUP_SESSION_FEATURE (requester));
295         g_object_unref (requester);
296         soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_SNIFFER);
297
298         debug_printf (1, "  basic test\n");
299         uri = soup_uri_new (base_uri);
300         do_one_test (session, uri, test_sent, response, TRUE);
301         soup_uri_free (uri);
302
303         debug_printf (1, "  chunked test\n");
304         uri = soup_uri_new (base_uri);
305         soup_uri_set_path (uri, "/chunked");
306         do_one_test (session, uri, test_sent, response, TRUE);
307         soup_uri_free (uri);
308
309         debug_printf (1, "  auth test\n");
310         uri = soup_uri_new (base_uri);
311         soup_uri_set_path (uri, "/auth");
312         do_one_test (session, uri, auth_test_sent, auth_response, TRUE);
313         soup_uri_free (uri);
314
315         debug_printf (1, "  non-persistent test\n");
316         uri = soup_uri_new (base_uri);
317         soup_uri_set_path (uri, "/non-persistent");
318         do_one_test (session, uri, test_sent, response, FALSE);
319         soup_uri_free (uri);
320 }
321
322 static void
323 do_simple_test (const char *uri)
324 {
325         SoupSession *session;
326
327         debug_printf (1, "Simple streaming test\n");
328
329         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
330                                          SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
331                                          NULL);
332         do_test_for_thread_and_context (session, uri);
333         soup_test_session_abort_unref (session);
334 }
335
336 static gpointer
337 do_test_with_context (const char *uri)
338 {
339         GMainContext *async_context;
340         SoupSession *session;
341
342         async_context = g_main_context_new ();
343         g_main_context_push_thread_default (async_context);
344
345         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
346                                          SOUP_SESSION_ASYNC_CONTEXT, async_context,
347                                          SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
348                                          NULL);
349
350         do_test_for_thread_and_context (session, uri);
351         soup_test_session_abort_unref (session);
352
353         g_main_context_pop_thread_default (async_context);
354         g_main_context_unref (async_context);
355         return NULL;
356 }
357
358 static void
359 do_context_test (const char *uri)
360 {
361         debug_printf (1, "Streaming with a non-default-context\n");
362         do_test_with_context (uri);
363 }
364
365 static void
366 do_thread_test (const char *uri)
367 {
368         GThread *thread;
369
370         debug_printf (1, "Streaming in another thread\n");
371
372         thread = g_thread_new ("do_test_with_context",
373                                (GThreadFunc)do_test_with_context,
374                                (gpointer)uri);
375         g_thread_join (thread);
376 }
377
378 static void
379 do_sync_request (SoupSession *session, SoupRequest *request,
380                  guint expected_status, SoupBuffer *expected_response,
381                  gboolean persistent)
382 {
383         GInputStream *in;
384         SoupMessage *msg;
385         GError *error = NULL;
386         GString *body;
387         char buf[1024];
388         gssize nread;
389         guint started_id;
390         SoupSocket *socket = NULL;
391
392         started_id = g_signal_connect (session, "request-started",
393                                        G_CALLBACK (request_started),
394                                        &socket);
395
396         in = soup_request_send (request, NULL, &error);
397         g_signal_handler_disconnect (session, started_id);
398         if (!in) {
399                 debug_printf (1, "    soup_request_send failed: %s\n",
400                               error->message);
401                 g_clear_error (&error);
402                 errors++;
403                 return;
404         }
405
406         msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (request));
407         if (msg->status_code != expected_status) {
408                 debug_printf (1, "    GET failed: %d %s", msg->status_code,
409                               msg->reason_phrase);
410                 errors++;
411                 return;
412         }
413         g_object_unref (msg);
414
415         body = g_string_new (NULL);
416         do {
417                 nread = g_input_stream_read (in, buf, sizeof (buf),
418                                              NULL, &error);
419                 if (nread == -1) {
420                         debug_printf (1, "    g_input_stream_read failed: %s\n",
421                                       error->message);
422                         g_clear_error (&error);
423                         errors++;
424                         break;
425                 }
426                 g_string_append_len (body, buf, nread);
427         } while (nread > 0);
428
429         if (!g_input_stream_close (in, NULL, &error)) {
430                 debug_printf (1, "    g_input_stream_close failed: %s\n",
431                               error->message);
432                 g_clear_error (&error);
433                 errors++;
434         }
435         g_object_unref (in);
436
437         if (body->len != expected_response->length) {
438                 debug_printf (1, "    body length mismatch: expected %d, got %d\n",
439                               (int)expected_response->length, (int)body->len);
440                 errors++;
441         } else if (memcmp (body->str, expected_response->data, body->len) != 0) {
442                 debug_printf (1, "    body data mismatch\n");
443                 errors++;
444         }
445
446         if (persistent) {
447                 if (!soup_socket_is_connected (socket)) {
448                         debug_printf (1, "    socket not still connected!\n");
449                         errors++;
450                 }
451         } else {
452                 if (soup_socket_is_connected (socket)) {
453                         debug_printf (1, "    socket still connected!\n");
454                         errors++;
455                 }
456         }
457         g_object_unref (socket);
458
459         g_string_free (body, TRUE);
460 }
461
462 static void
463 do_sync_test (const char *uri_string)
464 {
465         SoupSession *session;
466         SoupRequester *requester;
467         SoupRequest *request;
468         SoupURI *uri;
469
470         debug_printf (1, "Sync streaming\n");
471
472         session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
473         requester = soup_requester_new ();
474         soup_session_add_feature (session, SOUP_SESSION_FEATURE (requester));
475         g_object_unref (requester);
476
477         uri = soup_uri_new (uri_string);
478
479         debug_printf (1, "  basic test\n");
480         request = soup_requester_request_uri (requester, uri, NULL);
481         do_sync_request (session, request, SOUP_STATUS_OK, response, TRUE);
482         g_object_unref (request);
483
484         debug_printf (1, "  chunked test\n");
485         soup_uri_set_path (uri, "/chunked");
486         request = soup_requester_request_uri (requester, uri, NULL);
487         do_sync_request (session, request, SOUP_STATUS_OK, response, TRUE);
488         g_object_unref (request);
489
490         debug_printf (1, "  auth test\n");
491         soup_uri_set_path (uri, "/auth");
492         request = soup_requester_request_uri (requester, uri, NULL);
493         do_sync_request (session, request, SOUP_STATUS_UNAUTHORIZED,
494                          auth_response, TRUE);
495         g_object_unref (request);
496
497         debug_printf (1, "  non-persistent test\n");
498         soup_uri_set_path (uri, "/non-persistent");
499         request = soup_requester_request_uri (requester, uri, NULL);
500         do_sync_request (session, request, SOUP_STATUS_OK, response, FALSE);
501         g_object_unref (request);
502
503         soup_test_session_abort_unref (session);
504         soup_uri_free (uri);
505 }
506
507 int
508 main (int argc, char **argv)
509 {
510         char *uri;
511
512         test_init (argc, argv, NULL);
513         get_index ();
514
515         server = soup_test_server_new (TRUE);
516         soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
517
518         uri = g_strdup_printf ("http://127.0.0.1:%u/foo", soup_server_get_port (server));
519
520         do_simple_test (uri);
521         do_thread_test (uri);
522         do_context_test (uri);
523         do_sync_test (uri);
524
525         g_free (uri);
526         soup_buffer_free (response);
527         soup_buffer_free (auth_response);
528         soup_test_server_quit_unref (server);
529
530         test_cleanup ();
531         return errors != 0;
532 }