1 /*****************************************************************************
2  * message_test.c: HTTP request/response test
3  *****************************************************************************
4  * Copyright (C) 2015 Rémi Denis-Courmont
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19  *****************************************************************************/
20 
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24 
25 #undef NDEBUG
26 
27 #include <assert.h>
28 #include <errno.h>
29 #include <stdbool.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #include <vlc_common.h>
34 #include "message.h"
35 #include "h2frame.h"
36 
check_req(const struct vlc_http_msg * m)37 static void check_req(const struct vlc_http_msg *m)
38 {
39     const char *str;
40 
41     assert(vlc_http_msg_get_status(m) < 0);
42     str = vlc_http_msg_get_method(m);
43     assert(str != NULL && !strcmp(str, "GET"));
44     str = vlc_http_msg_get_scheme(m);
45     assert(str != NULL && !strcmp(str, "http"));
46     str = vlc_http_msg_get_authority(m);
47     assert(str != NULL && !strcmp(str, "www.example.com"));
48     str = vlc_http_msg_get_path(m);
49     assert(str != NULL && !strcmp(str, "/"));
50     assert(vlc_http_msg_get_size(m) == 0);
51 
52     str = vlc_http_msg_get_header(m, "Cache-Control");
53     assert(str != NULL && !strcmp(str, "no-cache"));
54     str = vlc_http_msg_get_header(m, "Custom-Key");
55     assert(str != NULL && !strcmp(str, "custom-value"));
56 
57     str = vlc_http_msg_get_header(m, "Date");
58     assert(str == NULL);
59 }
60 
check_resp(const struct vlc_http_msg * m)61 static void check_resp(const struct vlc_http_msg *m)
62 {
63     const char *str;
64 
65     assert(vlc_http_msg_get_status(m) == 200);
66     str = vlc_http_msg_get_method(m);
67     assert(str == NULL);
68     str = vlc_http_msg_get_scheme(m);
69     assert(str == NULL);
70     str = vlc_http_msg_get_authority(m);
71     assert(str == NULL);
72     str = vlc_http_msg_get_path(m);
73     assert(str == NULL);
74 
75     str = vlc_http_msg_get_header(m, "Cache-Control");
76     assert(str != NULL && !strcmp(str, "private"));
77     str = vlc_http_msg_get_header(m, "Date");
78     assert(str != NULL && !strcmp(str, "Mon, 21 Oct 2013 20:13:22 GMT"));
79     str = vlc_http_msg_get_header(m, "Location");
80     assert(str != NULL && !strcmp(str, "https://www.example.com"));
81     str = vlc_http_msg_get_header(m, "Content-Encoding");
82     assert(str != NULL && !strcmp(str, "gzip"));
83     str = vlc_http_msg_get_header(m, "Set-Cookie");
84     assert(str != NULL && !strcmp(str, "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; "
85                                   "max-age=3600; version=1"));
86 
87     str = vlc_http_msg_get_header(m, "Custom-Key");
88     assert(str == NULL);
89 }
90 
check_connect(const struct vlc_http_msg * m)91 static void check_connect(const struct vlc_http_msg *m)
92 {
93     const char *str;
94 
95     assert(vlc_http_msg_get_status(m) < 0);
96     str = vlc_http_msg_get_method(m);
97     assert(str != NULL && !strcmp(str, "CONNECT"));
98     str = vlc_http_msg_get_scheme(m);
99     assert(str == NULL);
100     str = vlc_http_msg_get_authority(m);
101     assert(str != NULL && !strcmp(str, "www.example.com"));
102     str = vlc_http_msg_get_path(m);
103     assert(str == NULL);
104 
105     str = vlc_http_msg_get_header(m, "Custom-Key");
106     assert(str == NULL);
107 }
108 
check_msg(struct vlc_http_msg * in,void (* cb)(const struct vlc_http_msg *))109 static void check_msg(struct vlc_http_msg *in,
110                       void (*cb)(const struct vlc_http_msg *))
111 {
112     struct vlc_http_msg *out;
113     char *m1;
114     size_t len;
115 
116     cb(in);
117 
118     m1 = vlc_http_msg_format(in, &len, false);
119     assert(m1 != NULL);
120     assert(strlen(m1) == len);
121     out = vlc_http_msg_headers(m1);
122     fprintf(stderr, "%s", m1);
123     free(m1);
124     /* XXX: request parsing not implemented/needed yet */
125     if (vlc_http_msg_get_status(in) >= 0)
126     {
127         assert(out != NULL);
128         cb(out);
129         vlc_http_msg_destroy(out);
130     }
131 
132     out = (struct vlc_http_msg *)vlc_http_msg_h2_frame(in, 1, true);
133     assert(out != NULL);
134     cb(out);
135     assert(vlc_http_msg_read(out) == NULL);
136     vlc_http_msg_destroy(out);
137 
138     cb(in);
139     vlc_http_msg_destroy(in);
140 }
141 
parse_date(const char * str)142 static time_t parse_date(const char *str)
143 {
144     struct vlc_http_msg *m;
145     time_t t1, t2;
146 
147     m = vlc_http_req_create("GET", "http", "www.example.com", "/");
148     assert(m != NULL);
149     assert(vlc_http_msg_add_header(m, "Date", "%s", str) == 0);
150     t1 = vlc_http_msg_get_atime(m);
151     assert(vlc_http_msg_add_header(m, "Last-Modified", "%s", str) == 0);
152     t2 = vlc_http_msg_get_mtime(m);
153     assert(vlc_http_msg_get_retry_after(m) == 0);
154     assert(vlc_http_msg_add_header(m, "Retry-After", "%s", str) == 0);
155     vlc_http_msg_get_retry_after(m);
156     vlc_http_msg_destroy(m);
157 
158     assert(t1 == t2);
159     return t1;
160 }
161 
check_realm(const char * line,const char * realm)162 static const char *check_realm(const char *line, const char *realm)
163 {
164     struct vlc_http_msg *m;
165     char *value;
166 
167     m = vlc_http_resp_create(401);
168     assert(m != NULL);
169     assert(vlc_http_msg_add_header(m, "WWW-Authenticate", "%s", line) == 0);
170     value = vlc_http_msg_get_basic_realm(m);
171     if (realm == NULL)
172         assert(value == NULL);
173     else
174     {
175         assert(value != NULL);
176         assert(!strcmp(value, realm));
177         free(value);
178     }
179     vlc_http_msg_destroy(m);
180     return realm;
181 }
182 
main(void)183 int main(void)
184 {
185     struct vlc_http_msg *m;
186     const char *str;
187     int ret;
188 
189     /* Formatting and parsing */
190     m = vlc_http_req_create("GET", "http", "www.example.com", "/");
191     assert(m != NULL);
192     ret = vlc_http_msg_add_header(m, "Cache-Control", "no-cache");
193     assert(ret == 0);
194     vlc_http_msg_add_header(m, "Custom-Key", "%s", "custom-value");
195     assert(ret == 0);
196     check_msg(m, check_req);
197 
198     m = vlc_http_resp_create(200);
199     assert(m != NULL);
200     ret = vlc_http_msg_add_header(m, "cache-control", "private");
201     assert(ret == 0);
202     ret = vlc_http_msg_add_header(m, "date", "Mon, 21 Oct 2013 20:13:22 GMT");
203     assert(ret == 0);
204     ret = vlc_http_msg_add_header(m, "location", "https://www.example.com");
205     assert(ret == 0);
206     ret = vlc_http_msg_add_header(m, "content-encoding", "gzip");
207     assert(ret == 0);
208     ret = vlc_http_msg_add_header(m, "set-cookie", "foo=%s; max-age=%u; "
209                                   "version=%u", "ASDJKHQKBZXOQWEOPIUAXQWEOIU",
210                                   3600, 1);
211     assert(ret == 0);
212     check_msg(m, check_resp);
213 
214     m = vlc_http_req_create("CONNECT", NULL, "www.example.com", NULL);
215     assert(m != NULL);
216     check_msg(m, check_connect);
217 
218     /* Helpers */
219     assert(parse_date("Sun, 06 Nov 1994 08:49:37 GMT") == 784111777);
220     assert(parse_date("Sunday, 06-Nov-94 08:49:37 GMT") == 784111777);
221     assert(parse_date("Sun Nov  6 08:49:37 1994") == 784111777);
222     assert(parse_date("Sunday, 06-Nov-14 08:49:37 GMT") == 1415263777);
223     assert(parse_date("Sun, 06 Bug 1994 08:49:37 GMT") == -1);
224     assert(parse_date("bogus") == -1);
225 
226     assert(check_realm("Basic realm=\"kingdom\"", "kingdom"));
227     assert(check_realm("BaSiC REALM= \"kingdom\"", "kingdom"));
228     assert(check_realm("basic Realm\t=\"kingdom\"", "kingdom"));
229     assert(check_realm("Basic charset=\"utf-8\", realm=\"kingdom\"",
230                        "kingdom"));
231     assert(check_realm("Basic realm=\"Realm is \\\"Hello world!\\\"\"",
232                        "Realm is \"Hello world!\""));
233     assert(check_realm("Basic", NULL) == NULL);
234 
235     m = vlc_http_req_create("PRI", "https", "*", NULL);
236     assert(m != NULL);
237 
238     assert(vlc_http_msg_add_agent(m, "Foo") == 0);
239     assert(vlc_http_msg_add_agent(m, "Foo/1.0") == 0);
240     assert(vlc_http_msg_add_agent(m, "Foo/1.0 (Hello world) Bar/2.3") == 0);
241     assert(vlc_http_msg_add_agent(m, "Foo/1.0 (compatible (\\(!))") == 0);
242 
243     assert(vlc_http_msg_add_atime(m) == 0);
244     time_t t = vlc_http_msg_get_atime(m);
245     assert(t != (time_t)-1);
246     assert(vlc_http_msg_add_time(m, "Last-Modified", &t) == 0);
247     assert(t == vlc_http_msg_get_mtime(m));
248 
249     assert(vlc_http_msg_add_creds_basic(m, false,
250                                         "Aladdin", "open sesame") == 0);
251     assert(vlc_http_msg_add_creds_basic(m, true, "test", "123£") == 0);
252     str = vlc_http_msg_get_header(m, "Authorization");
253     assert(str != NULL && !strcmp(str, "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="));
254     str = vlc_http_msg_get_header(m, "Proxy-Authorization");
255     assert(str != NULL && !strcmp(str, "Basic dGVzdDoxMjPCow=="));
256 
257     vlc_http_msg_add_header(m, "Content-Length", "1234");
258     assert(vlc_http_msg_get_size(m) == 1234);
259 
260     /* Folding */
261     vlc_http_msg_add_header(m, "X-Folded", "Hello\n\tworld!");
262     str = vlc_http_msg_get_header(m, "x-folded");
263     assert(str != NULL && !strcmp(str, "Hello \tworld!"));
264 
265     vlc_http_msg_add_header(m, "TE", "gzip");
266     vlc_http_msg_add_header(m, "TE", "deflate");
267     vlc_http_msg_add_header(m, "Pragma", " features=\"broadcast,playlist\"");
268     vlc_http_msg_add_header(m, "Pragma", " client-id=123456789 ");
269     vlc_http_msg_add_header(m, "Pragma", "evulz=\"foo \\\"\\ bar\"");
270     vlc_http_msg_add_header(m, "Pragma", "no-cache ");
271 
272     str = vlc_http_msg_get_header(m, "TE");
273     assert(str != NULL && !strcmp(str, "gzip, deflate"));
274     str = vlc_http_msg_get_token(m, "TE", "gzip");
275     assert(str != NULL && !strncmp(str, "gzip", 4));
276     str = vlc_http_msg_get_token(m, "TE", "deflate");
277     assert(str != NULL && !strncmp(str, "deflate", 7));
278     str = vlc_http_msg_get_token(m, "TE", "compress");
279     assert(str == NULL);
280     str = vlc_http_msg_get_token(m, "TE", "zip");
281     assert(str == NULL);
282     str = vlc_http_msg_get_token(m, "TE", "late");
283     assert(str == NULL);
284     str = vlc_http_msg_get_token(m, "Accept-Encoding", "gzip");
285     assert(str == NULL);
286     str = vlc_http_msg_get_token(m, "Pragma", "features");
287     assert(str != NULL && !strncmp(str, "features=\"", 10));
288     str = vlc_http_msg_get_token(m, "Pragma", "broadcast");
289     assert(str == NULL);
290     str = vlc_http_msg_get_token(m, "Pragma", "playlist");
291     assert(str == NULL);
292     str = vlc_http_msg_get_token(m, "Pragma", "client-id");
293     assert(str != NULL && !strncmp(str, "client-id=", 10));
294     str = vlc_http_msg_get_token(m, "Pragma", "123456789");
295     assert(str == NULL);
296     str = vlc_http_msg_get_token(m, "Pragma", "no-cache");
297     assert(str != NULL && !strcmp(str, "no-cache"));
298 
299     vlc_http_msg_add_header(m, "Cookie", "a=1");
300     vlc_http_msg_add_header(m, "Cookie", "b=2");
301     str = vlc_http_msg_get_header(m, "Cookie");
302     assert(str != NULL && !strcmp(str, "a=1; b=2"));
303 
304     /* Error cases */
305     assert(vlc_http_msg_add_header(m, "/naughty", "header") == -1);
306     assert(vlc_http_msg_add_header(m, "", "void") == -1);
307 
308     assert(vlc_http_msg_add_agent(m, "") != 0);
309     assert(vlc_http_msg_add_agent(m, "/1.0") != 0);
310     assert(vlc_http_msg_add_agent(m, "Bad/1.0\"") != 0);
311     assert(vlc_http_msg_add_agent(m, "Bad/1.0 (\\)") != 0);
312     assert(vlc_http_msg_add_agent(m, "Bad/1.0 (\\\x08)") != 0);
313     assert(vlc_http_msg_add_agent(m, "Bad/1.0 \"Evil\"") != 0);
314     assert(vlc_http_msg_add_agent(m, "(Hello world)") != 0);
315 
316     assert(vlc_http_msg_add_creds_basic(m, false, "User:name", "12345") != 0);
317     assert(vlc_http_msg_add_creds_basic(m, false, "Alice\nand\nbob", "") != 0);
318     assert(vlc_http_msg_add_creds_basic(m, false, "john", "asdfg\x7f") != 0);
319 
320     vlc_http_msg_destroy(m);
321 
322     const char *bad1[][2] = {
323         { ":status", "200" },
324         { ":status", "200" },
325         { "Server",  "BigBad/1.0" },
326     };
327 
328     m = vlc_http_msg_h2_headers(3, bad1);
329     assert(m == NULL);
330 
331     /* HTTP 1.x parser error cases */
332     assert(vlc_http_msg_headers("") == NULL);
333     assert(vlc_http_msg_headers("\r\n") == NULL);
334     assert(vlc_http_msg_headers("HTTP/1.1 200 OK") == NULL);
335     assert(vlc_http_msg_headers("HTTP/1.1 200 OK\r\nH: V\r\n") == NULL);
336     assert(vlc_http_msg_headers("HTTP/1.1 200 OK\r\n"
337                                 ":status: 200\r\n\r\n") == NULL);
338     assert(vlc_http_msg_headers("HTTP/1.1 200 OK\r\n"
339                                 "/naughty: invalid\r\n\r\n") == NULL);
340 
341     return 0;
342 }
343 
344 /* Callback for vlc_http_msg_h2_frame */
345 struct vlc_h2_frame *
vlc_h2_frame_headers(uint_fast32_t id,uint_fast32_t mtu,bool eos,unsigned count,const char * const tab[][2])346 vlc_h2_frame_headers(uint_fast32_t id, uint_fast32_t mtu, bool eos,
347                      unsigned count, const char *const tab[][2])
348 {
349     struct vlc_http_msg *m;
350 
351     assert(id == 1);
352     assert(mtu == VLC_H2_DEFAULT_MAX_FRAME);
353     assert(eos);
354 
355     const char *headers[VLC_H2_MAX_HEADERS][2];
356 
357     for (unsigned i = 0; i < count; i++)
358     {
359         headers[i][0] = tab[i][0];
360         headers[i][1] = tab[i][1];
361     }
362 
363     m = vlc_http_msg_h2_headers(count, headers);
364     return (struct vlc_h2_frame *)m; /* gruik */
365 }
366