#define HTTPD_TEST_REQUIRE_APACHE 2 #if CONFIG_FOR_HTTPD_TEST SetHandler test-apr-uri #endif /* ==================================================================== * The Apache Software License, Version 1.1 * * Copyright (c) 2000-2004 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Apache" and "Apache Software Foundation" must * not be used to endorse or promote products derived from this * software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache", * nor may "Apache" appear in their name, without prior written * permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * * Portions of this software are based upon public domain software * originally written at the National Center for Supercomputing Applications, * University of Illinois, Urbana-Champaign. */ /* * This module is intended to test the apr_uri routines by parsing a * bunch of urls and comparing the results with what we expect to * see. * * Usage: * * * SetHandler test-apr-uri * * * Then make a request to /test-apr-uri. An html apr_table_t of errors will * be output... and a total count of errors. */ #include "httpd.h" #include "http_protocol.h" #include "http_config.h" #include "http_main.h" typedef struct { const char *scheme; const char *user; const char *password; const char *hostname; const char *port_str; const char *path; const char *query; const char *fragment; } test_uri_t; #define T_scheme 0x01 #define T_user 0x02 #define T_password 0x04 #define T_hostname 0x08 #define T_port_str 0x10 #define T_path 0x20 #define T_query 0x40 #define T_fragment 0x80 #define T_MAX 0x100 /* The idea is that we list here a bunch of url pieces that we want * stitched together in every way that's valid. */ static const test_uri_t uri_tests[] = { { "http", "userid", "passwd", "hostname.goes.here", "80", "/path/goes/here", "query-here", "frag-here" }, { "http", "", "passwd", "hostname.goes.here", "80", "/path/goes/here", "query-here", "frag-here" }, { "http", "userid", "", "hostname.goes.here", "80", "/path/goes/here", "query-here", "frag-here" }, { "http", "userid", "passwd", "", "80", "/path/goes/here", "query-here", "frag-here" }, { "http", "userid", "passwd", "hostname.goes.here", "", "/path/goes/here", "query-here", "frag-here" }, #if 0 /* An empty path means two different things depending on whether this is a * relative or an absolute uri... consider versus "GET * http://hostname HTTP/1.1". So this is why parse_uri_components returns * a NULL for path when it doesn't find one, instead of returning an empty * string. * * We don't really need to test it explicitly since path has no explicit * character that indicates its presence, and so we test empty paths all * the time by varying T_path in the loop. It would just cost us extra * code to special case the empty path string... */ { "http", "userid", "passwd", "hostname.goes.here", "80", "", "query-here", "frag-here" }, #endif { "http", "userid", "passwd", "hostname.goes.here", "80", "/path/goes/here", "", "frag-here" }, { "http", "userid", "passwd", "hostname.goes.here", "80", "/path/goes/here", "query-here", "" }, { "https", "user@d", "pa:swd", "hostname.goes.here.", "", "/~path/goes/here", "query&query?crud", "frag-here?baby" } }; static char *my_stpcpy(char *d, const char *s) { while((*d = *s)) { ++d; ++s; } return d; } /* return the number of failures */ static unsigned iterate_pieces(request_rec *r, const test_uri_t *pieces, int row) { unsigned u; apr_pool_t *sub; char *input_uri; char *strp; apr_uri_t result; unsigned expect; int status; unsigned failures; failures = 0; input_uri = apr_palloc(r->pool, strlen(pieces->scheme) + 3 + strlen(pieces->user) + 1 + strlen(pieces->password) + 1 + strlen(pieces->hostname) + 1 + strlen(pieces->port_str) + 1 + strlen(pieces->path) + + strlen(pieces->query) + 1 + strlen(pieces->fragment) + 1 + 1); for (u = 0; u < T_MAX; ++u) { strp = input_uri; expect = 0; /* a scheme requires a hostinfo and vice versa */ /* a hostinfo requires a hostname */ if (u & (T_scheme|T_user|T_password|T_hostname|T_port_str)) { expect |= T_scheme; strp = my_stpcpy(strp, pieces->scheme); *strp++ = ':'; *strp++ = '/'; *strp++ = '/'; /* can't have password without user */ if (u & (T_user|T_password)) { expect |= T_user; strp = my_stpcpy(strp, pieces->user); if (u & T_password) { expect |= T_password; *strp++ = ':'; strp = my_stpcpy(strp, pieces->password); } *strp++ = '@'; } expect |= T_hostname; strp = my_stpcpy(strp, pieces->hostname); if (u & T_port_str) { expect |= T_port_str; *strp++ = ':'; strp = my_stpcpy(strp, pieces->port_str); } } if (u & T_path) { expect |= T_path; strp = my_stpcpy(strp, pieces->path); } if (u & T_query) { expect |= T_query; *strp++ = '?'; strp = my_stpcpy(strp, pieces->query); } if (u & T_fragment) { expect |= T_fragment; *strp++ = '#'; strp = my_stpcpy(strp, pieces->fragment); } *strp = 0; apr_pool_create_ex(&sub, r->pool, NULL, NULL); status = apr_uri_parse(sub, input_uri, &result); if (status == APR_SUCCESS) { #define CHECK(f) \ if ((expect & T_##f) \ && (result.f == NULL || strcmp(result.f, pieces->f))) { \ status = HTTP_INTERNAL_SERVER_ERROR; \ } \ else if (!(expect & T_##f) && result.f != NULL) { \ status = HTTP_INTERNAL_SERVER_ERROR; \ } CHECK(scheme) CHECK(user) CHECK(password) CHECK(hostname) CHECK(port_str) CHECK(path) CHECK(query) CHECK(fragment) #undef CHECK } if (status != APR_SUCCESS) { ap_rprintf(r, "%d0x%02x0x%02x%d\"%s\"", row, u, expect, status, input_uri); #define DUMP(f) \ if (result.f) { \ ap_rvputs(r, "\"", result.f, "\"
", NULL); \ } \ else { \ ap_rputs("NULL
", r); \ } \ if (expect & T_##f) { \ ap_rvputs(r, "\"", pieces->f, "\"", NULL); \ } \ else { \ ap_rputs("NULL", r); \ } DUMP(scheme); DUMP(user); DUMP(password); DUMP(hostname); DUMP(port_str); DUMP(path); DUMP(query); DUMP(fragment); #undef DUMP ap_rputs("\n", r); ++failures; } apr_pool_destroy(sub); } return failures; } static int test_apr_uri_handler(request_rec *r) { unsigned total_failures; int i; r->allowed |= (AP_METHOD_BIT << M_GET); if (r->method_number != M_GET) return DECLINED; if (strcmp(r->handler, "test-apr-uri")) { return DECLINED; } r->content_type = "text/html"; ap_rputs( DOCTYPE_HTML_2_0 "\n\ \n\

Key:\n\

\n\
row\n\
entry number in the uri_tests array\n\
u\n\
fields under test\n\
expected\n\
fields expected in the result\n\
status\n\
response from parse_uri_components, or 500 if unexpected results\n\
input uri\n\
the uri given to parse_uri_components\n\
\n\

The remaining fields are the pieces returned from parse_uri_components, and\n\ the values we expected for each piece (resp.).\n\

Only failures are displayed.\n\

\n\ ", r); #define HEADER(f) ap_rprintf(r, "", T_##f) HEADER(scheme); HEADER(user); HEADER(password); HEADER(hostname); HEADER(port_str); HEADER(path); HEADER(query); HEADER(fragment); #undef HEADER if (r->args) { i = atoi(r->args); total_failures = iterate_pieces(r, &uri_tests[i], i); } else { total_failures = 0; for (i = 0; i < sizeof(uri_tests) / sizeof(uri_tests[0]); ++i) { total_failures += iterate_pieces(r, &uri_tests[i], i); if (total_failures > 256) { ap_rprintf(r, "
rowuexpectstatusinput uri" #f "
0x%02x
\nStopped early to save your browser " "from certain death!\nTOTAL FAILURES = %u\n", total_failures); return OK; } } } ap_rprintf(r, "\nTOTAL FAILURES = %u\n", total_failures); return OK; } static void test_apr_uri_register_hooks(apr_pool_t *p) { ap_hook_handler(test_apr_uri_handler, NULL, NULL, APR_HOOK_MIDDLE); } module AP_MODULE_DECLARE_DATA test_apr_uri_module = { STANDARD20_MODULE_STUFF, NULL, /* create per-dir config structures */ NULL, /* merge per-dir config structures */ NULL, /* create per-server config structures */ NULL, /* merge per-server config structures */ NULL, /* table of config file commands */ test_apr_uri_register_hooks /* register hooks */ };