1 /*
2    Tests for high-level HTTP interface (ne_basic.h)
3    Copyright (C) 2002-2008, Joe Orton <joe@manyfish.co.uk>
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 
19 */
20 
21 #include "config.h"
22 
23 #include <sys/types.h>
24 
25 #ifdef HAVE_STDLIB_H
26 #include <stdlib.h>
27 #endif
28 #ifdef HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
31 
32 #include <fcntl.h>
33 
34 #include "ne_basic.h"
35 
36 #include "tests.h"
37 #include "child.h"
38 #include "utils.h"
39 
content_type(void)40 static int content_type(void)
41 {
42     int n;
43     static const struct {
44 	const char *value, *type, *subtype, *charset;
45     } ctypes[] = {
46 	{ "foo/bar", "foo", "bar", NULL },
47 	{ "foo/bar  ", "foo", "bar", NULL },
48 	{ "application/xml", "application", "xml", NULL },
49 	/* text/ subtypes default to charset ISO-8859-1, per 2616. */
50 	{ "text/lemon", "text", "lemon", "ISO-8859-1" },
51         /* text/xml defaults to charset us-ascii, per 3280 */
52         { "text/xml", "text", "xml", "us-ascii" },
53 #undef TXU
54 #define TXU "text", "xml", "utf-8"
55 	/* 2616 doesn't *say* that charset can be quoted, but bets are
56 	 * that some servers do it anyway. */
57 	{ "text/xml; charset=utf-8", TXU },
58 	{ "text/xml; charset=utf-8; foo=bar", TXU },
59 	{ "text/xml;charset=utf-8", TXU },
60 	{ "text/xml ;charset=utf-8", TXU },
61 	{ "text/xml;charset=utf-8;foo=bar", TXU },
62 	{ "text/xml; foo=bar; charset=utf-8", TXU },
63 	{ "text/xml; foo=bar; charset=utf-8; bar=foo", TXU },
64 	{ "text/xml; charset=\"utf-8\"", TXU },
65 	{ "text/xml; charset='utf-8'", TXU },
66 	{ "text/xml; foo=bar; charset=\"utf-8\"; bar=foo", TXU },
67 #undef TXU
68 	/* badly quoted charset should come out as NULL */
69 	{ "foo/lemon; charset=\"utf-8", "foo", "lemon", NULL },
70 	{ NULL }
71     };
72 
73     for (n = 0; ctypes[n].value != NULL; n++) {
74         ne_content_type ct;
75         ne_session *sess;
76         ne_request *req;
77         char resp[200];
78         int rv;
79 
80         ct.type = ct.subtype = ct.charset = ct.value = "unset";
81 
82         ne_snprintf(resp, sizeof resp,
83                     "HTTP/1.0 200 OK\r\n" "Content-Length: 0\r\n"
84                     "Content-Type: %s\r\n" "\r\n", ctypes[n].value);
85 
86         CALL(make_session(&sess, single_serve_string, resp));
87 
88         req = ne_request_create(sess, "GET", "/anyfoo");
89         ONREQ(ne_request_dispatch(req));
90         rv = ne_get_content_type(req, &ct);
91 
92         ONV(rv == 0 && !ctypes[n].type,
93             ("expected c-t parse failure for %s", ctypes[n].value));
94 
95         ONV(rv != 0 && ctypes[n].type,
96             ("c-t parse failure %d for %s", rv, ctypes[n].value));
97 
98         ne_request_destroy(req);
99         ne_session_destroy(sess);
100         CALL(await_server());
101 
102         if (rv) continue;
103 
104 	ONV(strcmp(ct.type, ctypes[n].type),
105 	    ("for `%s': type was `%s'", ctypes[n].value, ct.type));
106 
107 	ONV(strcmp(ct.subtype, ctypes[n].subtype),
108 	    ("for `%s': subtype was `%s'", ctypes[n].value, ct.subtype));
109 
110 	ONV(ctypes[n].charset && ct.charset == NULL,
111 	    ("for `%s': charset unset", ctypes[n].value));
112 
113 	ONV(ctypes[n].charset == NULL && ct.charset != NULL,
114 	    ("for `%s': unexpected charset `%s'", ctypes[n].value,
115 	     ct.charset));
116 
117 	ONV(ctypes[n].charset && ct.charset &&
118 	    strcmp(ctypes[n].charset, ct.charset),
119 	    ("for `%s': charset was `%s'", ctypes[n].value, ct.charset));
120 
121 	ne_free(ct.value);
122     }
123 
124     return OK;
125 }
126 
127 /* Do ranged GET for range 'start' to 'end'; with 'resp' as response.
128  * If 'fail' is non-NULL, expect ne_get_range to fail, and fail the
129  * test with given message if it doesn't. */
do_range(off_t start,off_t end,const char * fail,char * resp)130 static int do_range(off_t start, off_t end, const char *fail,
131 		    char *resp)
132 {
133     ne_session *sess;
134     ne_content_range range = {0};
135     int fd, ret;
136 
137     CALL(make_session(&sess, single_serve_string, resp));
138 
139     range.start = start;
140     range.end = end;
141 
142     fd = open("/dev/null", O_WRONLY);
143 
144     ret = ne_get_range(sess, "/foo", &range, fd);
145 
146     close(fd);
147     CALL(await_server());
148 
149     if (fail) {
150 #if 0
151 	t_warning("error was %s", ne_get_error(sess));
152 #endif
153 	ONV(ret == NE_OK, ("%s", fail));
154     } else {
155 	ONREQ(ret);
156     }
157 
158     ne_session_destroy(sess);
159     return OK;
160 }
161 
get_range(void)162 static int get_range(void)
163 {
164     return do_range(1, 10, NULL,
165 		    "HTTP/1.1 206 Widgets\r\n" "Connection: close\r\n"
166 		    "Content-Range: bytes 1-10/10\r\n"
167 		    "Content-Length: 10\r\n\r\nabcdefghij");
168 }
169 
get_eof_range(void)170 static int get_eof_range(void)
171 {
172     return do_range(1, -1, NULL,
173 		    "HTTP/1.1 206 Widgets\r\n" "Connection: close\r\n"
174 		    "Content-Range: bytes 1-10/10\r\n"
175 		    "Content-Length: 10\r\n\r\nabcdefghij");
176 }
177 
fail_range_length(void)178 static int fail_range_length(void)
179 {
180     return do_range(1, 10, "range response length mismatch should fail",
181 		    "HTTP/1.1 206 Widgets\r\n" "Connection: close\r\n"
182 		    "Content-Range: bytes 1-2/2\r\n"
183 		    "Content-Length: 2\r\n\r\nab");
184 }
185 
fail_range_units(void)186 static int fail_range_units(void)
187 {
188     return do_range(1, 2, "range response units check should fail",
189 		    "HTTP/1.1 206 Widgets\r\n" "Connection: close\r\n"
190 		    "Content-Range: fish 1-2/2\r\n"
191 		    "Content-Length: 2\r\n\r\nab");
192 }
193 
fail_range_notrange(void)194 static int fail_range_notrange(void)
195 {
196     return do_range(1, 2, "non-ranged response should fail",
197 		    "HTTP/1.1 200 Widgets\r\n" "Connection: close\r\n"
198 		    "Content-Range: bytes 1-2/2\r\n"
199 		    "Content-Length: 2\r\n\r\nab");
200 }
201 
fail_range_unsatify(void)202 static int fail_range_unsatify(void)
203 {
204     return do_range(1, 2, "unsatisfiable range should fail",
205 		    "HTTP/1.1 416 No Go\r\n" "Connection: close\r\n"
206 		    "Content-Length: 2\r\n\r\nab");
207 }
208 
dav_capabilities(void)209 static int dav_capabilities(void)
210 {
211     static const struct {
212 	const char *hdrs;
213 	unsigned int class1, class2, exec;
214     } caps[] = {
215 	{ "DAV: 1,2\r\n", 1, 1, 0 },
216 	{ "DAV: 1 2\r\n", 0, 0, 0 },
217 	/* these aren't strictly legal DAV: headers: */
218 	{ "DAV: 2,1\r\n", 1, 1, 0 },
219 	{ "DAV:  1, 2  \r\n", 1, 1, 0 },
220 	{ "DAV: 1\r\nDAV:2\r\n", 1, 1, 0 },
221 	{ NULL, 0, 0, 0 }
222     };
223     char resp[BUFSIZ];
224     int n;
225 
226     for (n = 0; caps[n].hdrs != NULL; n++) {
227 	ne_server_capabilities c = {0};
228 	ne_session *sess;
229 
230 	ne_snprintf(resp, BUFSIZ, "HTTP/1.0 200 OK\r\n"
231 		    "Connection: close\r\n"
232 		    "%s" "\r\n", caps[n].hdrs);
233 
234 	CALL(make_session(&sess, single_serve_string, resp));
235 
236 	ONREQ(ne_options(sess, "/foo", &c));
237 
238 	ONV(c.dav_class1 != caps[n].class1,
239 	    ("class1 was %d not %d", c.dav_class1, caps[n].class1));
240 	ONV(c.dav_class2 != caps[n].class2,
241 	    ("class2 was %d not %d", c.dav_class2, caps[n].class2));
242 	ONV(c.dav_executable != caps[n].exec,
243 	    ("class2 was %d not %d", c.dav_executable, caps[n].exec));
244 
245 	CALL(await_server());
246 
247         ne_session_destroy(sess);
248     }
249 
250     return OK;
251 }
252 
get(void)253 static int get(void)
254 {
255     ne_session *sess;
256     int fd;
257 
258     CALL(make_session(&sess, single_serve_string,
259                       "HTTP/1.0 200 OK\r\n"
260                       "Content-Length: 5\r\n"
261                       "\r\n"
262                       "abcde"));
263 
264     fd = open("/dev/null", O_WRONLY);
265     ONREQ(ne_get(sess, "/getit", fd));
266     close(fd);
267 
268     ne_session_destroy(sess);
269     CALL(await_server());
270 
271     return OK;
272 }
273 
274 ne_test tests[] = {
275     T(lookup_localhost),
276     T(content_type),
277     T(get_range),
278     T(get_eof_range),
279     T(fail_range_length),
280     T(fail_range_units),
281     T(fail_range_notrange),
282     T(fail_range_unsatify),
283     T(dav_capabilities),
284     T(get),
285     T(NULL)
286 };
287 
288