Git init
[profile/ivi/libsoup2.4.git] / tests / range-test.c
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4
5 #include <stdarg.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <unistd.h>
10
11 #include "libsoup/soup.h"
12
13 #include "test-utils.h"
14
15 SoupBuffer *full_response;
16 int total_length;
17 char *test_response;
18
19 static void
20 get_full_response (void)
21 {
22         char *contents;
23         gsize length;
24         GError *error = NULL;
25
26         if (!g_file_get_contents (SRCDIR "/index.txt", &contents, &length, &error)) {
27                 fprintf (stderr, "Could not read index.txt: %s\n",
28                          error->message);
29                 exit (1);
30         }
31
32         full_response = soup_buffer_new (SOUP_MEMORY_TAKE, contents, length);
33         debug_printf (1, "Total response length is %d\n\n", (int)length);
34 }
35
36 static void
37 check_part (SoupMessageHeaders *headers, const char *body, gsize body_len,
38             gboolean check_start_end, int expected_start, int expected_end)
39 {
40         goffset start, end, total_length;
41
42         debug_printf (1, "    Content-Range: %s\n",
43                       soup_message_headers_get_one (headers, "Content-Range"));
44
45         if (!soup_message_headers_get_content_range (headers, &start, &end, &total_length)) {
46                 debug_printf (1, "    Could not find/parse Content-Range\n");
47                 errors++;
48                 return;
49         }
50
51         if (total_length != full_response->length && total_length != -1) {
52                 debug_printf (1, "    Unexpected total length %" G_GINT64_FORMAT " in response\n",
53                               total_length);
54                 errors++;
55                 return;
56         }
57
58         if (check_start_end) {
59                 if ((expected_start >= 0 && start != expected_start) ||
60                     (expected_start < 0 && start != full_response->length + expected_start)) {
61                         debug_printf (1, "    Unexpected range start %" G_GINT64_FORMAT " in response\n",
62                                       start);
63                         errors++;
64                         return;
65                 }
66
67                 if ((expected_end >= 0 && end != expected_end) ||
68                     (expected_end < 0 && end != full_response->length - 1)) {
69                         debug_printf (1, "    Unexpected range end %" G_GINT64_FORMAT " in response\n",
70                                       end);
71                         errors++;
72                         return;
73                 }
74         }
75
76         if (end - start + 1 != body_len) {
77                 debug_printf (1, "    Range length (%d) does not match body length (%d)\n",
78                               (int)(end - start) + 1,
79                               (int)body_len);
80                 errors++;
81                 return;
82         }
83
84         memcpy (test_response + start, body, body_len);
85 }
86
87 static void
88 do_single_range (SoupSession *session, SoupMessage *msg,
89                  int start, int end)
90 {
91         const char *content_type;
92
93         debug_printf (1, "    Range: %s\n",
94                       soup_message_headers_get_one (msg->request_headers, "Range"));
95
96         soup_session_send_message (session, msg);
97
98         if (msg->status_code != SOUP_STATUS_PARTIAL_CONTENT) {
99                 debug_printf (1, "    Unexpected status %d %s\n",
100                               msg->status_code, msg->reason_phrase);
101                 g_object_unref (msg);
102                 errors++;
103                 return;
104         }
105
106         content_type = soup_message_headers_get_content_type (
107                 msg->response_headers, NULL);
108         if (content_type && !strcmp (content_type, "multipart/byteranges")) {
109                 debug_printf (1, "    Response body should not have been multipart/byteranges\n");
110                 g_object_unref (msg);
111                 errors++;
112                 return;
113         }
114
115         check_part (msg->response_headers, msg->response_body->data,
116                     msg->response_body->length, TRUE, start, end);
117         g_object_unref (msg);
118 }
119
120 static void
121 request_single_range (SoupSession *session, const char *uri,
122                       int start, int end)
123 {
124         SoupMessage *msg;
125
126         msg = soup_message_new ("GET", uri);
127         soup_message_headers_set_range (msg->request_headers, start, end);
128         do_single_range (session, msg, start, end);
129 }
130
131 static void
132 do_multi_range (SoupSession *session, SoupMessage *msg,
133                 int expected_return_ranges)
134 {
135         SoupMultipart *multipart;
136         const char *content_type;
137         int i, length;
138
139         debug_printf (1, "    Range: %s\n",
140                       soup_message_headers_get_one (msg->request_headers, "Range"));
141
142         soup_session_send_message (session, msg);
143
144         if (msg->status_code != SOUP_STATUS_PARTIAL_CONTENT) {
145                 debug_printf (1, "    Unexpected status %d %s\n",
146                               msg->status_code, msg->reason_phrase);
147                 g_object_unref (msg);
148                 errors++;
149                 return;
150         }
151
152         content_type = soup_message_headers_get_content_type (msg->response_headers, NULL);
153         if (!content_type || strcmp (content_type, "multipart/byteranges") != 0) {
154                 debug_printf (1, "    Response Content-Type (%s) was not multipart/byteranges\n",
155                               content_type);
156                 g_object_unref (msg);
157                 errors++;
158                 return;
159         }
160
161         multipart = soup_multipart_new_from_message (msg->response_headers,
162                                                      msg->response_body);
163         if (!multipart) {
164                 debug_printf (1, "    Could not parse multipart\n");
165                 g_object_unref (msg);
166                 errors++;
167                 return;
168         }
169
170         length = soup_multipart_get_length (multipart);
171         if (length != expected_return_ranges) {
172                 debug_printf (1, "    Expected %d ranges, got %d\n",
173                               expected_return_ranges, length);
174                 errors++;
175         }
176
177         for (i = 0; i < length; i++) {
178                 SoupMessageHeaders *headers;
179                 SoupBuffer *body;
180
181                 debug_printf (1, "  Part %d\n", i + 1);
182                 soup_multipart_get_part (multipart, i, &headers, &body);
183                 check_part (headers, body->data, body->length, FALSE, 0, 0);
184         }
185
186         soup_multipart_free (multipart);
187         g_object_unref (msg);
188 }
189
190 static void
191 request_double_range (SoupSession *session, const char *uri,
192                       int first_start, int first_end,
193                       int second_start, int second_end,
194                       int expected_return_ranges)
195 {
196         SoupMessage *msg;
197         SoupRange ranges[2];
198
199         msg = soup_message_new ("GET", uri);
200         ranges[0].start = first_start;
201         ranges[0].end = first_end;
202         ranges[1].start = second_start;
203         ranges[1].end = second_end;
204         soup_message_headers_set_ranges (msg->request_headers, ranges, 2);
205
206         if (expected_return_ranges == 1) {
207                 do_single_range (session, msg,
208                                  MIN (first_start, second_start),
209                                  MAX (first_end, second_end));
210         } else
211                 do_multi_range (session, msg, expected_return_ranges);
212 }
213
214 static void
215 request_triple_range (SoupSession *session, const char *uri,
216                       int first_start, int first_end,
217                       int second_start, int second_end,
218                       int third_start, int third_end,
219                       int expected_return_ranges)
220 {
221         SoupMessage *msg;
222         SoupRange ranges[3];
223
224         msg = soup_message_new ("GET", uri);
225         ranges[0].start = first_start;
226         ranges[0].end = first_end;
227         ranges[1].start = second_start;
228         ranges[1].end = second_end;
229         ranges[2].start = third_start;
230         ranges[2].end = third_end;
231         soup_message_headers_set_ranges (msg->request_headers, ranges, 3);
232
233         if (expected_return_ranges == 1) {
234                 do_single_range (session, msg,
235                                  MIN (first_start, MIN (second_start, third_start)),
236                                  MAX (first_end, MAX (second_end, third_end)));
237         } else
238                 do_multi_range (session, msg, expected_return_ranges);
239 }
240
241 static void
242 do_range_test (SoupSession *session, const char *uri, gboolean expect_coalesce)
243 {
244         int twelfths = full_response->length / 12;
245
246         memset (test_response, 0, full_response->length);
247
248         /* We divide the response into 12 ranges and request them
249          * as follows:
250          *
251          *  0: A (first single request)
252          *  1: D (2nd part of triple request)
253          *  2: C (1st part of double request)
254          *  3: D (1st part of triple request)
255          *  4: F (trickier overlapping request)
256          *  5: C (2nd part of double request)
257          *  6: D (3rd part of triple request)
258          *  7: E (overlapping request)
259          *  8: E (overlapping request)
260          *  9: F (trickier overlapping request)
261          * 10: F (trickier overlapping request)
262          * 11: B (second and third single requests)
263          */
264
265         /* A: 0, simple request */
266         debug_printf (1, "Requesting %d-%d\n", 0 * twelfths, 1 * twelfths);
267         request_single_range (session, uri,
268                               0 * twelfths, 1 * twelfths);
269
270         /* B: 11, end-relative request. These two are mostly redundant
271          * in terms of data coverage, but they may still catch
272          * Range-header-generating bugs.
273          */
274         debug_printf (1, "Requesting %d-\n", 11 * twelfths);
275         request_single_range (session, uri,
276                               11 * twelfths, -1);
277         debug_printf (1, "Requesting -%d\n", 1 * twelfths);
278         request_single_range (session, uri,
279                               -1 * twelfths, -1);
280
281         /* C: 2 and 5 */
282         debug_printf (1, "Requesting %d-%d,%d-%d\n",
283                       2 * twelfths, 3 * twelfths,
284                       5 * twelfths, 6 * twelfths);
285         request_double_range (session, uri,
286                               2 * twelfths, 3 * twelfths,
287                               5 * twelfths, 6 * twelfths,
288                               2);
289
290         /* D: 1, 3, 6 */
291         debug_printf (1, "Requesting %d-%d,%d-%d,%d-%d\n",
292                       3 * twelfths, 4 * twelfths,
293                       1 * twelfths, 2 * twelfths,
294                       6 * twelfths, 7 * twelfths);
295         request_triple_range (session, uri,
296                               3 * twelfths, 4 * twelfths,
297                               1 * twelfths, 2 * twelfths,
298                               6 * twelfths, 7 * twelfths,
299                               3);
300
301         /* E: 7 and 8: should coalesce into a single response */
302         debug_printf (1, "Requesting %d-%d,%d-%d (can coalesce)\n",
303                       7 * twelfths, 8 * twelfths,
304                       8 * twelfths, 9 * twelfths);
305         request_double_range (session, uri,
306                               7 * twelfths, 8 * twelfths,
307                               8 * twelfths, 9 * twelfths,
308                               expect_coalesce ? 1 : 2);
309
310         /* F: 4, 9, 10: 9 and 10 should coalesce even though 4 was
311          * requested between them. (Also, they actually overlap in
312          * this case, as opposed to just touching.)
313          */
314         debug_printf (1, "Requesting %d-%d,%d-%d,%d-%d (can partially coalesce)\n",
315                       9 * twelfths, 10 * twelfths + 5,
316                       4 * twelfths, 5 * twelfths,
317                       10 * twelfths - 5, 11 * twelfths);
318         request_triple_range (session, uri,
319                               9 * twelfths, 10 * twelfths + 5,
320                               4 * twelfths, 5 * twelfths,
321                               10 * twelfths - 5, 11 * twelfths,
322                               expect_coalesce ? 2 : 3);
323
324         if (memcmp (full_response->data, test_response, full_response->length) != 0) {
325                 debug_printf (1, "\nfull_response and test_response don't match\n");
326                 errors++;
327         }
328 }
329
330 static void
331 server_handler (SoupServer        *server,
332                 SoupMessage       *msg, 
333                 const char        *path,
334                 GHashTable        *query,
335                 SoupClientContext *client,
336                 gpointer           user_data)
337 {
338         soup_message_set_status (msg, SOUP_STATUS_OK);
339         soup_message_body_append_buffer (msg->response_body,
340                                          full_response);
341 }
342
343 int
344 main (int argc, char **argv)
345 {
346         SoupSession *session;
347         SoupServer *server;
348         char *base_uri;
349
350         test_init (argc, argv, NULL);
351         apache_init ();
352
353         get_full_response ();
354         test_response = g_malloc0 (full_response->length);
355
356         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
357
358         debug_printf (1, "1. Testing against apache\n");
359         do_range_test (session, "http://127.0.0.1:47524/", FALSE);
360
361         debug_printf (1, "\n2. Testing against SoupServer\n");
362         server = soup_test_server_new (FALSE);
363         soup_server_add_handler (server, NULL, server_handler, NULL, NULL);
364         base_uri = g_strdup_printf ("http://127.0.0.1:%u/",
365                                     soup_server_get_port (server));
366         do_range_test (session, base_uri, TRUE);
367         g_free (base_uri);
368         soup_test_server_quit_unref (server);
369
370         soup_test_session_abort_unref (session);
371
372         soup_buffer_free (full_response);
373         g_free (test_response);
374
375         test_cleanup ();
376         return errors != 0;
377 }