blob: ce012e70e8d0985cbfa00d2168290113008915a1 [file] [log] [blame]
/*
** Licensed to the Apache Software Foundation (ASF) under one or more
** contributor license agreements. See the NOTICE file distributed with
** this work for additional information regarding copyright ownership.
** The ASF licenses this file to You 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_parser.h"
#include "apreq_util.h"
#include "apreq_error.h"
#include "apr_strings.h"
#include "apr_xml.h"
#include "at.h"
#define CRLF "\015\012"
static apr_pool_t *p;
static apr_pool_t *test_pool;
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;
/* This (invalid) case used to segfault the parser before r164254 so should
be tested separately to form_data:
static char form_data_fail[] =
"content-disposition: form-data; name=\"\"" CRLF
"content-type: text/plain;" CRLF " charset=windows-1250" CRLF
"content-transfer-encoding: quoted-printable" CRLF CRLF
"Joe owes =80100." 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--"; /* omit CRLF, which is ok per rfc 2046 */
#define URL_ENCTYPE "application/x-www-form-urlencoded"
#define MFD_ENCTYPE "multipart/form-data"
#define MR_ENCTYPE "multipart/related"
#define XML_ENCTYPE "application/xml"
static void locate_default_parsers(dAT, void *ctx)
{
#ifdef __ELF__
apreq_parser_function_t f;
AT_trace_on();
f = apreq_parser(URL_ENCTYPE);
AT_EQ(f, (apreq_parser_function_t)apreq_parse_urlencoded, "%pp");
f = apreq_parser(MFD_ENCTYPE);
AT_EQ(f, (apreq_parser_function_t)apreq_parse_multipart, "%pp");
f = apreq_parser(MR_ENCTYPE);
AT_EQ(f, (apreq_parser_function_t)apreq_parse_multipart, "%pp");
AT_trace_off();
#else
AT_skip(3, "skipping ELF-dependent tests");
#endif
}
static void parse_urlencoded(dAT, void *ctx)
{
apr_status_t rv;
apr_bucket_alloc_t *ba;
apr_bucket_brigade *bb;
apreq_parser_t *parser;
apr_table_t *body;
body = apr_table_make(test_pool, APREQ_DEFAULT_NELTS);
ba = apr_bucket_alloc_create(test_pool);
bb = apr_brigade_create(test_pool, ba);
parser = apreq_parser_make(test_pool, ba, URL_ENCTYPE, apreq_parse_urlencoded,
100, NULL, NULL, NULL);
APR_BRIGADE_INSERT_HEAD(bb,
apr_bucket_immortal_create(url_data,strlen(url_data),
bb->bucket_alloc));
rv = apreq_parser_run(parser, body, bb);
AT_int_eq(rv, APR_INCOMPLETE);
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_parser_run(parser, body, bb);
AT_int_eq(rv, APR_SUCCESS);
AT_str_eq(apr_table_get(body,"alpha"), "one");
AT_str_eq(apr_table_get(body,"beta"), "two");
AT_str_eq(apr_table_get(body,"omega"),"last+last");
apr_pool_clear(test_pool);
}
static void parse_multipart(dAT, void *ctx)
{
apr_size_t i, j;
apr_bucket_alloc_t *ba;
for (j = 0; j <= strlen(form_data); ++j) {
ba = apr_bucket_alloc_create(test_pool);
/* 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();
for (i = 0; i <= strlen(form_data); ++i) {
const char *val;
char *val2;
apr_size_t len;
apr_table_t *t, *body;
apreq_parser_t *parser;
apr_bucket_brigade *bb, *vb, *tail;
apr_status_t rv;
apr_bucket *e, *f;
bb = apr_brigade_create(test_pool, ba);
body = apr_table_make(test_pool, APREQ_DEFAULT_NELTS);
parser = apreq_parser_make(test_pool, ba, MFD_ENCTYPE
"; charset=\"iso-8859-1\""
"; boundary=\"AaB03x\"",
apreq_parse_multipart,
1000, NULL, NULL, NULL);
e = apr_bucket_immortal_create(form_data,
strlen(form_data),
bb->bucket_alloc);
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);
rv = apreq_parser_run(parser, body, bb);
AT_int_eq(rv, (j < strlen(form_data)) ? APR_INCOMPLETE : APR_SUCCESS);
rv = apreq_parser_run(parser, body, tail);
AT_int_eq(rv, APR_SUCCESS);
AT_int_eq(apr_table_elts(body)->nelts, 2);
val = apr_table_get(body,"field1");
AT_str_eq(val, "Joe owes =80100.");
t = apreq_value_to_param(val)->info;
val = apr_table_get(t, "content-transfer-encoding");
AT_str_eq(val, "quoted-printable");
val = apr_table_get(body, "pics");
AT_str_eq(val, "file1.txt");
t = apreq_value_to_param(val)->info;
vb = apreq_value_to_param(val)->upload;
apr_brigade_pflatten(vb, &val2, &len, test_pool);
AT_int_eq(len, strlen("... contents of file1.txt ..." CRLF));
AT_mem_eq(val2 ,"... contents of file1.txt ..." CRLF, len);
val = apr_table_get(t, "content-type");
AT_str_eq(val, "text/plain");
apr_brigade_cleanup(vb);
apr_brigade_cleanup(bb);
}
AT_delocalize();
apr_pool_clear(test_pool);
}
}
static void parse_disable_uploads(dAT, void *ctx)
{
const char *val;
apr_table_t *t, *body;
apr_status_t rv;
apr_bucket_alloc_t *ba;
apr_bucket_brigade *bb;
apr_bucket *e;
apreq_parser_t *parser;
apreq_hook_t *hook;
ba = apr_bucket_alloc_create(test_pool);
bb = apr_brigade_create(test_pool, ba);
e = apr_bucket_immortal_create(form_data, strlen(form_data), ba);
APR_BRIGADE_INSERT_HEAD(bb, e);
APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_eos_create(bb->bucket_alloc));
body = apr_table_make(test_pool, APREQ_DEFAULT_NELTS);
hook = apreq_hook_make(test_pool, apreq_hook_disable_uploads, NULL, NULL);
parser = apreq_parser_make(test_pool, ba, MFD_ENCTYPE
"; charset=\"iso-8859-1\""
"; boundary=\"AaB03x\"",
apreq_parse_multipart,
1000, NULL, hook, NULL);
rv = apreq_parser_run(parser, body, bb);
AT_int_eq(rv, APREQ_ERROR_GENERAL);
AT_int_eq(apr_table_elts(body)->nelts, 1);
val = apr_table_get(body,"field1");
AT_str_eq(val, "Joe owes =80100.");
t = apreq_value_to_param(val)->info;
val = apr_table_get(t, "content-transfer-encoding");
AT_str_eq(val, "quoted-printable");
val = apr_table_get(body, "pics");
AT_is_null(val);
apr_pool_clear(test_pool);
}
static void parse_generic(dAT, void *ctx)
{
char *val;
apr_size_t vlen;
apr_status_t rv;
apreq_param_t *dummy;
apreq_parser_t *parser;
apr_table_t *body;
apr_bucket_alloc_t *ba = apr_bucket_alloc_create(test_pool);
apr_bucket_brigade *bb = apr_brigade_create(test_pool, ba);
apr_bucket *e = apr_bucket_immortal_create(xml_data,
strlen(xml_data),
ba);
APR_BRIGADE_INSERT_HEAD(bb, e);
APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_eos_create(ba));
body = apr_table_make(test_pool, APREQ_DEFAULT_NELTS);
parser = apreq_parser_make(test_pool, ba, "application/xml",
apreq_parse_generic, 1000, NULL, NULL, NULL);
rv = apreq_parser_run(parser, body, bb);
AT_int_eq(rv, APR_SUCCESS);
dummy = *(apreq_param_t **)parser->ctx;
AT_not_null(dummy);
apr_brigade_pflatten(dummy->upload, &val, &vlen, test_pool);
AT_int_eq(vlen, strlen(xml_data));
AT_mem_eq(val, xml_data, vlen);
apr_pool_clear(test_pool);
}
static void hook_discard(dAT, void *ctx)
{
apr_status_t rv;
apreq_param_t *dummy;
apreq_parser_t *parser;
apreq_hook_t *hook;
apr_table_t *body;
apr_bucket_alloc_t *ba = apr_bucket_alloc_create(test_pool);
apr_bucket_brigade *bb = apr_brigade_create(test_pool, ba);
apr_bucket *e = apr_bucket_immortal_create(xml_data,
strlen(xml_data),
ba);
APR_BRIGADE_INSERT_HEAD(bb, e);
APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_eos_create(ba));
body = apr_table_make(test_pool, APREQ_DEFAULT_NELTS);
hook = apreq_hook_make(test_pool, apreq_hook_discard_brigade, NULL, NULL);
parser = apreq_parser_make(test_pool, ba, "application/xml",
apreq_parse_generic, 1000, NULL, hook, NULL);
rv = apreq_parser_run(parser, body, bb);
AT_int_eq(rv, APR_SUCCESS);
dummy = *(apreq_param_t **)parser->ctx;
AT_not_null(dummy);
AT_not_null(dummy->upload);
AT_ok(APR_BRIGADE_EMPTY(dummy->upload), "brigade has no contents");
apr_pool_clear(test_pool);
}
static void parse_related(dAT, void *ctx)
{
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;
char *val2;
apr_size_t vlen;
apr_status_t rv;
int ns_map = 0;
apr_xml_doc *doc;
apr_table_t *body;
apreq_parser_t *parser;
apreq_hook_t *xml_hook;
apreq_param_t *param;
apr_bucket_alloc_t *ba = apr_bucket_alloc_create(test_pool);
apr_bucket_brigade *bb = apr_brigade_create(test_pool, ba);
apr_bucket *e = apr_bucket_immortal_create(rel_data,
strlen(rel_data),
bb->bucket_alloc);
APR_BRIGADE_INSERT_HEAD(bb, e);
APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_eos_create(bb->bucket_alloc));
xml_hook = apreq_hook_make(test_pool, apreq_hook_apr_xml_parser, NULL, NULL);
body = apr_table_make(test_pool, APREQ_DEFAULT_NELTS);
parser = apreq_parser_make(test_pool, ba, ct, apreq_parse_multipart,
1000, NULL, xml_hook, NULL);
rv = apreq_parser_run(parser, body, bb);
AT_int_eq(rv, APR_SUCCESS);
val = apr_table_get(body, "<980119.X53GGT@example.com>");
AT_not_null(val);
param = apreq_value_to_param(val);
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->upload);
apr_brigade_pflatten(param->upload, &val2, &vlen, test_pool);
AT_int_eq(vlen, 400);
AT_mem_eq(val2, rel_data + 122, 400);
doc = *(apr_xml_doc **)xml_hook->ctx;
apr_xml_to_text(test_pool, 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);
val = apr_table_get(body, "<980119.X25MNC@example.com>");
AT_not_null(val);
param = apreq_value_to_param(val);
AT_not_null(param);
AT_not_null(param->upload);
apr_brigade_pflatten(param->upload, &val2, &vlen, test_pool);
AT_int_eq(vlen, dlen);
AT_mem_eq(val2, data, vlen);
val = apr_table_get(body, "<980119.X17AXM@example.com>");
AT_not_null(val);
param = apreq_value_to_param(val);
AT_not_null(param);
AT_not_null(param->upload);
apr_brigade_pflatten(param->upload, &val2, &vlen, test_pool);
AT_int_eq(vlen, dlen);
AT_mem_eq(val2, data, vlen);
apr_pool_clear(test_pool);
}
typedef struct {
const char *key;
const char *val;
} array_elt;
static void parse_mixed(dAT, void *ctx)
{
const char *val;
char *val2;
apr_size_t vlen;
apr_status_t rv;
apreq_param_t *param;
const apr_array_header_t *arr;
array_elt *elt;
char ct[] = MFD_ENCTYPE "; charset=\"iso-8859-1\"; boundary=\"AaB03x\"";
apreq_parser_t *parser;
apr_table_t *body = apr_table_make(test_pool, APREQ_DEFAULT_NELTS);
apr_bucket_alloc_t *ba = apr_bucket_alloc_create(test_pool);
apr_bucket_brigade *bb = apr_brigade_create(test_pool, ba);
apr_bucket *e = apr_bucket_immortal_create(mix_data,
strlen(mix_data),
bb->bucket_alloc);
APR_BRIGADE_INSERT_HEAD(bb, e);
APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_eos_create(bb->bucket_alloc));
parser = apreq_parser_make(test_pool, ba, ct, apreq_parse_multipart,
1000, NULL, NULL, NULL);
rv = apreq_parser_run(parser, body, bb);
AT_int_eq(rv, APR_SUCCESS);
val = apr_table_get(body, "submit-name");
AT_not_null(val);
AT_str_eq(val, "Larry");
val = apr_table_get(body,"field1");
AT_str_eq(val, "Joe owes =80100.");
val = apr_table_get(body, "files");
AT_not_null(val);
AT_str_eq(val, "file1.txt");
param = apreq_value_to_param(val);
AT_not_null(param->upload);
apr_brigade_pflatten(param->upload, &val2, &vlen, test_pool);
AT_int_eq(vlen, strlen("... contents of file1.txt ..."));
AT_mem_eq(val2, "... contents of file1.txt ...", vlen);
arr = apr_table_elts(body);
AT_int_eq(arr->nelts, 4);
elt = (array_elt *)&arr->elts[2 * arr->elt_size];
AT_str_eq(elt->key, "files");
AT_str_eq(elt->val, "file2.gif");
param = apreq_value_to_param(elt->val);
AT_not_null(param->upload);
apr_brigade_pflatten(param->upload, &val2, &vlen, test_pool);
AT_int_eq(vlen, strlen("...contents of file2.gif..."));
AT_mem_eq(val2, "...contents of file2.gif...", vlen);
apr_pool_clear(test_pool);
}
#define dT(func, plan) {#func, func, plan}
int main(int argc, char *argv[])
{
unsigned i, plan = 0;
dAT;
at_test_t test_list [] = {
dT(locate_default_parsers, 3),
dT(parse_urlencoded, 5),
dT(parse_multipart, sizeof form_data),
dT(parse_disable_uploads, 5),
dT(parse_generic, 4),
dT(hook_discard, 4),
dT(parse_related, 20),
dT(parse_mixed, 15)
};
apr_initialize();
atexit(apr_terminate);
apr_pool_create(&p, NULL);
apreq_initialize(p);
AT = at_create(0, at_report_stdout_make());
for (i = 0; i < sizeof(test_list) / sizeof(at_test_t); ++i)
plan += test_list[i].plan;
AT_begin(plan);
apr_pool_create(&test_pool, p);
for (i = 0; i < sizeof(test_list) / sizeof(at_test_t); ++i)
AT_run(&test_list[i]);
apr_pool_destroy(test_pool);
AT_end();
apr_pool_destroy(p);
return 0;
}