From 338ad01dd0fd2844ab3f892a17090794fe506b98 Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Fri, 20 May 2022 16:08:53 +0200 Subject: [PATCH 01/24] Adjustments for current varnish-cache --- configure.ac | 10 +++------- src/Makefile.am | 2 +- src/vmod_soap.c | 3 ++- src/vmod_soap.h | 2 -- 4 files changed, 6 insertions(+), 11 deletions(-) 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..82f8a5a 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 diff --git a/src/vmod_soap.c b/src/vmod_soap.c index 21a4015..8597521 100644 --- a/src/vmod_soap.c +++ b/src/vmod_soap.c @@ -230,7 +230,8 @@ int process_request(struct priv_soap_task *task, enum soap_state state) * */ int __match_proto__(vmod_event_f) - event_function(VRT_CTX, struct vmod_priv *priv /* PRIV_VCL */, enum vcl_event_e e) + VPFX(event_function)(VRT_CTX, struct vmod_priv *priv /* PRIV_VCL */, + enum vcl_event_e e) { struct priv_soap_vcl *priv_soap_vcl; diff --git a/src/vmod_soap.h b/src/vmod_soap.h index e30cb46..5207c43 100644 --- a/src/vmod_soap.h +++ b/src/vmod_soap.h @@ -29,8 +29,6 @@ #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" From c797fe1ca2151630a97874e6e4de01029a241722 Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Fri, 20 May 2022 16:09:43 +0200 Subject: [PATCH 02/24] Adjustments for current varnish-cache: __match_proto__ -> v_matchproto_ --- src/vmod_soap.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vmod_soap.c b/src/vmod_soap.c index 8597521..179acdf 100644 --- a/src/vmod_soap.c +++ b/src/vmod_soap.c @@ -229,7 +229,7 @@ int process_request(struct priv_soap_task *task, enum soap_state state) * (un)registration here. * */ -int __match_proto__(vmod_event_f) +int v_matchproto_(vmod_event_f) VPFX(event_function)(VRT_CTX, struct vmod_priv *priv /* PRIV_VCL */, enum vcl_event_e e) { @@ -291,7 +291,7 @@ 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); @@ -299,7 +299,7 @@ VCL_BOOL __match_proto__(td_soap_is_valid) return (process_request(soap_task, ACTION_AVAILABLE) == 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); @@ -309,7 +309,7 @@ VCL_STRING __match_proto__(td_soap_action) 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); @@ -319,7 +319,7 @@ VCL_STRING __match_proto__(td_soap_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; @@ -335,7 +335,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;; @@ -353,7 +353,7 @@ VCL_STRING __match_proto__(td_soap_xpath_header) 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;; @@ -371,7 +371,7 @@ VCL_STRING __match_proto__(td_soap_xpath_body) 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; From 54f051c430e9f1c5f735a73e08ab890209119d91 Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Fri, 20 May 2022 16:15:22 +0200 Subject: [PATCH 03/24] Adjustments for current varnish-cache: task methods --- src/vmod_soap.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/vmod_soap.c b/src/vmod_soap.c index 179acdf..e266d4a 100644 --- a/src/vmod_soap.c +++ b/src/vmod_soap.c @@ -61,11 +61,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) { @@ -114,11 +115,11 @@ 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); @@ -224,6 +225,12 @@ int process_request(struct priv_soap_task *task, enum soap_state state) return (0); } +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. @@ -248,7 +255,7 @@ int v_matchproto_(vmod_event_f) 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; @@ -268,6 +275,12 @@ int v_matchproto_(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; @@ -276,7 +289,7 @@ 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) { From 103350c1cd96bd0ad8c714b5f3556a992b8e6103 Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Fri, 20 May 2022 16:19:01 +0200 Subject: [PATCH 04/24] Quick fix for cache_param access Vmods are banned from cache_param for quite some time now, so the vmod should have its own parameter for the gzip buffer size. --- src/vmod_soap_gzip.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vmod_soap_gzip.c b/src/vmod_soap_gzip.c index 013f946..1197929 100644 --- a/src/vmod_soap_gzip.c +++ b/src/vmod_soap_gzip.c @@ -29,6 +29,10 @@ #include "vmod_soap_http.h" #include "vmod_soap_gzip.h" +// XXX this should be replaced by a vmod parameter +#include "common/common_param.h" +extern volatile struct params * cache_param; + /* ------------------------------------------------------/ Init HTTP context with encoding type */ From c1e92432348edda6da43d3f58c73c2620b0cac2e Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Fri, 20 May 2022 16:27:33 +0200 Subject: [PATCH 05/24] Adjustments for current varnish-cache: Workspace interface --- src/vmod_soap_gzip.c | 2 +- src/vmod_soap_xml.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vmod_soap_gzip.c b/src/vmod_soap_gzip.c index 1197929..9ca1ee5 100644 --- a/src/vmod_soap_gzip.c +++ b/src/vmod_soap_gzip.c @@ -69,7 +69,7 @@ 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) { - char *init; + intptr_t init; z_stream *stream; char *buf; Bytef *res_buf = 0; diff --git a/src/vmod_soap_xml.c b/src/vmod_soap_xml.c index 6b63f0b..dab753b 100644 --- a/src/vmod_soap_xml.c +++ b/src/vmod_soap_xml.c @@ -109,7 +109,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); From 1a8451ee2b51d4d74ce74debf308fb703210f719 Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Fri, 20 May 2022 16:28:02 +0200 Subject: [PATCH 06/24] Adjustments for current varnish-cache: VRT interface --- src/vmod_soap_xml.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/vmod_soap_xml.c b/src/vmod_soap_xml.c index dab753b..7c9a5c5 100644 --- a/src/vmod_soap_xml.c +++ b/src/vmod_soap_xml.c @@ -324,11 +324,9 @@ 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); } From b441c2ed35a231aff11e6a32aec8babe2d621c60 Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Fri, 20 May 2022 16:37:25 +0200 Subject: [PATCH 07/24] Adjustments for current varnish-cache: Misc & includes Much more can be done here to reduce the required varnish internal headers, but for now this commit concludes the required changes for varnish-cache master as of now (post 7.1) --- src/vmod_soap.c | 4 ++++ src/vmod_soap.h | 8 -------- src/vmod_soap_gzip.c | 5 +++++ src/vmod_soap_request.c | 10 ++++++++-- src/vmod_soap_xml.c | 5 +++++ 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/vmod_soap.c b/src/vmod_soap.c index e266d4a..8b34ab7 100644 --- a/src/vmod_soap.c +++ b/src/vmod_soap.c @@ -25,7 +25,11 @@ * 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" #define POOL_KEY "VRN_IH_PK" diff --git a/src/vmod_soap.h b/src/vmod_soap.h index 5207c43..139ca67 100644 --- a/src/vmod_soap.h +++ b/src/vmod_soap.h @@ -28,14 +28,6 @@ #ifndef __VMOD_SOAP__H__ #define __VMOD_SOAP__H__ -/* need vcl.h before vrt.h for vmod_evet_f typedef */ -#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 diff --git a/src/vmod_soap_gzip.c b/src/vmod_soap_gzip.c index 9ca1ee5..cdf91f1 100644 --- a/src/vmod_soap_gzip.c +++ b/src/vmod_soap_gzip.c @@ -25,7 +25,12 @@ * 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_http.h" #include "vmod_soap_gzip.h" diff --git a/src/vmod_soap_request.c b/src/vmod_soap_request.c index b430cec..b98e7f4 100644 --- a/src/vmod_soap_request.c +++ b/src/vmod_soap_request.c @@ -25,7 +25,13 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "config.h" + +// XXX for struct http_conn, should be replaced with buffered body +#include "cache/cache_varnishd.h" #include "vmod_soap.h" +#include "vcc_soap_if.h" + #include "vmod_soap_xml.h" #include "vmod_soap_gzip.h" @@ -63,7 +69,7 @@ fill_pipeline(struct soap_req_http *req_http, struct http_conn *htc, body_part * htc->pipeline_b = buf; htc->pipeline_e = buf + original_pipeline_len; - i = read(htc->fd, htc->pipeline_e, bytes_left); + i = read(*htc->rfd, htc->pipeline_e, bytes_left); if (i <= 0) { if (htc->pipeline_b == htc->pipeline_e) { htc->pipeline_b = NULL; @@ -72,7 +78,7 @@ fill_pipeline(struct soap_req_http *req_http, struct http_conn *htc, body_part * // 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; + req_http->ctx->req->req_body_status = BS_ERROR; return (i); } pipeline->data = htc->pipeline_b + bytes_read; diff --git a/src/vmod_soap_xml.c b/src/vmod_soap_xml.c index 7c9a5c5..2d584d0 100644 --- a/src/vmod_soap_xml.c +++ b/src/vmod_soap_xml.c @@ -25,7 +25,12 @@ * 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" From 47ccd3ea4ee04ce1e2c5750f1b5d7bcdcee45bbf Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Fri, 20 May 2022 16:42:54 +0200 Subject: [PATCH 08/24] Remove superfluous mutex varnish-cache serializes the vmod events, so, unless the vmod itself has any concurent access, no mutex is required. --- src/vmod_soap.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/vmod_soap.c b/src/vmod_soap.c index 8b34ab7..c6ad596 100644 --- a/src/vmod_soap.c +++ b/src/vmod_soap.c @@ -33,7 +33,6 @@ #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; @@ -250,12 +249,10 @@ int v_matchproto_(vmod_event_f) switch (e) { case VCL_EVENT_LOAD: - AZ(pthread_mutex_lock(&soap_mutex)); if(0 == refcount++) { init_xml(); init_apr(); } - AZ(pthread_mutex_unlock(&soap_mutex)); priv_soap_vcl = init_vcl(); priv->priv = priv_soap_vcl; @@ -266,12 +263,10 @@ int v_matchproto_(vmod_event_f) 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); From bd51e8657778342aed3c7243edcc9e50b30e6d5e Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Sat, 21 May 2022 18:08:27 +0200 Subject: [PATCH 09/24] vmod_soap_gzip.c: Use workspace instead of apr pool --- src/vmod_soap_gzip.c | 60 ++++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/src/vmod_soap_gzip.c b/src/vmod_soap_gzip.c index cdf91f1..e67028a 100644 --- a/src/vmod_soap_gzip.c +++ b/src/vmod_soap_gzip.c @@ -34,21 +34,24 @@ #include "vmod_soap_http.h" #include "vmod_soap_gzip.h" -// XXX this should be replaced by a vmod parameter -#include "common/common_param.h" -extern volatile struct params * cache_param; - /* ------------------------------------------------------/ Init HTTP context with encoding type */ void init_gzip(struct soap_req_http *req_http) { + VRT_CTX; int init_flags = MAX_WBITS; + + ctx = req_http->ctx; + CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); + 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)); + req_http->compression_stream = + (z_stream*)WS_Alloc(ctx->ws, sizeof(z_stream)); + XXXAN(req_http->compression_stream); XXXAZ(inflateInit2(req_http->compression_stream, init_flags)); break; default: @@ -74,25 +77,35 @@ 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) { - intptr_t init; + VRT_CTX; z_stream *stream; char *buf; - Bytef *res_buf = 0; - int res_len = 0; + unsigned len; int sts = 0; - init = WS_Snapshot(req_http->ctx->ws); - buf = WS_Alloc(req_http->ctx->ws, cache_param->gzip_buffer); - XXXAN(buf); + if (compressed_body_part->length == 0) + return (0); + + ctx = req_http->ctx; + CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); stream = req_http->compression_stream; AN(stream); + + len = WS_ReserveAll(ctx->ws); + XXXAN(len); + stream->avail_out = len; + + buf = WS_Reservation(ctx->ws); + AN(buf); + stream->next_out = (Bytef *)buf; + 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; + + AN(stream->avail_in); + + while(stream->avail_in > 0) { int err = inflate(stream, Z_SYNC_FLUSH); if ((err != Z_OK && err != Z_STREAM_END) || (err == Z_STREAM_END && stream->avail_in != 0)) @@ -100,16 +113,13 @@ int uncompress_body_part(struct soap_req_http *req_http, body_part *compressed_b 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); + assert(stream->avail_out <= len); + len -= stream->avail_out; + + uncompressed_body_part->data = buf; + uncompressed_body_part->length = len; + + WS_Release(ctx->ws, len); return sts; } From 6fa1d0ac5a0e750245a6ad3483a63fcc3c0e5cdd Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Thu, 2 Jun 2022 09:45:53 +0200 Subject: [PATCH 10/24] Define OO interface --- src/vmod_soap.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++ src/vmod_soap.vcc | 30 +++++++++++++- 2 files changed, 129 insertions(+), 1 deletion(-) diff --git a/src/vmod_soap.c b/src/vmod_soap.c index c6ad596..2e8ef92 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 @@ -393,3 +394,102 @@ VCL_VOID v_matchproto_(td_soap_synthetic) synth_soap_fault(soap_task->req_xml, soap_code, soap_message); } + +/* + * ============================================================ + * + * Object Interface (rework) + */ + +enum soap_source { + SOAPS_INVALID = 0, + SOAPS_REQ_BODY, + SOAPS_RESP_BODY +}; + +struct VPFX(soap_parser) { + unsigned magic; +#define SOAP_PARSER_MAGIC 0x017ce81e + char *vcl_name; + enum soap_source source; + enum vrb_what_e vrb_what; +}; + +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); + + ALLOC_OBJ(soap, SOAP_PARSER_MAGIC); + AN(soap); + + REPLACE(soap->vcl_name, vcl_name); + 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; + } + if (args->req_body == VENUM(all)) + soap->vrb_what = VRB_ALL; + else if (args->req_body == VENUM(cached)) + soap->vrb_what = VRB_CACHED; + else + WRONG("req_body argument"); + } else if (args->source == VENUM(resp_body)) { + soap->source = SOAPS_RESP_BODY; + } else { + WRONG("source argument"); + } + *soapp = soap; +} + +VCL_VOID +vmod_parser__fini(struct VPFX(soap_parser) **soapp) +{ + struct VPFX(soap_parser) *soap; + + TAKE_OBJ_NOTNULL(soap, soapp, SOAP_PARSER_MAGIC); + REPLACE(soap->vcl_name, NULL); + FREE_OBJ(soap); +} + +VCL_VOID +vmod_parser_add_namespace(VRT_CTX, + struct VPFX(soap_parser) *soap, VCL_STRING pfx, VCL_STRING uri) +{ + + CHECK_OBJ_NOTNULL(soap, SOAP_PARSER_MAGIC); + AN(pfx); + AN(uri); +} + +VCL_STRING +vmod_parser_header_xpath(VRT_CTX, + struct VPFX(soap_parser) *soap, VCL_STRING xpath) +{ + + CHECK_OBJ_NOTNULL(soap, SOAP_PARSER_MAGIC); + AN(xpath); + return (NULL); +} + +VCL_STRING +vmod_parser_body_xpath(VRT_CTX, + struct VPFX(soap_parser) *soap, VCL_STRING xpath) +{ + + CHECK_OBJ_NOTNULL(soap, SOAP_PARSER_MAGIC); + AN(xpath); + return (NULL); +} diff --git a/src/vmod_soap.vcc b/src/vmod_soap.vcc index de1532e..0b30ed6 100644 --- a/src/vmod_soap.vcc +++ b/src/vmod_soap.vcc @@ -69,6 +69,34 @@ 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. + +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. From 06b56947ac5d5aefdabe2bd2f141d0708eb6bb2a Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Thu, 2 Jun 2022 14:04:06 +0200 Subject: [PATCH 11/24] oo interface: Basic namespaces support --- src/vmod_soap.c | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/vmod_soap.c b/src/vmod_soap.c index 2e8ef92..5c32b84 100644 --- a/src/vmod_soap.c +++ b/src/vmod_soap.c @@ -408,11 +408,12 @@ enum soap_source { }; struct VPFX(soap_parser) { - unsigned magic; -#define SOAP_PARSER_MAGIC 0x017ce81e - char *vcl_name; - enum soap_source source; - enum vrb_what_e vrb_what; + unsigned magic; +#define SOAP_PARSER_MAGIC 0x017ce81e + enum soap_source source; + enum vrb_what_e vrb_what; + char *vcl_name; + VSLIST_HEAD(, soap_namespace) namespaces; }; VCL_VOID @@ -430,7 +431,6 @@ vmod_parser__init(VRT_CTX, struct VPFX(soap_parser) **soapp, ALLOC_OBJ(soap, SOAP_PARSER_MAGIC); AN(soap); - REPLACE(soap->vcl_name, vcl_name); if (args->source == VENUM(req_body)) { soap->source = SOAPS_REQ_BODY; if (! args->valid_req_body) { @@ -451,6 +451,8 @@ vmod_parser__init(VRT_CTX, struct VPFX(soap_parser) **soapp, } else { WRONG("source argument"); } + REPLACE(soap->vcl_name, vcl_name); + VSLIST_INIT(&soap->namespaces); *soapp = soap; } @@ -458,20 +460,42 @@ 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->namespaces, list, ns2) { + VSLIST_REMOVE_HEAD(&soap->namespaces, list); + FREE_OBJ(ns); + } + FREE_OBJ(soap); } VCL_VOID vmod_parser_add_namespace(VRT_CTX, - struct VPFX(soap_parser) *soap, VCL_STRING pfx, VCL_STRING uri) + struct VPFX(soap_parser) *soap, VCL_STRING prefix, VCL_STRING uri) { + struct soap_namespace *ns; CHECK_OBJ_NOTNULL(soap, SOAP_PARSER_MAGIC); - AN(pfx); - AN(uri); + + if (prefix == NULL || *prefix == '\0' || + uri == NULL || *uri == '\0') { + VRT_fail(ctx, "%s.add_namespace: prefix or uri " + "empty", 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->namespaces, ns, list); } VCL_STRING From cbbd2222cbd7d4b8278f768fea388dd0f9f0821c Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Thu, 2 Jun 2022 14:03:34 +0200 Subject: [PATCH 12/24] Require threads support in libxml2 --- src/vmod_soap.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vmod_soap.c b/src/vmod_soap.c index 5c32b84..2cf3f9a 100644 --- a/src/vmod_soap.c +++ b/src/vmod_soap.c @@ -250,6 +250,10 @@ int v_matchproto_(vmod_event_f) switch (e) { case VCL_EVENT_LOAD: + if (! xmlHasFeature(XML_WITH_THREAD)) { + VRT_fail(ctx, "Need libxml2 with threads support"); + return (1); + } if(0 == refcount++) { init_xml(); init_apr(); From d52291d78c1b26029bb6a69f151ea908d5952c3b Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Thu, 2 Jun 2022 14:50:21 +0200 Subject: [PATCH 13/24] Register error callbacks writing to ctx->vsl / ctx->msg --- src/vmod_soap.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/src/vmod_soap.c b/src/vmod_soap.c index 2cf3f9a..58bb82e 100644 --- a/src/vmod_soap.c +++ b/src/vmod_soap.c @@ -28,7 +28,10 @@ #include "config.h" -#include "cache/cache.h" +#include +#include +#include + #include "vmod_soap.h" #include "vcc_soap_if.h" @@ -405,6 +408,59 @@ VCL_VOID v_matchproto_(td_soap_synthetic) * 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); + } +} + enum soap_source { SOAPS_INVALID = 0, SOAPS_REQ_BODY, @@ -432,6 +488,8 @@ vmod_parser__init(VRT_CTX, struct VPFX(soap_parser) **soapp, AN(vcl_name); AN(args); + soap_init_thread(ctx); + ALLOC_OBJ(soap, SOAP_PARSER_MAGIC); AN(soap); @@ -486,6 +544,12 @@ vmod_parser_add_namespace(VRT_CTX, 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 " @@ -493,6 +557,9 @@ vmod_parser_add_namespace(VRT_CTX, return; } + //// called in _init() + // soap_init_thread(ctx->vsl); + // ref vmod_add_namespace ALLOC_OBJ(ns, PRIV_SOAP_NAMESPACE_MAGIC); AN(ns); From 9f13d853f93ffbbf0d782f99bb0c07a5b62789d4 Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Thu, 2 Jun 2022 15:14:38 +0200 Subject: [PATCH 14/24] oo interface: .add_namespace() test arguments --- src/tests/b00014.vtc | 65 ++++++++++++++++++++++++++++++++++++++++++++ src/vmod_soap.c | 7 +++++ src/vmod_soap_xml.c | 19 +++++++++++++ src/vmod_soap_xml.h | 1 + 4 files changed, 92 insertions(+) create mode 100644 src/tests/b00014.vtc diff --git a/src/tests/b00014.vtc b/src/tests/b00014.vtc new file mode 100644 index 0000000..7af800f --- /dev/null +++ b/src/tests/b00014.vtc @@ -0,0 +1,65 @@ +varnishtest "SOAP OO: valid xpath header and body" + +server s1 { + rxreq + expect req.http.soap-uuid == "lenin" + txresp + + rxreq + expect req.http.soap-uuid == "obama" + txresp +} -start + +varnish v1 -vcl+backend { + import ${vmod_soap}; + + sub vcl_init { + new soap_parser = soap.parser( + source=req_body, + req_body=all); + soap_parser.add_namespace("ui", "http://schemas.reuters.com/ns/2005/08/infrastructure/tornado_soap"); + soap_parser.add_namespace("uuid", "http://schemas.reuters.com/ns/2007/10/cp/user_identity"); + } + + sub vcl_recv { + return (synth(200)); # XXX WIP + + if(req.url == "/header") { + set req.http.soap-uuid = + soap.xpath_header("ui:userIdentity/uuid:UUID"); + } + else { + set req.http.soap-uuid = + soap.xpath_body("ui:userIdentity/uuid:UUID"); + } + } +} -start + +client c1 { + timeout 8 + txreq -req POST -url "/header" -body { + +
+ + lenin + +
+ +
+ } + rxresp + expect resp.status == 200 + + txreq -req POST -url "/body" -body { + +
+
+ + + obama + +
+ } + rxresp + expect resp.status == 200 +} -run diff --git a/src/vmod_soap.c b/src/vmod_soap.c index 58bb82e..f82052d 100644 --- a/src/vmod_soap.c +++ b/src/vmod_soap.c @@ -560,6 +560,13 @@ vmod_parser_add_namespace(VRT_CTX, //// 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); diff --git a/src/vmod_soap_xml.c b/src/vmod_soap_xml.c index 2d584d0..e3bbeb5 100644 --- a/src/vmod_soap_xml.c +++ b/src/vmod_soap_xml.c @@ -78,6 +78,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 */ diff --git a/src/vmod_soap_xml.h b/src/vmod_soap_xml.h index 93411c2..d77315b 100644 --- a/src/vmod_soap_xml.h +++ b/src/vmod_soap_xml.h @@ -69,6 +69,7 @@ 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 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 From b2dd173cfc80128e11fd30a3ca13f37ec575297c Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Thu, 2 Jun 2022 16:43:08 +0200 Subject: [PATCH 15/24] soap task on workspace --- src/vmod_soap.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/vmod_soap.c b/src/vmod_soap.c index f82052d..ffb4c3e 100644 --- a/src/vmod_soap.c +++ b/src/vmod_soap.c @@ -98,22 +98,35 @@ 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)); + TASK_ALLOC_OBJ(ctx, soap_task->req_http, SOAP_REQ_HTTP_MAGIC); + if (! soap_task->req_http) + return (NULL); - 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; @@ -136,16 +149,13 @@ static void clean_task(VRT_CTX, void *priv) 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) From 1770be57b75c5bafb6e6a9c83a9ec8d8ef64d412 Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Thu, 2 Jun 2022 16:55:14 +0200 Subject: [PATCH 16/24] oo: soap task mechanics --- src/vmod_soap.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/vmod_soap.c b/src/vmod_soap.c index ffb4c3e..bdb8bc3 100644 --- a/src/vmod_soap.c +++ b/src/vmod_soap.c @@ -586,12 +586,33 @@ vmod_parser_add_namespace(VRT_CTX, VSLIST_INSERT_HEAD(&soap->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); - CHECK_OBJ_NOTNULL(soap, SOAP_PARSER_MAGIC); + if (soap_task == NULL) + return (NULL); AN(xpath); return (NULL); } @@ -600,8 +621,10 @@ 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); - CHECK_OBJ_NOTNULL(soap, SOAP_PARSER_MAGIC); + if (soap_task == NULL) + return (NULL); AN(xpath); return (NULL); } From d6d6897214c58b8d0b3228f15e28c4177d682207 Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Fri, 3 Jun 2022 11:00:23 +0200 Subject: [PATCH 17/24] Refactor process_request: pull out init --- src/vmod_soap.c | 55 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/src/vmod_soap.c b/src/vmod_soap.c index bdb8bc3..562cd72 100644 --- a/src/vmod_soap.c +++ b/src/vmod_soap.c @@ -158,32 +158,49 @@ static void clean_task(VRT_CTX, void *priv) INIT_OBJ(soap_task, PRIV_SOAP_TASK_MAGIC); } +static int +process_init_read(struct priv_soap_task *task) +{ + + AN(task); + assert(task->state == NONE); + + VSLb(task->ctx->vsl, SLT_Debug, "process_request 1: %d", task->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); + + 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; + return (0); +} + int process_request(struct priv_soap_task *task, enum soap_state state) { + int r; + 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); - - 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; + r = process_init_read(task); + if (r) + return (r); break; case INIT: case HEADER_DONE: From 74fe291482d7384dd90f008020788fb37526a2aa Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Fri, 3 Jun 2022 11:02:35 +0200 Subject: [PATCH 18/24] Refactor process_request: generalize debug --- src/vmod_soap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vmod_soap.c b/src/vmod_soap.c index 562cd72..05aa45d 100644 --- a/src/vmod_soap.c +++ b/src/vmod_soap.c @@ -165,7 +165,6 @@ process_init_read(struct priv_soap_task *task) AN(task); assert(task->state == NONE); - VSLb(task->ctx->vsl, SLT_Debug, "process_request 1: %d", task->state); task->req_http->pool = task->pool; task->req_http->ctx = task->ctx; init_req_http(task->req_http); @@ -196,6 +195,8 @@ int process_request(struct priv_soap_task *task, enum soap_state state) VSLb(task->ctx->vsl, SLT_Debug, "process_request 0: %d/%d", task->state, state); ssize_t bytes_read = 0; while (task->state < state) { + VSLb(task->ctx->vsl, SLT_Debug, "process_request: %d/%d (%ld bytes)", + task->state, state, task->bytes_total); switch (task->state) { case NONE: // init r = process_init_read(task); @@ -205,7 +206,6 @@ int process_request(struct priv_soap_task *task, enum soap_state state) 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); From 18fbaf8f40552fdc235b41131feeaa3eea99a967 Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Fri, 3 Jun 2022 11:10:22 +0200 Subject: [PATCH 19/24] Refactor process_request: pull out process_read --- src/vmod_soap.c | 103 +++++++++++++++++++++++++++++------------------- src/vmod_soap.h | 1 + 2 files changed, 64 insertions(+), 40 deletions(-) diff --git a/src/vmod_soap.c b/src/vmod_soap.c index 05aa45d..3147c94 100644 --- a/src/vmod_soap.c +++ b/src/vmod_soap.c @@ -188,12 +188,71 @@ process_init_read(struct priv_soap_task *task) return (0); } +static int +process_read(struct priv_soap_task *task) +{ + + AN(task); + assert(task->state == INIT || + task->state == HEADER_DONE || + task->state == ACTION_AVAILABLE); + + if (task->bytes_total <= 0) { + VSLb(task->ctx->vsl, SLT_Error, "Not enough data"); + return (-1); + } + // If everything is read, but state not switched to BODY_DONE that mean + // XML body isn't present in request + if (task->bytes_read >= task->bytes_total) { + VSLb(task->ctx->vsl, SLT_Error, + "SOAP: http read error: incomplete xml"); + return (-1); + } + while (task->bytes_read < task->bytes_total) { + int just_read = read_body_part(task->req_http, + task->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); + } + task->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: " + "total %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; + } + } + return (0); +} + int process_request(struct priv_soap_task *task, enum soap_state state) { int r; VSLb(task->ctx->vsl, SLT_Debug, "process_request 0: %d/%d", task->state, state); - ssize_t bytes_read = 0; while (task->state < state) { VSLb(task->ctx->vsl, SLT_Debug, "process_request: %d/%d (%ld bytes)", task->state, state, task->bytes_total); @@ -206,45 +265,9 @@ int process_request(struct priv_soap_task *task, enum soap_state state) case INIT: case HEADER_DONE: case ACTION_AVAILABLE: - if (task->bytes_total <= 0) { - VSLb(task->ctx->vsl, SLT_Error, "Not enough data"); - return (-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); - } - 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; - } - } + r = process_read(task); + if (r) + return (r); break; case BODY_DONE: // read from memory case DONE: diff --git a/src/vmod_soap.h b/src/vmod_soap.h index 139ca67..5626ac9 100644 --- a/src/vmod_soap.h +++ b/src/vmod_soap.h @@ -75,6 +75,7 @@ typedef struct priv_soap_task { struct soap_req_http *req_http; struct soap_req_xml *req_xml; int state; + ssize_t bytes_read; ssize_t bytes_total; } sess_record; From bc866aa1b82a6c1d5d33ec30be628b0ddebc9314 Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Fri, 3 Jun 2022 11:12:07 +0200 Subject: [PATCH 20/24] gc unused state and panic for unknown state --- src/vmod_soap.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/vmod_soap.c b/src/vmod_soap.c index 3147c94..5577d98 100644 --- a/src/vmod_soap.c +++ b/src/vmod_soap.c @@ -46,7 +46,6 @@ enum soap_state { 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 }; /* -------------------------------------------------------------------------------------/ @@ -270,12 +269,10 @@ int process_request(struct priv_soap_task *task, enum soap_state state) 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); break; default: - VSLb(task->ctx->vsl, SLT_Debug, "process_request 9: %d/%d", task->state, state); - break; + WRONG("task->state"); } } VSLb(task->ctx->vsl, SLT_Debug, "process_request .: %d/%d", task->state, state); From 1cd6768debc7357640d4b83ddcaa1e99a42fb505 Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Tue, 7 Jun 2022 18:35:16 +0200 Subject: [PATCH 21/24] Rewrite request body processing to use proposed new API See https://github.com/varnishcache/varnish-cache/pull/3798#issuecomment-1148576467 Changes: * need std.cache_req_body() now Unless the request body is only used by the soap vmod (that is, whenever it is to be sent to the backend also), std.cache_req_body(), optionally with the partial argument, needs to be used to cache at least as much of the request body to fulfil any xml lookup used. Partial caching can be used if, for example, only the XML header is read and the header is known to be placed at the top. * VCL is now responsible for checking Content-Encoding ... because it could push filters to support other Content-Encodings * Varnish Cache supports chunked Encoding for bodies The test for a body with no Content-Length and no Chunked-Encoding did not make sense, request bodies could never be closed with "EOL" semantics (write side shutdown), not even in HTTP/1.0 https://www.rfc-editor.org/rfc/rfc1945.html#section-8.3 A valid Content-Length is required on all HTTP/1.0 POST requests. * Polish vcc file $ABI is implicitly required by varnish-cache as of af87f0cd1a7b8be8a124dca355f04dd641bc5512 The $Module quotes are currently not required. --- src/Makefile.am | 5 +- src/tests/a00000.vtc | 7 ++ src/tests/a00001.vtc | 9 ++- src/tests/a00002.vtc | 2 + src/tests/a00003.vtc | 2 + src/tests/a00004.vtc | 2 + src/tests/a00005.vtc | 3 + src/tests/a00006.vtc | 16 ++--- src/tests/a00007.vtc | 3 + src/tests/b00000.vtc | 4 +- src/tests/b00001.vtc | 2 + src/tests/b00002.vtc | 2 + src/tests/b00003.vtc | 2 + src/tests/b00004.vtc | 2 + src/tests/b00005.vtc | 2 + src/tests/b00006.vtc | 2 + src/tests/b00007.vtc | 2 + src/tests/b00008.vtc | 2 + src/tests/c00000.vtc | 2 + src/tests/c00001.vtc | 2 + src/tests/c00002.vtc | 5 ++ src/tests/g00000.vtc | 8 ++- src/tests/r00000.vtc | 18 +++--- src/vmod_soap.c | 139 ++++++++++++++-------------------------- src/vmod_soap.h | 8 +-- src/vmod_soap.vcc | 3 +- src/vmod_soap_gzip.c | 125 ------------------------------------ src/vmod_soap_gzip.h | 37 ----------- src/vmod_soap_http.c | 106 ------------------------------ src/vmod_soap_http.h | 42 ------------ src/vmod_soap_request.c | 138 --------------------------------------- src/vmod_soap_request.h | 55 ---------------- src/vmod_soap_xml.c | 14 ++-- src/vmod_soap_xml.h | 3 +- 34 files changed, 139 insertions(+), 635 deletions(-) delete mode 100644 src/vmod_soap_gzip.c delete mode 100644 src/vmod_soap_gzip.h delete mode 100644 src/vmod_soap_http.c delete mode 100644 src/vmod_soap_http.h delete mode 100644 src/vmod_soap_request.c delete mode 100644 src/vmod_soap_request.h diff --git a/src/Makefile.am b/src/Makefile.am index 82f8a5a..ba045aa 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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/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 5577d98..40a8cbc 100644 --- a/src/vmod_soap.c +++ b/src/vmod_soap.c @@ -46,6 +46,7 @@ enum soap_state { HEADER_DONE, // Header element completely read ACTION_AVAILABLE, // Body parsing is started and action name and namespace available BODY_DONE, // Body element completely read + FAILED }; /* -------------------------------------------------------------------------------------/ @@ -117,10 +118,6 @@ static struct priv_soap_task* init_task(VRT_CTX) soap_task->ctx = ctx; - TASK_ALLOC_OBJ(ctx, soap_task->req_http, SOAP_REQ_HTTP_MAGIC); - if (! soap_task->req_http) - return (NULL); - TASK_ALLOC_OBJ(ctx, soap_task->req_xml, SOAP_REQ_XML_MAGIC); if (! soap_task->req_xml) return (NULL); @@ -144,13 +141,8 @@ static void clean_task(VRT_CTX, void *priv) 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); - INIT_OBJ(soap_task->req_http, SOAP_REQ_HTTP_MAGIC); - AN(soap_task->pool); apr_pool_destroy(soap_task->pool); @@ -164,98 +156,57 @@ process_init_read(struct priv_soap_task *task) AN(task); assert(task->state == NONE); - 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); - 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->vrb_what = VRB_CACHED; return (0); } -static int -process_read(struct priv_soap_task *task) +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) + return (0); - AN(task); assert(task->state == INIT || task->state == HEADER_DONE || task->state == ACTION_AVAILABLE); - if (task->bytes_total <= 0) { - VSLb(task->ctx->vsl, SLT_Error, "Not enough data"); - return (-1); - } - // If everything is read, but state not switched to BODY_DONE that mean - // XML body isn't present in request - if (task->bytes_read >= task->bytes_total) { - VSLb(task->ctx->vsl, SLT_Error, - "SOAP: http read error: incomplete xml"); - return (-1); - } - while (task->bytes_read < task->bytes_total) { - int just_read = read_body_part(task->req_http, - task->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); - } - task->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: " - "total %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); - } + 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; - 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; - } - } return (0); } int process_request(struct priv_soap_task *task, enum soap_state state) { + enum soap_state old; int r; - VSLb(task->ctx->vsl, SLT_Debug, "process_request 0: %d/%d", task->state, state); + if (task->ctx->req->req_body_status == BS_NONE) + return (-1); while (task->state < state) { - VSLb(task->ctx->vsl, SLT_Debug, "process_request: %d/%d (%ld bytes)", - task->state, state, task->bytes_total); - switch (task->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) @@ -264,19 +215,31 @@ int process_request(struct priv_soap_task *task, enum soap_state state) case INIT: case HEADER_DONE: case ACTION_AVAILABLE: - r = process_read(task); - if (r) + r = VRB_Iterate(task->ctx->req->wrk, task->ctx->vsl, + task->ctx->req, read_iter_f, task, task->vrb_what); + if (task->vrb_what == VRB_CACHED) { + task->vrb_what = VRB_REMAIN; + continue; + } + /* 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 - 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; + 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] = {{ @@ -348,9 +311,6 @@ sess_record* priv_soap_get(VRT_CTX, struct vmod_priv *priv /* PRIV_TASK */) 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; } @@ -517,8 +477,8 @@ enum soap_source { struct VPFX(soap_parser) { unsigned magic; #define SOAP_PARSER_MAGIC 0x017ce81e + unsigned can_VRB_REMAIN:1; enum soap_source source; - enum vrb_what_e vrb_what; char *vcl_name; VSLIST_HEAD(, soap_namespace) namespaces; }; @@ -549,12 +509,7 @@ vmod_parser__init(VRT_CTX, struct VPFX(soap_parser) **soapp, vmod_parser__fini(&soap); return; } - if (args->req_body == VENUM(all)) - soap->vrb_what = VRB_ALL; - else if (args->req_body == VENUM(cached)) - soap->vrb_what = VRB_CACHED; - else - WRONG("req_body argument"); + soap->can_VRB_REMAIN = (args->req_body == VENUM(all)); } else if (args->source == VENUM(resp_body)) { soap->source = SOAPS_RESP_BODY; } else { diff --git a/src/vmod_soap.h b/src/vmod_soap.h index 5626ac9..7bd8d70 100644 --- a/src/vmod_soap.h +++ b/src/vmod_soap.h @@ -40,8 +40,6 @@ #include -#include "vmod_soap_http.h" - #define HANDLER_ERROR -1 #define HANDLER_SUCCESS_NOT_MODIFIED 0 #define HANDLER_SUCCESS_MODIFIED 1 @@ -72,15 +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_read; - 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 0b30ed6..6d3a8b8 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 =========== diff --git a/src/vmod_soap_gzip.c b/src/vmod_soap_gzip.c deleted file mode 100644 index e67028a..0000000 --- a/src/vmod_soap_gzip.c +++ /dev/null @@ -1,125 +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 "config.h" - -#include "cache/cache.h" -#include "vmod_soap.h" -#include "vcc_soap_if.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) -{ - VRT_CTX; - int init_flags = MAX_WBITS; - - ctx = req_http->ctx; - CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); - - switch (req_http->encoding) { - case CE_GZIP: - init_flags += 16; - case CE_DEFLATE: - req_http->compression_stream = - (z_stream*)WS_Alloc(ctx->ws, sizeof(z_stream)); - XXXAN(req_http->compression_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) -{ - VRT_CTX; - z_stream *stream; - char *buf; - unsigned len; - int sts = 0; - - if (compressed_body_part->length == 0) - return (0); - - ctx = req_http->ctx; - CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); - - stream = req_http->compression_stream; - AN(stream); - - len = WS_ReserveAll(ctx->ws); - XXXAN(len); - stream->avail_out = len; - - buf = WS_Reservation(ctx->ws); - AN(buf); - stream->next_out = (Bytef *)buf; - - stream->next_in = (Bytef *)compressed_body_part->data; - stream->avail_in = compressed_body_part->length; - - AN(stream->avail_in); - - while(stream->avail_in > 0) { - 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; - } - } - assert(stream->avail_out <= len); - len -= stream->avail_out; - - uncompressed_body_part->data = buf; - uncompressed_body_part->length = len; - - WS_Release(ctx->ws, len); - 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 b98e7f4..0000000 --- a/src/vmod_soap_request.c +++ /dev/null @@ -1,138 +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 "config.h" - -// XXX for struct http_conn, should be replaced with buffered body -#include "cache/cache_varnishd.h" -#include "vmod_soap.h" -#include "vcc_soap_if.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->rfd, 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 = BS_ERROR; - 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 e3bbeb5..753770f 100644 --- a/src/vmod_soap_xml.c +++ b/src/vmod_soap_xml.c @@ -32,8 +32,6 @@ #include "vcc_soap_if.h" #include "vmod_soap_xml.h" -#include "vmod_soap_http.h" -#include "vmod_soap_request.h" #include #include @@ -355,9 +353,17 @@ void synth_soap_fault(struct soap_req_xml *req_xml, int code, const char* messag 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 d77315b..aaebd97 100644 --- a/src/vmod_soap_xml.h +++ b/src/vmod_soap_xml.h @@ -68,7 +68,8 @@ 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); From aab6f665a97fdfc99b7aa454e4c54c5395072ba1 Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Mon, 4 Jul 2022 12:01:53 +0200 Subject: [PATCH 22/24] Only consume VRB_REMAIN if allowed --- src/vmod_soap.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/vmod_soap.c b/src/vmod_soap.c index 40a8cbc..9d0b5ec 100644 --- a/src/vmod_soap.c +++ b/src/vmod_soap.c @@ -195,7 +195,8 @@ read_iter_f(void *priv, unsigned flush, const void *ptr, ssize_t len) return (0); } -int process_request(struct priv_soap_task *task, enum soap_state state) +int process_request(struct priv_soap_task *task, enum soap_state state, + unsigned can_eat) { enum soap_state old; int r; @@ -217,7 +218,7 @@ int process_request(struct priv_soap_task *task, enum soap_state state) case ACTION_AVAILABLE: r = VRB_Iterate(task->ctx->req->wrk, task->ctx->vsl, task->ctx->req, read_iter_f, task, task->vrb_what); - if (task->vrb_what == VRB_CACHED) { + if (task->vrb_what == VRB_CACHED && can_eat) { task->vrb_what = VRB_REMAIN; continue; } @@ -323,14 +324,14 @@ VCL_BOOL v_matchproto_(td_soap_is_valid) { 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, 1) == 0); } 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, 1) == 0) { return (soap_task->req_xml->action_name); } return (""); @@ -340,7 +341,7 @@ 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, 1) == 0) { return (soap_task->req_xml->action_namespace); } return (""); @@ -374,7 +375,7 @@ VCL_STRING v_matchproto_(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, 1) == 0) { return (evaluate_xpath(soap_vcl, soap_task, soap_task->req_xml->header, xpath)); } return (""); @@ -392,7 +393,7 @@ VCL_STRING v_matchproto_(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, 1) == 0) { return (evaluate_xpath(soap_vcl, soap_task, soap_task->req_xml->body, xpath)); } return (""); From d9f4e40966a45e6803f12d789ed58da579d04b34 Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Mon, 4 Jul 2022 12:42:44 +0200 Subject: [PATCH 23/24] Basic oo xpath --- src/tests/b00014.vtc | 75 +++++++++++++++++++++++++++++++++++--------- src/vmod_soap.c | 28 ++++++++++++----- 2 files changed, 82 insertions(+), 21 deletions(-) diff --git a/src/tests/b00014.vtc b/src/tests/b00014.vtc index 7af800f..169b170 100644 --- a/src/tests/b00014.vtc +++ b/src/tests/b00014.vtc @@ -6,38 +6,70 @@ server s1 { txresp rxreq - expect req.http.soap-uuid == "obama" - txresp + # aborted } -start varnish v1 -vcl+backend { import ${vmod_soap}; + import std; sub vcl_init { - new soap_parser = soap.parser( + new body_all = soap.parser( source=req_body, req_body=all); - soap_parser.add_namespace("ui", "http://schemas.reuters.com/ns/2005/08/infrastructure/tornado_soap"); - soap_parser.add_namespace("uuid", "http://schemas.reuters.com/ns/2007/10/cp/user_identity"); + 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 { - return (synth(200)); # XXX WIP - + std.cache_req_body(401B, partial=true); if(req.url == "/header") { - set req.http.soap-uuid = - soap.xpath_header("ui:userIdentity/uuid:UUID"); + 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 { - set req.http.soap-uuid = - soap.xpath_body("ui:userIdentity/uuid:UUID"); + 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" -body { + txreq -req POST -url "/header" -hdr "how: all" \ + -body { + +
+ + lenin + +
+ +
+ } + rxresp + expect resp.status == 204 + + txreq -req POST -url "/header" -hdr "how: cached" \ + -body {
@@ -50,7 +82,8 @@ client c1 { rxresp expect resp.status == 200 - txreq -req POST -url "/body" -body { + txreq -req POST -url "/body" -hdr "how: all" \ + -body {
@@ -61,5 +94,19 @@ client c1 {
} rxresp - expect resp.status == 200 + expect resp.status == 204 + + txreq -req POST -url "/body" -hdr "how: cached" \ + -body { + +
+
+ + + obama + +
+ } + rxresp + expect resp.status == 503 } -run diff --git a/src/vmod_soap.c b/src/vmod_soap.c index 9d0b5ec..6753bc4 100644 --- a/src/vmod_soap.c +++ b/src/vmod_soap.c @@ -481,7 +481,8 @@ struct VPFX(soap_parser) { unsigned can_VRB_REMAIN:1; enum soap_source source; char *vcl_name; - VSLIST_HEAD(, soap_namespace) namespaces; + // XXX basically just namespaces + struct priv_soap_vcl priv; }; VCL_VOID @@ -517,7 +518,8 @@ vmod_parser__init(VRT_CTX, struct VPFX(soap_parser) **soapp, WRONG("source argument"); } REPLACE(soap->vcl_name, vcl_name); - VSLIST_INIT(&soap->namespaces); + soap->priv.magic = 0x5FF42842; + VSLIST_INIT(&soap->priv.namespaces); *soapp = soap; } @@ -531,8 +533,8 @@ vmod_parser__fini(struct VPFX(soap_parser) **soapp) REPLACE(soap->vcl_name, NULL); // ref clean_vcl - VSLIST_FOREACH_SAFE(ns, &soap->namespaces, list, ns2) { - VSLIST_REMOVE_HEAD(&soap->namespaces, list); + VSLIST_FOREACH_SAFE(ns, &soap->priv.namespaces, list, ns2) { + VSLIST_REMOVE_HEAD(&soap->priv.namespaces, list); FREE_OBJ(ns); } @@ -576,7 +578,7 @@ vmod_parser_add_namespace(VRT_CTX, ns->prefix = prefix; ns->uri = uri; - VSLIST_INSERT_HEAD(&soap->namespaces, ns, list); + VSLIST_INSERT_HEAD(&soap->priv.namespaces, ns, list); } sess_record * @@ -607,7 +609,13 @@ vmod_parser_header_xpath(VRT_CTX, if (soap_task == NULL) return (NULL); AN(xpath); - return (NULL); + + if (process_request(soap_task, HEADER_DONE, + soap->can_VRB_REMAIN) == 0) { + return (evaluate_xpath(&soap->priv, soap_task, + soap_task->req_xml->header, xpath)); + } + return (""); } VCL_STRING @@ -619,5 +627,11 @@ vmod_parser_body_xpath(VRT_CTX, if (soap_task == NULL) return (NULL); AN(xpath); - return (NULL); + + if (process_request(soap_task, BODY_DONE, + soap->can_VRB_REMAIN) == 0) { + return (evaluate_xpath(&soap->priv, soap_task, + soap_task->req_xml->body, xpath)); + } + return (""); } From 37920a4baf35f0825cc7422677dc620198e9f4c3 Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Mon, 4 Jul 2022 19:46:36 +0200 Subject: [PATCH 24/24] Basic response body parsing As of now, the response body must be uncached. --- src/tests/b00019.vtc | 42 +++++++++++++++++++++++++++++++++++++ src/vmod_soap.c | 49 +++++++++++++++++++++++++++++--------------- src/vmod_soap.vcc | 5 +++++ 3 files changed, 80 insertions(+), 16 deletions(-) create mode 100644 src/tests/b00019.vtc 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/vmod_soap.c b/src/vmod_soap.c index 6753bc4..7637afa 100644 --- a/src/vmod_soap.c +++ b/src/vmod_soap.c @@ -40,6 +40,12 @@ 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, @@ -173,7 +179,7 @@ read_iter_f(void *priv, unsigned flush, const void *ptr, ssize_t len) CAST_OBJ_NOTNULL(task, priv, PRIV_SOAP_TASK_MAGIC); - if (task->state == FAILED) + if (task->state == FAILED || task->state == BODY_DONE) return (0); assert(task->state == INIT || @@ -196,12 +202,13 @@ read_iter_f(void *priv, unsigned flush, const void *ptr, ssize_t len) } int process_request(struct priv_soap_task *task, enum soap_state state, - unsigned can_eat) + enum soap_source source, unsigned can_eat) { enum soap_state old; int r; - if (task->ctx->req->req_body_status == BS_NONE) + if (source == SOAPS_REQ_BODY && + task->ctx->req->req_body_status == BS_NONE) return (-1); while (task->state < state) { old = task->state; @@ -216,8 +223,22 @@ int process_request(struct priv_soap_task *task, enum soap_state state, case INIT: case HEADER_DONE: case ACTION_AVAILABLE: - r = VRB_Iterate(task->ctx->req->wrk, task->ctx->vsl, - task->ctx->req, read_iter_f, task, task->vrb_what); + 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 (task->vrb_what == VRB_CACHED && can_eat) { task->vrb_what = VRB_REMAIN; continue; @@ -324,14 +345,14 @@ VCL_BOOL v_matchproto_(td_soap_is_valid) { struct priv_soap_task *soap_task = priv_soap_get(ctx, priv); - return (process_request(soap_task, ACTION_AVAILABLE, 1) == 0); + return (process_request(soap_task, ACTION_AVAILABLE, SOAPS_REQ_BODY, 1) == 0); } 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, 1) == 0) { + if(process_request(soap_task, ACTION_AVAILABLE, SOAPS_REQ_BODY, 1) == 0) { return (soap_task->req_xml->action_name); } return (""); @@ -341,7 +362,7 @@ 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, 1) == 0) { + if(process_request(soap_task, ACTION_AVAILABLE, SOAPS_REQ_BODY, 1) == 0) { return (soap_task->req_xml->action_namespace); } return (""); @@ -375,7 +396,7 @@ VCL_STRING v_matchproto_(td_soap_xpath_header) AN(priv_task); soap_task = priv_soap_get(ctx, priv_task); - if(process_request(soap_task, HEADER_DONE, 1) == 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 (""); @@ -393,7 +414,7 @@ VCL_STRING v_matchproto_(td_soap_xpath_body) AN(priv_task); soap_task = priv_soap_get(ctx, priv_task); - if(process_request(soap_task, BODY_DONE, 1) == 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 (""); @@ -469,12 +490,6 @@ soap_init_thread(VRT_CTX) } } -enum soap_source { - SOAPS_INVALID = 0, - SOAPS_REQ_BODY, - SOAPS_RESP_BODY -}; - struct VPFX(soap_parser) { unsigned magic; #define SOAP_PARSER_MAGIC 0x017ce81e @@ -611,6 +626,7 @@ vmod_parser_header_xpath(VRT_CTX, 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)); @@ -629,6 +645,7 @@ vmod_parser_body_xpath(VRT_CTX, 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)); diff --git a/src/vmod_soap.vcc b/src/vmod_soap.vcc index 6d3a8b8..0774bf3 100644 --- a/src/vmod_soap.vcc +++ b/src/vmod_soap.vcc @@ -85,6 +85,11 @@ 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.