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 40static 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. */ 130static 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 162static 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 170static 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 178static 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 186static 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 194static 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 202static 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 209static 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 253static 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 274ne_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