1 /*
2 * Copyright (C) 2012 Nokia.
3 *
4 * Author: Jens Georg <jensg@openismus.com>
5 *
6 * SPDX-License-Identifier: LGPL-2.1-or-later
7 */
8
9 #ifdef HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <string.h>
14
15 #include <libsoup/soup.h>
16 #include "libgupnp/gupnp.h"
17
18 static GUPnPContext *
create_context(guint16 port,GError ** error)19 create_context (guint16 port, GError **error) {
20 return GUPNP_CONTEXT (g_initable_new (GUPNP_TYPE_CONTEXT,
21 NULL,
22 error,
23 "host-ip", "127.0.0.1",
24 "msearch-port", port,
25 NULL));
26 }
27
28 static void
on_message_finished(G_GNUC_UNUSED SoupSession * session,G_GNUC_UNUSED SoupMessage * message,gpointer user_data)29 on_message_finished (G_GNUC_UNUSED SoupSession *session,
30 G_GNUC_UNUSED SoupMessage *message,
31 gpointer user_data) {
32 GMainLoop *loop = (GMainLoop*) user_data;
33
34 g_main_loop_quit (loop);
35 }
36
37 static void
request_range_and_compare(GMappedFile * file,SoupSession * session,GMainLoop * loop,const char * uri,goffset want_start,goffset want_end)38 request_range_and_compare (GMappedFile *file,
39 SoupSession *session,
40 GMainLoop *loop,
41 const char *uri,
42 goffset want_start,
43 goffset want_end)
44 {
45 SoupMessage *message = NULL;
46 goffset want_length = 0, full_length = 0;
47 goffset got_start = 0, got_end = 0, got_length = 0;
48 int result = 0;
49
50 full_length = g_mapped_file_get_length (file);
51
52 message = soup_message_new ("GET", uri);
53 g_object_ref (message);
54
55 soup_message_headers_set_range (message->request_headers,
56 want_start,
57 want_end);
58
59 /* interpretation according to SoupRange documentation */
60 if (want_end == -1) {
61 if (want_start < 0) {
62 want_length = -want_start;
63 want_start = full_length + want_start;
64 want_end = want_start + want_length - 1;
65 }
66 else {
67 want_length = full_length - want_start;
68 want_end = full_length - 1;
69 }
70 } else
71 want_length = want_end - want_start + 1;
72
73
74 soup_session_queue_message (session,
75 message,
76 on_message_finished,
77 loop);
78
79 g_main_loop_run (loop);
80 g_assert_cmpint (message->status_code, ==, SOUP_STATUS_PARTIAL_CONTENT);
81 g_assert_cmpint (message->response_body->length, ==, want_length);
82 got_length = soup_message_headers_get_content_length
83 (message->response_headers);
84 g_assert_cmpint (got_length, ==, want_length);
85 soup_message_headers_get_content_range (message->response_headers,
86 &got_start,
87 &got_end,
88 &got_length);
89 g_assert_cmpint (got_start, ==, want_start);
90 g_assert_cmpint (got_end, ==, want_end);
91 result = memcmp (g_mapped_file_get_contents (file) + want_start,
92 message->response_body->data,
93 want_length);
94 g_assert_cmpint (result, ==, 0);
95
96 g_object_unref (message);
97
98 message = soup_message_new ("GET", uri);
99 g_object_ref (message);
100 }
101
102 static void
test_gupnp_context_http_ranged_requests(void)103 test_gupnp_context_http_ranged_requests (void)
104 {
105 GUPnPContext *context = NULL;
106 GError *error = NULL;
107 SoupSession *session = NULL;
108 SoupMessage *message = NULL;
109 guint port = 0;
110 char *uri = NULL;
111 GMainLoop *loop;
112 GMappedFile *file;
113 goffset file_length = 0;
114
115 loop = g_main_loop_new (NULL, FALSE);
116 g_assert (loop != NULL);
117
118 file = g_mapped_file_new (DATA_PATH "/random4k.bin",
119 FALSE,
120 &error);
121 g_assert (file != NULL);
122 g_assert (error == NULL);
123 file_length = g_mapped_file_get_length (file);
124
125 context = create_context (0, &error);
126 g_assert (context != NULL);
127 g_assert (error == NULL);
128 port = gupnp_context_get_port (context);
129
130 gupnp_context_host_path (context,
131 DATA_PATH "/random4k.bin",
132 "/random4k.bin");
133
134 uri = g_strdup_printf ("http://127.0.0.1:%u/random4k.bin", port);
135 g_assert (uri != NULL);
136
137 session = soup_session_new ();
138
139 /* Corner cases: First byte */
140 request_range_and_compare (file, session, loop, uri, 0, 0);
141
142 /* Corner cases: Last byte */
143 request_range_and_compare (file,
144 session,
145 loop,
146 uri,
147 file_length - 1,
148 file_length - 1);
149
150 /* Examples from http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html */
151 /* Request first 500 bytes */
152 request_range_and_compare (file, session, loop, uri, 0, 499);
153
154 /* Request second 500 bytes */
155 request_range_and_compare (file, session, loop, uri, 500, 999);
156
157 /* Request everything but the first 500 bytes */
158 request_range_and_compare (file, session, loop, uri, 500, file_length - 1);
159
160 /* Request the last 500 bytes */
161 request_range_and_compare (file, session, loop, uri, file_length - 500, file_length - 1);
162
163 /* Request the last 500 bytes by using negative requests: Range:
164 * bytes: -500 */
165 request_range_and_compare (file, session, loop, uri, -500, -1);
166
167 /* Request the last 1k bytes by using negative requests: Range:
168 * bytes: 3072- */
169 request_range_and_compare (file, session, loop, uri, 3072, -1);
170
171 /* Try to get 1 byte after the end of the file */
172 message = soup_message_new ("GET", uri);
173 g_object_ref (message);
174
175 soup_message_headers_set_range (message->request_headers,
176 file_length,
177 file_length);
178 soup_session_queue_message (session,
179 message,
180 on_message_finished,
181 loop);
182
183 g_main_loop_run (loop);
184 g_assert_cmpint (message->status_code, ==, SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE);
185
186 g_object_unref (message);
187
188 g_free (uri);
189 g_object_unref (context);
190 g_main_loop_unref (loop);
191 g_mapped_file_unref (file);
192 }
193
main(int argc,char * argv[])194 int main (int argc, char *argv[]) {
195 g_test_init (&argc, &argv, NULL);
196 g_test_add_func ("/context/http/ranged-requests",
197 test_gupnp_context_http_ranged_requests);
198
199 g_test_run ();
200
201 return 0;
202 }
203