1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4
5 #include "wocky-test-stream.h"
6 #include "wocky-test-helper.h"
7
8 #include <string.h>
9
10 #include <wocky/wocky.h>
11
12 typedef enum
13 {
14 DOMAIN_NONE = 0,
15 DOMAIN_G_IO_ERROR
16 } HttpErrorDomain;
17
18 typedef struct
19 {
20 const gchar *path;
21 const gchar *reply;
22 HttpErrorDomain domain;
23 gint code;
24 const gchar *username;
25 const gchar *password;
26 } HttpTestCase;
27
28 typedef struct
29 {
30 GMainLoop *mainloop;
31 GMainLoop *thread_mainloop;
32 GCancellable *cancellable;
33 GCancellable *thread_cancellable;
34 GThread *thread;
35 GSocketListener *listener;
36 guint16 port;
37 const HttpTestCase *test_case;
38 } HttpTestData;
39
40 static HttpTestCase test_cases[] = {
41 { "/http-proxy/close-by-peer",
42 "", DOMAIN_G_IO_ERROR, G_IO_ERROR_PROXY_FAILED },
43 { "/http-proxy/bad-reply",
44 "BAD REPLY", DOMAIN_G_IO_ERROR, G_IO_ERROR_PROXY_FAILED },
45 { "/http-proxy/very-short-reply",
46 "HTTP/1.\r\n\r\n", DOMAIN_G_IO_ERROR, G_IO_ERROR_PROXY_FAILED },
47 { "/http-proxy/short-reply",
48 "HTTP/1.0\r\n\r\n", DOMAIN_G_IO_ERROR, G_IO_ERROR_PROXY_FAILED },
49 { "/http-proxy/http-404",
50 "HTTP/1.0 404 Not Found\r\n"
51 "Content-Type: text/html; charset=UTF-8\r\n"
52 "Content-Length: 27\r\n"
53 "\r\n"
54 "<html><h1>Hello</h1></html>",
55 DOMAIN_G_IO_ERROR, G_IO_ERROR_PROXY_FAILED },
56 { "/http-proxy/need-authentication",
57 "HTTP/1.0 407 Proxy Authentication Required\r\n"
58 "Content-Type: text/html; charset=UTF-8\r\n"
59 "Content-Length: 27\r\n"
60 "\r\n"
61 "<html><h1>Hello</h1></html>",
62 DOMAIN_G_IO_ERROR, G_IO_ERROR_PROXY_NEED_AUTH },
63 { "/http-proxy/success",
64 "HTTP/1.0 200 OK\r\n"
65 "\r\n",
66 DOMAIN_NONE, 0 },
67 { "/http-proxy/authentication-failed",
68 "HTTP/1.0 407 Proxy Authentication Required\r\n"
69 "Content-Type: text/html; charset=UTF-8\r\n"
70 "Content-Length: 27\r\n"
71 "\r\n"
72 "<html><h1>Hello</h1></html>",
73 DOMAIN_G_IO_ERROR, G_IO_ERROR_PROXY_AUTH_FAILED,
74 "username", "bad-password" },
75 { "/http-proxy/authenticated",
76 "HTTP/1.0 200 OK\r\n"
77 "\r\n",
78 DOMAIN_NONE, 0,
79 "Aladdin", "open sesame"},
80 };
81
82 static HttpTestData *
tearup(const HttpTestCase * test_case)83 tearup (const HttpTestCase *test_case)
84 {
85 HttpTestData *data;
86
87 data = g_new0 (HttpTestData, 1);
88
89 data->mainloop = g_main_loop_new (NULL, FALSE);
90
91 data->cancellable = g_cancellable_new ();
92 data->thread_cancellable = g_cancellable_new ();
93
94 data->listener = g_socket_listener_new ();
95 data->port = g_socket_listener_add_any_inet_port (data->listener, NULL, NULL);
96 g_assert_cmpuint (data->port, !=, 0);
97
98 data->test_case = test_case;
99
100 return data;
101 }
102
103 static void
run_in_thread(HttpTestData * data,GThreadFunc func)104 run_in_thread (HttpTestData *data,
105 GThreadFunc func)
106 {
107 data->thread = g_thread_new ("server_thread", func, data);
108 g_assert (data->thread != NULL);
109 }
110
111 static gboolean
tear_down_idle_cb(gpointer user_data)112 tear_down_idle_cb (gpointer user_data)
113 {
114 HttpTestData *data = user_data;
115 g_main_loop_quit (data->mainloop);
116 return FALSE;
117 }
118
119 static void
teardown(HttpTestData * data)120 teardown (HttpTestData *data)
121 {
122 if (!g_cancellable_is_cancelled (data->cancellable))
123 g_cancellable_cancel (data->cancellable);
124
125 if (!g_cancellable_is_cancelled (data->thread_cancellable))
126 g_cancellable_cancel (data->thread_cancellable);
127
128 if (data->thread)
129 g_thread_join (data->thread);
130
131 if (g_main_loop_is_running (data->mainloop))
132 g_main_loop_quit (data->mainloop);
133
134 g_idle_add_full (G_PRIORITY_LOW, tear_down_idle_cb, data, NULL);
135
136 g_main_loop_run (data->mainloop);
137
138 g_object_unref (data->cancellable);
139 g_object_unref (data->thread_cancellable);
140 g_object_unref (data->listener);
141 g_main_loop_unref (data->mainloop);
142
143 g_free (data);
144 }
145
146 static gboolean
str_has_prefix_case(const gchar * str,const gchar * prefix)147 str_has_prefix_case (const gchar *str,
148 const gchar *prefix)
149 {
150 return g_ascii_strncasecmp (prefix, str, strlen (prefix)) == 0;
151 }
152
153 static void
test_http_proxy_instantiation(void)154 test_http_proxy_instantiation (void)
155 {
156 GProxy *proxy;
157
158 proxy = g_proxy_get_default_for_protocol ("http");
159 g_assert (G_IS_PROXY (proxy));
160 g_object_unref (proxy);
161 }
162
163 static gpointer
server_thread(gpointer user_data)164 server_thread (gpointer user_data)
165 {
166 HttpTestData *data = user_data;
167 GSocketConnection *conn;
168 GDataInputStream *data_in;
169 GOutputStream *out;
170 gchar *buffer;
171 gint has_host = 0;
172 gint has_user_agent = 0;
173 gint has_cred = 0;
174
175 conn = g_socket_listener_accept (data->listener, NULL,
176 data->thread_cancellable, NULL);
177 g_assert (conn != NULL);
178
179 data_in = g_data_input_stream_new (
180 g_io_stream_get_input_stream (G_IO_STREAM (conn)));
181 g_data_input_stream_set_newline_type (data_in,
182 G_DATA_STREAM_NEWLINE_TYPE_CR_LF);
183
184 buffer = g_data_input_stream_read_line (data_in, NULL,
185 data->thread_cancellable, NULL);
186 g_assert_cmpstr (buffer, ==, "CONNECT to:443 HTTP/1.0");
187
188 do {
189 g_free (buffer);
190 buffer = g_data_input_stream_read_line (data_in, NULL,
191 data->thread_cancellable, NULL);
192 g_assert (buffer != NULL);
193
194 if (str_has_prefix_case (buffer, "Host:"))
195 {
196 has_host++;
197 g_assert_cmpstr (buffer, ==, "Host: to:443");
198 }
199 else if (str_has_prefix_case (buffer, "User-Agent:"))
200 has_user_agent++;
201 else if (str_has_prefix_case (buffer, "Proxy-Authorization:"))
202 {
203 gchar *cred;
204 gchar *base64_cred;
205 const gchar *received_cred;
206
207 has_cred++;
208
209 g_assert (data->test_case->username != NULL);
210 g_assert (data->test_case->password != NULL);
211
212 cred = g_strdup_printf ("%s:%s",
213 data->test_case->username, data->test_case->password);
214 base64_cred = g_base64_encode ((guchar *) cred, strlen (cred));
215 g_free (cred);
216
217 received_cred = buffer + 20;
218 while (*received_cred == ' ')
219 received_cred++;
220
221 g_assert (str_has_prefix_case (received_cred, "Basic"));
222 received_cred += 5;
223 while (*received_cred == ' ')
224 received_cred++;
225
226 g_assert_cmpstr (base64_cred, ==, received_cred);
227 g_free (base64_cred);
228 }
229 } while (buffer[0] != '\0');
230
231 g_assert_cmpuint (has_host, ==, 1);
232 g_assert_cmpuint (has_user_agent, ==, 1);
233
234 if (data->test_case->username != NULL)
235 g_assert_cmpuint (has_cred, ==, 1);
236 else
237 g_assert_cmpuint (has_cred, ==, 0);
238
239 g_free (buffer);
240
241 out = g_io_stream_get_output_stream (G_IO_STREAM (conn));
242 g_assert (g_output_stream_write_all (out,
243 data->test_case->reply, strlen (data->test_case->reply),
244 NULL, data->thread_cancellable, NULL));
245 g_object_unref (data_in);
246 g_object_unref (conn);
247
248 return NULL;
249 }
250
251 static GQuark
get_error_domain(HttpErrorDomain id)252 get_error_domain (HttpErrorDomain id)
253 {
254 GQuark domain = 0;
255
256 switch (id)
257 {
258 case DOMAIN_G_IO_ERROR:
259 domain = G_IO_ERROR;
260 break;
261 default:
262 g_assert_not_reached ();
263 }
264
265 return domain;
266 }
267
268 static GSocketAddress *
create_proxy_address(HttpTestData * data)269 create_proxy_address (HttpTestData *data)
270 {
271 GSocketAddress *proxy_address;
272 GInetAddress *inet_address;
273
274 inet_address = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4);
275 proxy_address = g_proxy_address_new (inet_address, data->port, "http",
276 "to", 443, data->test_case->username, data->test_case->password);
277 g_object_unref (inet_address);
278
279 return proxy_address;
280 }
281
282 static void
check_result(const HttpTestCase * test_case,GSocketConnection * connection,GError * error)283 check_result (const HttpTestCase *test_case,
284 GSocketConnection *connection,
285 GError *error)
286 {
287 if (test_case->domain != DOMAIN_NONE)
288 {
289 g_assert_error (error, get_error_domain (test_case->domain),
290 test_case->code);
291 g_error_free (error);
292 }
293 else
294 {
295 g_assert_no_error (error);
296 g_object_unref (connection);
297 }
298 }
299
300 static void
test_http_proxy_with_data(gconstpointer user_data)301 test_http_proxy_with_data (gconstpointer user_data)
302 {
303 const HttpTestCase *test_case = user_data;
304 HttpTestData *data;
305 GSocketClient *client;
306 GSocketAddress *proxy_address;
307 GSocketConnection *connection;
308 GError *error = NULL;
309
310 data = tearup (test_case);
311
312 run_in_thread (data, server_thread);
313
314 client = g_socket_client_new ();
315 proxy_address = create_proxy_address (data);
316 connection = g_socket_client_connect (client,
317 G_SOCKET_CONNECTABLE (proxy_address), data->cancellable, &error);
318
319 g_object_unref (proxy_address);
320 g_object_unref (client);
321
322 check_result (test_case, connection, error);
323
324 teardown (data);
325 }
326
327 static void
connect_cb(GObject * source,GAsyncResult * result,gpointer user_data)328 connect_cb (GObject *source,
329 GAsyncResult *result,
330 gpointer user_data)
331 {
332 HttpTestData *data = user_data;
333 GSocketConnection *connection;
334 GError *error = NULL;
335
336 connection = g_socket_client_connect_finish (G_SOCKET_CLIENT (source),
337 result, &error);
338
339 check_result (data->test_case, connection, error);
340
341 g_main_loop_quit (data->mainloop);
342 }
343
344 static void
test_http_proxy_with_data_async(gconstpointer user_data)345 test_http_proxy_with_data_async (gconstpointer user_data)
346 {
347 const HttpTestCase *test_case = user_data;
348 HttpTestData *data;
349 GSocketClient *client;
350 GSocketAddress *proxy_address;
351
352 data = tearup (test_case);
353
354 run_in_thread (data, server_thread);
355
356 client = g_socket_client_new ();
357 proxy_address = create_proxy_address (data);
358 g_socket_client_connect_async (client, G_SOCKET_CONNECTABLE (proxy_address),
359 data->cancellable, connect_cb, data);
360
361 g_object_unref (client);
362 g_object_unref (proxy_address);
363
364 g_main_loop_run (data->mainloop);
365
366 teardown (data);
367 }
368
main(int argc,char ** argv)369 int main (int argc,
370 char **argv)
371 {
372 int result;
373 guint i;
374
375 test_init (argc, argv);
376
377 g_test_add_func ("/http-proxy/instantiation",
378 test_http_proxy_instantiation);
379
380 for (i = 0; i < G_N_ELEMENTS (test_cases); i++)
381 {
382 gchar *async_path;
383
384 g_test_add_data_func (test_cases[i].path,
385 test_cases + i, test_http_proxy_with_data);
386
387 async_path = g_strdup_printf ("%s-async", test_cases[i].path);
388 g_test_add_data_func (async_path,
389 test_cases + i, test_http_proxy_with_data_async);
390 g_free (async_path);
391 }
392
393 result = g_test_run ();
394 test_deinit ();
395 return result;
396 }
397