| /** @file |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| #include <limits.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <functional> |
| |
| #include "ts/ts.h" |
| |
| static const std::string_view MultipartBoundary{"\r\n--- ATS xDebug Probe Injection Boundary ---\r\n\r\n"}; |
| |
| static char Hostname[1024]; |
| |
| static std::string |
| getPreBody(TSHttpTxn txn) |
| { |
| std::stringstream output; |
| output << "{'xDebugProbeAt' : '" << Hostname << "'\n 'captured':["; |
| print_request_headers(txn, output); |
| output << "\n ]\n}"; |
| output << MultipartBoundary; |
| return output.str(); |
| } |
| |
| static std::string |
| getPostBody(TSHttpTxn txn) |
| { |
| std::stringstream output; |
| output << MultipartBoundary; |
| output << "{'xDebugProbeAt' : '" << Hostname << "'\n 'captured':["; |
| print_response_headers(txn, output); |
| output << "\n ]\n}"; |
| return output.str(); |
| } |
| |
| static void |
| writePostBody(TSHttpTxn txn, BodyBuilder *data) |
| { |
| if (data->wrote_body && data->hdr_ready && !data->wrote_postbody.test_and_set()) { |
| TSDebug("xdebug_transform", "body_transform(): Writing postbody headers..."); |
| std::string postbody = getPostBody(txn); |
| TSIOBufferWrite(data->output_buffer.get(), postbody.data(), postbody.length()); |
| data->nbytes += postbody.length(); |
| TSVIONBytesSet(data->output_vio, data->nbytes); |
| TSVIOReenable(data->output_vio); |
| } |
| } |
| |
| static int |
| body_transform(TSCont contp, TSEvent event, void *edata) |
| { |
| TSHttpTxn txn = static_cast<TSHttpTxn>(TSContDataGet(contp)); |
| BodyBuilder *data = AuxDataMgr::data(txn).body_builder.get(); |
| if (!data) { |
| return TS_ERROR; |
| } |
| if (TSVConnClosedGet(contp)) { |
| // write connection destroyed. |
| return 0; |
| } |
| |
| TSVIO src_vio = TSVConnWriteVIOGet(contp); |
| |
| switch (event) { |
| case TS_EVENT_ERROR: { |
| // Notify input vio of this error event |
| TSContCall(TSVIOContGet(src_vio), TS_EVENT_ERROR, src_vio); |
| return 0; |
| } |
| case TS_EVENT_VCONN_WRITE_COMPLETE: { |
| TSVConnShutdown(TSTransformOutputVConnGet(contp), 0, 1); |
| return 0; |
| } |
| case TS_EVENT_VCONN_WRITE_READY: |
| TSDebug("xdebug_transform", "body_transform(): Event is TS_EVENT_VCONN_WRITE_READY"); |
| // fall through |
| default: |
| if (!data->output_buffer.get()) { |
| data->output_buffer.reset(TSIOBufferCreate()); |
| data->output_reader.reset(TSIOBufferReaderAlloc(data->output_buffer.get())); |
| data->output_vio = TSVConnWrite(TSTransformOutputVConnGet(contp), contp, data->output_reader.get(), INT64_MAX); |
| } |
| |
| if (data->wrote_prebody == false) { |
| TSDebug("xdebug_transform", "body_transform(): Writing prebody headers..."); |
| std::string prebody = getPreBody(txn); |
| TSIOBufferWrite(data->output_buffer.get(), prebody.data(), prebody.length()); // write prebody |
| data->wrote_prebody = true; |
| data->nbytes += prebody.length(); |
| } |
| |
| TSIOBuffer src_buf = TSVIOBufferGet(src_vio); |
| |
| if (!src_buf) { |
| // upstream continuation shuts down write operation. |
| data->wrote_body = true; |
| writePostBody(txn, data); |
| return 0; |
| } |
| |
| int64_t towrite = TSVIONTodoGet(src_vio); |
| TSDebug("xdebug_transform", "body_transform(): %" PRId64 " bytes of body is expected", towrite); |
| int64_t avail = TSIOBufferReaderAvail(TSVIOReaderGet(src_vio)); |
| towrite = towrite > avail ? avail : towrite; |
| if (towrite > 0) { |
| TSIOBufferCopy(TSVIOBufferGet(data->output_vio), TSVIOReaderGet(src_vio), towrite, 0); |
| TSIOBufferReaderConsume(TSVIOReaderGet(src_vio), towrite); |
| TSVIONDoneSet(src_vio, TSVIONDoneGet(src_vio) + towrite); |
| TSDebug("xdebug_transform", "body_transform(): writing %" PRId64 " bytes of body", towrite); |
| } |
| |
| if (TSVIONTodoGet(src_vio) > 0) { |
| TSVIOReenable(data->output_vio); |
| TSContCall(TSVIOContGet(src_vio), TS_EVENT_VCONN_WRITE_READY, src_vio); |
| } else { |
| // End of src vio |
| // Write post body content and update output VIO |
| data->wrote_body = true; |
| data->nbytes += TSVIONDoneGet(src_vio); |
| writePostBody(txn, data); |
| TSContCall(TSVIOContGet(src_vio), TS_EVENT_VCONN_WRITE_COMPLETE, src_vio); |
| } |
| } |
| return 0; |
| } |