| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398 |
- /*
- Test cases for the ne_207.h interface.
- Copyright (C) 2023, Joe Orton <[email protected]>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
- #include "config.h"
- #include <sys/types.h>
- #ifdef HAVE_STDLIB_H
- #include <stdlib.h>
- #endif
- #ifdef HAVE_UNISTD_H
- #include <unistd.h>
- #endif
- #include "ne_207.h"
- #include "tests.h"
- #include "utils.h"
- #define PFX "<?xml version='1.0' encoding='utf-8'?>\r\n"
- #define RESP(status, rdesc) "<d:response>" \
- "<d:href>http://localhost/container/resource3</d:href>" \
- status rdesc \
- "</d:response>"
- #define MS_207_1(status, rdesc) PFX \
- "<d:multistatus xmlns:d=\"DAV:\">" \
- RESP(status, rdesc) \
- "</d:multistatus>"
- #define MS_207_2(s1, rd1, s2, rd2) PFX \
- "<d:multistatus xmlns:d=\"DAV:\">" \
- RESP(s1, rd1) \
- RESP(s2, rd2) \
- "</d:multistatus>"
- static int errors(void)
- {
- static const struct {
- int status;
- const char *ctype;
- const char *body;
- const char *expect;
- } ts[] = {
- { 207, "application/xml",
- MS_207_1("<d:status>HTTP/1.1 423 Locked</d:status>", ""),
- "423 Locked" },
- { 207, "application/xml",
- MS_207_1("<d:status>HTTP/1.1 423 Locked</d:status>",
- "<d:responsedescription>The thing was locked</d:responsedescription>"),
- "The thing was" },
- #if 0
- { 207, "application/xml",
- MS_207_1("<d:status>HTTP/1.1 423 Locked</d:status>",
- "<d:error><d:lock-token-submitted/></d:error>"),
- "Resource locked" },
- #endif
- { 207, "application/xml",
- MS_207_2("<d:status>HTTP/1.1 423 Locked</d:status>",
- "<d:responsedescription>The thing was locked</d:responsedescription>",
- "<d:status>HTTP/1.1 404 Gone</d:status>",
- "<d:responsedescription>No such thingy</d:responsedescription>"),
- "such thingy" }
- };
- unsigned n;
- for (n = 0; n < sizeof(ts)/sizeof(ts[0]); n++) {
- char resp[1024];
- ne_session *sess;
- ne_request *req;
- char *err;
- int ret;
- ne_snprintf(resp, sizeof resp,
- "HTTP/1.1 %d OK\r\n"
- "Content-Type: %s\r\n"
- "Connection: close\r\n" "\r\n"
- "%s", ts[n].status, ts[n].ctype, ts[n].body);
- CALL(make_session(&sess, single_serve_string, resp));
- req = ne_request_create(sess, "SIMPLE", "/");
- ret = ne_simple_request(sess, req);
- ONN("ne_simple_request didn't fail", ret == NE_OK);
- err = ne_strclean(ne_strdup(ne_get_error(sess)));
- ONV(strcmp(err, ne_get_error(sess)),
- ("error string wasn't cleaned: %s", ne_get_error(sess)));
- NE_DEBUG(NE_DBG_HTTP, "test: got error string: %s\n", err);
- ne_free(err);
- ONV(strstr(ne_get_error(sess), ts[n].expect) == NULL,
- ("error string didn't match: '%s' - expected '%s'",
- ne_get_error(sess), ts[n].expect));
- ne_session_destroy(sess);
- CALL(await_server());
- }
- return OK;
- }
- static int pstat_count;
- /* tos_*: set of 207 callbacks which serialize the data back into a
- * text stream, which can be easily checked for correctness. */
- static void *tos_startresp(void *buf, const ne_uri *uri)
- {
- ne_buffer_concat(buf, "start-resp[", uri->path, "];", NULL);
- pstat_count = 0;
- return ne_strdup(uri->path);
- }
- static void tos_status_descr(ne_buffer *buf, const ne_status *status,
- const char *description)
- {
- if (status) {
- char s[50];
- ne_snprintf(s, sizeof s, "-status={%d %s}", status->code,
- status->reason_phrase);
- ne_buffer_zappend(buf, s);
- }
- if (description)
- ne_buffer_concat(buf, "-descr={", description, "}", NULL);
- }
- static void tos_endresp(void *buf, void *response,
- const ne_status *status, const char *description)
- {
- char *href = response;
- ne_buffer_concat(buf, "end-resp[", href, "]", NULL);
- ne_free(href);
- tos_status_descr(buf, status, description);
- ne_buffer_zappend(buf, ";");
- }
- static void *tos_startpstat(void *buf, void *resphref)
- {
- char num[20], *href;
- sprintf(num, "-%d", ++pstat_count);
- href = ne_concat(resphref, num, NULL);
- ne_buffer_concat(buf, "start-pstat[", href, "];", NULL);
- return href;
- }
- static void tos_endpstat(void *buf, void *href,
- const ne_status *status, const char *description)
- {
- ne_buffer_concat(buf, "end-pstat[", href, "]", NULL);
- tos_status_descr(buf, status, description);
- ne_buffer_zappend(buf, ";");
- ne_free(href);
- }
- struct propctx {
- ne_207_parser *p207;
- ne_buffer *buf;
- };
- #define STATE_myprop (NE_207_STATE_TOP)
- static int tos_startprop(void *userdata, int parent,
- const char *nspace, const char *name,
- const char **atts)
- {
- if (parent == NE_207_STATE_PROP
- && strcmp(nspace, "DAV:") == 0
- && (strcmp(name, "propone") == 0 || strcmp(name, "proptwo") == 0)) {
- /* Handle this! */
- struct propctx *ctx = userdata;
- char *resphref = ne_207_get_current_response(ctx->p207);
- char *pstathref = ne_207_get_current_propstat(ctx->p207);
-
- ne_buffer_concat(ctx->buf, "start-prop[", resphref, ",", pstathref,
- ",", name, "];", NULL);
- return STATE_myprop;
- }
- else {
- return NE_XML_DECLINE;
- }
- }
- static int tos_cdata(void *userdata, int state,
- const char *cdata, size_t len)
- {
- struct propctx *ctx = userdata;
- ne_buffer_zappend(ctx->buf, "cdata-prop[");
- ne_buffer_append(ctx->buf, cdata, len);
- ne_buffer_zappend(ctx->buf, "];");
- return 0;
- }
- static int tos_endprop(void *userdata, int state,
- const char *nspace, const char *name)
- {
- struct propctx *ctx = userdata;
- ne_buffer_concat(ctx->buf, "end-prop[", name, "];", NULL);
- return 0;
- }
- static int run_207_response(const char *resp, const char *expected)
- {
- ne_buffer *buf = ne_buffer_create();
- ne_session *sess;
- ne_xml_parser *p = ne_xml_create();
- ne_207_parser *p207;
- ne_request *req;
- ne_uri base = {0};
- struct propctx ctx;
- CALL(session_server(&sess, single_serve_string, (char *)resp));
- req = ne_request_create(sess, "PROPFIND", "/foo");
- ne_fill_server_uri(sess, &base);
- base.path = ne_strdup("/foo");
- p207 = ne_207_create(p, &base, buf);
- ne_uri_free(&base);
- ne_add_response_body_reader(req, ne_accept_207, ne_xml_parse_v, p);
- ne_207_set_response_handlers(p207, tos_startresp, tos_endresp);
- ne_207_set_propstat_handlers(p207, tos_startpstat, tos_endpstat);
- ctx.buf = buf;
- ctx.p207 = p207;
- ne_xml_push_handler(p, tos_startprop, tos_cdata, tos_endprop, &ctx);
- ONREQ(ne_request_dispatch(req));
- CALL(await_server());
- ONV(ne_xml_failed(p),
- ("parse error in response body: %s", ne_xml_get_error(p)));
- ONV(strcmp(buf->data, expected),
- ("comparison failed.\n"
- "expected string: `%s'\n"
- "got string: `%s'", expected, buf->data));
- ne_buffer_destroy(buf);
- ne_207_destroy(p207);
- ne_xml_destroy(p);
- ne_request_destroy(req);
- ne_session_destroy(sess);
- return OK;
- }
- /* Tests for the 207 interface: send a 207 response body, compare the
- * re-serialized string returned with that expected. */
- static int two_oh_seven(void)
- {
- static const char *ts[][2] = {
- { MULTI_207(RESP_207("/foo", "")),
- "start-resp[/foo];end-resp[/foo];" },
- /* test for response status handling */
- { MULTI_207(RESP_207("/bar", STAT_207("200 OK"))),
- "start-resp[/bar];end-resp[/bar]-status={200 OK};" },
- /* test that empty description == NULL description argument */
- { MULTI_207(RESP_207("/bar", STAT_207("200 OK") DESCR_207(""))),
- "start-resp[/bar];end-resp[/bar]-status={200 OK};" },
- /* test multiple responses */
- { MULTI_207(RESP_207("/hello/world", STAT_207("200 OK"))
- RESP_207("/foo/bar", STAT_207("599 French Fries"))),
- "start-resp[/hello/world];end-resp[/hello/world]-status={200 OK};"
- "start-resp[/foo/bar];end-resp[/foo/bar]"
- "-status={599 French Fries};"
- },
- /* test multiple propstats in multiple responses */
- { MULTI_207(RESP_207("/al/pha",
- PSTAT_207(STAT_207("321 Une"))
- PSTAT_207(STAT_207("432 Deux"))
- PSTAT_207(STAT_207("543 Trois")))
- RESP_207("/be/ta",
- PSTAT_207(STAT_207("587 Quatre"))
- PSTAT_207(STAT_207("578 Cinq")))),
- "start-resp[/al/pha];"
- "start-pstat[/al/pha-1];end-pstat[/al/pha-1]-status={321 Une};"
- "start-pstat[/al/pha-2];end-pstat[/al/pha-2]-status={432 Deux};"
- "start-pstat[/al/pha-3];end-pstat[/al/pha-3]-status={543 Trois};"
- "end-resp[/al/pha];"
- "start-resp[/be/ta];"
- "start-pstat[/be/ta-1];end-pstat[/be/ta-1]-status={587 Quatre};"
- "start-pstat[/be/ta-2];end-pstat[/be/ta-2]-status={578 Cinq};"
- "end-resp[/be/ta];"
- },
- /* test that incomplete responses are completely ignored. */
- { MULTI_207("<D:response/>"
- RESP_207("/", STAT_207("123 Hoorah"))
- "<D:response/>"
- "<D:response><D:propstat>hello</D:propstat></D:response>"
- "<D:response><D:href/></D:response>"
- RESP_207("/bar", STAT_207("200 OK"))),
- "start-resp[/];end-resp[/]-status={123 Hoorah};"
- "start-resp[/bar];end-resp[/bar]-status={200 OK};" },
- /* tests for propstat status */
- { MULTI_207(RESP_207("/pstat",
- PSTAT_207("<D:prop/>" STAT_207("466 Doomed")))),
- "start-resp[/pstat];start-pstat[/pstat-1];"
- "end-pstat[/pstat-1]-status={466 Doomed};end-resp[/pstat];" },
- { MULTI_207(RESP_207("/pstat", PSTAT_207("<D:status/>"))),
- "start-resp[/pstat];start-pstat[/pstat-1];"
- "end-pstat[/pstat-1];end-resp[/pstat];" },
- /* tests for responsedescription handling */
- { MULTI_207(RESP_207("/bar", STAT_207("200 OK") DESCR_207(DESCR_REM))),
- "start-resp[/bar];end-resp[/bar]-status={200 OK}"
- "-descr={" DESCR_REM "};" },
- { MULTI_207(RESP_207("/bar",
- PSTAT_207(STAT_207("456 Too Hungry")
- DESCR_207("Not enough food available"))
- STAT_207("200 OK") DESCR_207("Not " DESCR_REM))),
- "start-resp[/bar];"
- "start-pstat[/bar-1];end-pstat[/bar-1]-status={456 Too Hungry}"
- "-descr={Not enough food available};"
- "end-resp[/bar]-status={200 OK}-descr={Not " DESCR_REM "};" },
- /* intermingle some random elements and cdata to make sure
- * they are ignored. */
- { MULTI_207("<D:fish-food/>blargl"
- RESP_207("/b<ping-pong/>ar", "<D:sausages/>"
- PSTAT_207("<D:hello-mum/>blergl")
- STAT_207("200 OK") "<D:blah>foop</D:blah>"
- DESCR_207(DESCR_REM) "carroon")
- "carapi"),
- "start-resp[/bar];start-pstat[/bar-1];end-pstat[/bar-1];"
- "end-resp[/bar]-status={200 OK}-descr={" DESCR_REM "};" },
- /* test for properties within a 207. */
- { MULTI_207(RESP_207("/alpha",
- PSTAT_207(PROPS_207(
- APROP_207("propone", "hello")
- APROP_207("proptwo", "foobar"))
- STAT_207("200 OK")))),
- "start-resp[/alpha];start-pstat[/alpha-1];"
- "start-prop[/alpha,/alpha-1,propone];cdata-prop[hello];"
- "end-prop[propone];"
- "start-prop[/alpha,/alpha-1,proptwo];cdata-prop[foobar];"
- "end-prop[proptwo];"
- "end-pstat[/alpha-1]-status={200 OK};end-resp[/alpha];" },
- /* test for whitespace around href, which should be
- * ignored. */
- { MULTI_207(RESP_207(" /spaces ", STAT_207("200 OK") DESCR_207(""))),
- "start-resp[/spaces];end-resp[/spaces]-status={200 OK};" },
- /* test for whitespace around href, which should be
- * ignored. */
- { MULTI_207(RESP_207(" /spaces ", STAT_207("200 OK\n") DESCR_207(""))),
- "start-resp[/spaces];end-resp[/spaces]-status={200 OK};" },
- /* test for omitted reason-phrase in status-line, which is
- * valid, per https://github.com/notroj/neon/issues/188 */
- { MULTI_207(RESP_207("/bar", STAT_207("200 ") DESCR_207(""))),
- "start-resp[/bar];end-resp[/bar]-status={200 };" }
- };
- unsigned n;
- for (n = 0; n < sizeof(ts)/sizeof(ts[0]); n++)
- CALL(run_207_response(ts[n][0], ts[n][1]));
- return OK;
- }
- ne_test tests[] = {
- T(errors),
- T(two_oh_seven),
- T(NULL)
- };
|