diff --git a/configure.ac b/configure.ac index 56fb498..c02bcb2 100644 --- a/configure.ac +++ b/configure.ac @@ -28,24 +28,20 @@ AC_ARG_WITH([rst2man], m4_ifndef([VARNISH_PREREQ], AC_MSG_ERROR([Need varnish.m4 -- see README.md])) -VARNISH_PREREQ([4.1], [5.0.0]) +VARNISH_PREREQ([4.1], [trunk]) VARNISH_VMODS([soap]) AC_SUBST([VARNISHSRC]) AC_ARG_VAR([VARNISHSRC], [path to Varnish source tree]) AS_IF([test "x${VARNISHSRC}" = "x" ], AC_MSG_FAILURE([\$VARNISHSRC is not set]), AC_MSG_RESULT([yes])) AC_CHECK_FILE([$VARNISHSRC/include/vrt.h], - [], - [AC_MSG_FAILURE([\$VARNISHSRC "$VARNISHSRC" is not a Varnish source directory])] + [], + [AC_MSG_FAILURE([\$VARNISHSRC "$VARNISHSRC" is not a Varnish source directory])] ) VMOD_TESTS="$(cd $srcdir/src && echo tests/*.vtc)" AC_SUBST(VMOD_TESTS) -PKG_CHECK_VAR([LIBVARNISHAPI_LIBDIR], [varnishapi], [libdir]) -AC_SUBST([VARNISH_LIBRARY_PATH], - [$LIBVARNISHAPI_LIBDIR:$LIBVARNISHAPI_LIBDIR/varnish]) - AC_CONFIG_FILES([ Makefile src/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index 84c4f6a..ba045aa 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,7 +6,7 @@ AM_CFLAGS = $(VARNISHAPI_CFLAGS) -Wall -Werror \ -I/usr/include/apr-1.0 \ -I$(VARNISHSRC) -AM_LDFLAGS = $(VARNISHAPI_LIBS) $(VMOD_LDFLAGS) \ +AM_LDFLAGS = $(VMOD_LDFLAGS) \ -lxml2 \ -lapr-1 @@ -14,10 +14,7 @@ vmod_LTLIBRARIES = libvmod_soap.la libvmod_soap_la_SOURCES = \ vmod_soap.c \ - vmod_soap_http.c \ - vmod_soap_gzip.c \ - vmod_soap_xml.c \ - vmod_soap_request.c + vmod_soap_xml.c nodist_libvmod_soap_la_SOURCES = \ vcc_soap_if.c \ diff --git a/src/tests/a00000.vtc b/src/tests/a00000.vtc index 8e61049..82b3c01 100644 --- a/src/tests/a00000.vtc +++ b/src/tests/a00000.vtc @@ -9,9 +9,16 @@ server s1 { varnish v1 -vcl+backend { import ${vmod_soap}; + import std; sub vcl_recv { + if (req.http.Content-Encoding && + req.http.Content-Encoding != "gzip") { + return (synth(400, "Content-Encoding not supported")); + } + std.cache_req_body(1M); + if(soap.is_valid()) { set req.http.soap-ws-ns = soap.action_namespace(); } diff --git a/src/tests/a00001.vtc b/src/tests/a00001.vtc index c565290..6f3d17e 100644 --- a/src/tests/a00001.vtc +++ b/src/tests/a00001.vtc @@ -1,6 +1,11 @@ varnishtest "No Content-Length (or zero/negative) / Transfer-Encoding: chunked" server s1 { + rxreq + expect req.http.soap-ws-ns == "http://schemas.reuters.com/mytest" + expect req.http.content-length == 215 + txresp -status 200 + rxreq expect req.http.soap-ws-ns == "http://schemas.reuters.com/mytest" expect req.http.content-length == 181 @@ -9,9 +14,11 @@ server s1 { varnish v1 -vcl+backend { import ${vmod_soap}; + import std; sub vcl_recv { + std.cache_req_body(1K); if(soap.is_valid()) { set req.http.soap-ws-ns = soap.action_namespace(); } @@ -31,7 +38,7 @@ client c1 { } chunkedlen 0 rxresp - expect resp.status == 400 + expect resp.status == 200 } -run client c1 { diff --git a/src/tests/a00002.vtc b/src/tests/a00002.vtc index 86d027d..db31373 100644 --- a/src/tests/a00002.vtc +++ b/src/tests/a00002.vtc @@ -9,9 +9,11 @@ server s1 { varnish v1 -vcl+backend { import ${vmod_soap}; + import std; sub vcl_recv { + std.cache_req_body(1K); if(soap.is_valid()) { set req.http.soap-ws-ns = soap.action_namespace(); } diff --git a/src/tests/a00003.vtc b/src/tests/a00003.vtc index 0471dcb..bc19186 100644 --- a/src/tests/a00003.vtc +++ b/src/tests/a00003.vtc @@ -9,9 +9,11 @@ server s1 { varnish v1 -vcl+backend { import ${vmod_soap}; + import std; sub vcl_recv { + std.cache_req_body(1K); if(soap.is_valid()) { set req.http.soap-ws-ns = soap.action_namespace(); } diff --git a/src/tests/a00004.vtc b/src/tests/a00004.vtc index 5ccd6b8..5ecf0d0 100644 --- a/src/tests/a00004.vtc +++ b/src/tests/a00004.vtc @@ -9,9 +9,11 @@ server s1 { varnish v1 -vcl+backend { import ${vmod_soap}; + import std; sub vcl_recv { + std.cache_req_body(1K); if(soap.is_valid()) { set req.http.soap-ws-ns = soap.action_namespace(); } diff --git a/src/tests/a00005.vtc b/src/tests/a00005.vtc index 1886b40..3c8d8fb 100644 --- a/src/tests/a00005.vtc +++ b/src/tests/a00005.vtc @@ -9,9 +9,12 @@ server s1 { varnish v1 -vcl+backend { import ${vmod_soap}; + import std; sub vcl_recv { + std.cache_req_body(1K); + if(soap.is_valid()) { set req.http.soap-ws-ns = soap.action_namespace(); } diff --git a/src/tests/a00006.vtc b/src/tests/a00006.vtc index 1f38deb..bbb5dd1 100644 --- a/src/tests/a00006.vtc +++ b/src/tests/a00006.vtc @@ -1,25 +1,23 @@ varnishtest "SOAP: req body is not in XML format." -server s1 { - rxreq - expect req.http.soap-ws-ns == "http://schemas.reuters.com/mytest" - expect req.http.content-length == 198 - txresp -status 200 -} -start - -varnish v1 -vcl+backend { +varnish v1 -vcl { import ${vmod_soap}; + import std; + + backend foo none; sub vcl_recv { + # cache just enough to determine if xml + std.cache_req_body(10B, partial=true); if(soap.is_valid()) { set req.http.soap-ws-ns = soap.action_namespace(); + return (synth(200)); } else { return (synth(400, "Wrong SOAP message")); } } - } -start client c1 { diff --git a/src/tests/a00007.vtc b/src/tests/a00007.vtc index bdc6ff1..b59d250 100644 --- a/src/tests/a00007.vtc +++ b/src/tests/a00007.vtc @@ -9,9 +9,12 @@ server s1 { varnish v1 -vcl+backend { import ${vmod_soap}; + import std; sub vcl_recv { + std.cache_req_body(1K); + if(soap.is_valid()) { set req.http.soap-ws-ns = soap.action_namespace(); } diff --git a/src/tests/b00000.vtc b/src/tests/b00000.vtc index 15c4815..6b995fc 100644 --- a/src/tests/b00000.vtc +++ b/src/tests/b00000.vtc @@ -9,12 +9,14 @@ server s1 { varnish v1 -vcl+backend { import ${vmod_soap}; + import std; sub vcl_recv { + # 196b: just enough to read the namespace + std.cache_req_body(196b, partial=true); set req.http.soap-ws-ns = soap.action_namespace(); } - } -start client c1 { diff --git a/src/tests/b00001.vtc b/src/tests/b00001.vtc index 2430826..f9f9474 100644 --- a/src/tests/b00001.vtc +++ b/src/tests/b00001.vtc @@ -9,9 +9,11 @@ server s1 { varnish v1 -vcl+backend { import ${vmod_soap}; + import std; sub vcl_recv { + std.cache_req_body(1M, partial=true); set req.http.soap-ws-ns = soap.action(); } } -start diff --git a/src/tests/b00002.vtc b/src/tests/b00002.vtc index 0686812..7dfffac 100644 --- a/src/tests/b00002.vtc +++ b/src/tests/b00002.vtc @@ -16,9 +16,11 @@ server s1 { varnish v1 -vcl+backend { import ${vmod_soap}; + import std; sub vcl_recv { + std.cache_req_body(1K); set req.http.soap-action = soap.action(); set req.http.soap-action-ns = soap.action_namespace(); } diff --git a/src/tests/b00003.vtc b/src/tests/b00003.vtc index 5410b5c..929490d 100644 --- a/src/tests/b00003.vtc +++ b/src/tests/b00003.vtc @@ -9,9 +9,11 @@ server s1 { varnish v1 -vcl+backend { import ${vmod_soap}; + import std; sub vcl_recv { + std.cache_req_body(1M, partial=true); set req.http.soap-action = soap.action(); set req.http.soap-action-ns = soap.action_namespace(); } diff --git a/src/tests/b00004.vtc b/src/tests/b00004.vtc index c2283c4..c42767f 100644 --- a/src/tests/b00004.vtc +++ b/src/tests/b00004.vtc @@ -12,6 +12,7 @@ server s1 { varnish v1 -vcl+backend { import ${vmod_soap}; + import std; sub vcl_init { @@ -21,6 +22,7 @@ varnish v1 -vcl+backend { sub vcl_recv { + std.cache_req_body(1K); if(req.url == "/header") { set req.http.soap-uuid = soap.xpath_header("ui:userIdentity/uuid:UUID"); } diff --git a/src/tests/b00005.vtc b/src/tests/b00005.vtc index 5e1ddf2..cff26a1 100644 --- a/src/tests/b00005.vtc +++ b/src/tests/b00005.vtc @@ -12,9 +12,11 @@ server s1 { varnish v1 -vcl+backend { import ${vmod_soap}; + import std; sub vcl_recv { + std.cache_req_body(1M); set req.http.soap-ws-ns = soap.action_namespace(); if(!req.http.soap-ws-ns) { return (synth(400, "SOAP Action URI is mandatory")); diff --git a/src/tests/b00006.vtc b/src/tests/b00006.vtc index 7973dda..9fcd68a 100644 --- a/src/tests/b00006.vtc +++ b/src/tests/b00006.vtc @@ -8,6 +8,7 @@ server s1 { varnish v1 -vcl+backend { import ${vmod_soap}; + import std; sub vcl_init { @@ -17,6 +18,7 @@ varnish v1 -vcl+backend { sub vcl_recv { + std.cache_req_body(1K); set req.http.soap-uuid = soap.xpath_header("ui:userIdentity/uuid:UUID"); if(!req.http.soap-uuid || req.http.soap-uuid == "") { return (synth(400, "SOAP UUID is mandatory")); diff --git a/src/tests/b00007.vtc b/src/tests/b00007.vtc index 3fd69a5..9e1f484 100644 --- a/src/tests/b00007.vtc +++ b/src/tests/b00007.vtc @@ -8,6 +8,7 @@ server s1 { varnish v1 -vcl+backend { import ${vmod_soap}; + import std; sub vcl_init { @@ -17,6 +18,7 @@ varnish v1 -vcl+backend { sub vcl_recv { + std.cache_req_body(1K); set req.http.soap-uuid = soap.xpath_body("ui:userIdentity/uuid:UUID"); if(!req.http.soap-uuid || req.http.soap-uuid == "") { return (synth(400, "SOAP UUID is mandatory")); diff --git a/src/tests/b00008.vtc b/src/tests/b00008.vtc index a511430..9fd4acd 100644 --- a/src/tests/b00008.vtc +++ b/src/tests/b00008.vtc @@ -8,9 +8,11 @@ server s1 { varnish v1 -vcl+backend { import ${vmod_soap}; + import std; sub vcl_recv { + std.cache_req_body(1K); if(soap.is_valid()) { return (synth(800, "SOAP is devil")); } diff --git a/src/tests/b00014.vtc b/src/tests/b00014.vtc new file mode 100644 index 0000000..169b170 --- /dev/null +++ b/src/tests/b00014.vtc @@ -0,0 +1,112 @@ +varnishtest "SOAP OO: valid xpath header and body" + +server s1 { + rxreq + expect req.http.soap-uuid == "lenin" + txresp + + rxreq + # aborted +} -start + +varnish v1 -vcl+backend { + import ${vmod_soap}; + import std; + + sub vcl_init { + new body_all = soap.parser( + source=req_body, + req_body=all); + body_all.add_namespace("ui", "http://schemas.reuters.com/ns/2005/08/infrastructure/tornado_soap"); + body_all.add_namespace("uuid", "http://schemas.reuters.com/ns/2007/10/cp/user_identity"); + + new body_cached = soap.parser( + source=req_body, + req_body=cached); + body_cached.add_namespace("ui", "http://schemas.reuters.com/ns/2005/08/infrastructure/tornado_soap"); + body_cached.add_namespace("uuid", "http://schemas.reuters.com/ns/2007/10/cp/user_identity"); + } + + sub vcl_recv { + std.cache_req_body(401B, partial=true); + if(req.url == "/header") { + if (req.http.how == "all") { + set req.http.soap-uuid = + body_all.header_xpath("ui:userIdentity/uuid:UUID"); + return (synth(204)); + } else { + set req.http.soap-uuid = + body_cached.header_xpath("ui:userIdentity/uuid:UUID"); + } + } + else { + if (req.http.how == "all") { + set req.http.soap-uuid = + body_all.body_xpath("ui:userIdentity/uuid:UUID"); + return (synth(204)); + } else { + set req.http.soap-uuid = + body_cached.body_xpath("ui:userIdentity/uuid:UUID"); + } + } + } +} -start + +client c1 { + timeout 8 + txreq -req POST -url "/header" -hdr "how: all" \ + -body { + +
+ + lenin + +
+ +
+ } + rxresp + expect resp.status == 204 + + txreq -req POST -url "/header" -hdr "how: cached" \ + -body { + +
+ + lenin + +
+ +
+ } + rxresp + expect resp.status == 200 + + txreq -req POST -url "/body" -hdr "how: all" \ + -body { + +
+
+ + + obama + +
+ } + rxresp + expect resp.status == 204 + + txreq -req POST -url "/body" -hdr "how: cached" \ + -body { + +
+
+ + + obama + +
+ } + rxresp + expect resp.status == 503 +} -run diff --git a/src/tests/b00019.vtc b/src/tests/b00019.vtc new file mode 100644 index 0000000..f3110b6 --- /dev/null +++ b/src/tests/b00019.vtc @@ -0,0 +1,42 @@ +varnishtest "SOAP OO: valid xpath header and body from object / response" + +server s1 { + rxreq + txresp -body { + +
+ + lenin + +
+ +
+ } +} -start + +varnish v1 -vcl+backend { + import ${vmod_soap}; + import std; + + sub vcl_init { + new body_resp = soap.parser(source=resp_body); + body_resp.add_namespace("ui", "http://schemas.reuters.com/ns/2005/08/infrastructure/tornado_soap"); + body_resp.add_namespace("uuid", "http://schemas.reuters.com/ns/2007/10/cp/user_identity"); + } + + sub vcl_recv { + return (pass); + } + + sub vcl_deliver { + set resp.http.soap-uuid = body_resp.header_xpath( + "ui:userIdentity/uuid:UUID"); + } +} -start + +client c1 { + txreq + rxresp + expect resp.bodylen == 398 + expect resp.http.soap-uuid == lenin +} -run diff --git a/src/tests/c00000.vtc b/src/tests/c00000.vtc index c600273..c5f9cd4 100644 --- a/src/tests/c00000.vtc +++ b/src/tests/c00000.vtc @@ -14,9 +14,11 @@ server s1 { varnish v1 -vcl+backend { import ${vmod_soap}; + import std; sub vcl_recv { + std.cache_req_body(1K); set req.http.soap-ws-ns = soap.action_namespace(); set req.http.soap-ws-name1 = soap.action(); set req.http.soap-ws-ns1 = soap.action_namespace(); diff --git a/src/tests/c00001.vtc b/src/tests/c00001.vtc index 569daf3..75b5f45 100644 --- a/src/tests/c00001.vtc +++ b/src/tests/c00001.vtc @@ -28,9 +28,11 @@ server s1 { varnish v1 -vcl+backend { import ${vmod_soap}; + import std; sub vcl_recv { + std.cache_req_body(1M, partial=true); if(soap.is_valid()) { set req.http.soap-ws-ns = soap.action_namespace(); } diff --git a/src/tests/c00002.vtc b/src/tests/c00002.vtc index f23d12b..2b3e8b2 100644 --- a/src/tests/c00002.vtc +++ b/src/tests/c00002.vtc @@ -12,9 +12,14 @@ server s1 { varnish v1 -vcl+backend { import ${vmod_soap}; + import std; sub vcl_recv { + if (req.http.Content-Encoding ~ "(?i)\bgzip\b") { + set req.filters = "gunzip"; + } + std.cache_req_body(1M, partial=true); if(soap.is_valid()) { set req.http.soap-ws-ns = soap.action_namespace(); } diff --git a/src/tests/g00000.vtc b/src/tests/g00000.vtc index 8049b69..a4c8e19 100644 --- a/src/tests/g00000.vtc +++ b/src/tests/g00000.vtc @@ -3,16 +3,20 @@ varnishtest "SOAP: read namespace" server s1 { rxreq expect req.http.soap-ws-ns == "http://schemas.reuters.com/mytest" - expect req.http.content-encoding == "gzip" - expect req.http.content-length == "238" + expect req.http.content-length == "215" txresp -status 200 } -start varnish v1 -vcl+backend { import ${vmod_soap}; + import std; sub vcl_recv { + if (req.http.Content-Encoding ~ "(?i)\bgzip\b") { + set req.filters = "gunzip"; + } + std.cache_req_body(1M, partial=true); set req.http.soap-ws-ns = soap.action_namespace(); } } -start diff --git a/src/tests/r00000.vtc b/src/tests/r00000.vtc index 17b3744..57fcf43 100644 --- a/src/tests/r00000.vtc +++ b/src/tests/r00000.vtc @@ -1,6 +1,10 @@ varnishtest "No Content-Length (or zero/negative) / Transfer-Encoding: chunked" server s1 { + rxreq + expect req.http.content-length == 215 + txresp -status 200 + rxreq expect req.http.content-length == 181 txresp -status 200 @@ -8,9 +12,11 @@ server s1 { varnish v1 -vcl+backend { import ${vmod_soap}; + import std; sub vcl_recv { + std.cache_req_body(1M); if(soap.is_valid()) { set req.http.soap-ws-ns = soap.action_namespace(); } @@ -20,6 +26,7 @@ varnish v1 -vcl+backend { } } -start +# chunked is supported by varnish-cache client c1 { timeout 4 txreq -req POST -nolen -hdr "Transfer-Encoding: chunked" @@ -30,19 +37,10 @@ client c1 { } chunkedlen 0 rxresp - expect resp.status == 400 + expect resp.status == 200 } -run client c1 { - timeout 4 - txreq -req POST -nolen -body { - -
- -
} - rxresp - expect resp.status == 400 - txreq -req POST -nolen -hdr "Content-Length: -42" -body { } diff --git a/src/vmod_soap.c b/src/vmod_soap.c index 21a4015..7637afa 100644 --- a/src/vmod_soap.c +++ b/src/vmod_soap.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2019, Refinitiv + * Copyright 2022 UPLEX - Nils Goroll Systemoptimierung * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,21 +26,33 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "config.h" + +#include +#include +#include + #include "vmod_soap.h" +#include "vcc_soap_if.h" #define POOL_KEY "VRN_IH_PK" -static pthread_mutex_t soap_mutex = PTHREAD_MUTEX_INITIALIZER; static int refcount = 0; static apr_pool_t *apr_pool = NULL; +enum soap_source { + SOAPS_INVALID = 0, + SOAPS_REQ_BODY, + SOAPS_RESP_BODY +}; + enum soap_state { NONE = 0, INIT, HEADER_DONE, // Header element completely read ACTION_AVAILABLE, // Body parsing is started and action name and namespace available BODY_DONE, // Body element completely read - DONE + FAILED }; /* -------------------------------------------------------------------------------------/ @@ -61,11 +74,12 @@ static void clean_apr() /* -------------------------------------------------------------------------------------/ init vcl */ -static void clean_vcl(void *priv) +static void clean_vcl(VRT_CTX, void *priv) { struct priv_soap_vcl *priv_soap_vcl; struct soap_namespace *ns, *ns2; + CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); CAST_OBJ_NOTNULL(priv_soap_vcl, priv, PRIV_SOAP_VCL_MAGIC); VSLIST_FOREACH_SAFE(ns, &priv_soap_vcl->namespaces, list, ns2) { @@ -90,22 +104,31 @@ static struct priv_soap_vcl* init_vcl() /* ------------------------------------------------------------------/ initialize session */ + +#define TASK_ALLOC_OBJ(ctx, ptr, magic) do { \ + assert(sizeof *(ptr) <= UINT_MAX); \ + ptr = WS_Alloc((ctx)->ws, sizeof *(ptr)); \ + if ((ptr) == NULL) \ + VRT_fail(ctx, "Out of workspace for " #magic); \ + else \ + INIT_OBJ(ptr, magic); \ +} while(0) + static struct priv_soap_task* init_task(VRT_CTX) { struct priv_soap_task *soap_task; - ALLOC_OBJ(soap_task, PRIV_SOAP_TASK_MAGIC); - AN(soap_task); + TASK_ALLOC_OBJ(ctx, soap_task, PRIV_SOAP_TASK_MAGIC); + if (! soap_task) + return (NULL); soap_task->ctx = ctx; - XXXAZ(apr_pool_create(&soap_task->pool, apr_pool)); - - ALLOC_OBJ(soap_task->req_http, SOAP_REQ_HTTP_MAGIC); - XXXAN(soap_task->req_http); + TASK_ALLOC_OBJ(ctx, soap_task->req_xml, SOAP_REQ_XML_MAGIC); + if (! soap_task->req_xml) + return (NULL); - ALLOC_OBJ(soap_task->req_xml, SOAP_REQ_XML_MAGIC); - XXXAN(soap_task->req_xml); + XXXAZ(apr_pool_create(&soap_task->pool, apr_pool)); VSLb(soap_task->ctx->vsl, SLT_Debug, "init_task"); return soap_task; @@ -114,123 +137,147 @@ static struct priv_soap_task* init_task(VRT_CTX) /* -----------------------------------------------------------------/ destroy session */ -static void clean_task(void *priv) +static void clean_task(VRT_CTX, void *priv) { struct priv_soap_task *soap_task; - AN(priv); + CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); CAST_OBJ_NOTNULL(soap_task, priv, PRIV_SOAP_TASK_MAGIC); clean_req_xml(soap_task->req_xml); INIT_OBJ(soap_task->req_xml, SOAP_REQ_XML_MAGIC); - clean_req_http(soap_task->req_http); - INIT_OBJ(soap_task->req_http, SOAP_REQ_HTTP_MAGIC); - INIT_OBJ(soap_task->req_xml, SOAP_REQ_XML_MAGIC); - FREE_OBJ(soap_task->req_xml); - - INIT_OBJ(soap_task->req_http, SOAP_REQ_HTTP_MAGIC); - FREE_OBJ(soap_task->req_http); AN(soap_task->pool); apr_pool_destroy(soap_task->pool); INIT_OBJ(soap_task, PRIV_SOAP_TASK_MAGIC); - FREE_OBJ(soap_task); } -int process_request(struct priv_soap_task *task, enum soap_state state) +static int +process_init_read(struct priv_soap_task *task) { - VSLb(task->ctx->vsl, SLT_Debug, "process_request 0: %d/%d", task->state, state); - ssize_t bytes_read = 0; - while (task->state < state) { - switch (task->state) { - case NONE: // init - VSLb(task->ctx->vsl, SLT_Debug, "process_request 1: %d/%d", task->state, state); - task->req_http->pool = task->pool; - task->req_http->ctx = task->ctx; - init_req_http(task->req_http); - if (task->req_http->encoding != CE_GZIP && task->req_http->encoding != CE_NONE) { - VSLb(task->ctx->vsl, SLT_Error, "Unsupported Content-Encoding"); - return (-1); - } - task->req_xml->pool = task->pool; - task->req_xml->ctx = task->ctx; - init_req_xml(task->req_xml); + AN(task); + assert(task->state == NONE); - task->bytes_total = http_GetContentLength(task->ctx->http_req); - if(task->bytes_total <= 0) { - VSLb(task->ctx->vsl, SLT_Error, "Invalid content-length %ld", task->bytes_total); - return (-1); - } - task->state = INIT; + task->req_xml->pool = task->pool; + task->req_xml->ctx = task->ctx; + init_req_xml(task->req_xml); + + task->state = INIT; + task->vrb_what = VRB_CACHED; + return (0); +} + +int v_matchproto_(objiterate_f) +read_iter_f(void *priv, unsigned flush, const void *ptr, ssize_t len) +{ + struct priv_soap_task *task; + int err; + + CAST_OBJ_NOTNULL(task, priv, PRIV_SOAP_TASK_MAGIC); + + if (task->state == FAILED || task->state == BODY_DONE) + return (0); + + assert(task->state == INIT || + task->state == HEADER_DONE || + task->state == ACTION_AVAILABLE); + + err = soap_iter_f(task->req_xml, flush, ptr, len); + + // we never fail the iterator for custom error handling from vcl + if (err < 0) + task->state = FAILED; + else if (task->req_xml->body) + task->state = BODY_DONE; + else if (task->req_xml->action_namespace && task->req_xml->action_name) + task->state = ACTION_AVAILABLE; + else if (task->req_xml->header) + task->state = HEADER_DONE; + + return (0); +} + +int process_request(struct priv_soap_task *task, enum soap_state state, + enum soap_source source, unsigned can_eat) +{ + enum soap_state old; + int r; + + if (source == SOAPS_REQ_BODY && + task->ctx->req->req_body_status == BS_NONE) + return (-1); + while (task->state < state) { + old = task->state; + VSLb(task->ctx->vsl, SLT_Debug, + "process_request 0: %d/%d", old, state); + switch (old) { + case NONE: // init + r = process_init_read(task); + if (r) + return (r); break; case INIT: case HEADER_DONE: case ACTION_AVAILABLE: - VSLb(task->ctx->vsl, SLT_Debug, "process_request 5: %d/%d (%ld bytes)", task->state, state, task->bytes_total); - if (task->bytes_total <= 0) { - VSLb(task->ctx->vsl, SLT_Error, "Not enough data"); - return (-1); + if (source == SOAPS_REQ_BODY) { + r = VRB_Iterate(task->ctx->req->wrk, task->ctx->vsl, + task->ctx->req, read_iter_f, task, task->vrb_what); + } else { + assert(source == SOAPS_RESP_BODY); + if (task->ctx->req != NULL && + task->ctx->req->objcore != NULL) { + r = ObjIterate(task->ctx->req->wrk, + task->ctx->req->objcore, + task, read_iter_f, 0); + } else { + VSLb(task->ctx->vsl, SLT_Debug, + "no objcore"); + r = -1; + } } - // If everything is read, but state not switched to BODY_DONE that mean - // XML body isn't present in request - if (bytes_read >= task->bytes_total) { - VSLb(task->ctx->vsl, SLT_Error, "SOAP: http read error: incomplete xml"); - return (-1); + if (task->vrb_what == VRB_CACHED && can_eat) { + task->vrb_what = VRB_REMAIN; + continue; } - while (bytes_read < task->bytes_total) { - int just_read = read_body_part(task->req_http, bytes_read, task->bytes_total); - if (just_read <= 0) { - VSLb(task->ctx->vsl, SLT_Error, "SOAP: http read failed (%d, errno: %d)", just_read, errno); - return (-1); - } - bytes_read += just_read; - VSLb(task->ctx->vsl, SLT_Debug, "process_request 6: read %d bytes", just_read); - - // parse chunk - VSLb(task->ctx->vsl, SLT_Debug, "process_request 7: tota %d bytes", task->req_http->body.length); - if (parse_soap_chunk(task->req_xml, task->req_http->body.data, task->req_http->body.length)) { - VSLb(task->ctx->vsl, SLT_Error, "SOAP: soap read failed %d", errno); - return (-1); - } - - if (task->req_xml->body) { - task->state = BODY_DONE; - break; - } - if (task->req_xml->action_namespace && task->req_xml->action_name) { - task->state = ACTION_AVAILABLE; - break; - } - if (task->req_xml->header) { - task->state = HEADER_DONE; - break; - } + /* error or no progress */ + if (r == 0 && old == task->state) + r = -1; + if (r) { + task->state = FAILED; + return (r); } break; case BODY_DONE: // read from memory - case DONE: - VSLb(task->ctx->vsl, SLT_Debug, "process_request 8: %d/%d", task->state, state); + VSLb(task->ctx->vsl, SLT_Debug, "process_request 8: %d/%d", old, state); break; - default: - VSLb(task->ctx->vsl, SLT_Debug, "process_request 9: %d/%d", task->state, state); + case FAILED: break; + default: + WRONG("task->state"); } } VSLb(task->ctx->vsl, SLT_Debug, "process_request .: %d/%d", task->state, state); - return (0); + return (task->state == FAILED); } +static const struct vmod_priv_methods priv_soap_vcl_methods[1] = {{ + .magic = VMOD_PRIV_METHODS_MAGIC, + .type = "soap priv_vcl", + .fini = clean_vcl +}}; + /* * handle vmod internal state, vmod init/fini and/or varnish callback * (un)registration here. * */ -int __match_proto__(vmod_event_f) - event_function(VRT_CTX, struct vmod_priv *priv /* PRIV_VCL */, enum vcl_event_e e) +int v_matchproto_(vmod_event_f) + VPFX(event_function)(VRT_CTX, struct vmod_priv *priv /* PRIV_VCL */, + enum vcl_event_e e) { struct priv_soap_vcl *priv_soap_vcl; @@ -238,28 +285,28 @@ int __match_proto__(vmod_event_f) switch (e) { case VCL_EVENT_LOAD: - AZ(pthread_mutex_lock(&soap_mutex)); + if (! xmlHasFeature(XML_WITH_THREAD)) { + VRT_fail(ctx, "Need libxml2 with threads support"); + return (1); + } if(0 == refcount++) { init_xml(); init_apr(); } - AZ(pthread_mutex_unlock(&soap_mutex)); priv_soap_vcl = init_vcl(); priv->priv = priv_soap_vcl; - priv->free = clean_vcl; + priv->methods = priv_soap_vcl_methods; break; case VCL_EVENT_WARM: break; case VCL_EVENT_COLD: break; case VCL_EVENT_DISCARD: - AZ(pthread_mutex_lock(&soap_mutex)); if(0 == --refcount) { clean_xml(); clean_apr(); } - AZ(pthread_mutex_unlock(&soap_mutex)); break; default: return (0); @@ -267,6 +314,12 @@ int __match_proto__(vmod_event_f) return (0); } +static const struct vmod_priv_methods priv_soap_task_methods[1] = {{ + .magic = VMOD_PRIV_METHODS_MAGIC, + .type = "soap priv_task", + .fini = clean_task +}}; + sess_record* priv_soap_get(VRT_CTX, struct vmod_priv *priv /* PRIV_TASK */) { struct priv_soap_task *soap_task; @@ -275,14 +328,11 @@ sess_record* priv_soap_get(VRT_CTX, struct vmod_priv *priv /* PRIV_TASK */) AN(priv); if(priv->priv == NULL) { priv->priv = init_task(ctx); - priv->free = clean_task; + priv->methods = priv_soap_task_methods; } CAST_OBJ_NOTNULL(soap_task, priv->priv, PRIV_SOAP_TASK_MAGIC); if(soap_task->ctx != ctx) { soap_task->ctx = ctx; - if(soap_task->req_http) { - soap_task->req_http->ctx = ctx; - } if(soap_task->req_xml) { soap_task->req_xml->ctx = ctx; } @@ -290,35 +340,35 @@ sess_record* priv_soap_get(VRT_CTX, struct vmod_priv *priv /* PRIV_TASK */) return (soap_task); } -VCL_BOOL __match_proto__(td_soap_is_valid) +VCL_BOOL v_matchproto_(td_soap_is_valid) vmod_is_valid(VRT_CTX, struct vmod_priv *priv /* PRIV_TASK */) { struct priv_soap_task *soap_task = priv_soap_get(ctx, priv); - return (process_request(soap_task, ACTION_AVAILABLE) == 0); + return (process_request(soap_task, ACTION_AVAILABLE, SOAPS_REQ_BODY, 1) == 0); } -VCL_STRING __match_proto__(td_soap_action) +VCL_STRING v_matchproto_(td_soap_action) vmod_action(VRT_CTX, struct vmod_priv *priv /* PRIV_TASK */) { struct priv_soap_task *soap_task = priv_soap_get(ctx, priv); - if(process_request(soap_task, ACTION_AVAILABLE) == 0) { + if(process_request(soap_task, ACTION_AVAILABLE, SOAPS_REQ_BODY, 1) == 0) { return (soap_task->req_xml->action_name); } return (""); } -VCL_STRING __match_proto__(td_soap_action_namespace) +VCL_STRING v_matchproto_(td_soap_action_namespace) vmod_action_namespace(VRT_CTX, struct vmod_priv *priv /* PRIV_TASK */) { struct priv_soap_task *soap_task = priv_soap_get(ctx, priv); - if(process_request(soap_task, ACTION_AVAILABLE) == 0) { + if(process_request(soap_task, ACTION_AVAILABLE, SOAPS_REQ_BODY, 1) == 0) { return (soap_task->req_xml->action_namespace); } return (""); } -VCL_VOID __match_proto__(td_soap_add_namespace) +VCL_VOID v_matchproto_(td_soap_add_namespace) vmod_add_namespace(VRT_CTX, struct vmod_priv *priv /* PRIV_VCL */, VCL_STRING prefix, VCL_STRING uri) { struct priv_soap_vcl *priv_soap_vcl; @@ -334,7 +384,7 @@ VCL_VOID __match_proto__(td_soap_add_namespace) VSLIST_INSERT_HEAD(&priv_soap_vcl->namespaces, namespace, list); } -VCL_STRING __match_proto__(td_soap_xpath_header) +VCL_STRING v_matchproto_(td_soap_xpath_header) vmod_xpath_header(VRT_CTX, struct vmod_priv *priv_vcl /* PRIV_VCL */, struct vmod_priv *priv_task /* PRIV_TASK */, VCL_STRING xpath) { struct priv_soap_vcl *soap_vcl;; @@ -346,13 +396,13 @@ VCL_STRING __match_proto__(td_soap_xpath_header) AN(priv_task); soap_task = priv_soap_get(ctx, priv_task); - if(process_request(soap_task, HEADER_DONE) == 0) { + if(process_request(soap_task, HEADER_DONE, SOAPS_REQ_BODY, 1) == 0) { return (evaluate_xpath(soap_vcl, soap_task, soap_task->req_xml->header, xpath)); } return (""); } -VCL_STRING __match_proto__(td_soap_xpath_body) +VCL_STRING v_matchproto_(td_soap_xpath_body) vmod_xpath_body(VRT_CTX, struct vmod_priv *priv_vcl /* PRIV_VCL */, struct vmod_priv *priv_task /* PRIV_TASK */, VCL_STRING xpath) { struct priv_soap_vcl *soap_vcl;; @@ -364,13 +414,13 @@ VCL_STRING __match_proto__(td_soap_xpath_body) AN(priv_task); soap_task = priv_soap_get(ctx, priv_task); - if(process_request(soap_task, BODY_DONE) == 0) { + if(process_request(soap_task, BODY_DONE, SOAPS_REQ_BODY, 1) == 0) { return (evaluate_xpath(soap_vcl, soap_task, soap_task->req_xml->body, xpath)); } return (""); } -VCL_VOID __match_proto__(td_soap_synthetic) +VCL_VOID v_matchproto_(td_soap_synthetic) vmod_synthetic(VRT_CTX, struct vmod_priv *priv_task /* PRIV_TASK */, VCL_INT soap_code, VCL_STRING soap_message) { struct priv_soap_task *soap_task; @@ -380,3 +430,225 @@ VCL_VOID __match_proto__(td_soap_synthetic) synth_soap_fault(soap_task->req_xml, soap_code, soap_message); } + +/* + * ============================================================ + * + * Object Interface (rework) + */ + +static void +soap_vsl_generic_error(void *priv, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + VSLbv(priv, SLT_Error, fmt, ap); + va_end(ap); +} + +static void +soap_vsl_structured_error(void *priv, xmlErrorPtr error) +{ + + AN(error); + VSLb(priv, SLT_Error, "xml: domain=%d, code=%d, msg=%s", + error->domain, error->code, error->message); +} + +static void +soap_vsb_generic_error(void *priv, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + VSB_vprintf(priv, fmt, ap); + va_end(ap); +} + +static void +soap_vsb_structured_error(void *priv, xmlErrorPtr error) +{ + + AN(error); + VSB_printf(priv, "xml: domain=%d, code=%d, msg=%s", + error->domain, error->code, error->message); +} + +static void +soap_init_thread(VRT_CTX) +{ + + CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); + if (ctx->vsl != NULL) { + xmlSetGenericErrorFunc(ctx->vsl, soap_vsl_generic_error); + xmlSetStructuredErrorFunc(ctx->vsl, soap_vsl_structured_error); + } else { + AN(ctx->msg); + xmlSetGenericErrorFunc(ctx->msg, soap_vsb_generic_error); + xmlSetStructuredErrorFunc(ctx->msg, soap_vsb_structured_error); + } +} + +struct VPFX(soap_parser) { + unsigned magic; +#define SOAP_PARSER_MAGIC 0x017ce81e + unsigned can_VRB_REMAIN:1; + enum soap_source source; + char *vcl_name; + // XXX basically just namespaces + struct priv_soap_vcl priv; +}; + +VCL_VOID +vmod_parser__init(VRT_CTX, struct VPFX(soap_parser) **soapp, + const char *vcl_name, struct VARGS(parser__init)*args) +{ + struct VPFX(soap_parser) *soap; + + CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); + AN(soapp); + AZ(*soapp); + AN(vcl_name); + AN(args); + + soap_init_thread(ctx); + + ALLOC_OBJ(soap, SOAP_PARSER_MAGIC); + AN(soap); + + if (args->source == VENUM(req_body)) { + soap->source = SOAPS_REQ_BODY; + if (! args->valid_req_body) { + VRT_fail(ctx, "new %s: req_body argument " + "is required with source=req_body", + vcl_name); + vmod_parser__fini(&soap); + return; + } + soap->can_VRB_REMAIN = (args->req_body == VENUM(all)); + } else if (args->source == VENUM(resp_body)) { + soap->source = SOAPS_RESP_BODY; + } else { + WRONG("source argument"); + } + REPLACE(soap->vcl_name, vcl_name); + soap->priv.magic = 0x5FF42842; + VSLIST_INIT(&soap->priv.namespaces); + *soapp = soap; +} + +VCL_VOID +vmod_parser__fini(struct VPFX(soap_parser) **soapp) +{ + struct VPFX(soap_parser) *soap; + struct soap_namespace *ns, *ns2; + + TAKE_OBJ_NOTNULL(soap, soapp, SOAP_PARSER_MAGIC); + REPLACE(soap->vcl_name, NULL); + + // ref clean_vcl + VSLIST_FOREACH_SAFE(ns, &soap->priv.namespaces, list, ns2) { + VSLIST_REMOVE_HEAD(&soap->priv.namespaces, list); + FREE_OBJ(ns); + } + + FREE_OBJ(soap); +} + +VCL_VOID +vmod_parser_add_namespace(VRT_CTX, + struct VPFX(soap_parser) *soap, VCL_STRING prefix, VCL_STRING uri) +{ + struct soap_namespace *ns; + + CHECK_OBJ_NOTNULL(soap, SOAP_PARSER_MAGIC); + + if (ctx->method != VCL_MET_INIT) { + VRT_fail(ctx, "%s.add_namespace() may only be called " + "from vcl_init{}", soap->vcl_name); + return; + } + + if (prefix == NULL || *prefix == '\0' || + uri == NULL || *uri == '\0') { + VRT_fail(ctx, "%s.add_namespace: prefix or uri " + "empty", soap->vcl_name); + return; + } + + //// called in _init() + // soap_init_thread(ctx->vsl); + + if (test_ns(prefix, uri)) { + // XXX no idea if this can ever fail + VRT_fail(ctx, "%s.add_namespace: validation failed", + soap->vcl_name); + return; + } + + // ref vmod_add_namespace + ALLOC_OBJ(ns, PRIV_SOAP_NAMESPACE_MAGIC); + AN(ns); + + ns->prefix = prefix; + ns->uri = uri; + VSLIST_INSERT_HEAD(&soap->priv.namespaces, ns, list); +} + +sess_record * +obj_priv_soap_get(VRT_CTX, const struct VPFX(soap_parser) *soap) +{ + struct vmod_priv *priv; + + CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); + CHECK_OBJ_NOTNULL(soap, SOAP_PARSER_MAGIC); + + soap_init_thread(ctx); + + priv = VRT_priv_task(ctx, soap); + if (priv == NULL) { + VRT_fail(ctx, "No priv_task"); + return (NULL); + } + + return (priv_soap_get(ctx, priv)); +} + +VCL_STRING +vmod_parser_header_xpath(VRT_CTX, + struct VPFX(soap_parser) *soap, VCL_STRING xpath) +{ + struct priv_soap_task *soap_task = obj_priv_soap_get(ctx, soap); + + if (soap_task == NULL) + return (NULL); + AN(xpath); + + if (process_request(soap_task, HEADER_DONE, + soap->source, + soap->can_VRB_REMAIN) == 0) { + return (evaluate_xpath(&soap->priv, soap_task, + soap_task->req_xml->header, xpath)); + } + return (""); +} + +VCL_STRING +vmod_parser_body_xpath(VRT_CTX, + struct VPFX(soap_parser) *soap, VCL_STRING xpath) +{ + struct priv_soap_task *soap_task = obj_priv_soap_get(ctx, soap); + + if (soap_task == NULL) + return (NULL); + AN(xpath); + + if (process_request(soap_task, BODY_DONE, + soap->source, + soap->can_VRB_REMAIN) == 0) { + return (evaluate_xpath(&soap->priv, soap_task, + soap_task->req_xml->body, xpath)); + } + return (""); +} diff --git a/src/vmod_soap.h b/src/vmod_soap.h index e30cb46..7bd8d70 100644 --- a/src/vmod_soap.h +++ b/src/vmod_soap.h @@ -28,16 +28,6 @@ #ifndef __VMOD_SOAP__H__ #define __VMOD_SOAP__H__ -/* need vcl.h before vrt.h for vmod_evet_f typedef */ -#include "vcl.h" -#include "vrt.h" -#include "cache/cache.h" -#include "cache/cache_director.h" -#include "cache/cache_backend.h" -#include "vtim.h" -#include "vcc_soap_if.h" -#include - #include #include #include @@ -50,8 +40,6 @@ #include -#include "vmod_soap_http.h" - #define HANDLER_ERROR -1 #define HANDLER_SUCCESS_NOT_MODIFIED 0 #define HANDLER_SUCCESS_MODIFIED 1 @@ -82,14 +70,11 @@ typedef struct priv_soap_task { #define PRIV_SOAP_TASK_MAGIC 0x5FF52A40 VRT_CTX; apr_pool_t *pool; - struct soap_req_http *req_http; struct soap_req_xml *req_xml; int state; - ssize_t bytes_total; + enum vrb_what_e vrb_what; } sess_record; -#include "vmod_soap_request.h" -#include "vmod_soap_gzip.h" #include "vmod_soap_xml.h" #endif diff --git a/src/vmod_soap.vcc b/src/vmod_soap.vcc index de1532e..0774bf3 100644 --- a/src/vmod_soap.vcc +++ b/src/vmod_soap.vcc @@ -25,7 +25,8 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # -$Module soap 3 Soap VMOD +$Module soap 3 "Soap VMOD" +$ABI strict DESCRIPTION =========== @@ -69,6 +70,39 @@ It uses the namespace table (see `add_namespace`). $Function VOID synthetic(PRIV_TASK, INT, STRING) -Create a SOAP synthetic response's body containing SOAP FaultCode and +Create a SOAP synthetic response's body containing SOAP FaultCode and FaultMessage. Note that FaultMessage will contain any internal errors found. Format of the response depends of the SOAP version of the request (1.1 or 1.2). + +$Object parser(ENUM {req_body, resp_body} source, + [ENUM {cached, all} req_body]) + +Create a soap parser object on the given source. + +For ``source=req_body``, the ``req_body`` argument also needs to be +specified to define whether the parser is allowed to read ``all`` of +the requets body (potentially making it unavailable to the backend +request and other consumers) or if it is limited to the ``cached`` +portion. + +.. XXX run own filter chain? + +For ``source=resp_body``, the object in cache must be uncompressed, so +``set beresp.do_gunzip = true`` should be used on the backend side. + +Multiple instances on the same *source* can be instantiated to parse +multiple reponse bodies across restarts. + +$Method VOID .add_namespace(STRING prefix, STRING uri) + +Define namespace *prefix* from *uri* for the parser instance. + +Can only be called from ``vcl_init {}``. + +$Method STRING .header_xpath(STRING) + +Return an xpath value from the SOAP header. + +$Method STRING .body_xpath(STRING) + +Return an xpath value from the SOAP body. diff --git a/src/vmod_soap_gzip.c b/src/vmod_soap_gzip.c deleted file mode 100644 index 013f946..0000000 --- a/src/vmod_soap_gzip.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2019, Refinitiv - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS 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 COPYRIGHT - * HOLDER OR 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. - */ - -#include "vmod_soap.h" -#include "vmod_soap_http.h" -#include "vmod_soap_gzip.h" - -/* ------------------------------------------------------/ - Init HTTP context with encoding type -*/ -void init_gzip(struct soap_req_http *req_http) -{ - int init_flags = MAX_WBITS; - switch (req_http->encoding) { - case CE_GZIP: - init_flags += 16; - case CE_DEFLATE: - req_http->compression_stream = (z_stream*)apr_pcalloc(req_http->pool, sizeof(z_stream)); - XXXAZ(inflateInit2(req_http->compression_stream, init_flags)); - break; - default: - req_http->compression_stream = NULL; - break; - } -} - -/* ----------------------------------------------------/ - HTTP context cleanup -*/ -void clean_gzip(struct soap_req_http *req_http) -{ - AN(req_http); - if (req_http->compression_stream) { - inflateEnd(req_http->compression_stream); - req_http->compression_stream = NULL; - } -} - -/* -------------------------------------------------------------------------------------/ - Decompress one part -*/ -int uncompress_body_part(struct soap_req_http *req_http, body_part *compressed_body_part, body_part *uncompressed_body_part) -{ - char *init; - z_stream *stream; - char *buf; - Bytef *res_buf = 0; - int res_len = 0; - int sts = 0; - - init = WS_Snapshot(req_http->ctx->ws); - buf = WS_Alloc(req_http->ctx->ws, cache_param->gzip_buffer); - XXXAN(buf); - - stream = req_http->compression_stream; - AN(stream); - stream->next_in = (Bytef *)compressed_body_part->data; - stream->avail_in = compressed_body_part->length; - while(stream->avail_in > 0) - { - stream->next_out =(Bytef*) buf; - stream->avail_out = cache_param->gzip_buffer; - int err = inflate(stream, Z_SYNC_FLUSH); - if ((err != Z_OK && err != Z_STREAM_END) || - (err == Z_STREAM_END && stream->avail_in != 0)) - { - sts = 1; - break; - } - Bytef *new_buf = (Bytef*)malloc(stream->total_out); - if (res_buf) memcpy(new_buf, res_buf, res_len); - memcpy(new_buf + res_len, buf, stream->next_out - (Bytef*)buf); - if (res_buf) free(res_buf); - res_buf = new_buf; - res_len += stream->next_out - (Bytef*)buf; - } - uncompressed_body_part->data = (char*)apr_pmemdup(req_http->pool, res_buf, res_len); - uncompressed_body_part->length = res_len; - if (res_buf) free(res_buf); - WS_Reset(req_http->ctx->ws, init); - return sts; -} diff --git a/src/vmod_soap_gzip.h b/src/vmod_soap_gzip.h deleted file mode 100644 index 81e6a30..0000000 --- a/src/vmod_soap_gzip.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2019, Refinitiv - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS 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 COPYRIGHT - * HOLDER OR 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. - */ - -#ifndef __VMOD_SOAP_GZIP_H__ -#define __VMOD_SOAP_GZIP_H__ - -#include - -void init_gzip(struct soap_req_http *req_http); -void clean_gzip(struct soap_req_http *req_http); -int uncompress_body_part(struct soap_req_http *req_http, body_part *compressed_body_part, body_part *uncompressed_body_part); - -#endif diff --git a/src/vmod_soap_http.c b/src/vmod_soap_http.c deleted file mode 100644 index a36ea65..0000000 --- a/src/vmod_soap_http.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2019, Refinitiv - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS 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 COPYRIGHT - * HOLDER OR 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. - */ - -#include -#include -#include "cache/cache.h" -#include "vmod_soap_http.h" - -/* -------------------------------------------------------------------------------------/ - Returns the individual char value of the numeric status codes, according to RFC 2616 -*/ - -const char* http_status2str(const int status) -{ - switch (status) - { - default: return "Internal Server Error"; - case 100: return "Continue"; - case 101: return "Switching Protocols"; - case 200: return "OK"; - case 201: return "Created"; - case 202: return "Accepted"; - case 203: return "Non-Authoritative Information"; - case 204: return "No Content"; - case 205: return "Reset Content"; - case 206: return "Partial Content"; - case 300: return "Multiple Choices"; - case 301: return "Moved Permanently"; - case 302: return "Found"; - case 303: return "See Other"; - case 304: return "Not Modified"; - case 305: return "Use Proxy"; - case 306: return "(Unused)"; - case 307: return "Temporary Redirect"; - case 400: return "Bad Request"; - case 401: return "Unauthorized"; - case 402: return "Payment Required"; - case 403: return "Forbidden"; - case 404: return "Not Found"; - case 405: return "Method Not Allowed"; - case 406: return "Not Acceptable"; - case 407: return "Proxy Authentication Required"; - case 408: return "Request Timeout"; - case 409: return "Conflict"; - case 410: return "Gone"; - case 411: return "Length Required"; - case 412: return "Precondition Failed"; - case 413: return "Request Entity Too Large"; - case 414: return "Request-URI Too Long"; - case 415: return "Unsupported Media Type"; - case 416: return "Requested Range Not Satisfiable"; - case 417: return "Expectation Failed"; - case 500: return "Internal Server Error"; - case 501: return "Not Implemented"; - case 502: return "Bad Gateway"; - case 503: return "Service Unavailable"; - case 504: return "Gateway Timeout"; - case 505: return "HTTP Version Not Supported"; - } - return "Unknown"; -} - -int http_content_encoding(struct http *http) -{ - const char *ptr; - if (http_GetHdr(http, H_Content_Encoding, &ptr)) - { - if (strstr(ptr, "gzip") != 0) - { - return CE_GZIP; - } - else if (strstr(ptr, "deflate") != 0) - { - return CE_DEFLATE; - } - else - { - return CE_UNKNOWN; - } - } - return CE_NONE; -} diff --git a/src/vmod_soap_http.h b/src/vmod_soap_http.h deleted file mode 100644 index b67b203..0000000 --- a/src/vmod_soap_http.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2019, Refinitiv - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS 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 COPYRIGHT - * HOLDER OR 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. - */ - -#ifndef __VMOD_SOAP_HTTP_H__ -#define __VMOD_SOAP_HTTP_H__ - -const char* http_status2str(const int status); -int http_content_encoding(struct http *http); - -// Content-Encoding types -enum ce_type { - CE_UNKNOWN = -1, - CE_NONE = 0, - CE_GZIP = 1, - CE_DEFLATE = 2 -}; - -#endif diff --git a/src/vmod_soap_request.c b/src/vmod_soap_request.c deleted file mode 100644 index b430cec..0000000 --- a/src/vmod_soap_request.c +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2019, Refinitiv - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS 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 COPYRIGHT - * HOLDER OR 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. - */ - -#include "vmod_soap.h" -#include "vmod_soap_xml.h" -#include "vmod_soap_gzip.h" - -static ssize_t -fill_pipeline(struct soap_req_http *req_http, struct http_conn *htc, body_part *pipeline, ssize_t bytes_read, ssize_t bytes_total) -{ - char *buf; - ssize_t original_pipeline_len; - ssize_t i = 0; - ssize_t bytes_left; - - CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC); - assert(bytes_total > bytes_read); - bytes_left = bytes_total - bytes_read; - original_pipeline_len = 0; - if (htc->pipeline_b) { - original_pipeline_len = htc->pipeline_e - htc->pipeline_b; - assert(original_pipeline_len > 0); - // If varnish already fill pipeline with all necessary data - // fill pipeline variable with it, and skip call to read - if (original_pipeline_len >= bytes_left + bytes_read) { - pipeline->data = htc->pipeline_b + bytes_read; - pipeline->length = bytes_left; - return bytes_left; - } - buf = (char*)apr_palloc(req_http->pool, bytes_left + original_pipeline_len); - XXXAN(buf); - - memcpy(buf, htc->pipeline_b, original_pipeline_len); - } - else { - buf = (char*)apr_palloc(req_http->pool, bytes_left); - XXXAN(buf); - } - htc->pipeline_b = buf; - htc->pipeline_e = buf + original_pipeline_len; - - i = read(htc->fd, htc->pipeline_e, bytes_left); - if (i <= 0) { - if (htc->pipeline_b == htc->pipeline_e) { - htc->pipeline_b = NULL; - htc->pipeline_e = NULL; - } - // XXX: VTCP_Assert(i); // but also: EAGAIN - VSLb(req_http->ctx->vsl, SLT_FetchError, - "%s", strerror(errno)); - req_http->ctx->req->req_body_status = REQ_BODY_FAIL; - return (i); - } - pipeline->data = htc->pipeline_b + bytes_read; - pipeline->length = original_pipeline_len - bytes_read + i; - htc->pipeline_e = htc->pipeline_e + i; - - return (pipeline->length); -} - -/* -------------------------------------------------------------------------------------/ - Read body part from varnish pipeline and uncompress the data if necessary -*/ -int read_body_part(struct soap_req_http *req_http, ssize_t bytes_read, ssize_t bytes_total) -{ - body_part pipeline; - int res; - - res = fill_pipeline(req_http, req_http->ctx->req->htc, &pipeline, bytes_read, bytes_total); - if (res <= 0) - { - VSLb(req_http->ctx->vsl, SLT_Error, "v1_read error (%d bytes)", res); - return res; - } - VSLb(req_http->ctx->vsl, SLT_Debug, "v1_read %d bytes", res); - if (req_http->encoding == CE_NONE) { - req_http->body = pipeline; - } - else if (uncompress_body_part(req_http, &pipeline, &req_http->body) != 0) { - VSLb(req_http->ctx->vsl, SLT_Error, "Can't uncompress gzip body"); - return -1; - } - return res; -} - -void init_req_http(struct soap_req_http *req_http) -{ - AN(req_http); - req_http->encoding = http_content_encoding(req_http->ctx->http_req); - if (req_http->encoding == CE_GZIP) { - init_gzip(req_http); - } -} - -/* -------------------------------------------------------------------------------------/ - Return set of body parts back to varnish internal pipeline -*/ -void clean_req_http(struct soap_req_http *req_http) -{ - AN(req_http); - if (req_http->encoding == CE_GZIP) { - if (req_http->body.data) { - req_http->body.data = NULL; - req_http->body.length = 0; - } - clean_gzip(req_http); - } -} diff --git a/src/vmod_soap_request.h b/src/vmod_soap_request.h deleted file mode 100644 index 7f40fe1..0000000 --- a/src/vmod_soap_request.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2019, Refinitiv - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS 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 COPYRIGHT - * HOLDER OR 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. - */ - -#ifndef __VMOD_SOAP_REQUEST_H__ -#define __VMOD_SOAP_REQUEST_H__ - -typedef struct body_part { - char *data; - int length; -} body_part; - -struct soap_req_http { - unsigned magic; -#define SOAP_REQ_HTTP_MAGIC 0x5B13F1F0 - VRT_CTX; - apr_pool_t *pool; - - enum ce_type encoding; - - body_part body; - z_stream *compression_stream; -}; - -int read_body_part(struct soap_req_http *req_http, ssize_t bytes_read, ssize_t bytes_total); -int convert_parts(struct soap_req_http *req_http, apr_array_header_t *parts, char **buf); - -void init_req_http(struct soap_req_http *req_http); -void clean_req_http(struct soap_req_http *req_http); - - -#endif diff --git a/src/vmod_soap_xml.c b/src/vmod_soap_xml.c index 6b63f0b..753770f 100644 --- a/src/vmod_soap_xml.c +++ b/src/vmod_soap_xml.c @@ -25,10 +25,13 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "config.h" + +#include "cache/cache.h" #include "vmod_soap.h" +#include "vcc_soap_if.h" + #include "vmod_soap_xml.h" -#include "vmod_soap_http.h" -#include "vmod_soap_request.h" #include #include @@ -73,6 +76,25 @@ void add_soap_error(struct soap_req_xml *req_xml, int status, const char* fmt, . va_end(args); } +/* test-register a namespace */ +int +test_ns(VCL_STRING prefix, VCL_STRING uri) +{ + xmlDocPtr doc; + xmlXPathContextPtr xpathCtx; + int r; + + doc = xmlNewDoc(XMLSTR("1.0")); + XXXAN(doc); + xpathCtx = xmlXPathNewContext(doc); + XXXAN(xpathCtx); + r = xmlXPathRegisterNs(xpathCtx, XMLSTR(prefix), XMLSTR(uri)); + xmlXPathFreeContext(xpathCtx); + xmlFreeDoc(doc); + + return (r); +} + /* -------------------------------------------------------------------------------------/ Runs XPath expression against single xml node */ @@ -109,7 +131,7 @@ const char* evaluate_xpath(struct priv_soap_vcl *soap_vcl, struct priv_soap_task if( xpathObj->nodesetval->nodeTab[i]->children && xpathObj->nodesetval->nodeTab[i]->children->content ) { // Save free pointer to reset workspace in case of overflow - void *f = soap_task->ctx->ws->f; + intptr_t f = WS_Snapshot(soap_task->ctx->ws); char *res = WS_Copy(soap_task->ctx->ws, xpathObj->nodesetval->nodeTab[i]->children->content, -1); xmlXPathFreeContext(xpathCtx); xmlXPathFreeObject(xpathObj); @@ -324,18 +346,24 @@ void synth_soap_fault(struct soap_req_xml *req_xml, int code, const char* messag doc = xmlNewDoc(XMLSTR("1.0")); create_soap_fault(doc, req_xml->error_info); xmlDocDumpMemory(doc, &content, &length); - VRT_synth_page(req_xml->ctx, (const char*)content, vrt_magic_string_end); + VRT_synth_page(req_xml->ctx, TOSTRAND((const char*)content)); VRT_SetHdr(req_xml->ctx, &VGC_HDR_RESP_Content_2d_Type, - "application/soap+xml; charset=utf-8", - vrt_magic_string_end - ); + "application/soap+xml; charset=utf-8", NULL); xmlFree(content); xmlFreeDoc(doc); } -int parse_soap_chunk(struct soap_req_xml *soap_req_xml, const char *data, int length) +int v_matchproto_(objiterate_f) +soap_iter_f(void *priv, unsigned flush, const void *ptr, ssize_t len) { - int err = xmlParseChunk(soap_req_xml->parser, data, length, 0); + struct soap_req_xml *soap_req_xml; + int err; + + CAST_OBJ_NOTNULL(soap_req_xml, priv, SOAP_REQ_XML_MAGIC); + (void) flush; + AN(ptr); + + err = xmlParseChunk(soap_req_xml->parser, ptr, len, 0); // real error occured if (soap_req_xml->error || (err && !soap_req_xml->stop)) diff --git a/src/vmod_soap_xml.h b/src/vmod_soap_xml.h index 93411c2..aaebd97 100644 --- a/src/vmod_soap_xml.h +++ b/src/vmod_soap_xml.h @@ -68,7 +68,9 @@ void clean_xml(); void init_req_xml(struct soap_req_xml *req_xml); void clean_req_xml(struct soap_req_xml *req_xml); -int parse_soap_chunk(struct soap_req_xml *soap_req_xml, const char *data, int length); +int v_matchproto_(objiterate_f) +soap_iter_f(void *priv, unsigned flush, const void *ptr, ssize_t len); +int test_ns(VCL_STRING prefix, VCL_STRING uri); const char* evaluate_xpath(struct priv_soap_vcl *soap_vcl, struct priv_soap_task *soap_task, xmlNodePtr node, const char* xpath); #endif