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);
101 stream_closed (GObject *source, GAsyncResult *res, gpointer user_data)
103 GInputStream *stream = G_INPUT_STREAM (source);
104 GError *error = NULL;
106 if (!g_input_stream_close_finish (stream, res, &error)) {
107 debug_printf (1, " close failed: %s", error->message);
108 g_error_free (error);
111 g_main_loop_quit (loop);
112 g_object_unref (stream);
116 test_read_ready (GObject *source, GAsyncResult *res, gpointer user_data)
118 GInputStream *stream = G_INPUT_STREAM (source);
119 GString *body = user_data;
120 GError *error = NULL;
123 nread = g_input_stream_read_finish (stream, res, &error);
125 debug_printf (1, " read_async failed: %s", error->message);
126 g_error_free (error);
128 g_input_stream_close (stream, NULL, NULL);
129 g_object_unref (stream);
130 g_main_loop_quit (loop);
132 } else if (nread == 0) {
133 g_input_stream_close_async (stream,
134 G_PRIORITY_DEFAULT, NULL,
135 stream_closed, NULL);
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);
146 auth_test_sent (GObject *source, GAsyncResult *res, gpointer user_data)
148 GString *body = user_data;
149 GInputStream *stream;
150 GError *error = NULL;
152 const char *content_type;
154 stream = soup_request_send_finish (SOUP_REQUEST (source), res, &error);
156 debug_printf (1, " send_async failed: %s\n", error->message);
158 g_main_loop_quit (loop);
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,
167 g_main_loop_quit (loop);
170 g_object_unref (msg);
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)");
179 g_input_stream_read_async (stream, buf, sizeof (buf),
180 G_PRIORITY_DEFAULT, NULL,
181 test_read_ready, body);
185 test_sent (GObject *source, GAsyncResult *res, gpointer user_data)
187 GString *body = user_data;
188 GInputStream *stream;
189 GError *error = NULL;
191 const char *content_type;
193 stream = soup_request_send_finish (SOUP_REQUEST (source), res, &error);
195 debug_printf (1, " send_async failed: %s\n", error->message);
197 g_main_loop_quit (loop);
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,
206 g_main_loop_quit (loop);
209 g_object_unref (msg);
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)");
218 g_input_stream_read_async (stream, buf, sizeof (buf),
219 G_PRIORITY_DEFAULT, NULL,
220 test_read_ready, body);
224 request_started (SoupSession *session, SoupMessage *msg,
225 SoupSocket *socket, gpointer user_data)
227 SoupSocket **save_socket = user_data;
229 *save_socket = g_object_ref (socket);
233 do_one_test (SoupSession *session, SoupURI *uri,
234 GAsyncReadyCallback callback, SoupBuffer *expected_response,
237 SoupRequester *requester;
238 SoupRequest *request;
241 SoupSocket *socket = NULL;
243 requester = SOUP_REQUESTER (soup_session_get_feature (session, SOUP_TYPE_REQUESTER));
245 body = g_string_new (NULL);
246 request = soup_requester_request_uri (requester, uri, NULL);
248 started_id = g_signal_connect (session, "request-started",
249 G_CALLBACK (request_started),
252 soup_request_send_async (request, NULL, callback, body);
253 g_object_unref (request);
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);
259 g_signal_handler_disconnect (session, started_id);
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);
265 } else if (memcmp (body->str, expected_response->data,
266 expected_response->length) != 0) {
267 debug_printf (1, " body data mismatch\n");
272 if (!soup_socket_is_connected (socket)) {
273 debug_printf (1, " socket not still connected!\n");
277 if (soup_socket_is_connected (socket)) {
278 debug_printf (1, " socket still connected!\n");
282 g_object_unref (socket);
284 g_string_free (body, TRUE);
288 do_test_for_thread_and_context (SoupSession *session, const char *base_uri)
290 SoupRequester *requester;
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);
298 debug_printf (1, " basic test\n");
299 uri = soup_uri_new (base_uri);
300 do_one_test (session, uri, test_sent, response, TRUE);
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);
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);
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);
323 do_simple_test (const char *uri)
325 SoupSession *session;
327 debug_printf (1, "Simple streaming test\n");
329 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
330 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
332 do_test_for_thread_and_context (session, uri);
333 soup_test_session_abort_unref (session);
337 do_test_with_context (const char *uri)
339 GMainContext *async_context;
340 SoupSession *session;
342 async_context = g_main_context_new ();
343 g_main_context_push_thread_default (async_context);
345 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
346 SOUP_SESSION_ASYNC_CONTEXT, async_context,
347 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
350 do_test_for_thread_and_context (session, uri);
351 soup_test_session_abort_unref (session);
353 g_main_context_pop_thread_default (async_context);
354 g_main_context_unref (async_context);
359 do_context_test (const char *uri)
361 debug_printf (1, "Streaming with a non-default-context\n");
362 do_test_with_context (uri);
366 do_thread_test (const char *uri)
370 debug_printf (1, "Streaming in another thread\n");
372 thread = g_thread_new ("do_test_with_context",
373 (GThreadFunc)do_test_with_context,
375 g_thread_join (thread);
379 do_sync_request (SoupSession *session, SoupRequest *request,
380 guint expected_status, SoupBuffer *expected_response,
385 GError *error = NULL;
390 SoupSocket *socket = NULL;
392 started_id = g_signal_connect (session, "request-started",
393 G_CALLBACK (request_started),
396 in = soup_request_send (request, NULL, &error);
397 g_signal_handler_disconnect (session, started_id);
399 debug_printf (1, " soup_request_send failed: %s\n",
401 g_clear_error (&error);
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,
413 g_object_unref (msg);
415 body = g_string_new (NULL);
417 nread = g_input_stream_read (in, buf, sizeof (buf),
420 debug_printf (1, " g_input_stream_read failed: %s\n",
422 g_clear_error (&error);
426 g_string_append_len (body, buf, nread);
429 if (!g_input_stream_close (in, NULL, &error)) {
430 debug_printf (1, " g_input_stream_close failed: %s\n",
432 g_clear_error (&error);
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);
441 } else if (memcmp (body->str, expected_response->data, body->len) != 0) {
442 debug_printf (1, " body data mismatch\n");
447 if (!soup_socket_is_connected (socket)) {
448 debug_printf (1, " socket not still connected!\n");
452 if (soup_socket_is_connected (socket)) {
453 debug_printf (1, " socket still connected!\n");
457 g_object_unref (socket);
459 g_string_free (body, TRUE);
463 do_sync_test (const char *uri_string)
465 SoupSession *session;
466 SoupRequester *requester;
467 SoupRequest *request;
470 debug_printf (1, "Sync streaming\n");
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);
477 uri = soup_uri_new (uri_string);
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);
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);
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);
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);
503 soup_test_session_abort_unref (session);
508 main (int argc, char **argv)
512 test_init (argc, argv, NULL);
515 server = soup_test_server_new (TRUE);
516 soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
518 uri = g_strdup_printf ("http://127.0.0.1:%u/foo", soup_server_get_port (server));
520 do_simple_test (uri);
521 do_thread_test (uri);
522 do_context_test (uri);
526 soup_buffer_free (response);
527 soup_buffer_free (auth_response);
528 soup_test_server_quit_unref (server);