1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2011 Red Hat, Inc.
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>
15 #include "test-utils.h"
21 SoupBuffer *response, *auth_response;
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"
33 if (!g_file_get_contents (SRCDIR "/index.txt", &contents, &length, &error)) {
34 fprintf (stderr, "Could not read index.txt: %s\n",
39 response = soup_buffer_new (SOUP_MEMORY_TAKE, contents, length);
41 auth_response = soup_buffer_new (SOUP_MEMORY_STATIC,
43 strlen (AUTH_HTML_BODY));
47 server_callback (SoupServer *server, SoupMessage *msg,
48 const char *path, GHashTable *query,
49 SoupClientContext *context, gpointer data)
51 gboolean chunked = FALSE;
54 if (strcmp (path, "/auth") == 0) {
55 soup_message_set_status (msg, SOUP_STATUS_UNAUTHORIZED);
56 soup_message_set_response (msg, "text/html",
59 strlen (AUTH_HTML_BODY));
60 soup_message_headers_append (msg->response_headers,
62 "Basic: realm=\"requester-test\"");
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.
69 soup_message_set_response (msg, "text/html",
72 strlen (REDIRECT_HTML_BODY));
74 } else if (strcmp (path, "/chunked") == 0) {
76 } else if (strcmp (path, "/non-persistent") == 0) {
77 soup_message_headers_append (msg->response_headers,
78 "Connection", "close");
81 soup_message_set_status (msg, SOUP_STATUS_OK);
84 soup_message_headers_set_encoding (msg->response_headers,
85 SOUP_ENCODING_CHUNKED);
87 for (i = 0; i < response->length; i += 8192) {
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);
95 soup_message_body_complete (msg->response_body);
97 soup_message_body_append_buffer (msg->response_body, response);
106 stream_closed (GObject *source, GAsyncResult *res, gpointer user_data)
108 GInputStream *stream = G_INPUT_STREAM (source);
109 GError *error = NULL;
111 if (!g_input_stream_close_finish (stream, res, &error)) {
112 debug_printf (1, " close failed: %s", error->message);
113 g_error_free (error);
116 g_main_loop_quit (loop);
117 g_object_unref (stream);
121 test_read_ready (GObject *source, GAsyncResult *res, gpointer user_data)
123 GInputStream *stream = G_INPUT_STREAM (source);
124 RequestData *data = user_data;
125 GString *body = data->body;
126 GError *error = NULL;
129 nread = g_input_stream_read_finish (stream, res, &error);
131 debug_printf (1, " read_async failed: %s", error->message);
132 g_error_free (error);
134 g_input_stream_close (stream, NULL, NULL);
135 g_object_unref (stream);
136 g_main_loop_quit (loop);
138 } else if (nread == 0) {
139 g_input_stream_close_async (stream,
140 G_PRIORITY_DEFAULT, NULL,
141 stream_closed, NULL);
145 g_string_append_len (body, buf, nread);
146 g_input_stream_read_async (stream, buf, sizeof (buf),
147 G_PRIORITY_DEFAULT, NULL,
148 test_read_ready, data);
152 auth_test_sent (GObject *source, GAsyncResult *res, gpointer user_data)
154 RequestData *data = user_data;
155 GInputStream *stream;
156 GError *error = NULL;
158 const char *content_type;
160 stream = soup_request_send_finish (SOUP_REQUEST (source), res, &error);
162 debug_printf (1, " send_async failed: %s\n", error->message);
164 g_main_loop_quit (loop);
168 msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (source));
169 if (msg->status_code != SOUP_STATUS_UNAUTHORIZED) {
170 debug_printf (1, " GET failed: %d %s\n", msg->status_code,
173 g_main_loop_quit (loop);
176 g_object_unref (msg);
178 content_type = soup_request_get_content_type (SOUP_REQUEST (source));
179 if (g_strcmp0 (content_type, "text/html") != 0) {
180 debug_printf (1, " failed to sniff Content-Type: got %s\n",
181 content_type ? content_type : "(NULL)");
185 g_input_stream_read_async (stream, buf, sizeof (buf),
186 G_PRIORITY_DEFAULT, NULL,
187 test_read_ready, data);
191 test_sent (GObject *source, GAsyncResult *res, gpointer user_data)
193 RequestData *data = user_data;
194 GInputStream *stream;
195 GError *error = NULL;
196 const char *content_type;
198 stream = soup_request_send_finish (SOUP_REQUEST (source), res, &error);
201 debug_printf (1, " send_async succeeded??\n");
203 g_input_stream_close (stream, NULL, NULL);
204 g_object_unref (stream);
205 } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
206 debug_printf (1, " send_async failed with wrong error: %s\n", error->message);
208 g_clear_error (&error);
210 g_main_loop_quit (loop);
214 debug_printf (1, " send_async failed: %s\n", error->message);
216 g_main_loop_quit (loop);
217 g_clear_error (&error);
222 content_type = soup_request_get_content_type (SOUP_REQUEST (source));
223 if (g_strcmp0 (content_type, "text/plain") != 0) {
224 debug_printf (1, " failed to sniff Content-Type: got %s\n",
225 content_type ? content_type : "(NULL)");
229 g_input_stream_read_async (stream, buf, sizeof (buf),
230 G_PRIORITY_DEFAULT, NULL,
231 test_read_ready, data);
235 cancel_message (SoupMessage *msg, gpointer session)
237 soup_session_cancel_message (session, msg, SOUP_STATUS_FORBIDDEN);
241 request_started (SoupSession *session, SoupMessage *msg,
242 SoupSocket *socket, gpointer user_data)
244 SoupSocket **save_socket = user_data;
246 *save_socket = g_object_ref (socket);
250 do_async_test (SoupSession *session, SoupURI *uri,
251 GAsyncReadyCallback callback, guint expected_status,
252 SoupBuffer *expected_response,
253 gboolean persistent, gboolean cancel)
255 SoupRequester *requester;
256 SoupRequest *request;
258 SoupSocket *socket = NULL;
262 requester = SOUP_REQUESTER (soup_session_get_feature (session, SOUP_TYPE_REQUESTER));
264 data.body = g_string_new (NULL);
265 data.cancel = cancel;
266 request = soup_requester_request_uri (requester, uri, NULL);
267 msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (request));
270 g_signal_connect (msg, "got-headers",
271 G_CALLBACK (cancel_message), session);
274 started_id = g_signal_connect (session, "request-started",
275 G_CALLBACK (request_started),
278 soup_request_send_async (request, NULL, callback, &data);
279 g_object_unref (request);
281 loop = g_main_loop_new (soup_session_get_async_context (session), TRUE);
282 g_main_loop_run (loop);
283 g_main_loop_unref (loop);
285 g_signal_handler_disconnect (session, started_id);
287 if (msg->status_code != expected_status) {
288 debug_printf (1, " GET failed: %d %s (expected %d)\n",
289 msg->status_code, msg->reason_phrase,
291 g_object_unref (msg);
295 g_object_unref (msg);
297 if (!expected_response) {
298 if (data.body->len) {
299 debug_printf (1, " body length mismatch: expected 0, got %d\n",
300 (int)data.body->len);
303 } else if (data.body->len != expected_response->length) {
304 debug_printf (1, " body length mismatch: expected %d, got %d\n",
305 (int)expected_response->length, (int)data.body->len);
307 } else if (memcmp (data.body->str, expected_response->data,
308 expected_response->length) != 0) {
309 debug_printf (1, " body data mismatch\n");
314 if (!soup_socket_is_connected (socket)) {
315 debug_printf (1, " socket not still connected!\n");
319 if (soup_socket_is_connected (socket)) {
320 debug_printf (1, " socket still connected!\n");
324 g_object_unref (socket);
326 g_string_free (data.body, TRUE);
330 do_test_for_thread_and_context (SoupSession *session, const char *base_uri)
332 SoupRequester *requester;
335 requester = soup_requester_new ();
336 soup_session_add_feature (session, SOUP_SESSION_FEATURE (requester));
337 g_object_unref (requester);
338 soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_SNIFFER);
340 debug_printf (1, " basic test\n");
341 uri = soup_uri_new (base_uri);
342 do_async_test (session, uri, test_sent,
343 SOUP_STATUS_OK, response,
347 debug_printf (1, " chunked test\n");
348 uri = soup_uri_new (base_uri);
349 soup_uri_set_path (uri, "/chunked");
350 do_async_test (session, uri, test_sent,
351 SOUP_STATUS_OK, response,
355 debug_printf (1, " auth test\n");
356 uri = soup_uri_new (base_uri);
357 soup_uri_set_path (uri, "/auth");
358 do_async_test (session, uri, auth_test_sent,
359 SOUP_STATUS_UNAUTHORIZED, auth_response,
363 debug_printf (1, " non-persistent test\n");
364 uri = soup_uri_new (base_uri);
365 soup_uri_set_path (uri, "/non-persistent");
366 do_async_test (session, uri, test_sent,
367 SOUP_STATUS_OK, response,
371 debug_printf (1, " cancellation test\n");
372 uri = soup_uri_new (base_uri);
373 soup_uri_set_path (uri, "/");
374 do_async_test (session, uri, test_sent,
375 SOUP_STATUS_FORBIDDEN, NULL,
381 do_simple_test (const char *uri)
383 SoupSession *session;
385 debug_printf (1, "Simple streaming test\n");
387 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
388 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
390 do_test_for_thread_and_context (session, uri);
391 soup_test_session_abort_unref (session);
395 do_test_with_context (const char *uri)
397 GMainContext *async_context;
398 SoupSession *session;
400 async_context = g_main_context_new ();
401 g_main_context_push_thread_default (async_context);
403 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
404 SOUP_SESSION_ASYNC_CONTEXT, async_context,
405 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
408 do_test_for_thread_and_context (session, uri);
409 soup_test_session_abort_unref (session);
411 g_main_context_pop_thread_default (async_context);
412 g_main_context_unref (async_context);
417 do_context_test (const char *uri)
419 debug_printf (1, "Streaming with a non-default-context\n");
420 do_test_with_context (uri);
424 do_thread_test (const char *uri)
428 debug_printf (1, "Streaming in another thread\n");
430 thread = g_thread_new ("do_test_with_context",
431 (GThreadFunc)do_test_with_context,
433 g_thread_join (thread);
437 do_sync_request (SoupSession *session, SoupRequest *request,
438 guint expected_status, SoupBuffer *expected_response,
439 gboolean persistent, gboolean cancel)
443 GError *error = NULL;
448 SoupSocket *socket = NULL;
450 msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (request));
452 g_signal_connect (msg, "got-headers",
453 G_CALLBACK (cancel_message), session);
456 started_id = g_signal_connect (session, "request-started",
457 G_CALLBACK (request_started),
460 in = soup_request_send (request, NULL, &error);
461 g_signal_handler_disconnect (session, started_id);
464 debug_printf (1, " send succeeded??\n");
466 g_input_stream_close (in, NULL, NULL);
468 } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
469 debug_printf (1, " send failed with wrong error: %s\n", error->message);
471 g_clear_error (&error);
473 g_object_unref (msg);
476 debug_printf (1, " soup_request_send failed: %s\n",
478 g_object_unref (msg);
479 g_clear_error (&error);
484 if (msg->status_code != expected_status) {
485 debug_printf (1, " GET failed: %d %s\n", msg->status_code,
487 g_object_unref (msg);
492 g_object_unref (msg);
494 body = g_string_new (NULL);
496 nread = g_input_stream_read (in, buf, sizeof (buf),
499 debug_printf (1, " g_input_stream_read failed: %s\n",
501 g_clear_error (&error);
505 g_string_append_len (body, buf, nread);
508 if (!g_input_stream_close (in, NULL, &error)) {
509 debug_printf (1, " g_input_stream_close failed: %s\n",
511 g_clear_error (&error);
516 if (!expected_response) {
518 debug_printf (1, " body length mismatch: expected 0, got %d\n",
522 } else if (body->len != expected_response->length) {
523 debug_printf (1, " body length mismatch: expected %d, got %d\n",
524 (int)expected_response->length, (int)body->len);
526 } else if (memcmp (body->str, expected_response->data, body->len) != 0) {
527 debug_printf (1, " body data mismatch\n");
532 if (!soup_socket_is_connected (socket)) {
533 debug_printf (1, " socket not still connected!\n");
537 if (soup_socket_is_connected (socket)) {
538 debug_printf (1, " socket still connected!\n");
542 g_object_unref (socket);
544 g_string_free (body, TRUE);
548 do_sync_test (const char *uri_string)
550 SoupSession *session;
551 SoupRequester *requester;
552 SoupRequest *request;
555 debug_printf (1, "Sync streaming\n");
557 session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
558 requester = soup_requester_new ();
559 soup_session_add_feature (session, SOUP_SESSION_FEATURE (requester));
560 g_object_unref (requester);
562 uri = soup_uri_new (uri_string);
564 debug_printf (1, " basic test\n");
565 request = soup_requester_request_uri (requester, uri, NULL);
566 do_sync_request (session, request,
567 SOUP_STATUS_OK, response,
569 g_object_unref (request);
571 debug_printf (1, " chunked test\n");
572 soup_uri_set_path (uri, "/chunked");
573 request = soup_requester_request_uri (requester, uri, NULL);
574 do_sync_request (session, request,
575 SOUP_STATUS_OK, response,
577 g_object_unref (request);
579 debug_printf (1, " auth test\n");
580 soup_uri_set_path (uri, "/auth");
581 request = soup_requester_request_uri (requester, uri, NULL);
582 do_sync_request (session, request,
583 SOUP_STATUS_UNAUTHORIZED, auth_response,
585 g_object_unref (request);
587 debug_printf (1, " non-persistent test\n");
588 soup_uri_set_path (uri, "/non-persistent");
589 request = soup_requester_request_uri (requester, uri, NULL);
590 do_sync_request (session, request,
591 SOUP_STATUS_OK, response,
593 g_object_unref (request);
595 debug_printf (1, " cancel test\n");
596 soup_uri_set_path (uri, "/");
597 request = soup_requester_request_uri (requester, uri, NULL);
598 do_sync_request (session, request,
599 SOUP_STATUS_FORBIDDEN, NULL,
601 g_object_unref (request);
603 soup_test_session_abort_unref (session);
608 main (int argc, char **argv)
612 test_init (argc, argv, NULL);
615 server = soup_test_server_new (TRUE);
616 soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
618 uri = g_strdup_printf ("http://127.0.0.1:%u/foo", soup_server_get_port (server));
620 do_simple_test (uri);
621 do_thread_test (uri);
622 do_context_test (uri);
626 soup_buffer_free (response);
627 soup_buffer_free (auth_response);
628 soup_test_server_quit_unref (server);