From 80d0be0c7eb382818e73c5f0553b145288bdaab7 Mon Sep 17 00:00:00 2001 From: Piotr Sikora Date: Tue, 3 Mar 2020 19:33:01 -0800 Subject: [PATCH] wasm: Add API to get buffered body data. (#437) (#177) Now, if a callback previously returned StopIterationAndBuffer, the various "getBuffer" methods will return the buffered data. But if the callback previously returned anything else, then the methods will only return the current chunk. Signed-off-by: Gregory Brail Signed-off-by: Piotr Sikora Co-authored-by: Greg Brail --- source/extensions/common/wasm/context.cc | 32 +++-- source/extensions/common/wasm/context.h | 8 +- .../filters/http/wasm/test_data/Makefile | 2 +- .../filters/http/wasm/test_data/body_cpp.cc | 76 ++++++++++++ .../filters/http/wasm/test_data/body_cpp.wasm | Bin 0 -> 38389 bytes .../filters/http/wasm/wasm_filter_test.cc | 116 ++++++++++++++++++ 6 files changed, 223 insertions(+), 11 deletions(-) create mode 100644 test/extensions/filters/http/wasm/test_data/body_cpp.cc create mode 100644 test/extensions/filters/http/wasm/test_data/body_cpp.wasm diff --git a/source/extensions/common/wasm/context.cc b/source/extensions/common/wasm/context.cc index 41235f914f41..dc14b943e93a 100644 --- a/source/extensions/common/wasm/context.cc +++ b/source/extensions/common/wasm/context.cc @@ -787,11 +787,17 @@ uint32_t Context::getHeaderMapSize(HeaderMapType type) { // Buffer -Buffer::Instance* Context::getBuffer(BufferType type) { +const Buffer::Instance* Context::getBuffer(BufferType type) { switch (type) { case BufferType::HttpRequestBody: + if (buffering_request_body_) { + return decoder_callbacks_->decodingBuffer(); + } return request_body_buffer_; case BufferType::HttpResponseBody: + if (buffering_response_body_) { + return encoder_callbacks_->encodingBuffer(); + } return response_body_buffer_; case BufferType::NetworkDownstreamData: return network_downstream_data_buffer_; @@ -1196,22 +1202,28 @@ Http::FilterHeadersStatus Context::onRequestHeaders() { return Http::FilterHeadersStatus::StopIteration; } -Http::FilterDataStatus Context::onRequestBody(int body_buffer_length, bool end_of_stream) { +Http::FilterDataStatus Context::onRequestBody(bool end_of_stream) { if (!wasm_->on_request_body_) { return Http::FilterDataStatus::Continue; } DeferAfterCallActions actions(this); + const auto buffer = getBuffer(BufferType::HttpRequestBody); + const auto body_len = (buffer == nullptr) ? 0 : buffer->length(); switch (wasm_ - ->on_request_body_(this, id_, static_cast(body_buffer_length), + ->on_request_body_(this, id_, static_cast(body_len), static_cast(end_of_stream)) .u64_) { case 0: + buffering_request_body_ = false; return Http::FilterDataStatus::Continue; case 1: + buffering_request_body_ = true; return Http::FilterDataStatus::StopIterationAndBuffer; case 2: + buffering_request_body_ = false; return Http::FilterDataStatus::StopIterationAndWatermark; default: + buffering_request_body_ = false; return Http::FilterDataStatus::StopIterationNoBuffer; } } @@ -1257,22 +1269,28 @@ Http::FilterHeadersStatus Context::onResponseHeaders() { return Http::FilterHeadersStatus::StopIteration; } -Http::FilterDataStatus Context::onResponseBody(int body_buffer_length, bool end_of_stream) { +Http::FilterDataStatus Context::onResponseBody(bool end_of_stream) { if (!wasm_->on_response_body_) { return Http::FilterDataStatus::Continue; } DeferAfterCallActions actions(this); + const auto buffer = getBuffer(BufferType::HttpResponseBody); + const auto body_len = (buffer == nullptr) ? 0 : buffer->length(); switch (wasm_ - ->on_response_body_(this, id_, static_cast(body_buffer_length), + ->on_response_body_(this, id_, static_cast(body_len), static_cast(end_of_stream)) .u64_) { case 0: + buffering_response_body_ = false; return Http::FilterDataStatus::Continue; case 1: + buffering_response_body_ = true; return Http::FilterDataStatus::StopIterationAndBuffer; case 2: + buffering_response_body_ = false; return Http::FilterDataStatus::StopIterationAndWatermark; default: + buffering_response_body_ = false; return Http::FilterDataStatus::StopIterationNoBuffer; } } @@ -1590,7 +1608,7 @@ Http::FilterHeadersStatus Context::decodeHeaders(Http::HeaderMap& headers, bool Http::FilterDataStatus Context::decodeData(Buffer::Instance& data, bool end_stream) { request_body_buffer_ = &data; end_of_stream_ = end_stream; - auto result = onRequestBody(data.length(), end_stream); + auto result = onRequestBody(end_stream); request_body_buffer_ = nullptr; return result; } @@ -1628,7 +1646,7 @@ Http::FilterHeadersStatus Context::encodeHeaders(Http::HeaderMap& headers, bool Http::FilterDataStatus Context::encodeData(Buffer::Instance& data, bool end_stream) { response_body_buffer_ = &data; end_of_stream_ = end_stream; - auto result = onResponseBody(data.length(), end_stream); + auto result = onResponseBody(end_stream); response_body_buffer_ = nullptr; return result; } diff --git a/source/extensions/common/wasm/context.h b/source/extensions/common/wasm/context.h index 1944a27604f4..30d4107d8803 100644 --- a/source/extensions/common/wasm/context.h +++ b/source/extensions/common/wasm/context.h @@ -129,12 +129,12 @@ class Context : public Logger::Loggable, virtual void onUpstreamConnectionClose(PeerType); // HTTP Filter Stream Request Downcalls. virtual Http::FilterHeadersStatus onRequestHeaders(); - virtual Http::FilterDataStatus onRequestBody(int body_buffer_length, bool end_of_stream); + virtual Http::FilterDataStatus onRequestBody(bool end_of_stream); virtual Http::FilterTrailersStatus onRequestTrailers(); virtual Http::FilterMetadataStatus onRequestMetadata(); // HTTP Filter Stream Response Downcalls. virtual Http::FilterHeadersStatus onResponseHeaders(); - virtual Http::FilterDataStatus onResponseBody(int body_buffer_length, bool end_of_stream); + virtual Http::FilterDataStatus onResponseBody(bool end_of_stream); virtual Http::FilterTrailersStatus onResponseTrailers(); virtual Http::FilterMetadataStatus onResponseMetadata(); // Async Response Downcalls on any Context. @@ -265,7 +265,7 @@ class Context : public Logger::Loggable, virtual uint32_t getHeaderMapSize(HeaderMapType type); // Buffer - virtual Buffer::Instance* getBuffer(BufferType type); + virtual const Buffer::Instance* getBuffer(BufferType type); bool end_of_stream() { return end_of_stream_; } // HTTP @@ -478,6 +478,8 @@ class Context : public Logger::Loggable, // Temporary state. ProtobufWkt::Struct temporary_metadata_; bool end_of_stream_; + bool buffering_request_body_ = false; + bool buffering_response_body_ = false; // MB: must be a node-type map as we take persistent references to the entries. std::map http_request_; diff --git a/test/extensions/filters/http/wasm/test_data/Makefile b/test/extensions/filters/http/wasm/test_data/Makefile index 782b1e8eb872..b2124c0fcaa9 100644 --- a/test/extensions/filters/http/wasm/test_data/Makefile +++ b/test/extensions/filters/http/wasm/test_data/Makefile @@ -1,3 +1,3 @@ -all: headers_cpp.wasm async_call_cpp.wasm metadata_cpp.wasm grpc_call_cpp.wasm shared_cpp.wasm queue_cpp.wasm http_callout_cpp.wasm grpc_callout_cpp.wasm +all: headers_cpp.wasm async_call_cpp.wasm metadata_cpp.wasm grpc_call_cpp.wasm shared_cpp.wasm queue_cpp.wasm body_cpp.wasm http_callout_cpp.wasm grpc_callout_cpp.wasm include ../../../../../../api/wasm/cpp/Makefile.base_lite diff --git a/test/extensions/filters/http/wasm/test_data/body_cpp.cc b/test/extensions/filters/http/wasm/test_data/body_cpp.cc new file mode 100644 index 000000000000..74415d391db2 --- /dev/null +++ b/test/extensions/filters/http/wasm/test_data/body_cpp.cc @@ -0,0 +1,76 @@ +// NOLINT(namespace-envoy) +#include +#include +#include +#include + +#include "proxy_wasm_intrinsics.h" + +class ExampleContext : public Context { +public: + explicit ExampleContext(uint32_t id, RootContext* root) : Context(id, root) {} + + FilterHeadersStatus onRequestHeaders(uint32_t) override; + FilterHeadersStatus onResponseHeaders(uint32_t) override; + FilterDataStatus onRequestBody(size_t body_buffer_length, bool end_of_stream) override; + FilterDataStatus onResponseBody(size_t body_buffer_length, bool end_of_stream) override; + +private: + FilterDataStatus onBody(BufferType bt, size_t bufLen, bool end); + static void logBody(BufferType bt); + + std::string test_op_; + int num_chunks_ = 0; +}; +static RegisterContextFactory register_ExampleContext(CONTEXT_FACTORY(ExampleContext)); + +FilterHeadersStatus ExampleContext::onRequestHeaders(uint32_t) { + test_op_ = getRequestHeader("x-test-operation")->toString(); + return FilterHeadersStatus::Continue; +} + +FilterHeadersStatus ExampleContext::onResponseHeaders(uint32_t) { + test_op_ = getResponseHeader("x-test-operation")->toString(); + return FilterHeadersStatus::Continue; +} + +void ExampleContext::logBody(BufferType bt) { + size_t bufferedSize; + uint32_t flags; + getBufferStatus(bt, &bufferedSize, &flags); + auto body = getBufferBytes(bt, 0, bufferedSize); + logError(std::string("onRequestBody ") + std::string(body->view())); +} + +FilterDataStatus ExampleContext::onBody(BufferType bt, size_t bufLen, bool end) { + if (test_op_ == "ReadBody") { + auto body = getBufferBytes(bt, 0, bufLen); + logError("onRequestBody " + std::string(body->view())); + + } else if (test_op_ == "BufferBody") { + logBody(bt); + return end ? FilterDataStatus::Continue : FilterDataStatus::StopIterationAndBuffer; + + } else if (test_op_ == "BufferTwoBodies") { + logBody(bt); + num_chunks_++; + if (end || num_chunks_ > 2) { + return FilterDataStatus::Continue; + } + return FilterDataStatus::StopIterationAndBuffer; + + } else { + // This is a test and the test was configured incorrectly. + logError("Invalid test op " + test_op_); + abort(); + } + return FilterDataStatus::Continue; +} + +FilterDataStatus ExampleContext::onRequestBody(size_t body_buffer_length, bool end_of_stream) { + return onBody(BufferType::HttpRequestBody, body_buffer_length, end_of_stream); +} + +FilterDataStatus ExampleContext::onResponseBody(size_t body_buffer_length, bool end_of_stream) { + return onBody(BufferType::HttpResponseBody, body_buffer_length, end_of_stream); +} \ No newline at end of file diff --git a/test/extensions/filters/http/wasm/test_data/body_cpp.wasm b/test/extensions/filters/http/wasm/test_data/body_cpp.wasm new file mode 100644 index 0000000000000000000000000000000000000000..df0a34292e50dfb068dcaa9e90fc1a6663f955f3 GIT binary patch literal 38389 zcmeI5du%1wec#VK?t`35@^Z=5QsUa0d$YDFd)MBz<&`We@s8dbS(f6st<&12C@m$E z++DuzQmfQ9H;x0Vjhh&#-KwYq2#t|Ai37Nm0;sKJaT`u>A0fJ`{Pah zPBu45&~KmQ?sx9~lvh$~x^b`Dod-*&oX_B);d3@!{AW=!nLocVw6eB*@zL3{%MZI$ z?^pTvhv$2<4_tWYq4~Ah2OjOsue(f{*Yq<#Ha~ZU%yV-qvm0|~FU-5_sz>J57iQPb z&#kO~Y`HhPvNpf5F#pJjjCu=e%jail(#3le8ZQg2FZ}%c`o2X4@Po7S7Z-ZY38n*m zn0Q*5U+X>UhLv%spAkl~-ka-PSZAD%Y`7>&(7!78o8)eOKMR)jq5IQ!;Pd- zOIxX{MJ}zmIF3_YyfqX3(^HL-E+vDyNu!=5L(N8`ITWX9Es2wwtH;bA$tvEA=!cG| zw-Vicd#xTtm*PuteTaE2o_PFnoHc%@p4ZRKpIcsgG_G!$*!kM|={Op6v=O0>l_`=#;kBaW+1AeGdX3V?0N2+#u z3lA>2>qC#eP;dTXZ}!2pc^Y#+-Oo8c|A?A6KmVXL{8Lj^8)uduIltbc!ns)>m~(do zBBHqqD}xyyKeAh$extJwo?Tv_cXxKHVpaVeh2GWge{FvC!u)z~*3;y=d+*g%{W9y^ zd~eQRyXoMLya$%gJnC-VS*N!)w{RBxyY~#{t*JN2e2 z_u;jb2gA(HE}UQJEzF&rt$@Al&0EW@%|AH5ura^0;4N?2y`T+X;rzq>CW}6@t?SUF zyLDn~3x3*fpR7`u&kOSmVUC&k$$FslPt?ybu;mBcp`o9SToGlrb^qF1-tvKa&YwGb z_0;(I1NYDV&8Y{c{>Dc>`d2>m#;eDFcKnT3UG;`HeDJ`}9Juemy$8m}ubw(^)q$&y zKYH~WFLpYe@i#oO(OX|zT^atpFT@+aFcLL3<5Ri2eZ=LixVYF&xP5$aCU)JZxVYMN zi=7(zQ4y_DA}W&A+ee~2$rF``m%2&u{DdCU(R(vv#A6Y#ayH=7>AUYYh@+ePF#?4lo zsr*!@4nzh5Rn?0%)9l9TDW<_|vOF#;#;O=)s!>cXcF6`JJ__DkXE=o`iGp@K-ynJx6o6-j(^1y?%k*IiPg8$nGS1;Pd zWL~G)Jh@|fQ4mx5_7R2|<@J;Clt$a`#xxb>jHB&GHDy?s?8cf2%a{xq3?Z-0#DYIr zEs{G&T=DEgE?Dy#bN5NG3aXgkXiZNbVbOBOsFo-9fR#`?VwN2vi;8&lcDk<>F9>=~ z(+d-MLW9L~6Z|JFcZ*%?q2F#f|04%1(yQ?El9BS$tp(zSIyGK)lAc`L8t>Xdn zbOpREriPoyi(zkQ>_%SQ2BhJs)94}HMr4sEGLRkTTZ%%aqPf`9oJXmrxtcoiH0K-k z9lr+M)wU2*?T15mLV|(wdg+ew4$xg~knR|2pgV@SE8UT9bO)IVNhRGukUNR05(AKb zphBawedsWw6+f?Ta3-1YO)neZF-X4Gg+5JU2n6{d zZrp?3(&EykF-wV8yJ@c&+c1FgbZrSTjCw#46$ckL5T0wr;l->A2#0CM%ZW}DpVE() z4BeEW(Q3nn_RIv*qhUis+G>P}#KV*gvv9@Lv=VJB@%!34rh8sEA;S=0k;G4Gta8VQ zfPTY9hlYWMQeu&Gb+Oxk2;tpsWYSC>fDqBUWJ`%QAyNI&UbAaBZP9>InObm)cv6*s z;bvC6IKh7jxy>LF90KUEL!AV$kYuh4wii3h2feEBCgaMocSq4CsE=|t-Ej%{$X)wT zH%d42s8)PU?Ez*~xQw}r#_O)-T;{T0ilZ8et9bg53Lpwy@su9pO~$&3SX<5G1+-Iy zRJ(?GfGTWad0NT3HKxELKza|sP9I+4B}IH%T7)(_v4=+aX*V%-mM5!u?T(SCRRb9C zlDj)bP*c7IhJ}jSQ7Osedl?P46{IW@Q1#P(eTjQNO6rRTC1aeZng6Zv$aM3L#11W_ z*{x9`^{w%PWOvM=-111bnlunZxYz-+x;YzEH>UGAlV+_gb$}+xK9$l4nU^lRd#M9H z^0@uia|gbu!-UZRiR;q+spbb_x=RH+u_s4MuU>^V&@CWta@6x6PzQCJC-`JpiJUEnG?Yd+Ihqo3 ze48otnF`lM+2UO+#$5cOct4ME)?os3vVM z-JaE4d(vg!YDLY>pKwKcshb{gaFLWtOpW5}GIWbe&EoP9blh5OzZ3H9m|~Kt<%&O! z&UfqlKHE)}AYsZCm*7KWV*8!EEEVwu+1gB(#1ko!P?%^PSJcFW`XMtZ-hlw2#ReDR zsz;w*mYs*bqSM{xG&U@f56&&30$Y`G5Y5>;rt{{s$vI?<=q)LdyGM}dMYJ@U;1xhB ze?2m3O~dEAD4x@35LHgj`Yw}Vc(Kbc)KAEwU4tfJ$a`jCYVTF+z}{I`?VWXa@2ofO zopogIteL&D-nw_z+xO18e($WC_RhL_@2r1~p}iLp|8SqYXZOkbxBKM%@jiJk?34H6 zK6x+gllLe4Yz^UAU-Ux1lVB{#?2^JTv)Zpq8XX)7DfT$p1ZT60veaSh z9zYX$MT9XXhlY~^y4Gd?E{!pKKapeU#-;h244S`*%-?7=PZlt9z4?m(&!e!eNicuw zrTLr6{6*Wy>`=W<4Fx0jMVB0eyq;4Q(i&RcSk3EqPLr$!K^|$$g)@VtoEwe~;#WRQAN*VN~?0gXY zWM@b!G1usG)+A4UJ|FsM5v}XBZvGsGtx>HO?oJ+((`(Xoy|qrvDq$#Ze&O$So3}c5 z>E4g#x;uCODo{6ni`iiYx;4xz47=0Q?VkwCjL@7cgn;F;8)28RCo3JfnwS_{jG$&& zXLz4eq*`h5H36^DeC?1HPs}n;x*OKz{4s;06KP=9OV_(#(BP}SH`B{iZ-YJgd^1_p z2WsB>$Us?zHT4~3H#ylP0>G2opI#qj!K}%SIE`TE4`ls{**NB=gNWLsWQ(@cEV9$n zkQ?q7XC4?KGAcILS^3IAB$pp%{>u~IP|-4wPRy9c;h4%B*bV*}yc3lJka;lg;x(WG zSj*+7#$_$?%N$v}B9jeq-nG?g$c?2||fmjYvkg*?O@FRjsr>*3$z%`L4$ zC0+4tYolnBs4>)R!IWQjy!!Dd(@ze_W zsG8=ZY6c&b@@$Uq1(nlL&!c-8T>`n)<&B~YvqKwrCP>y4vOmb2$&V%1m9UDloXk4z zA|@(9xXZPb_?0ZaH-Qo7%^nalYB;?CM{6Sd`%Yeu1FutJO)d6$ouSi5pVzm%!|UOF zv5y65b0fOslr0}69mSw5m;@xW7f&-p=3Ng6Vee_h{{m_mZe~P)mXsEg;}n9T0U-$u zG1ZY$Q2r45l8Q8pdqiW@LQY^S0h!3JwAIip=1o!=QaZkv*R{D;!6KTb zg7Z`bVK(ymQa~n%t}y6l3|y6XVGx6phc%t-z)Ng+F=hje-Z@ieip=F)Y~Bf+hB_v- z0Od=DaAtR~Vd=~r4SR%}VF@|%UoE7v8>IXK&d(OuS{SFDk1EH6yKe| zQdGjrWzMH3i&rh>)iUd|lf@O?@^9sjo~HPgtoZ%Hm%xQ=XbEzHC`vEg!x%S$x$}o-QArnJk{Rl*{GAbCbno zOL?|@_{L=Mtff3(K74bsc-~T8nk-(hhZo9}7t56IOcpO%)+>|6x9#EEWy*IaiYy2MuRs*5?lvk6FrNW!4uD7N4_}&y`tUI#@hzDUX*~UpZJj zVJT0PSx+4-zHBL9F0;ONu=uK_e6`Ga=3w!(r953`J$JCUY$=z^tZy7Fp0$)`%dBr6 zES|TN=gX{@4i+z1$_r)IcMcXWTFQ%M*0;+Pw&>X_M*tQLmcBeU7kQc{mO~qm+PX&+ zeb_=hFzZM`pkv*I6$B!f9g(#7=0x%3uvmq-wYSt_D^ymAM7ZjpVPGIcZGBM{6d6Fb zN$55i=++c*6)^zs(P8KsRta6x;4WQD+6Kp=K}%KI6JykA$5EDuaK#V&@5j6=o~64>t0D#=aX?5M z5Tel!Ey-}l&d7%rSrjmItq8%OEvhO^F=IAAEE_zMq}pO{Z>d6m6&5VT9Tfn#v1oG5 zs!DQUPpuXTxe@fMo#9vye%swvIVkzTg(G1)W&*Z$gz2PJ1O-*JMual77-Gt32wRnw z!%C_ye~@{3nq3PBVxgo}l*JpgFdnEM!gTs3XiZec^2l4RyrEX>w9cd@9ZWrxdui6H zC=LmE0FX*ZCBL20wn6^!DVk)kSEr>;;&mNnkF^(pJ*zK@2f{G`9 z3UUJ;gi8=9rj9Di^I)wZqR!fts}DC>fC^A2Avzwkhweh(K^Tgb8g_x zi%aK*Fftge1A`nU$%_lKYAuG3c>wGl3piPLP-R?t&%7%Pa!idJk=c-=^W=#3zAqp=3=5|h+@4c})CDS~aR z0eWaxtYLJH1e5x!=$slAox`xk8j&LuM)r zRi=U$lSY+kJ|qx<2-lktS)fYFl%|428bEO@&Zf1)F%H_5T#QVSlB7NpC>>sX%J zAvC*EFN#Cswj!67HEw&Q0d;iNt=Sfx_DV;gn&|G%7+??=n~DSSF}YXce00^v#(79c zph1&)s0z_gt>cYdY6$qQ@5>vymd#YFGvfM~NKECtcFaw5TY0OP>J?KLx=`_5(`;1K zG_Vm!M9vkOO>h`OVr8Y%~Bl#`!!%oe^QMVZZX>>3pc2;g^$; zq<6Ay_6Ui8t(I#DMXIsOjYfilAcu}7D3(MI;7IsX;KcN0ivp^k=Ubg&M$yFS>OqMy zckp=TBi#nKe!=(@jSg$O&NiAT6Lql573~z~*le1A)xf*v!I2GN5&1+uT6`vKgnodc z@TKG46x#}KyJKy7#Fc}v*+4s2!x_ZxG@Q%$5ifvmr0S_B(@s@LGIFv6qh?BzVJj72 zP;8l1>A+8(mW?Nj+V!4h*;mtWSOaR)20aX<7POm^;TK|4!m^Z^lw};=r#~Uav5~Fv;8NrUSQ6LxoixK?mt;tpw6Rz%UJPO4Xk)(ERCUjp@_hJ!* zrXoOfTj?l0&Uu$RC zzYx@PteU@P{~E z8jT~TX*uRt`SvQ}KUVaPu*Nya1;6H>do2D-G6v?2&fpco~IhcFE) z(N~-1z=>Lwyc^b3H}jel2V1(ZSQlB3p2m7yz0+cehlD9)Dt>HsRbwbbkt3^li};@C`vY7XxO)MPjd=xt zBbT=T?w2;qfJAFC)gFS5;P1HovD|Dh5fsFaSH;+)c){X>MvHG~mO;#uoiQUTlX9aL zA2e3R2esIy5!ZV{*d8BLr`~2ExjL4Q5+yWxtFtH}-JOpT8ZD!Q(CA`QTb?5gAt2No z3JLmxa(HXEvu+Zou+BaMoO#0cUDvKIdtRpz6`LOHC;ce)@3RABOj1p?7cfD%$3*(( z*6YJdJ?_AxKCE*Yj_5Z*m|K@|`vfaH&lZM&5MqQ(giTe?^MbJgZdPL>;~iL35*YEO z2rh4Ghj>_!i)jkuhCmE(36LT+4g-1F!hlYQ3p%&P&L&` z%UvGAnyg6TLL^-ST4>i?_WKdX9DL+PDr%+f2FxT!V4Ldv80A~vgc#E@4 zV-f-r_IFLh>sRaawBZ1~ELz!K+ndW7D_-h~Xo>c>%4Q&>zA~1i9Zf+8v@_fH*LEmF zjHMyC-7Vjsd_RC|aavJ!%C#5UA0}i>ZV}eg;r`4a+0NgU!439(-m~HULO-v)oc*&X zPBx=Yh`dpko?vO6=aOKZ2sSDY)`(!E_S?ryoNn6MYAv=7He5vLrEh+x0JnVa1J$?FLrYOnkE`J-B0rRBls1 zt(lG3j^x+WVmrE~Xj=RurDsr~CXuJ)^f!eh+T-*oqR-a1(JjBCCDNz3h(XXvGLG1c za{C`+*Ex5CLk<<+@{g)VEBKeYy=>MMvGc z{Gv#hl@(nRd)qQ8Hlw@gX1xJHjpp5x!>u z0*l3Wt%id`AB}X(k#Vu=GVWs%kL1WV6AMyn?I2{e6=-c1G4@|5qSHtPlXFsy`YJSb z#5z5aWxpN)hVtLd%##6m$ilRw>wUNWU{evgMO7e;tXm7A>khn@28_`e>j(LP7429nhFSP+w^8~QPB&T7 z5mj^@JionIFlAUcCP-ZvZ$cJs!rVy_SAFs%V)BjVk|NRzDdW|&`m>}avk+@bvZlz5 z+DD>tIIVb(a}it~!x5;%K6!U=$7BL6uaT>ObfsO3Fdb-0K%e>{OlX|0Cru7->@ zTuQtmsJz`3OEBuds~3#kSVu{be#f+M1ygkhnjFeZGp0feOOuq5BTeiLV~)j`B_O|v zFe+1P3@@kOy5&X1u$&Q16J>rW?^paKLkdx1O-Opelb* zK@Ldpi9FF_I(Pm=HO&F8490S`V8?e!TVb^j+O?HJINM-0mlO)3rt~ps0&M|Js1yy> zR9TKk93q+E_1>BYr6<1#>eNnDc>iGVQYGdJGI`376xL?;hV1jEoI0XFnZ&fs9*y` z8>L_r@Of;b+y>xG*%k|qfwqVrDo1_gZB3SKVe3|H9d&#GL%TL$!)u7_pM>480l}`_ zFo_f`61``n6La@*&xU33^mAUYBhnJ zkhrkCCtbd?08T_}6*#Opnz9vC1FIjcc1T}0sEVOM~a;Dexxf0k^@k+pl>BK6Z6ybY!toiM*$U6W{q3=;n z&J@$9fUnj?vl_|pfC3vxF>4?vD6wa^E3xN%LY57?+(Aci`$Be*$M$en zuhNk-@zHVIeiKR^A=RhNh}U0Hi!jHS3_92a%&s%t0fG{ji3^v2l_2W&K9U9E@>8Hl= zRP2&|^B(E9?U8=t9_e=}eRoXe)nErFbVd{UYksv;Iw&D6c1P@33TADvidbLWgNYB_ zv1f@f)Qx1BB zP224EQ|;FW5IZw12R7f6IwaX?;TjPMp-J{RD-~t_2MtDB&fvjzhYb>w|D|fX(weC^ zw&}~a5pe6*Q=t*!O{v|We zV|n*_XwE1N0+YF81XqVu{rpSk^Q6UAUejSKUP_j2%C%n>i>w%)SffX7AzmRk(WoUd z(CIAyC4sT(O#G9bw%?!7?2Oq?VY}QZ43s9Ic6>)byGHGNjJ=PsTb=C?>hAmw;aGo% z(A*vU?+D1$U_gAT4)ns2Z0Cxw4PPAc4`B-45hhBAq1{rj8HM|>2N_pQzfNbqdGK*9 z^|0u+=#u{WU44a)ko_9XZXFGFW6g=raJNEmMpKry{d)ph4Qr3XoU2$m_~+XP=}z9# zK>?-jEOke)dvRU44*>m$tSNn&l1&%ccp@e|sOI=m$HXRSX~C^Yu`SsUJ)Ul1hK|gm zqnr2WL>SA`Za$z)I~wv&;|ahIEsw~g+UDsdhohG|6c(N&Q26G9dAsG|GS?N`T;H^v z(qS#qTiS`qN5lYe@d_Uub0|`Ph7klRyY_0o1zDI0KfrIkfP|WtYZM0?8Z=Jt&S;^} zj|yRvd1_vAfcK{DsA9J;|LPi2M#! zhACW-@C+gW%RZ2c|DY3y=4M?es)nJDV%$Ly_+AkZ7)!=S6tn1@sW4M^^-ksE|MDv-m}%~0iRYTz>yy-E)do`Ppi{!&s`IvANW=2!@LJt~J4 zb7G2vC-GNbYvF`Z$Mz*Z0_CK&xFIF5(KF<{4#QG{^@COy3XHAHbG9n@dI>GdCRdNF zGR%)pFcsE$H(`_n=T#va9$^aSASY5`ndC{{+8MGz3pJ&)36e)R4hh-ta15wSLqMmM zh}gG|qP6Nl_ui^21D!L)k{#A@0DWjD=x~fcr?3ZoXjjk)BHRLc={1WbYMHJWzWNcc zP2d;p1nRs{PN1Ns?(ld6ovE!ynq6BdS9B|7s1{_*06D-^ zOC`X#53A?-$LL)ZJQz`YnRNP%>oP=u>XZ&RS~JXt0D}G zpo$VBwgF{iM%8}Gh#e>@UztSjDRTgEHDWic%T*eflc{ zy|0X5OAq?@t&9}?KBz~%Y%@KYnR2&iNE0eYVH~8Cj8?Ymf(|i`0O_R~;jC&{TiYi> zy=l7_%XrCB@nIVHu-Q>H&!Z@&tz0RoE>-LcNVK>?n-TVFmYNI+EqyeYFBXPRQy+^U zV(FIIg7=zMJu7O(e;F_Sn*!}s-PjJ#v)Wk;cs>}M%zvE^lGA6 zBP&9s0~csjP|b-q_~B|33e=ZNjww9OX0LZX4m$R7c>)s`Wgm#5O!J{h&QPr`bnz%*Vb$*Ke++<~-=~{GpA_*AZyny9+GpCNyrFk+@~I?=5C6 z=&yihSVg@#dphP5-FOxVoQepKGo6bvR6kQiTs+?919B0cMdufhaIuesV`kt#79jR8 zrMNc?QSbKfTHADB0)qPyZQIs=b#yqwg!Rmz4(C*obhy@Mro*-V^Py}M#`uEy*nFJ5EuUv~4S#i(R4AC^98o}}xN<_%KB8X-%^ zNed>W2q&6Af#Q{l5OQW{q}*$xkuMMZ7>%9bULQWqkj+$%L7?DbK(pwq_zbPHWm=$MH>lr8FsJaR~bt`lc_$ zNo?sPzpn;Gj( zA2`9ABLAA*1a9yaEgrXEUXv0mTKTQCE+nL#Hob>pSSDdI-4)ZB(`lTxd2hhQAjLZ< z?8Q!m)dn0C#7u7F^~yn!Rt^fqDdKuS49F*u1P8^CUI>_9WhITXVpU+Nl;U=AP+(o* zM-v*rAgk8WK|$cDIVe(&gs|#iRVxRDf1{&82L<`5?7*~yiAKFk!W(dz%tbqT#S=lz zh$fw5SAFO0x{bm3ekg(>7a{690Fa_okw5K~XKy7Q zcQS_vTHx);&V*M zJaMZtKeFz@k33QOk>N&u>O{peX?D^?Cr|2X7IL}<*AapHsbFG7qqu!~ZC~R%A5s+k zkdB(v=}TZgX=B*GOTbfHVO@1Y*ggl^ik!v#=Ay#(IWp)Enl#imM+EsX0P(I-372A< zm^p*UBfx(&0cxqk!vf8-if${MMVjH=mPMeZta1`UZ#d~HV`3cD#6(~? zw7R2SE12ft(CjW+p)OZTvm&Lr!!c)Eh`uJ7Q=rJG26)gg5fIl)!LG>9*co zE7%c3b6d0AmR4|Ev_d?n72+R~R*0n)Vh$%7dd-Po%rDpQR;{2tKCcTrsh3)zI$+rM z`1-0w|Vztjq}AeS#Rpl`622MkN$x`$SX^#So%TA@5(_$IgAd~-{}Zj@f7_Z8 zg#^SpnDES~Rv`W^Es`(ti-R?;{a1zU!_s2%An?o*-`pHL9vw)|%Vh8UcqtAi7!+^e zVl*AW@NAQ)4W6)SIOU4+0cu;I>oko2i&Q}Z^Jbv~nr_wyXsYCt7WD`X1;yDRgb)=c zD#gPAw=BXk_LrzIMB-C+IuYB9P#s?vNogdl<08x_aD;|fj@aL?B3EBRq_hfZ@6=96 zEHc+xV3BL*GXzH*3L$4hq0m+=Ya5B~o66(x=XanUYa<+~cUtC%DhC!rrtwr07NY+lyjiNKggEPKI&KYSbL^AFrd$Q9=+OEO4I+|ruC7gxp zkD)!2WhZve5S20FE^XRqdEvjD!`WlZuXV*@`gM=lP_*}u$>|RnoaKIaM+*f5k>}nU zT|WpI!dJ$o0XI25M3H0e-9n|<^{C#ihacpg42aNhJSO`n6xs}dhF3!%Pt|d*K{S+o zO(|?JyP-nFHp1ABJ&d;jAhX9~{=}a@JR3{$06tdXMKMoU_UlO&-Qyg=)}KCwA=pxo z+>-Neajku4N3(=aA=rT@S(rdrmDF1X&U7O@AUEGc*QEpp-Vo&?pGnc>_AD4 zPNj}YufoS_W(lm4blBI2`0kkYk6^BRHx9~;fb|N;JW<`qoX06UY+`3l z`I~+OI7R_898|{;h!}(o4m*VA95su@ zTR;{ZCCtDxYT5=%patf!Lta3JAj5Cgk?hftSRxjO`Q^dM<|MU=b@6^L z&}u5Uy`Z?p&$Qb|bf}7a&f>Clu7maTW%!0BIq{w>w&!zM9$N}nz?Im){kChePnyUI zo?a$&*hc{Bd|B>%028xx1qZs2aCV4Vz$XLC9)`ktNAmYGsH!#&VZWyFXMg{v)T2gN zjQ~{nGj1Az1#p#ll!8mPow|bvm$d87(BZ-DE!j}bmlpd7O1=?*emlnTWlq>Ga!Ky! ze)qIq5`Nvo5w&kg8n%zg%8+)9J5hXU5Jnr5=$g@mlX$dZO34s^>ADCZlu{`t9XDu1Z7p!THut4wMex(xuZ9ge(tZ2uIcw~&&j z3-i2-bTIT};YB)V9Q%QYc#W#s4`;_jE3N~lbX)>H_yfqoI}f-LM4L2ksu9c(`50BF z4zYYV_h5+7s+7ZrksfRvhaRC7?soF zOg@WvDZ9!Qe{brj<4A_Sw*Htt`L8%nl7F$E{Hk`7Lh}DQRF(dAnS6^Yz7xJ4@lv?I z8t$(gk_^E>nDRq=Y|3V=_%td*>7VoI*?(%--)D_a4gLsoNA(3Y&BTm9%g5jqETz9# zRa@YDK;_4)*vLle>f=>4e5kC7{pWZLHIn5XNd(2p?o$hLBQQA#H(1nvmj{)@2Zt2d z1&5Q&K8r=U`1&A?$r`MrzxRSFX8aRa3`e%<9?H;qvmMG!HUl-k&;~1;j>}IzBm8j?&L! z7^R;FZLb=;0Z^Oc0XER-tpm05GBl`Z<;Juk&bE_jT;R>}lY;lL;ts^@cl6b3kSyRt6SdSFZJ3ZEeP)L*M~!;Fm9suHEtb+7#Me1pK(iw`uczj2j1i$ z{A11>S{i!sydzKuRJ5nF?8VxT8T<7}s~-Vl!`9a7kA|@|+jekbD|@4D_GfP-Gd6&+ zkpE*#Cd7V-?A`jIv$yaF7xRBoZKHBT;op&E08bl#o4(;m!cKOMgk3VIr{J}qr?woT z>9e+;Xf0?ewV^dWDUo_$H&IFl8580;VQrF4Qz7Aa#BVgf+JlwYiS?RFCmoQDA<3kj>vzDa_{_UhYY9r(F@5Ukj5A zON2>&z2->xwxH=PCYVWvI9|gX`HN7{1{dt|x)pSjIoPV2_a5Sa#kQ+z?$fba&9b%1 zoY&XgU=#a$+ED_Z8Go7gzyy?hq5S+6W(;z!Ve>H6$q51YXVcbqmEal3WT3-@+C_(XS*=Kf~e%4K;}J2qtk^9|ZU7 zBPeQSJxVCq{-lEHw~o=y6kfq7lX8?YlMKCnyf!#R)-qTY$AMD};IQ9i0JF;kz?%NO z2fXE*vx6=0%~`7^!5HwgZJgG>9leUGZ_fHzV%F<+01#7FL-1z`V~8mnp628Xi!*a& zDuT@)DPfXq4HrK8%Hi|sj8$DHRy3RrFhqP+Ne)iKxt-yEy){!?e5rfiM!>E=RT#Qw z+qF7XXx+14Yo`jKXgLx^tI|q*bJiB>YDv&xs%otp0v)Ve4lRbw>gxLt6mNy?h?k!u)xUS`T6W4WIZ{vCg*Sopi%k`67w{qRdb(-s5uAk-l zIj%Xbd9H`KmblJwt#WO0{UX=j=K5u>FL3<>u3zK&b*|s!`dzLkxqgr954ipn*T3QV zBd)J=MO=ST@IFdk+hP7y(sqOUw@4p^{a^6D9sYk$+GNAI$3Xng_TJNbbY*^a;rv6( zB)g|cJ63n@5_dhn!1Xwn{(YDGFn5iczp7+&HOhUAtIef(`@o6gv$GFgyg2v3!p4ac zH}HIRZhd{W>f?6GsNE)ge2M#(^6#YX`ohjCTMC^xNuiG|oZH#vM`%;9ZY>TBD-X=A z&+jb%ZOY%r^+m3)a_Qew+%?7nT;&)=iyK^m`!e}E`grHNW@jI|aQ@8Px%uzBj)%Scawz7wUJCtL#^}J;-$x*CAJ29?jwp^F7q(N3;K&ivB(eESksr zx!!eT_BcuQ?^)ibc)FVF8m?)s!(8H}H*#Iem2-8tx?DfOb%g6pTyN%j3m0L2#&2)s z{x&Y$Ot`Q-4z6oDcQe-q=jYDcvV7)Iw|xGC^Q#x;*L!-y3wYt7hvwHT$3K4Pk!9W% z=GWcn^BZ$#7tZ883g^o!xx08wkH?l*=GW$W3(M!-iR*8_ICpO4?EJ0E;OSy7^m!Nk zeTeIZezJ}IBcz=;5fX1&+psC!onDestroy(); } +// Script that reads the body. +TEST_P(WasmHttpFilterTest, BodyRequestReadBody) { + setupConfig(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( + "{{ test_rundir }}/test/extensions/filters/http/wasm/test_data/body_cpp.wasm"))); + setupFilter(); + EXPECT_CALL(*filter_, + scriptLog_(spdlog::level::err, Eq(absl::string_view("onRequestBody hello")))); + Http::TestHeaderMapImpl request_headers{{":path", "/"}, {"x-test-operation", "ReadBody"}}; + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, true)); + Buffer::OwnedImpl data("hello"); + EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data, true)); + filter_->onDestroy(); +} + +// Script that buffers the body. +TEST_P(WasmHttpFilterTest, BodyRequestBufferBody) { + setupConfig(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( + "{{ test_rundir }}/test/extensions/filters/http/wasm/test_data/body_cpp.wasm"))); + setupFilter(); + + Http::TestHeaderMapImpl request_headers{{":path", "/"}, + {"x-test-operation", "BufferBody"}}; + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, false)); + + Buffer::OwnedImpl bufferedBody; + EXPECT_CALL(decoder_callbacks_, decodingBuffer()).WillRepeatedly(Return(&bufferedBody)); + + Buffer::OwnedImpl data1("hello"); + bufferedBody.add(data1); + EXPECT_CALL(*filter_, + scriptLog_(spdlog::level::err, Eq(absl::string_view("onRequestBody hello")))) + .Times(1); + EXPECT_EQ(Http::FilterDataStatus::StopIterationAndBuffer, filter_->decodeData(data1, false)); + + Buffer::OwnedImpl data2(" again "); + bufferedBody.add(data2); + EXPECT_CALL(*filter_, + scriptLog_(spdlog::level::err, Eq(absl::string_view("onRequestBody hello again ")))) + .Times(1); + EXPECT_EQ(Http::FilterDataStatus::StopIterationAndBuffer, filter_->decodeData(data2, false)); + + EXPECT_CALL(*filter_, scriptLog_(spdlog::level::err, + Eq(absl::string_view("onRequestBody hello again hello")))) + .Times(1); + Buffer::OwnedImpl data3("hello"); + bufferedBody.add(data3); + EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data3, true)); + + // Verify that the response still works even though we buffered the request. + Http::TestHeaderMapImpl response_headers{{":status", "200"}, + {"x-test-operation", "ReadBody"}}; + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers, false)); + // Should not buffer this time + EXPECT_CALL(*filter_, + scriptLog_(spdlog::level::err, Eq(absl::string_view("onRequestBody hello")))) + .Times(2); + EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(data1, false)); + EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(data1, true)); + + filter_->onDestroy(); +} + +// Script that buffers the first part of the body and streams the rest +TEST_P(WasmHttpFilterTest, BodyRequestBufferThenStreamBody) { + setupConfig(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( + "{{ test_rundir }}/test/extensions/filters/http/wasm/test_data/body_cpp.wasm"))); + setupFilter(); + + Http::TestHeaderMapImpl request_headers{{":path", "/"}}; + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, true)); + + Buffer::OwnedImpl bufferedBody; + EXPECT_CALL(decoder_callbacks_, decodingBuffer()).WillRepeatedly(Return(&bufferedBody)); + + Http::TestHeaderMapImpl response_headers{{":status", "200"}, + {"x-test-operation", "BufferTwoBodies"}}; + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers, false)); + + Buffer::OwnedImpl data1("hello"); + EXPECT_CALL(*filter_, + scriptLog_(spdlog::level::err, Eq(absl::string_view("onRequestBody hello")))) + .Times(1); + EXPECT_EQ(Http::FilterDataStatus::StopIterationAndBuffer, filter_->decodeData(data1, false)); + bufferedBody.add(data1); + + Buffer::OwnedImpl data2(", there, "); + bufferedBody.add(data2); + EXPECT_CALL(*filter_, + scriptLog_(spdlog::level::err, Eq(absl::string_view("onRequestBody hello, there, ")))) + .Times(1); + EXPECT_EQ(Http::FilterDataStatus::StopIterationAndBuffer, filter_->decodeData(data2, false)); + + // Previous callbacks returned "Buffer" so we have buffered so far + Buffer::OwnedImpl data3("world!"); + bufferedBody.add(data3); + EXPECT_CALL(*filter_, scriptLog_(spdlog::level::err, + Eq(absl::string_view("onRequestBody hello, there, world!")))) + .Times(1); + EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data3, false)); + + // Last callback returned "continue" so we just see individual chunks. + Buffer::OwnedImpl data4("So it's "); + EXPECT_CALL(*filter_, + scriptLog_(spdlog::level::err, Eq(absl::string_view("onRequestBody So it's ")))) + .Times(1); + EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data4, false)); + + Buffer::OwnedImpl data5("goodbye, then!"); + EXPECT_CALL(*filter_, + scriptLog_(spdlog::level::err, Eq(absl::string_view("onRequestBody goodbye, then!")))) + .Times(1); + EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data5, true)); + + filter_->onDestroy(); +} + // Script testing AccessLog::Instance::log. TEST_P(WasmHttpFilterTest, AccessLog) { setupConfig(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute(