| /* ==================================================================== |
| * The Apache Software License, Version 1.1 |
| * |
| * Copyright (c) 2000-2001 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 |
| * <http://www.apache.org/>. |
| * |
| * 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 util_uri routines by parsing a |
| * bunch of urls and comparing the results with what we expect to |
| * see. |
| * |
| * Usage: |
| * |
| * <Location /test-util-uri> |
| * SetHandler test-util-uri |
| * </Location> |
| * |
| * Then make a request to /test-util-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 <a href="#frag"> 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 precense, 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; |
| uri_components 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; |
| |
| sub = apr_pool_sub_make(r->pool); |
| status = ap_parse_uri_components(sub, input_uri, &result); |
| if (status == HTTP_OK) { |
| #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 != HTTP_OK) { |
| ap_rprintf(r, "<tr><td>%d</td><td>0x%02x</td><td>0x%02x</td><td>%d</td><td>\"%s\"</td>", row, u, expect, status, input_uri); |
| #define DUMP(f) \ |
| if (result.f) { \ |
| ap_rvputs(r, "<td>\"", result.f, "\"<br>", NULL); \ |
| } \ |
| else { \ |
| ap_rputs("<td>NULL<br>", r); \ |
| } \ |
| if (expect & T_##f) { \ |
| ap_rvputs(r, "\"", pieces->f, "\"</td>", NULL); \ |
| } \ |
| else { \ |
| ap_rputs("NULL</td>", r); \ |
| } |
| DUMP(scheme); |
| DUMP(user); |
| DUMP(password); |
| DUMP(hostname); |
| DUMP(port_str); |
| DUMP(path); |
| DUMP(query); |
| DUMP(fragment); |
| #undef DUMP |
| ap_rputs("</tr>\n", r); |
| ++failures; |
| } |
| apr_pool_destroy(sub); |
| } |
| return failures; |
| } |
| |
| static int test_util_uri(request_rec *r) |
| { |
| unsigned total_failures; |
| int i; |
| |
| r->allowed |= (1 << M_GET); |
| if (r->method_number != M_GET) |
| return DECLINED; |
| |
| r->content_type = "text/html"; |
| if(r->header_only) { |
| return 0; |
| } |
| ap_hard_timeout("test_util_uri", r); |
| |
| ap_rputs( |
| DOCTYPE_HTML_2_0 " |
| <html><body> |
| <p>Key: |
| <dl> |
| <dt>row |
| <dd>entry number in the uri_tests array |
| <dt>u |
| <dd>fields under test |
| <dt>expected |
| <dd>fields expected in the result |
| <dt>status |
| <dd>response from parse_uri_components, or 500 if unexpected results |
| <dt>input uri |
| <dd>the uri given to parse_uri_components |
| </dl> |
| <p>The remaining fields are the pieces returned from parse_uri_components, and |
| the values we expected for each piece (resp.). |
| <p>Only failures are displayed. |
| <p> |
| <table><tr><th>row</th><th>u</th><th>expect</th><th>status</th><th>input uri</th>", r); |
| #define HEADER(f) ap_rprintf(r, "<th>" #f "<br>0x%02x</th>", 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, "</table>\n<b>Stopped early to save your browser " |
| "from certain death!</b>\nTOTAL FAILURES = %u\n", |
| total_failures); |
| return OK; |
| } |
| } |
| } |
| ap_rprintf(r, "</table>\nTOTAL FAILURES = %u\n", total_failures); |
| |
| return OK; |
| } |
| |
| static const handler_rec test_util_uri_handlers[] = |
| { |
| {"test-util-uri", test_util_uri}, |
| {NULL} |
| }; |
| |
| module test_util_uri_module = { |
| STANDARD_MODULE_STUFF, |
| NULL, /* initializer */ |
| NULL, /* dir config creater */ |
| NULL, /* dir merger --- default is to override */ |
| NULL, /* server config */ |
| NULL, /* merge server config */ |
| NULL, /* command apr_table_t */ |
| test_util_uri_handlers, /* handlers */ |
| NULL, /* filename translation */ |
| NULL, /* check_user_id */ |
| NULL, /* check auth */ |
| NULL, /* check access */ |
| NULL, /* type_checker */ |
| NULL, /* fixups */ |
| NULL, /* logger */ |
| NULL /* header parser */ |
| }; |