blob: 0da8f1c3369feb68d58b26e81033d457916ee606 [file] [log] [blame]
/*
** 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;
}