1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2011 Red Hat, Inc.
6 #include "test-utils.h"
12 SoupBuffer *response, *auth_response;
14 #define REDIRECT_HTML_BODY "<html><body>Try again</body></html>\r\n"
15 #define AUTH_HTML_BODY "<html><body>Unauthorized</body></html>\r\n"
24 if (!g_file_get_contents (SRCDIR "/index.txt", &contents, &length, &error)) {
25 g_printerr ("Could not read index.txt: %s\n",
30 response = soup_buffer_new (SOUP_MEMORY_TAKE, contents, length);
32 auth_response = soup_buffer_new (SOUP_MEMORY_STATIC,
34 strlen (AUTH_HTML_BODY));
38 server_callback (SoupServer *server, SoupMessage *msg,
39 const char *path, GHashTable *query,
40 SoupClientContext *context, gpointer data)
42 gboolean chunked = FALSE;
45 if (strcmp (path, "/auth") == 0) {
46 soup_message_set_status (msg, SOUP_STATUS_UNAUTHORIZED);
47 soup_message_set_response (msg, "text/html",
50 strlen (AUTH_HTML_BODY));
51 soup_message_headers_append (msg->response_headers,
53 "Basic: realm=\"requester-test\"");
55 } else if (strcmp (path, "/foo") == 0) {
56 soup_message_set_redirect (msg, SOUP_STATUS_FOUND, "/");
57 /* Make the response HTML so if we sniff that instead of the
58 * real body, we'll notice.
60 soup_message_set_response (msg, "text/html",
63 strlen (REDIRECT_HTML_BODY));
65 } else if (strcmp (path, "/chunked") == 0) {
67 } else if (strcmp (path, "/non-persistent") == 0) {
68 soup_message_headers_append (msg->response_headers,
69 "Connection", "close");
72 soup_message_set_status (msg, SOUP_STATUS_OK);
75 soup_message_headers_set_encoding (msg->response_headers,
76 SOUP_ENCODING_CHUNKED);
78 for (i = 0; i < response->length; i += 8192) {
81 tmp = soup_buffer_new_subbuffer (response, i,
82 MIN (8192, response->length - i));
83 soup_message_body_append_buffer (msg->response_body, tmp);
84 soup_buffer_free (tmp);
86 soup_message_body_complete (msg->response_body);
88 soup_message_body_append_buffer (msg->response_body, response);
97 stream_closed (GObject *source, GAsyncResult *res, gpointer user_data)
99 GInputStream *stream = G_INPUT_STREAM (source);
100 GError *error = NULL;
102 if (!g_input_stream_close_finish (stream, res, &error)) {
103 debug_printf (1, " close failed: %s\n", error->message);
104 g_error_free (error);
107 g_main_loop_quit (loop);
108 g_object_unref (stream);
112 test_read_ready (GObject *source, GAsyncResult *res, gpointer user_data)
114 GInputStream *stream = G_INPUT_STREAM (source);
115 RequestData *data = user_data;
116 GString *body = data->body;
117 GError *error = NULL;
120 nread = g_input_stream_read_finish (stream, res, &error);
122 debug_printf (1, " read_async failed: %s\n", error->message);
123 g_error_free (error);
125 g_input_stream_close (stream, NULL, NULL);
126 g_object_unref (stream);
127 g_main_loop_quit (loop);
129 } else if (nread == 0) {
130 g_input_stream_close_async (stream,
131 G_PRIORITY_DEFAULT, NULL,
132 stream_closed, NULL);
136 g_string_append_len (body, buf, nread);
137 g_input_stream_read_async (stream, buf, sizeof (buf),
138 G_PRIORITY_DEFAULT, NULL,
139 test_read_ready, data);
143 auth_test_sent (GObject *source, GAsyncResult *res, gpointer user_data)
145 RequestData *data = user_data;
146 GInputStream *stream;
147 GError *error = NULL;
149 const char *content_type;
151 stream = soup_request_send_finish (SOUP_REQUEST (source), res, &error);
153 debug_printf (1, " send_async failed: %s\n", error->message);
155 g_main_loop_quit (loop);
159 msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (source));
160 if (msg->status_code != SOUP_STATUS_UNAUTHORIZED) {
161 debug_printf (1, " GET failed: %d %s\n", msg->status_code,
164 g_main_loop_quit (loop);
167 g_object_unref (msg);
169 content_type = soup_request_get_content_type (SOUP_REQUEST (source));
170 if (g_strcmp0 (content_type, "text/html") != 0) {
171 debug_printf (1, " failed to sniff Content-Type: got %s\n",
172 content_type ? content_type : "(NULL)");
176 g_input_stream_read_async (stream, buf, sizeof (buf),
177 G_PRIORITY_DEFAULT, NULL,
178 test_read_ready, data);
182 test_sent (GObject *source, GAsyncResult *res, gpointer user_data)
184 RequestData *data = user_data;
185 GInputStream *stream;
186 GError *error = NULL;
187 const char *content_type;
189 stream = soup_request_send_finish (SOUP_REQUEST (source), res, &error);
192 debug_printf (1, " send_async succeeded??\n");
194 g_input_stream_close (stream, NULL, NULL);
195 g_object_unref (stream);
196 } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
197 debug_printf (1, " send_async failed with wrong error: %s\n", error->message);
199 g_clear_error (&error);
201 g_main_loop_quit (loop);
205 debug_printf (1, " send_async failed: %s\n", error->message);
207 g_main_loop_quit (loop);
208 g_clear_error (&error);
213 content_type = soup_request_get_content_type (SOUP_REQUEST (source));
214 if (g_strcmp0 (content_type, "text/plain") != 0) {
215 debug_printf (1, " failed to sniff Content-Type: got %s\n",
216 content_type ? content_type : "(NULL)");
220 g_input_stream_read_async (stream, buf, sizeof (buf),
221 G_PRIORITY_DEFAULT, NULL,
222 test_read_ready, data);
226 cancel_message (SoupMessage *msg, gpointer session)
228 soup_session_cancel_message (session, msg, SOUP_STATUS_FORBIDDEN);
232 request_started (SoupSession *session, SoupMessage *msg,
233 SoupSocket *socket, gpointer user_data)
235 SoupSocket **save_socket = user_data;
237 *save_socket = g_object_ref (socket);
241 do_async_test (SoupSession *session, SoupURI *uri,
242 GAsyncReadyCallback callback, guint expected_status,
243 SoupBuffer *expected_response,
244 gboolean persistent, gboolean cancel)
246 SoupRequester *requester;
247 SoupRequest *request;
249 SoupSocket *socket = NULL;
253 requester = SOUP_REQUESTER (soup_session_get_feature (session, SOUP_TYPE_REQUESTER));
255 data.body = g_string_new (NULL);
256 data.cancel = cancel;
257 request = soup_requester_request_uri (requester, uri, NULL);
258 msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (request));
261 g_signal_connect (msg, "got-headers",
262 G_CALLBACK (cancel_message), session);
265 started_id = g_signal_connect (session, "request-started",
266 G_CALLBACK (request_started),
269 soup_request_send_async (request, NULL, callback, &data);
270 g_object_unref (request);
272 loop = g_main_loop_new (soup_session_get_async_context (session), TRUE);
273 g_main_loop_run (loop);
274 g_main_loop_unref (loop);
276 g_signal_handler_disconnect (session, started_id);
278 if (msg->status_code != expected_status) {
279 debug_printf (1, " GET failed: %d %s (expected %d)\n",
280 msg->status_code, msg->reason_phrase,
282 g_object_unref (msg);
286 g_object_unref (msg);
288 if (!expected_response) {
289 if (data.body->len) {
290 debug_printf (1, " body length mismatch: expected 0, got %d\n",
291 (int)data.body->len);
294 } else if (data.body->len != expected_response->length) {
295 debug_printf (1, " body length mismatch: expected %d, got %d\n",
296 (int)expected_response->length, (int)data.body->len);
298 } else if (memcmp (data.body->str, expected_response->data,
299 expected_response->length) != 0) {
300 debug_printf (1, " body data mismatch\n");
305 if (!soup_socket_is_connected (socket)) {
306 debug_printf (1, " socket not still connected!\n");
310 if (soup_socket_is_connected (socket)) {
311 debug_printf (1, " socket still connected!\n");
315 g_object_unref (socket);
317 g_string_free (data.body, TRUE);
321 do_test_for_thread_and_context (SoupSession *session, const char *base_uri)
323 SoupRequester *requester;
326 requester = soup_requester_new ();
327 soup_session_add_feature (session, SOUP_SESSION_FEATURE (requester));
328 g_object_unref (requester);
329 soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_SNIFFER);
331 debug_printf (1, " basic test\n");
332 uri = soup_uri_new (base_uri);
333 do_async_test (session, uri, test_sent,
334 SOUP_STATUS_OK, response,
338 debug_printf (1, " chunked test\n");
339 uri = soup_uri_new (base_uri);
340 soup_uri_set_path (uri, "/chunked");
341 do_async_test (session, uri, test_sent,
342 SOUP_STATUS_OK, response,
346 debug_printf (1, " auth test\n");
347 uri = soup_uri_new (base_uri);
348 soup_uri_set_path (uri, "/auth");
349 do_async_test (session, uri, auth_test_sent,
350 SOUP_STATUS_UNAUTHORIZED, auth_response,
354 debug_printf (1, " non-persistent test\n");
355 uri = soup_uri_new (base_uri);
356 soup_uri_set_path (uri, "/non-persistent");
357 do_async_test (session, uri, test_sent,
358 SOUP_STATUS_OK, response,
362 debug_printf (1, " cancellation test\n");
363 uri = soup_uri_new (base_uri);
364 soup_uri_set_path (uri, "/");
365 do_async_test (session, uri, test_sent,
366 SOUP_STATUS_FORBIDDEN, NULL,
372 do_simple_test (const char *uri)
374 SoupSession *session;
376 debug_printf (1, "Simple streaming test\n");
378 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
379 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
381 do_test_for_thread_and_context (session, uri);
382 soup_test_session_abort_unref (session);
386 do_test_with_context (const char *uri)
388 GMainContext *async_context;
389 SoupSession *session;
391 async_context = g_main_context_new ();
392 g_main_context_push_thread_default (async_context);
394 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
395 SOUP_SESSION_ASYNC_CONTEXT, async_context,
396 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
399 do_test_for_thread_and_context (session, uri);
400 soup_test_session_abort_unref (session);
402 g_main_context_pop_thread_default (async_context);
403 g_main_context_unref (async_context);
408 do_context_test (const char *uri)
410 debug_printf (1, "Streaming with a non-default-context\n");
411 do_test_with_context (uri);
415 do_thread_test (const char *uri)
419 debug_printf (1, "Streaming in another thread\n");
421 thread = g_thread_new ("do_test_with_context",
422 (GThreadFunc)do_test_with_context,
424 g_thread_join (thread);
428 do_sync_request (SoupSession *session, SoupRequest *request,
429 guint expected_status, SoupBuffer *expected_response,
430 gboolean persistent, gboolean cancel)
434 GError *error = NULL;
439 SoupSocket *socket = NULL;
441 msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (request));
443 g_signal_connect (msg, "got-headers",
444 G_CALLBACK (cancel_message), session);
447 started_id = g_signal_connect (session, "request-started",
448 G_CALLBACK (request_started),
451 in = soup_request_send (request, NULL, &error);
452 g_signal_handler_disconnect (session, started_id);
455 debug_printf (1, " send succeeded??\n");
457 g_input_stream_close (in, NULL, NULL);
459 } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
460 debug_printf (1, " send failed with wrong error: %s\n", error->message);
462 g_clear_error (&error);
464 g_object_unref (msg);
467 debug_printf (1, " soup_request_send failed: %s\n",
469 g_object_unref (msg);
470 g_clear_error (&error);
475 if (msg->status_code != expected_status) {
476 debug_printf (1, " GET failed: %d %s\n", msg->status_code,
478 g_object_unref (msg);
483 g_object_unref (msg);
485 body = g_string_new (NULL);
487 nread = g_input_stream_read (in, buf, sizeof (buf),
490 debug_printf (1, " g_input_stream_read failed: %s\n",
492 g_clear_error (&error);
496 g_string_append_len (body, buf, nread);
499 if (!g_input_stream_close (in, NULL, &error)) {
500 debug_printf (1, " g_input_stream_close failed: %s\n",
502 g_clear_error (&error);
507 if (!expected_response) {
509 debug_printf (1, " body length mismatch: expected 0, got %d\n",
513 } else if (body->len != expected_response->length) {
514 debug_printf (1, " body length mismatch: expected %d, got %d\n",
515 (int)expected_response->length, (int)body->len);
517 } else if (memcmp (body->str, expected_response->data, body->len) != 0) {
518 debug_printf (1, " body data mismatch\n");
523 if (!soup_socket_is_connected (socket)) {
524 debug_printf (1, " socket not still connected!\n");
528 if (soup_socket_is_connected (socket)) {
529 debug_printf (1, " socket still connected!\n");
533 g_object_unref (socket);
535 g_string_free (body, TRUE);
539 do_sync_test (const char *uri_string)
541 SoupSession *session;
542 SoupRequester *requester;
543 SoupRequest *request;
546 debug_printf (1, "Sync streaming\n");
548 session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
549 requester = soup_requester_new ();
550 soup_session_add_feature (session, SOUP_SESSION_FEATURE (requester));
551 g_object_unref (requester);
553 uri = soup_uri_new (uri_string);
555 debug_printf (1, " basic test\n");
556 request = soup_requester_request_uri (requester, uri, NULL);
557 do_sync_request (session, request,
558 SOUP_STATUS_OK, response,
560 g_object_unref (request);
562 debug_printf (1, " chunked test\n");
563 soup_uri_set_path (uri, "/chunked");
564 request = soup_requester_request_uri (requester, uri, NULL);
565 do_sync_request (session, request,
566 SOUP_STATUS_OK, response,
568 g_object_unref (request);
570 debug_printf (1, " auth test\n");
571 soup_uri_set_path (uri, "/auth");
572 request = soup_requester_request_uri (requester, uri, NULL);
573 do_sync_request (session, request,
574 SOUP_STATUS_UNAUTHORIZED, auth_response,
576 g_object_unref (request);
578 debug_printf (1, " non-persistent test\n");
579 soup_uri_set_path (uri, "/non-persistent");
580 request = soup_requester_request_uri (requester, uri, NULL);
581 do_sync_request (session, request,
582 SOUP_STATUS_OK, response,
584 g_object_unref (request);
586 debug_printf (1, " cancel test\n");
587 soup_uri_set_path (uri, "/");
588 request = soup_requester_request_uri (requester, uri, NULL);
589 do_sync_request (session, request,
590 SOUP_STATUS_FORBIDDEN, NULL,
592 g_object_unref (request);
594 soup_test_session_abort_unref (session);
599 main (int argc, char **argv)
603 test_init (argc, argv, NULL);
606 server = soup_test_server_new (TRUE);
607 soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
609 uri = g_strdup_printf ("http://127.0.0.1:%u/foo", soup_server_get_port (server));
611 do_simple_test (uri);
612 do_thread_test (uri);
613 do_context_test (uri);
617 soup_buffer_free (response);
618 soup_buffer_free (auth_response);
619 soup_test_server_quit_unref (server);