| /* |
| ** Copyright 2003-2004 The Apache Software Foundation |
| ** |
| ** Licensed under the Apache License, Version 2.0 (the "License"); |
| ** you may not use this file except in compliance with the License. |
| ** You may obtain a copy of the License at |
| ** |
| ** http://www.apache.org/licenses/LICENSE-2.0 |
| ** |
| ** Unless required by applicable law or agreed to in writing, software |
| ** distributed under the License is distributed on an "AS IS" BASIS, |
| ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| ** See the License for the specific language governing permissions and |
| ** limitations under the License. |
| */ |
| |
| #include "apreq_env.h" |
| #include "apreq.h" |
| #include "apreq_params.h" |
| #include "apr_strings.h" |
| #include "apr_xml.h" |
| #include "at.h" |
| |
| #define CRLF "\015\012" |
| |
| static char url_data[] = "alpha=one&beta=two;omega=last%2"; |
| static char form_data[] = |
| "--AaB03x" CRLF /* 10 chars |
| 012345678901234567890123456789012345678901234567890123456789 */ |
| "content-disposition: form-data; name=\"field1\"" CRLF /* 47 chars */ |
| "content-type: text/plain;charset=windows-1250" CRLF |
| "content-transfer-encoding: quoted-printable" CRLF CRLF |
| "Joe owes =80100." CRLF |
| "--AaB03x" CRLF |
| "content-disposition: form-data; name=\"pics\"; filename=\"file1.txt\"" CRLF |
| "Content-Type: text/plain" CRLF CRLF |
| "... contents of file1.txt ..." CRLF CRLF |
| "--AaB03x--" CRLF; |
| |
| static char xml_data[] = |
| "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>" |
| "<methodCall>" |
| " <methodName>foo.bar</methodName>" |
| " <params>" |
| " <param><value><int>1</int></value></param>" |
| " </params>" |
| "</methodCall>"; |
| |
| static char rel_data[] = /*offsets: 122, 522, */ |
| "--f93dcbA3" CRLF |
| "Content-Type: application/xml; charset=UTF-8" CRLF |
| "Content-Length: 400" CRLF |
| "Content-ID: <980119.X53GGT@example.com>" CRLF CRLF /*122*/ |
| "<?xml version=\"1.0\"?>" CRLF |
| "<uploadDocument>" |
| " <title>My Proposal</title>" |
| " <author>E. X. Ample</author>" |
| " <summary>A proposal for a new project.</summary>" |
| " <notes image=\"cid:980119.X17AXM@example.com\">(see handwritten region)</notes>" |
| " <keywords>project proposal funding</keywords>" |
| " <readonly>false</readonly>" |
| " <filename>image.png</filename>" |
| " <content>cid:980119.X25MNC@example.com</content>" |
| "</uploadDocument>" /*400*/ CRLF |
| "--f93dcbA3" CRLF /*14*/ |
| "Content-Type: image/png" CRLF |
| "Content-Transfer-Encoding: binary" CRLF |
| "Content-ID: <980119.X25MNC@example.com>" CRLF CRLF /*103*/ |
| "...Binary data here..." /*22*/ CRLF |
| "--f93dcbA3" CRLF /*14*/ |
| "Content-Type: image/png" CRLF |
| "Content-Transfer-Encoding: binary" CRLF |
| "Content-ID: <980119.X17AXM@example.com>" CRLF CRLF |
| "...Binary data here..." CRLF |
| "--f93dcbA3--" CRLF; |
| |
| static char mix_data[] = |
| "--AaB03x" CRLF |
| "Content-Disposition: form-data; name=\"submit-name\"" CRLF CRLF |
| "Larry" CRLF |
| "--AaB03x" CRLF |
| "Content-Disposition: form-data; name=\"files\"" CRLF |
| "Content-Type: multipart/mixed; boundary=BbC04y" CRLF CRLF |
| "--BbC04y" CRLF |
| "Content-Disposition: file; filename=\"file1.txt\"" CRLF |
| "Content-Type: text/plain" CRLF CRLF |
| "... contents of file1.txt ..." CRLF |
| "--BbC04y" CRLF |
| "Content-Disposition: file; filename=\"file2.gif\"" CRLF |
| "Content-Type: image/gif" CRLF |
| "Content-Transfer-Encoding: binary" CRLF CRLF |
| "...contents of file2.gif..." CRLF |
| "--BbC04y--" CRLF |
| "--AaB03x" CRLF |
| "content-disposition: form-data; name=\"field1\"" CRLF |
| "content-type: text/plain;charset=windows-1250" CRLF |
| "content-transfer-encoding: quoted-printable" CRLF CRLF |
| "Joe owes =80100." CRLF |
| "--AaB03x--" CRLF; |
| |
| |
| extern apr_bucket_brigade *bb; |
| extern apr_table_t *table; |
| extern apr_pool_t *p; |
| |
| static void locate_default_parsers(dAT) |
| { |
| apreq_request_t *req; |
| apreq_parser_t *parser; |
| |
| req = apreq_request("application/x-www-form-urlencoded", ""); |
| AT_not_null(req); |
| AT_str_eq(apreq_enctype(req->env), APREQ_URL_ENCTYPE); |
| parser = apreq_parser(req->env, NULL); |
| AT_not_null(parser); |
| AT_str_eq(parser->enctype, APREQ_URL_ENCTYPE); |
| |
| req = apreq_request(APREQ_MFD_ENCTYPE |
| "; charset=\"iso-8859-1\"; boundary=\"AaB03x\"" ,""); |
| AT_not_null(req); |
| parser = apreq_parser(req->env, NULL); |
| AT_not_null(parser); |
| AT_mem_eq(APREQ_MFD_ENCTYPE, parser->enctype, sizeof(APREQ_MFD_ENCTYPE)-1); |
| |
| req = apreq_request("multipart/related; boundary=f93dcbA3; " |
| "type=application/xml; start=\"<980119.X53GGT@example.com>\"",""); |
| AT_not_null(req); |
| parser = apreq_parser(req->env, NULL); |
| AT_not_null(parser); |
| AT_mem_eq("multipart/related", parser->enctype, sizeof("multipart/related")-1); |
| } |
| |
| |
| static void parse_urlencoded(dAT) |
| { |
| const char *val; |
| apreq_request_t *req = apreq_request(APREQ_URL_ENCTYPE,""); |
| apr_status_t rv; |
| const char *enctype; |
| AT_not_null(req); |
| |
| enctype = apreq_enctype(req->env); |
| AT_str_eq(APREQ_URL_ENCTYPE, enctype); |
| |
| bb = apr_brigade_create(p, apr_bucket_alloc_create(p)); |
| |
| APR_BRIGADE_INSERT_HEAD(bb, |
| apr_bucket_immortal_create(url_data,strlen(url_data), |
| bb->bucket_alloc)); |
| |
| rv = apreq_parse_request(req,bb); |
| AT_int_eq(APR_INCOMPLETE, rv); |
| |
| APR_BRIGADE_INSERT_HEAD(bb, |
| apr_bucket_immortal_create("blast",5, |
| bb->bucket_alloc)); |
| APR_BRIGADE_INSERT_TAIL(bb, |
| apr_bucket_eos_create(bb->bucket_alloc)); |
| |
| rv = apreq_parse_request(req,bb); |
| |
| AT_int_eq(APR_SUCCESS, rv); |
| |
| val = apr_table_get(req->body,"alpha"); |
| |
| AT_str_eq("one", val); |
| val = apr_table_get(req->body,"beta"); |
| AT_str_eq("two", val); |
| val = apr_table_get(req->body,"omega"); |
| AT_str_eq("last+last", val); |
| |
| } |
| |
| static void parse_multipart(dAT) |
| { |
| apr_status_t rv; |
| apr_size_t i, j; |
| apr_bucket_alloc_t *ba; |
| |
| for (j = 0; j <= strlen(form_data); ++j) { |
| const char *enctype; |
| apr_bucket_brigade *bb; |
| apreq_request_t *req = apreq_request(APREQ_MFD_ENCTYPE |
| "; charset=\"iso-8859-1\"; boundary=\"AaB03x\"" ,""); |
| |
| AT_not_null(req); |
| AT_str_eq(req->env, apreq_env_content_type(req->env)); |
| |
| enctype = apreq_enctype(req->env); |
| AT_str_eq(APREQ_MFD_ENCTYPE, enctype); |
| |
| ba = apr_bucket_alloc_create(p); |
| bb = apr_brigade_create(p, ba); |
| |
| /* AT_localize checks the inner loop tests itself |
| * (and interprets any such failures as being fatal), |
| * because doing IO to Test::Harness is just too slow |
| * when this many (~1M) tests are involved. |
| */ |
| |
| AT_localize(p); |
| |
| for (i = 0; i <= strlen(form_data); ++i) { |
| const char *val; |
| apr_size_t len; |
| apr_table_t *t; |
| |
| apr_bucket *e = apr_bucket_immortal_create(form_data, |
| strlen(form_data), |
| bb->bucket_alloc); |
| apr_bucket *f; |
| apr_bucket_brigade *tail; |
| APR_BRIGADE_INSERT_HEAD(bb, e); |
| APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_eos_create(bb->bucket_alloc)); |
| /* Split e into three buckets */ |
| apr_bucket_split(e, j); |
| f = APR_BUCKET_NEXT(e); |
| if (i < j) |
| apr_bucket_split(e, i); |
| else |
| apr_bucket_split(f, i - j); |
| |
| tail = apr_brigade_split(bb, f); |
| req->body = NULL; |
| req->parser = NULL; |
| req->body_status = APR_EINIT; |
| rv = apreq_parse_request(req,bb); |
| AT_int_eq(rv, (j < strlen(form_data)) ? APR_INCOMPLETE : APR_SUCCESS); |
| rv = apreq_parse_request(req, tail); |
| AT_int_eq(rv, APR_SUCCESS); |
| AT_not_null(req->body); |
| AT_int_eq(apr_table_elts(req->body)->nelts, 2); |
| |
| val = apr_table_get(req->body,"field1"); |
| |
| AT_str_eq("Joe owes =80100.", val); |
| t = apreq_value_to_param(apreq_strtoval(val))->info; |
| val = apr_table_get(t, "content-transfer-encoding"); |
| AT_str_eq("quoted-printable", val); |
| |
| val = apr_table_get(req->body, "pics"); |
| AT_str_eq("file1.txt", val); |
| t = apreq_value_to_param(apreq_strtoval(val))->info; |
| bb = apreq_value_to_param(apreq_strtoval(val))->bb; |
| apr_brigade_pflatten(bb, (char **)&val, &len, p); |
| AT_int_eq(strlen("... contents of file1.txt ..." CRLF), len); |
| AT_mem_eq("... contents of file1.txt ..." CRLF, val, len); |
| val = apr_table_get(t, "content-type"); |
| AT_str_eq("text/plain", val); |
| apr_brigade_cleanup(bb); |
| } |
| apr_bucket_alloc_destroy(ba); |
| apr_pool_clear(p); |
| } |
| } |
| |
| static void parse_disable_uploads(dAT) |
| { |
| const char *val; |
| apr_table_t *t; |
| apr_status_t rv; |
| apreq_request_t *req = apreq_request(APREQ_MFD_ENCTYPE |
| "; charset=\"iso-8859-1\"; boundary=\"AaB03x\"" ,""); |
| apr_bucket_brigade *bb = apr_brigade_create(p, |
| apr_bucket_alloc_create(p)); |
| apr_bucket *e = apr_bucket_immortal_create(form_data, |
| strlen(form_data), |
| bb->bucket_alloc); |
| |
| AT_not_null(req); |
| AT_str_eq(req->env, apreq_env_content_type(req->env)); |
| |
| APR_BRIGADE_INSERT_HEAD(bb, e); |
| APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_eos_create(bb->bucket_alloc)); |
| |
| req->body = NULL; |
| req->parser = apreq_parser(req->env, apreq_make_hook(p, |
| apreq_hook_disable_uploads, NULL, NULL)); |
| |
| rv = apreq_parse_request(req,bb); |
| AT_int_eq(APR_EGENERAL, rv); |
| AT_not_null(req->body); |
| AT_int_eq(apr_table_elts(req->body)->nelts, 1); |
| |
| val = apr_table_get(req->body,"field1"); |
| AT_str_eq("Joe owes =80100.", val); |
| t = apreq_value_to_param(apreq_strtoval(val))->info; |
| val = apr_table_get(t, "content-transfer-encoding"); |
| AT_str_eq("quoted-printable", val); |
| |
| val = apr_table_get(req->body, "pics"); |
| AT_is_null(val); |
| } |
| |
| static void parse_generic(dAT) |
| { |
| const char *val; |
| apr_size_t vlen; |
| apr_status_t rv; |
| apreq_param_t *dummy; |
| apreq_request_t *req = apreq_request(APREQ_XML_ENCTYPE, ""); |
| apr_bucket_brigade *bb = apr_brigade_create(p, |
| apr_bucket_alloc_create(p)); |
| apr_bucket *e = apr_bucket_immortal_create(xml_data, |
| strlen(xml_data), |
| bb->bucket_alloc); |
| |
| AT_not_null(req); |
| APR_BRIGADE_INSERT_HEAD(bb, e); |
| APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_eos_create(bb->bucket_alloc)); |
| |
| req->body = NULL; |
| req->parser = apreq_make_parser(p, APREQ_XML_ENCTYPE, |
| apreq_parse_generic, NULL, NULL); |
| rv = apreq_parse_request(req,bb); |
| AT_int_eq(APR_SUCCESS, rv); |
| dummy = *(apreq_param_t **)req->parser->ctx; |
| AT_not_null(dummy); |
| apr_brigade_pflatten(dummy->bb, (char **)&val, &vlen, p); |
| |
| AT_int_eq(strlen(xml_data), vlen); |
| AT_mem_eq(xml_data, val, vlen); |
| } |
| |
| static void hook_discard(dAT) |
| { |
| apr_status_t rv; |
| apreq_param_t *dummy; |
| apreq_request_t *req = apreq_request(APREQ_XML_ENCTYPE, ""); |
| apr_bucket_brigade *bb = apr_brigade_create(p, |
| apr_bucket_alloc_create(p)); |
| apr_bucket *e = apr_bucket_immortal_create(xml_data, |
| strlen(xml_data), |
| bb->bucket_alloc); |
| |
| AT_not_null(req); |
| APR_BRIGADE_INSERT_HEAD(bb, e); |
| APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_eos_create(bb->bucket_alloc)); |
| |
| req->body = NULL; |
| req->parser = apreq_make_parser(p, APREQ_XML_ENCTYPE, |
| apreq_parse_generic, |
| apreq_make_hook(p, apreq_hook_discard_brigade, |
| NULL,NULL), NULL); |
| rv = apreq_parse_request(req,bb); |
| AT_int_eq(rv, APR_SUCCESS); |
| dummy = *(apreq_param_t **)req->parser->ctx; |
| AT_not_null(dummy); |
| AT_not_null(dummy->bb); |
| AT_ok(APR_BRIGADE_EMPTY(dummy->bb), "nothing in dummy brigade"); |
| |
| } |
| |
| |
| static void parse_related(dAT) |
| { |
| char ct[] = "multipart/related; boundary=f93dcbA3; " |
| "type=application/xml; start=\"<980119.X53GGT@example.com>\""; |
| char data[] = "...Binary data here..."; |
| int dlen = strlen(data); |
| const char *val; |
| apr_size_t vlen; |
| apr_status_t rv; |
| int ns_map = 0; |
| apr_xml_doc *doc; |
| apreq_hook_t *xml_hook; |
| apreq_param_t *param; |
| apreq_request_t *req = apreq_request(ct, ""); |
| apr_bucket_brigade *bb = apr_brigade_create(p, |
| apr_bucket_alloc_create(p)); |
| apr_bucket *e = apr_bucket_immortal_create(rel_data, |
| strlen(rel_data), |
| bb->bucket_alloc); |
| |
| AT_not_null(req); |
| APR_BRIGADE_INSERT_HEAD(bb, e); |
| APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_eos_create(bb->bucket_alloc)); |
| xml_hook = apreq_make_hook(p, apreq_hook_apr_xml_parser, NULL, NULL); |
| |
| req->body = NULL; |
| req->parser = apreq_make_parser(p, ct, |
| apreq_parse_multipart, xml_hook, NULL); |
| rv = apreq_parse_request(req,bb); |
| AT_int_eq(rv, APR_SUCCESS); |
| AT_not_null(req->body); |
| |
| param = apreq_param(req, "<980119.X53GGT@example.com>"); |
| AT_not_null(param); |
| AT_not_null(param->info); |
| val = apr_table_get(param->info, "Content-Length"); |
| AT_str_eq(val, "400"); |
| AT_not_null(param->bb); |
| apr_brigade_pflatten(param->bb, (char **)&val, &vlen, p); |
| AT_int_eq(vlen, 400); |
| AT_mem_eq(val, rel_data + 122, 400); |
| |
| doc = *(apr_xml_doc **)xml_hook->ctx; |
| apr_xml_to_text(p, doc->root, APR_XML_X2T_FULL, |
| doc->namespaces, &ns_map, &val, &vlen); |
| AT_int_eq(vlen, 400 - 22); |
| AT_mem_eq(val, rel_data + 122 + 23, 400 - 23); |
| |
| param = apreq_param(req, "<980119.X25MNC@example.com>"); |
| AT_not_null(param); |
| AT_not_null(param->bb); |
| apr_brigade_pflatten(param->bb, (char **)&val, &vlen, p); |
| AT_int_eq(dlen, vlen); |
| AT_mem_eq(data, val, vlen); |
| |
| param = apreq_param(req, "<980119.X17AXM@example.com>"); |
| AT_not_null(param); |
| AT_not_null(param->bb); |
| apr_brigade_pflatten(param->bb, (char **)&val, &vlen, p); |
| AT_int_eq(dlen, vlen); |
| AT_mem_eq(data, val, vlen); |
| } |
| |
| typedef struct { |
| const char *key; |
| const char *val; |
| } array_elt; |
| |
| |
| static void parse_mixed(dAT) |
| { |
| const char *val; |
| apr_size_t vlen; |
| apr_status_t rv; |
| apreq_param_t *param; |
| const apr_array_header_t *arr; |
| array_elt *elt; |
| apreq_request_t *req = apreq_request(APREQ_MFD_ENCTYPE |
| "; charset=\"iso-8859-1\"; boundary=\"AaB03x\"" ,""); |
| apr_bucket_brigade *bb = apr_brigade_create(p, |
| apr_bucket_alloc_create(p)); |
| apr_bucket *e = apr_bucket_immortal_create(mix_data, |
| strlen(mix_data), |
| bb->bucket_alloc); |
| |
| AT_not_null(req); |
| APR_BRIGADE_INSERT_HEAD(bb, e); |
| APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_eos_create(bb->bucket_alloc)); |
| |
| rv = apreq_parse_request(req,bb); |
| AT_int_eq(APR_SUCCESS, rv); |
| |
| param = apreq_param(req, "submit-name"); |
| AT_not_null(param); |
| AT_str_eq("Larry", param->v.data); |
| |
| val = apr_table_get(req->body,"field1"); |
| AT_str_eq("Joe owes =80100.", val); |
| |
| param = apreq_param(req, "files"); |
| AT_not_null(param); |
| AT_str_eq("file1.txt", param->v.data); |
| |
| AT_not_null(param->bb); |
| apr_brigade_pflatten(param->bb, (char **)&val, &vlen, p); |
| AT_int_eq(strlen("... contents of file1.txt ..."), vlen); |
| AT_mem_eq("... contents of file1.txt ...", val, vlen); |
| |
| arr = apr_table_elts(req->body); |
| AT_int_eq(4, arr->nelts); |
| |
| elt = (array_elt *)&arr->elts[2 * arr->elt_size]; |
| AT_str_eq("files", elt->key); |
| AT_str_eq("file2.gif", elt->val); |
| |
| param = apreq_value_to_param(apreq_strtoval(elt->val)); |
| AT_not_null(param->bb); |
| apr_brigade_pflatten(param->bb, (char **)&val, &vlen, p); |
| AT_int_eq(strlen("...contents of file2.gif..."), vlen); |
| AT_mem_eq("...contents of file2.gif...", val, vlen); |
| |
| } |
| |
| |
| #define dT(func, plan) {#func, func, plan} |
| |
| int main(int argc, char *argv[]) |
| { |
| extern const apreq_env_t test_module; |
| extern apr_pool_t *p; |
| apr_pool_t *test_pool; |
| int i, plan = 0; |
| dAT; |
| at_test_t test_list [] = { |
| dT(locate_default_parsers, 10), |
| dT(parse_urlencoded, 7), |
| dT(parse_multipart, 4 * sizeof form_data), |
| dT(parse_disable_uploads, 8), |
| dT(parse_generic, 5), |
| dT(hook_discard, 5), |
| dT(parse_related, 19), |
| dT(parse_mixed, 16) |
| }; |
| |
| apr_initialize(); |
| atexit(apr_terminate); |
| apreq_env_module(&test_module); |
| |
| apr_pool_create(&p, NULL); |
| apr_pool_create(&test_pool, NULL); |
| |
| AT = at_create(test_pool, 0, at_report_stdout_make(test_pool)); |
| // AT_trace_on(); |
| for (i = 0; i < sizeof(test_list) / sizeof(at_test_t); ++i) |
| plan += test_list[i].plan; |
| |
| AT_begin(plan); |
| |
| for (i = 0; i < sizeof(test_list) / sizeof(at_test_t); ++i) |
| AT_run(&test_list[i]); |
| |
| AT_end(); |
| |
| return 0; |
| } |
| |
| |