plugins/experimental/wasm/ats_context.cc (1,561 lines of code) (raw):

/* 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 <deque> #include <map> #include <memory> #include <mutex> #include <unordered_map> #include <unordered_set> #include "ats_context.h" #include "ats_wasm.h" #include "include/proxy-wasm/context.h" #include "include/proxy-wasm/wasm.h" #include "src/shared_data.h" #include "src/shared_queue.h" namespace ats_wasm { DbgCtl dbg_ctl{WASM_DEBUG_TAG}; static int async_handler(TSCont cont, TSEvent event, void *edata) { // information for the handler TSHttpTxn txn = static_cast<TSHttpTxn>(edata); AsyncInfo *ai = static_cast<AsyncInfo *>(TSContDataGet(cont)); uint32_t token = ai->token; Context *root_context = ai->root_context; Wasm *wasm = root_context->wasm(); // variables to be used in handler TSEvent result = static_cast<TSEvent>(FETCH_EVENT_ID_BASE + 1); const void *body = nullptr; size_t body_size = 0; TSMBuffer hdr_buf = nullptr; TSMLoc hdr_loc = nullptr; int header_size = 0; TSMutexLock(wasm->mutex()); // filling in variables for a successful fetch if (event == static_cast<TSEvent>(FETCH_EVENT_ID_BASE)) { int data_len; const char *data_start = TSFetchRespGet(txn, &data_len); if (data_start && (data_len > 0)) { const char *data_end = data_start + data_len; TSHttpParser parser = TSHttpParserCreate(); hdr_buf = TSMBufferCreate(); hdr_loc = TSHttpHdrCreate(hdr_buf); TSHttpHdrTypeSet(hdr_buf, hdr_loc, TS_HTTP_TYPE_RESPONSE); if (TSHttpHdrParseResp(parser, hdr_buf, hdr_loc, &data_start, data_end) == TS_PARSE_DONE) { TSHttpStatus status = TSHttpHdrStatusGet(hdr_buf, hdr_loc); header_size = TSMimeHdrFieldsCount(hdr_buf, hdr_loc); body = data_start; // data_start will now be pointing to body body_size = data_end - data_start; Dbg(dbg_ctl, "[%s] Fetch result had a status code of %d with a body length of %ld", __FUNCTION__, status, body_size); } else { TSError("[wasm][%s] Unable to parse call response", __FUNCTION__); event = static_cast<TSEvent>(FETCH_EVENT_ID_BASE + 1); } TSHttpParserDestroy(parser); } else { TSError("[wasm][%s] Successful fetch did not result in any content. Assuming failure", __FUNCTION__); event = static_cast<TSEvent>(FETCH_EVENT_ID_BASE + 1); } result = event; } // callback function Dbg(dbg_ctl, "[%s] setting root context call result", __FUNCTION__); root_context->setHttpCallResult(hdr_buf, hdr_loc, body, body_size, result); Dbg(dbg_ctl, "[%s] trigger root context function, token: %d", __FUNCTION__, token); root_context->onHttpCallResponse(token, header_size, body_size, 0); Dbg(dbg_ctl, "[%s] resetting root context call result", __FUNCTION__); root_context->resetHttpCallResult(); // cleaning up if (hdr_loc) { TSMLoc null_parent_loc = nullptr; TSHandleMLocRelease(hdr_buf, null_parent_loc, hdr_loc); } if (hdr_buf) { TSMBufferDestroy(hdr_buf); } TSMutexUnlock(wasm->mutex()); Dbg(dbg_ctl, "[%s] delete async info and continuation", __FUNCTION__); // delete the Async Info delete ai; // delete continuation TSContDestroy(cont); return 0; } // utiltiy function for properties static void print_address(struct sockaddr const *ip, std::string *result) { if (ip != nullptr) { char cip[128] = ""; int64_t port = 0; if (ip->sa_family == AF_INET) { const auto *s_sockaddr_in = reinterpret_cast<const struct sockaddr_in *>(ip); inet_ntop(AF_INET, &s_sockaddr_in->sin_addr, cip, sizeof(cip)); port = s_sockaddr_in->sin_port; } else if (ip->sa_family == AF_INET6) { const auto *s_sockaddr_in6 = reinterpret_cast<const struct sockaddr_in6 *>(ip); inet_ntop(AF_INET6, &s_sockaddr_in6->sin6_addr, cip, sizeof(cip)); port = s_sockaddr_in6->sin6_port; } Dbg(dbg_ctl, "[%s] property retrieval - address: %.*s", __FUNCTION__, static_cast<int>(sizeof(cip)), cip); std::string cip_str(cip); result->assign(cip_str + ":" + std::to_string(port)); } else { *result = pv_empty; } } static void print_port(struct sockaddr const *ip, std::string *result) { if (ip != nullptr) { int64_t port = 0; if (ip->sa_family == AF_INET) { const auto *s_sockaddr_in = reinterpret_cast<const struct sockaddr_in *>(ip); port = s_sockaddr_in->sin_port; } else { const auto *s_sockaddr_in6 = reinterpret_cast<const struct sockaddr_in6 *>(ip); port = s_sockaddr_in6->sin6_port; } Dbg(dbg_ctl, "[%s] looking for source port: %d", __FUNCTION__, static_cast<int>(port)); result->assign(reinterpret_cast<const char *>(&port), sizeof(int64_t)); } else { *result = pv_empty; } } static void print_certificate(std::string *result, X509_NAME *name) { if (name == nullptr) { *result = pv_empty; return; } BIO *bio = BIO_new(BIO_s_mem()); if (bio == nullptr) { *result = pv_empty; return; } if (X509_NAME_print_ex(bio, name, 0 /* indent */, XN_FLAG_ONELINE) > 0) { int64_t len = 0; char *ptr = nullptr; len = BIO_get_mem_data(bio, &ptr); result->assign(ptr, len); Dbg(dbg_ctl, "print SSL certificate %.*s", static_cast<int>(len), ptr); } BIO_free(bio); } static void print_san_certificate(std::string *result, X509 *cert, int type) { int ext_ndx = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1); if (ext_ndx >= 0) { X509_EXTENSION *ext = nullptr; STACK_OF(GENERAL_NAME) *alt_names = nullptr; GENERAL_NAME *gen_name = nullptr; ext = X509_get_ext(cert, ext_ndx); alt_names = static_cast<stack_st_GENERAL_NAME *>(X509V3_EXT_d2i(ext)); if (alt_names != nullptr) { int num = sk_GENERAL_NAME_num(alt_names); bool found = false; for (int i = 0; i < num; i++) { gen_name = sk_GENERAL_NAME_value(alt_names, i); if (gen_name->type == type) { char *dnsname = reinterpret_cast<char *>(ASN1_STRING_data(gen_name->d.dNSName)); int dnsname_len = ASN1_STRING_length(gen_name->d.dNSName); result->assign(dnsname, dnsname_len); found = true; break; } } if (!found) { *result = pv_empty; } sk_GENERAL_NAME_free(alt_names); } else { *result = pv_empty; } } else { *result = pv_empty; } } static bool get_header(TSMBuffer bufp, TSMLoc hdr_loc, std::string_view v, std::string *result) { const char *key = v.data(); int key_len = v.size(); const char *val = nullptr; int val_len = 0; std::string res = ""; TSMLoc field_loc = nullptr; TSMLoc next_field_loc = nullptr; bool found = false; field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, key, key_len); if (field_loc != TS_NULL_MLOC) { while (field_loc != TS_NULL_MLOC) { val = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, -1, &val_len); next_field_loc = TSMimeHdrFieldNextDup(bufp, hdr_loc, field_loc); res.append(val, val_len); if (next_field_loc != TS_NULL_MLOC) { res.append(",", 1); } TSHandleMLocRelease(bufp, hdr_loc, field_loc); field_loc = next_field_loc; } found = true; } *result = res; return found; } static void set_header(TSMBuffer bufp, TSMLoc hdr_loc, std::string_view v, std::string_view serialized_value) { bool remove = false; if (serialized_value == "") { remove = true; } TSMLoc field_loc = nullptr; TSMLoc tmp = nullptr; bool first = true; field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, v.data(), v.size()); if (remove) { while (field_loc != TS_NULL_MLOC) { tmp = TSMimeHdrFieldNextDup(bufp, hdr_loc, field_loc); TSMimeHdrFieldDestroy(bufp, hdr_loc, field_loc); TSHandleMLocRelease(bufp, hdr_loc, field_loc); field_loc = tmp; } } else if (field_loc != TS_NULL_MLOC) { first = true; while (field_loc != TS_NULL_MLOC) { tmp = TSMimeHdrFieldNextDup(bufp, hdr_loc, field_loc); if (first) { first = false; TSMimeHdrFieldValueStringSet(bufp, hdr_loc, field_loc, -1, serialized_value.data(), serialized_value.size()); } else { TSMimeHdrFieldDestroy(bufp, hdr_loc, field_loc); } TSHandleMLocRelease(bufp, hdr_loc, field_loc); field_loc = tmp; } } else if (TSMimeHdrFieldCreateNamed(bufp, hdr_loc, v.data(), v.size(), &field_loc) != TS_SUCCESS) { TSError("[wasm][%s] TSMimeHdrFieldCreateNamed error", __FUNCTION__); } else { TSMimeHdrFieldValueStringSet(bufp, hdr_loc, field_loc, -1, serialized_value.data(), serialized_value.size()); TSMimeHdrFieldAppend(bufp, hdr_loc, field_loc); } if (field_loc != TS_NULL_MLOC) { TSHandleMLocRelease(bufp, hdr_loc, field_loc); } } // Buffer copyTo WasmResult Buffer::copyTo(WasmBase *wasm, size_t start, size_t length, uint64_t ptr_ptr, uint64_t size_ptr) const { if (owned_data_str_ != "") { std::string_view s(owned_data_str_); if (!wasm->copyToPointerSize(s, ptr_ptr, size_ptr)) { return WasmResult::InvalidMemoryAccess; } return WasmResult::Ok; } return BufferBase::copyTo(wasm, start, length, ptr_ptr, size_ptr); } Context::Context() : ContextBase() {} Context::Context(Wasm *wasm) : ContextBase(wasm) {} Context::Context(Wasm *wasm, const std::shared_ptr<PluginBase> &plugin) : ContextBase(wasm, plugin) {} // NB: wasm can be nullptr if it failed to be created successfully. Context::Context(Wasm *wasm, uint32_t parent_context_id, const std::shared_ptr<PluginBase> &plugin) : ContextBase(wasm, plugin) { // setting up parent context parent_context_id_ = parent_context_id; if (wasm_ != nullptr) { parent_context_ = wasm_->getContext(parent_context_id_); } } // utility functions for the extended class Wasm * Context::wasm() const { return static_cast<Wasm *>(wasm_); } Context * Context::parent_context() const { return static_cast<Context *>(parent_context_); } Context * Context::root_context() const { const ContextBase *previous = this; ContextBase *parent = parent_context_; while (parent != previous) { previous = parent; parent = parent->parent_context(); } return static_cast<Context *>(parent); } void Context::initialize(TSHttpTxn txnp) { txnp_ = txnp; } void Context::initialize(TSCont cont) { scheduler_cont_ = cont; } TSHttpTxn Context::txnp() { return txnp_; } TSCont Context::scheduler_cont() { return scheduler_cont_; } void Context::error(std::string_view message) { TSError("%.*s", static_cast<int>(message.size()), message.data()); abort(); } // local reply handler void Context::onLocalReply() { if (local_reply_) { if (txnp_ == nullptr) { return; } TSMBuffer bufp = nullptr; TSMLoc hdr_loc = nullptr; if (TSHttpTxnClientRespGet(txnp_, &bufp, &hdr_loc) != TS_SUCCESS) { return; } // set local reply reason if (!local_reply_details_.empty()) { TSHttpHdrReasonSet(bufp, hdr_loc, local_reply_details_.data(), local_reply_details_.size()); } // set local reply headers for (auto &p : local_reply_headers_) { std::string key(p.first); std::string value(p.second); auto *loc = TSMimeHdrFieldFind(bufp, hdr_loc, key.data(), static_cast<int>(key.size())); if (loc != TS_NULL_MLOC) { int first = 1; while (loc != TS_NULL_MLOC) { auto *tmp = TSMimeHdrFieldNextDup(bufp, hdr_loc, loc); if (first != 0) { first = 0; TSMimeHdrFieldValueStringSet(bufp, hdr_loc, loc, -1, value.data(), static_cast<int>(value.size())); } else { TSMimeHdrFieldDestroy(bufp, hdr_loc, loc); } TSHandleMLocRelease(bufp, hdr_loc, loc); loc = tmp; } } else if (TSMimeHdrFieldCreateNamed(bufp, hdr_loc, key.data(), static_cast<int>(key.size()), &loc) != TS_SUCCESS) { TSError("[wasm][%s] TSMimeHdrFieldCreateNamed error", __FUNCTION__); TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); return; } else { TSMimeHdrFieldValueStringSet(bufp, hdr_loc, loc, -1, value.data(), static_cast<int>(value.size())); TSMimeHdrFieldAppend(bufp, hdr_loc, loc); } if (loc != TS_NULL_MLOC) { TSHandleMLocRelease(bufp, hdr_loc, loc); } } TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); } } // // General functions // WasmResult Context::log(uint32_t level, std::string_view message) { auto l = static_cast<LogLevel>(level); switch (l) { case LogLevel::trace: Dbg(dbg_ctl, "wasm trace log%s: %.*s", std::string(log_prefix()).c_str(), static_cast<int>(message.size()), message.data()); return WasmResult::Ok; case LogLevel::debug: Dbg(dbg_ctl, "wasm debug log%s: %.*s", std::string(log_prefix()).c_str(), static_cast<int>(message.size()), message.data()); return WasmResult::Ok; case LogLevel::info: Dbg(dbg_ctl, "wasm info log%s: %.*s", std::string(log_prefix()).c_str(), static_cast<int>(message.size()), message.data()); return WasmResult::Ok; case LogLevel::warn: Dbg(dbg_ctl, "wasm warn log%s: %.*s", std::string(log_prefix()).c_str(), static_cast<int>(message.size()), message.data()); return WasmResult::Ok; case LogLevel::error: Dbg(dbg_ctl, "wasm error log%s: %.*s", std::string(log_prefix()).c_str(), static_cast<int>(message.size()), message.data()); return WasmResult::Ok; case LogLevel::critical: Dbg(dbg_ctl, "wasm critical log%s: %.*s", std::string(log_prefix()).c_str(), static_cast<int>(message.size()), message.data()); return WasmResult::Ok; default: // e.g. off return unimplemented(); } return unimplemented(); } uint64_t Context::getCurrentTimeNanoseconds() { return TShrtime(); } uint64_t Context::getMonotonicTimeNanoseconds() { return TShrtime(); } std::string_view Context::getConfiguration() { return plugin_->plugin_configuration_; } WasmResult Context::setTimerPeriod(std::chrono::milliseconds period, uint32_t *timer_token_ptr) { Wasm *wasm = this->wasm(); Context *root_context = this->root_context(); TSMutexLock(wasm->mutex()); if (!wasm->existsTimerPeriod(root_context->id())) { Dbg(dbg_ctl, "[%s] no previous timer period set", __FUNCTION__); TSCont contp = root_context->scheduler_cont(); if (contp != nullptr) { Dbg(dbg_ctl, "[%s] scheduling continuation for timer", __FUNCTION__); TSContDataSet(contp, root_context); TSContScheduleOnPool(contp, static_cast<TSHRTime>(period.count()), TS_THREAD_POOL_NET); } } wasm->setTimerPeriod(root_context->id(), period); *timer_token_ptr = 0; TSMutexUnlock(wasm->mutex()); return WasmResult::Ok; } BufferInterface * Context::getBuffer(WasmBufferType type) { switch (type) { case WasmBufferType::VmConfiguration: return buffer_.set(wasm_->vm_configuration()); case WasmBufferType::PluginConfiguration: return buffer_.set(plugin_->plugin_configuration_); case WasmBufferType::HttpCallResponseBody: if (cr_body_ != nullptr) { return buffer_.set(std::string(static_cast<const char *>(cr_body_), cr_body_size_)); } return buffer_.set(""); case WasmBufferType::HttpRequestBody: case WasmBufferType::HttpResponseBody: // return transform result return &transform_result_; case WasmBufferType::CallData: case WasmBufferType::NetworkDownstreamData: case WasmBufferType::NetworkUpstreamData: case WasmBufferType::GrpcReceiveBuffer: default: unimplemented(); return nullptr; } } WasmResult Context::httpCall(std::string_view /* target ATS_UNUSED */, const Pairs &request_headers, std::string_view request_body, const Pairs & /* request_trailers ATS_UNUSED */, int /* timeout_millisconds ATS_UNUSED */, uint32_t *token_ptr) { Wasm *wasm = this->wasm(); Context *root_context = this->root_context(); TSCont contp; std::string request, method, path, authority; // setup local address for API call struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_addr.s_addr = LOCAL_IP_ADDRESS; addr.sin_port = LOCAL_PORT; for (const auto &p : request_headers) { std::string key(p.first); std::string value(p.second); if (key == ":method") { method = value; } else if (key == ":path") { path = value; } else if (key == ":authority") { authority = value; } } /* request */ request = method + " https://" + authority + path + " HTTP/1.1\r\n"; for (const auto &p : request_headers) { std::string key(p.first); std::string value(p.second); request += key + ": " + value + "\r\n"; } request += "\r\n"; request += request_body; TSFetchEvent event_ids; event_ids.success_event_id = FETCH_EVENT_ID_BASE; event_ids.failure_event_id = FETCH_EVENT_ID_BASE + 1; event_ids.timeout_event_id = FETCH_EVENT_ID_BASE + 2; contp = TSContCreate(async_handler, TSMutexCreate()); AsyncInfo *ai = new AsyncInfo(); ai->token = wasm->nextHttpCallId(); ai->root_context = root_context; *token_ptr = ai->token; // to be returned to the caller TSContDataSet(contp, ai); // API call for async fetch TSFetchUrl(request.c_str(), request.size(), reinterpret_cast<struct sockaddr const *>(&addr), contp, AFTER_BODY, event_ids); return WasmResult::Ok; } // Metrics WasmResult Context::defineMetric(uint32_t metric_type, std::string_view name, uint32_t *metric_id_ptr) { int idp = 0; TSStatSync ats_metric_type = TS_STAT_SYNC_COUNT; auto type = static_cast<MetricType>(metric_type); switch (type) { case MetricType::Counter: ats_metric_type = TS_STAT_SYNC_COUNT; break; case MetricType::Gauge: ats_metric_type = TS_STAT_SYNC_SUM; break; case MetricType::Histogram: ats_metric_type = TS_STAT_SYNC_AVG; break; default: TSError("[wasm][%s] Invalid metric type", __FUNCTION__); return WasmResult::BadArgument; } if (TSStatFindName(name.data(), &idp) == TS_ERROR) { idp = TSStatCreate(name.data(), TS_RECORDDATATYPE_INT, TS_STAT_PERSISTENT, ats_metric_type); Dbg(dbg_ctl, "[%s] creating stat: %.*s", __FUNCTION__, static_cast<int>(name.size()), name.data()); *metric_id_ptr = idp; } else { TSError("[wasm][%s] Metric already exists", __FUNCTION__); *metric_id_ptr = idp; } return WasmResult::Ok; } WasmResult Context::incrementMetric(uint32_t metric_id, int64_t offset) { TSStatIntIncrement(metric_id, offset); return WasmResult::Ok; } WasmResult Context::recordMetric(uint32_t metric_id, uint64_t value) { TSStatIntSet(metric_id, value); return WasmResult::Ok; } WasmResult Context::getMetric(uint32_t metric_id, uint64_t *value_ptr) { *value_ptr = TSStatIntGet(metric_id); return WasmResult::Ok; } // Properties WasmResult Context::getProperty(std::string_view path, std::string *result) { if (path.substr(0, p_plugin_root_id.size()) == p_plugin_root_id) { *result = this->plugin_->root_id_; Dbg(dbg_ctl, "[%s] looking for plugin_root_id: %.*s", __FUNCTION__, static_cast<int>((*result).size()), (*result).data()); return WasmResult::Ok; } else if (path.substr(0, p_plugin_name.size()) == p_plugin_name) { *result = this->plugin_->name_; Dbg(dbg_ctl, "[%s] looking for plugin_name: %.*s", __FUNCTION__, static_cast<int>((*result).size()), (*result).data()); return WasmResult::Ok; } else if (path.substr(0, p_plugin_vm_id.size()) == p_plugin_vm_id) { *result = this->plugin_->vm_id_; Dbg(dbg_ctl, "[%s] looking for plugin_vm_id: %.*s", __FUNCTION__, static_cast<int>((*result).size()), (*result).data()); return WasmResult::Ok; } else if (path.substr(0, p_node.size()) == p_node) { *result = pv_empty; Dbg(dbg_ctl, "[%s] looking for node property: empty string for now", __FUNCTION__); return WasmResult::Ok; } else if (path.substr(0, p_source_address.size()) == p_source_address) { if (txnp_ == nullptr) { *result = pv_empty; return WasmResult::Ok; } struct sockaddr const *client_ip = nullptr; client_ip = TSHttpTxnClientAddrGet(txnp_); print_address(client_ip, result); return WasmResult::Ok; } else if (path.substr(0, p_source_port.size()) == p_source_port) { if (txnp_ == nullptr) { *result = pv_empty; return WasmResult::Ok; } struct sockaddr const *client_ip = nullptr; client_ip = TSHttpTxnClientAddrGet(txnp_); print_port(client_ip, result); return WasmResult::Ok; } else if (path.substr(0, p_destination_address.size()) == p_destination_address) { if (txnp_ == nullptr) { *result = pv_empty; return WasmResult::Ok; } struct sockaddr const *local_ip = nullptr; local_ip = TSHttpTxnIncomingAddrGet(txnp_); print_address(local_ip, result); return WasmResult::Ok; } else if (path.substr(0, p_destination_port.size()) == p_destination_port) { if (txnp_ == nullptr) { *result = pv_empty; return WasmResult::Ok; } struct sockaddr const *local_ip = nullptr; local_ip = TSHttpTxnIncomingAddrGet(txnp_); print_port(local_ip, result); return WasmResult::Ok; } else if (path.substr(0, p_connection_mtls.size()) == p_connection_mtls) { bool m = false; if (txnp_ == nullptr) { result->assign(reinterpret_cast<const char *>(&m), sizeof(bool)); return WasmResult::Ok; } TSHttpSsn ssnp = TSHttpTxnSsnGet(txnp_); TSVConn client_conn = TSHttpSsnClientVConnGet(ssnp); TSSslConnection sslobj = TSVConnSslConnectionGet(client_conn); SSL *ssl = reinterpret_cast<SSL *>(sslobj); #ifdef OPENSSL_IS_OPENSSL3 X509 *cert = SSL_get1_peer_certificate(ssl); #else X509 *cert = SSL_get_peer_certificate(ssl); #endif if (cert != nullptr) { m = true; X509_free(cert); } result->assign(reinterpret_cast<const char *>(&m), sizeof(bool)); return WasmResult::Ok; } else if (path.substr(0, p_connection_requested_server_name.size()) == p_connection_requested_server_name) { if (txnp_ == nullptr) { *result = pv_empty; return WasmResult::Ok; } TSHttpSsn ssnp = TSHttpTxnSsnGet(txnp_); TSVConn client_conn = TSHttpSsnClientVConnGet(ssnp); TSSslConnection sslobj = TSVConnSslConnectionGet(client_conn); SSL *ssl = reinterpret_cast<SSL *>(sslobj); char const *const sni_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); if (sni_name != nullptr) { result->assign(sni_name); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_connection_tls_version.size()) == p_connection_tls_version) { if (txnp_ == nullptr) { *result = pv_empty; return WasmResult::Ok; } const char *ssl_protocol = "-"; TSHttpSsn ssnp = TSHttpTxnSsnGet(txnp_); TSVConn client_conn = TSHttpSsnClientVConnGet(ssnp); if (TSVConnIsSsl(client_conn) != 0) { ssl_protocol = TSVConnSslProtocolGet(client_conn); } result->assign(ssl_protocol); return WasmResult::Ok; } else if (path.substr(0, p_connection_subject_local_certificate.size()) == p_connection_subject_local_certificate) { if (txnp_ == nullptr) { *result = pv_empty; return WasmResult::Ok; } TSHttpSsn ssnp = TSHttpTxnSsnGet(txnp_); TSVConn client_conn = TSHttpSsnClientVConnGet(ssnp); TSSslConnection sslobj = TSVConnSslConnectionGet(client_conn); SSL *ssl = reinterpret_cast<SSL *>(sslobj); X509 *cert = SSL_get_certificate(ssl); if (cert != nullptr) { print_certificate(result, X509_get_subject_name(cert)); X509_free(cert); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_connection_subject_peer_certificate.size()) == p_connection_subject_peer_certificate) { if (txnp_ == nullptr) { *result = pv_empty; return WasmResult::Ok; } TSHttpSsn ssnp = TSHttpTxnSsnGet(txnp_); TSVConn client_conn = TSHttpSsnClientVConnGet(ssnp); TSSslConnection sslobj = TSVConnSslConnectionGet(client_conn); SSL *ssl = reinterpret_cast<SSL *>(sslobj); #ifdef OPENSSL_IS_OPENSSL3 X509 *cert = SSL_get1_peer_certificate(ssl); #else X509 *cert = SSL_get_peer_certificate(ssl); #endif if (cert != nullptr) { print_certificate(result, X509_get_subject_name(cert)); X509_free(cert); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_connection_dns_san_local_certificate.size()) == p_connection_dns_san_local_certificate) { if (txnp_ == nullptr) { *result = pv_empty; return WasmResult::Ok; } TSHttpSsn ssnp = TSHttpTxnSsnGet(txnp_); TSVConn client_conn = TSHttpSsnClientVConnGet(ssnp); TSSslConnection sslobj = TSVConnSslConnectionGet(client_conn); SSL *ssl = reinterpret_cast<SSL *>(sslobj); X509 *cert = SSL_get_certificate(ssl); if (cert != nullptr) { print_san_certificate(result, cert, GEN_DNS); X509_free(cert); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_connection_dns_san_peer_certificate.size()) == p_connection_dns_san_peer_certificate) { if (txnp_ == nullptr) { *result = pv_empty; return WasmResult::Ok; } TSHttpSsn ssnp = TSHttpTxnSsnGet(txnp_); TSVConn client_conn = TSHttpSsnClientVConnGet(ssnp); TSSslConnection sslobj = TSVConnSslConnectionGet(client_conn); SSL *ssl = reinterpret_cast<SSL *>(sslobj); #ifdef OPENSSL_IS_OPENSSL3 X509 *cert = SSL_get1_peer_certificate(ssl); #else X509 *cert = SSL_get_peer_certificate(ssl); #endif if (cert != nullptr) { print_san_certificate(result, cert, GEN_DNS); X509_free(cert); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_connection_uri_san_local_certificate.size()) == p_connection_uri_san_local_certificate) { if (txnp_ == nullptr) { *result = pv_empty; return WasmResult::Ok; } TSHttpSsn ssnp = TSHttpTxnSsnGet(txnp_); TSVConn client_conn = TSHttpSsnClientVConnGet(ssnp); TSSslConnection sslobj = TSVConnSslConnectionGet(client_conn); SSL *ssl = reinterpret_cast<SSL *>(sslobj); X509 *cert = SSL_get_certificate(ssl); if (cert != nullptr) { print_san_certificate(result, cert, GEN_URI); X509_free(cert); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_connection_uri_san_peer_certificate.size()) == p_connection_uri_san_peer_certificate) { if (txnp_ == nullptr) { *result = pv_empty; return WasmResult::Ok; } TSHttpSsn ssnp = TSHttpTxnSsnGet(txnp_); TSVConn client_conn = TSHttpSsnClientVConnGet(ssnp); TSSslConnection sslobj = TSVConnSslConnectionGet(client_conn); SSL *ssl = reinterpret_cast<SSL *>(sslobj); #ifdef OPENSSL_IS_OPENSSL3 X509 *cert = SSL_get1_peer_certificate(ssl); #else X509 *cert = SSL_get_peer_certificate(ssl); #endif if (cert != nullptr) { print_san_certificate(result, cert, GEN_URI); X509_free(cert); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_upstream_address.size()) == p_upstream_address) { if (txnp_ == nullptr) { *result = pv_empty; return WasmResult::Ok; } struct sockaddr const *server_ip = nullptr; server_ip = TSHttpTxnServerAddrGet(txnp_); print_address(server_ip, result); return WasmResult::Ok; } else if (path.substr(0, p_upstream_port.size()) == p_upstream_port) { if (txnp_ == nullptr) { *result = pv_empty; return WasmResult::Ok; } struct sockaddr const *server_ip = nullptr; server_ip = TSHttpTxnClientAddrGet(txnp_); print_port(server_ip, result); return WasmResult::Ok; } else if (path.substr(0, p_upstream_local_address.size()) == p_upstream_local_address) { if (txnp_ == nullptr) { *result = pv_empty; return WasmResult::Ok; } struct sockaddr const *local_ip = nullptr; local_ip = TSHttpTxnOutgoingAddrGet(txnp_); print_address(local_ip, result); return WasmResult::Ok; } else if (path.substr(0, p_upstream_local_port.size()) == p_upstream_local_port) { if (txnp_ == nullptr) { *result = pv_empty; return WasmResult::Ok; } struct sockaddr const *local_ip = nullptr; local_ip = TSHttpTxnOutgoingAddrGet(txnp_); print_port(local_ip, result); return WasmResult::Ok; } else if (path.substr(0, p_upstream_tls_version.size()) == p_upstream_tls_version) { if (txnp_ == nullptr) { *result = pv_empty; return WasmResult::Ok; } const char *ssl_protocol = "-"; TSHttpSsn ssnp = TSHttpTxnSsnGet(txnp_); TSVConn server_conn = TSHttpSsnServerVConnGet(ssnp); if (TSVConnIsSsl(server_conn) != 0) { ssl_protocol = TSVConnSslProtocolGet(server_conn); } result->assign(ssl_protocol); return WasmResult::Ok; } else if (path.substr(0, p_upstream_subject_local_certificate.size()) == p_upstream_subject_local_certificate) { if (txnp_ == nullptr) { *result = pv_empty; return WasmResult::Ok; } TSHttpSsn ssnp = TSHttpTxnSsnGet(txnp_); TSVConn client_conn = TSHttpSsnServerVConnGet(ssnp); TSSslConnection sslobj = TSVConnSslConnectionGet(client_conn); SSL *ssl = reinterpret_cast<SSL *>(sslobj); X509 *cert = SSL_get_certificate(ssl); if (cert != nullptr) { print_certificate(result, X509_get_subject_name(cert)); X509_free(cert); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_upstream_subject_peer_certificate.size()) == p_upstream_subject_peer_certificate) { if (txnp_ == nullptr) { *result = pv_empty; return WasmResult::Ok; } TSHttpSsn ssnp = TSHttpTxnSsnGet(txnp_); TSVConn client_conn = TSHttpSsnServerVConnGet(ssnp); TSSslConnection sslobj = TSVConnSslConnectionGet(client_conn); SSL *ssl = reinterpret_cast<SSL *>(sslobj); #ifdef OPENSSL_IS_OPENSSL3 X509 *cert = SSL_get1_peer_certificate(ssl); #else X509 *cert = SSL_get_peer_certificate(ssl); #endif if (cert != nullptr) { print_certificate(result, X509_get_subject_name(cert)); X509_free(cert); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_upstream_dns_san_local_certificate.size()) == p_upstream_dns_san_local_certificate) { if (txnp_ == nullptr) { *result = pv_empty; return WasmResult::Ok; } TSHttpSsn ssnp = TSHttpTxnSsnGet(txnp_); TSVConn client_conn = TSHttpSsnServerVConnGet(ssnp); TSSslConnection sslobj = TSVConnSslConnectionGet(client_conn); SSL *ssl = reinterpret_cast<SSL *>(sslobj); X509 *cert = SSL_get_certificate(ssl); if (cert != nullptr) { print_san_certificate(result, cert, GEN_DNS); X509_free(cert); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_upstream_dns_san_peer_certificate.size()) == p_upstream_dns_san_peer_certificate) { if (txnp_ == nullptr) { *result = pv_empty; return WasmResult::Ok; } TSHttpSsn ssnp = TSHttpTxnSsnGet(txnp_); TSVConn client_conn = TSHttpSsnServerVConnGet(ssnp); TSSslConnection sslobj = TSVConnSslConnectionGet(client_conn); SSL *ssl = reinterpret_cast<SSL *>(sslobj); #ifdef OPENSSL_IS_OPENSSL3 X509 *cert = SSL_get1_peer_certificate(ssl); #else X509 *cert = SSL_get_peer_certificate(ssl); #endif if (cert != nullptr) { print_san_certificate(result, cert, GEN_DNS); X509_free(cert); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_upstream_uri_san_local_certificate.size()) == p_upstream_uri_san_local_certificate) { if (txnp_ == nullptr) { *result = pv_empty; return WasmResult::Ok; } TSHttpSsn ssnp = TSHttpTxnSsnGet(txnp_); TSVConn client_conn = TSHttpSsnServerVConnGet(ssnp); TSSslConnection sslobj = TSVConnSslConnectionGet(client_conn); SSL *ssl = reinterpret_cast<SSL *>(sslobj); X509 *cert = SSL_get_certificate(ssl); if (cert != nullptr) { print_san_certificate(result, cert, GEN_URI); X509_free(cert); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_upstream_uri_san_peer_certificate.size()) == p_upstream_uri_san_peer_certificate) { if (txnp_ == nullptr) { *result = pv_empty; return WasmResult::Ok; } TSHttpSsn ssnp = TSHttpTxnSsnGet(txnp_); TSVConn client_conn = TSHttpSsnServerVConnGet(ssnp); TSSslConnection sslobj = TSVConnSslConnectionGet(client_conn); SSL *ssl = reinterpret_cast<SSL *>(sslobj); #ifdef OPENSSL_IS_OPENSSL3 X509 *cert = SSL_get1_peer_certificate(ssl); #else X509 *cert = SSL_get_peer_certificate(ssl); #endif if (cert != nullptr) { print_san_certificate(result, cert, GEN_URI); X509_free(cert); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_request_path.size()) == p_request_path) { TSMBuffer bufp = nullptr; TSMLoc hdr_loc = nullptr; TSMLoc url_loc = nullptr; const char *path = nullptr; int path_len = 0; const char *query = nullptr; int query_len = 0; if (TSHttpTxnClientReqGet(txnp_, &bufp, &hdr_loc) == TS_SUCCESS) { if (TSHttpHdrUrlGet(bufp, hdr_loc, &url_loc) == TS_SUCCESS) { path = TSUrlPathGet(bufp, url_loc, &path_len); std::string path_str(path, path_len); query = TSUrlHttpQueryGet(bufp, url_loc, &query_len); std::string query_str(query, query_len); if (query_len > 0) { result->assign("/" + path_str + "?" + query_str); } else { result->assign("/" + path_str); } TSHandleMLocRelease(bufp, hdr_loc, url_loc); } else { *result = pv_empty; } TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_request_url_path.size()) == p_request_url_path) { TSMBuffer bufp = nullptr; TSMLoc hdr_loc = nullptr; TSMLoc url_loc = nullptr; const char *path = nullptr; int path_len = 0; if (TSHttpTxnClientReqGet(txnp_, &bufp, &hdr_loc) == TS_SUCCESS) { if (TSHttpHdrUrlGet(bufp, hdr_loc, &url_loc) == TS_SUCCESS) { path = TSUrlPathGet(bufp, url_loc, &path_len); std::string path_str(path, path_len); result->assign("/" + path_str); TSHandleMLocRelease(bufp, hdr_loc, url_loc); } else { *result = pv_empty; } TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_request_host.size()) == p_request_host) { TSMBuffer bufp = nullptr; TSMLoc hdr_loc = nullptr; TSMLoc url_loc = nullptr; const char *host = nullptr; int host_len = 0; if (TSHttpTxnClientReqGet(txnp_, &bufp, &hdr_loc) == TS_SUCCESS) { if (TSHttpHdrUrlGet(bufp, hdr_loc, &url_loc) == TS_SUCCESS) { host = TSUrlHostGet(bufp, url_loc, &host_len); if (host_len == 0) { const char *key = "Host"; const char *l_key = "host"; int key_len = 4; TSMLoc field_loc = nullptr; field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, key, key_len); if (field_loc != nullptr) { host = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, -1, &host_len); TSHandleMLocRelease(bufp, hdr_loc, field_loc); } else { field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, l_key, key_len); if (field_loc != nullptr) { host = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, -1, &host_len); TSHandleMLocRelease(bufp, hdr_loc, field_loc); } } } Dbg(dbg_ctl, "[%s] request host value(%d): %.*s", __FUNCTION__, host_len, host_len, host); result->assign(host, host_len); TSHandleMLocRelease(bufp, hdr_loc, url_loc); } else { *result = pv_empty; } TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_request_scheme.size()) == p_request_scheme) { TSMBuffer bufp = nullptr; TSMLoc hdr_loc = nullptr; TSMLoc url_loc = nullptr; const char *scheme = nullptr; int scheme_len = 0; if (TSHttpTxnClientReqGet(txnp_, &bufp, &hdr_loc) == TS_SUCCESS) { if (TSHttpHdrUrlGet(bufp, hdr_loc, &url_loc) == TS_SUCCESS) { scheme = TSUrlSchemeGet(bufp, url_loc, &scheme_len); result->assign(scheme, scheme_len); TSHandleMLocRelease(bufp, hdr_loc, url_loc); } else { *result = pv_empty; } TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_request_method.size()) == p_request_method) { TSMBuffer bufp = nullptr; TSMLoc hdr_loc = nullptr; const char *method = nullptr; int method_len = 0; if (TSHttpTxnClientReqGet(txnp_, &bufp, &hdr_loc) == TS_SUCCESS) { method = TSHttpHdrMethodGet(bufp, hdr_loc, &method_len); result->assign(method, method_len); TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_request_query.size()) == p_request_query) { TSMBuffer bufp = nullptr; TSMLoc hdr_loc = nullptr; TSMLoc url_loc = nullptr; const char *query = nullptr; int query_len = 0; if (TSHttpTxnClientReqGet(txnp_, &bufp, &hdr_loc) == TS_SUCCESS) { if (TSHttpHdrUrlGet(bufp, hdr_loc, &url_loc) == TS_SUCCESS) { query = TSUrlHttpQueryGet(bufp, url_loc, &query_len); result->assign(query, query_len); TSHandleMLocRelease(bufp, hdr_loc, url_loc); } else { *result = pv_empty; } TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_request_referer.size()) == p_request_referer) { TSMBuffer bufp = nullptr; TSMLoc hdr_loc = nullptr; if (TSHttpTxnClientReqGet(txnp_, &bufp, &hdr_loc) == TS_SUCCESS) { get_header(bufp, hdr_loc, "Referer", result); TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_request_useragent.size()) == p_request_useragent) { TSMBuffer bufp = nullptr; TSMLoc hdr_loc = nullptr; if (TSHttpTxnClientReqGet(txnp_, &bufp, &hdr_loc) == TS_SUCCESS) { get_header(bufp, hdr_loc, "User-Agent", result); TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_request_id.size()) == p_request_id) { TSMBuffer bufp = nullptr; TSMLoc hdr_loc = nullptr; if (TSHttpTxnClientReqGet(txnp_, &bufp, &hdr_loc) == TS_SUCCESS) { bool found = get_header(bufp, hdr_loc, "x-request-id", result); if (!found) { uint64_t id = TSHttpTxnIdGet(txnp_); result->assign(std::to_string(id)); } TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); } else { uint64_t id = TSHttpTxnIdGet(txnp_); result->assign(std::to_string(id)); } return WasmResult::Ok; } else if (path.substr(0, p_request_headers.size()) == p_request_headers) { std::string_view key_sv = path.substr(p_request_headers.size(), path.size() - p_request_headers.size() - 1); TSMBuffer bufp = nullptr; TSMLoc hdr_loc = nullptr; if (TSHttpTxnClientReqGet(txnp_, &bufp, &hdr_loc) == TS_SUCCESS) { get_header(bufp, hdr_loc, key_sv, result); TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_request_protocol.size()) == p_request_protocol) { if (TSHttpTxnClientProtocolStackContains(txnp_, "h2") != nullptr) { *result = pv_http2; } else if (TSHttpTxnClientProtocolStackContains(txnp_, "http/1.0") != nullptr) { *result = pv_http10; } else if (TSHttpTxnClientProtocolStackContains(txnp_, "http/1.1") != nullptr) { *result = pv_http11; } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_request_time.size()) == p_request_time) { TSHRTime epoch = 0; if (TS_SUCCESS == TSHttpTxnMilestoneGet(txnp_, TS_MILESTONE_SM_START, &epoch)) { double timestamp = static_cast<double>(epoch) / 1000000000; result->assign(reinterpret_cast<const char *>(&timestamp), sizeof(double)); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_request_duration.size()) == p_request_duration) { TSHRTime value = 0; TSHRTime epoch = 0; if (TS_SUCCESS == TSHttpTxnMilestoneGet(txnp_, TS_MILESTONE_SM_START, &epoch)) { if (TS_SUCCESS == TSHttpTxnMilestoneGet(txnp_, TS_MILESTONE_SM_FINISH, &value)) { double duration = static_cast<double>((value - epoch)) / 1000000000; result->assign(reinterpret_cast<const char *>(&duration), sizeof(double)); return WasmResult::Ok; } } *result = pv_empty; return WasmResult::Ok; } else if (path.substr(0, p_request_size.size()) == p_request_size) { int64_t bytes = TSHttpTxnClientReqBodyBytesGet(txnp_); result->assign(reinterpret_cast<const char *>(&bytes), sizeof(int64_t)); return WasmResult::Ok; } else if (path.substr(0, p_request_total_size.size()) == p_request_total_size) { int h_bytes = TSHttpTxnClientReqHdrBytesGet(txnp_); int64_t b_bytes = TSHttpTxnClientReqBodyBytesGet(txnp_); int64_t total = h_bytes + b_bytes; result->assign(reinterpret_cast<const char *>(&total), sizeof(int64_t)); return WasmResult::Ok; } else if (path.substr(0, p_response_code.size()) == p_response_code) { TSMBuffer bufp = nullptr; TSMLoc hdr_loc = nullptr; int status = 0; if (TSHttpTxnServerRespGet(txnp_, &bufp, &hdr_loc) == TS_SUCCESS) { status = TSHttpHdrStatusGet(bufp, hdr_loc); result->assign(reinterpret_cast<const char *>(&status), sizeof(int)); TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_response_code_details.size()) == p_response_code_details) { TSMBuffer bufp = nullptr; TSMLoc hdr_loc = nullptr; const char *reason = nullptr; int reason_len = 0; if (TSHttpTxnServerRespGet(txnp_, &bufp, &hdr_loc) == TS_SUCCESS) { reason = TSHttpHdrReasonGet(bufp, hdr_loc, &reason_len); result->assign(reason, reason_len); TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_response_headers.size()) == p_response_headers) { std::string_view key_sv = path.substr(p_response_headers.size(), path.size() - p_response_headers.size() - 1); TSMBuffer bufp = nullptr; TSMLoc hdr_loc = nullptr; if (TSHttpTxnServerRespGet(txnp_, &bufp, &hdr_loc) == TS_SUCCESS) { get_header(bufp, hdr_loc, key_sv, result); TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); } else { *result = pv_empty; } return WasmResult::Ok; } else if (path.substr(0, p_response_size.size()) == p_response_size) { int64_t bytes = TSHttpTxnServerRespBodyBytesGet(txnp_); result->assign(reinterpret_cast<const char *>(&bytes), sizeof(int64_t)); return WasmResult::Ok; } else if (path.substr(0, p_response_total_size.size()) == p_response_total_size) { int h_bytes = TSHttpTxnServerRespHdrBytesGet(txnp_); int64_t b_bytes = TSHttpTxnServerRespBodyBytesGet(txnp_); int64_t total = h_bytes + b_bytes; result->assign(reinterpret_cast<const char *>(&total), sizeof(int64_t)); return WasmResult::Ok; } else { *result = pv_empty; Dbg(dbg_ctl, "[%s] looking for unknown property: empty string", __FUNCTION__); return WasmResult::Ok; } } WasmResult Context::setProperty(std::string_view key, std::string_view serialized_value) { if (key.substr(0, p_request_url_path.size()) == p_request_url_path) { TSMBuffer bufp = nullptr; TSMLoc hdr_loc = nullptr; TSMLoc url_loc = nullptr; std::string_view result; if (serialized_value.substr(0, 1) == "/") { result = serialized_value.substr(1); } else { result = serialized_value; } if (TSHttpTxnClientReqGet(txnp_, &bufp, &hdr_loc) == TS_SUCCESS) { if (TSHttpHdrUrlGet(bufp, hdr_loc, &url_loc) == TS_SUCCESS) { TSUrlPathSet(bufp, url_loc, result.data(), result.size()); TSHandleMLocRelease(bufp, hdr_loc, url_loc); } TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); } return WasmResult::Ok; } else if (key.substr(0, p_request_host.size()) == p_request_host) { TSMBuffer bufp = nullptr; TSMLoc hdr_loc = nullptr; TSMLoc url_loc = nullptr; if (TSHttpTxnClientReqGet(txnp_, &bufp, &hdr_loc) == TS_SUCCESS) { if (TSHttpHdrUrlGet(bufp, hdr_loc, &url_loc) == TS_SUCCESS) { TSUrlHostSet(bufp, url_loc, serialized_value.data(), serialized_value.size()); TSHandleMLocRelease(bufp, hdr_loc, url_loc); } TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); } return WasmResult::Ok; } else if (key.substr(0, p_request_scheme.size()) == p_request_scheme) { TSMBuffer bufp = nullptr; TSMLoc hdr_loc = nullptr; TSMLoc url_loc = nullptr; if (TSHttpTxnClientReqGet(txnp_, &bufp, &hdr_loc) == TS_SUCCESS) { if (TSHttpHdrUrlGet(bufp, hdr_loc, &url_loc) == TS_SUCCESS) { TSUrlSchemeSet(bufp, url_loc, serialized_value.data(), serialized_value.size()); TSHandleMLocRelease(bufp, hdr_loc, url_loc); } TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); } return WasmResult::Ok; } else if (key.substr(0, p_request_method.size()) == p_request_method) { TSMBuffer bufp = nullptr; TSMLoc hdr_loc = nullptr; if (TSHttpTxnClientReqGet(txnp_, &bufp, &hdr_loc) == TS_SUCCESS) { TSHttpHdrMethodSet(bufp, hdr_loc, serialized_value.data(), serialized_value.size()); TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); } return WasmResult::Ok; } else if (key.substr(0, p_request_query.size()) == p_request_query) { TSMBuffer bufp = nullptr; TSMLoc hdr_loc = nullptr; TSMLoc url_loc = nullptr; if (TSHttpTxnClientReqGet(txnp_, &bufp, &hdr_loc) == TS_SUCCESS) { if (TSHttpHdrUrlGet(bufp, hdr_loc, &url_loc) == TS_SUCCESS) { TSUrlHttpQuerySet(bufp, url_loc, serialized_value.data(), serialized_value.size()); TSHandleMLocRelease(bufp, hdr_loc, url_loc); } TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); } return WasmResult::Ok; } else if (key.substr(0, p_request_headers.size()) == p_request_headers) { std::string_view key_sv = key.substr(p_request_headers.size(), key.size() - p_request_headers.size() - 1); TSMBuffer bufp = nullptr; TSMLoc hdr_loc = nullptr; if (TSHttpTxnClientReqGet(txnp_, &bufp, &hdr_loc) == TS_SUCCESS) { set_header(bufp, hdr_loc, key_sv, serialized_value); TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); } return WasmResult::Ok; } else if (key.substr(0, p_response_code.size()) == p_response_code) { TSMBuffer bufp = nullptr; TSMLoc hdr_loc = nullptr; if (TSHttpTxnServerRespGet(txnp_, &bufp, &hdr_loc) == TS_SUCCESS) { int64_t *status = reinterpret_cast<int64_t *>(const_cast<char *>(serialized_value.data())); TSHttpHdrStatusSet(bufp, hdr_loc, static_cast<TSHttpStatus>(*status)); TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); } return WasmResult::Ok; } else if (key.substr(0, p_response_code_details.size()) == p_response_code_details) { TSMBuffer bufp = nullptr; TSMLoc hdr_loc = nullptr; if (TSHttpTxnServerRespGet(txnp_, &bufp, &hdr_loc) == TS_SUCCESS) { TSHttpHdrReasonSet(bufp, hdr_loc, serialized_value.data(), serialized_value.size()); TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); } return WasmResult::Ok; } else if (key.substr(0, p_response_headers.size()) == p_response_headers) { std::string_view key_sv = key.substr(p_response_headers.size(), key.size() - p_response_headers.size() - 1); TSMBuffer bufp = nullptr; TSMLoc hdr_loc = nullptr; if (TSHttpTxnServerRespGet(txnp_, &bufp, &hdr_loc) == TS_SUCCESS) { set_header(bufp, hdr_loc, key_sv, serialized_value); TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); } return WasmResult::Ok; } return WasmResult::Ok; } WasmResult Context::continueStream(WasmStreamType /* stream_type */) { if (reenable_txn_) { TSError("[wasm][%s] transaction already reenabled", __FUNCTION__); return WasmResult::Ok; } if (txnp_ == nullptr) { TSError("[wasm][%s] Can't continue stream without a transaction", __FUNCTION__); return WasmResult::InternalFailure; } else { Dbg(dbg_ctl, "[%s] continuing txn for context %d", __FUNCTION__, id()); reenable_txn_ = true; TSHttpTxnReenable(txnp_, TS_EVENT_HTTP_CONTINUE); return WasmResult::Ok; } } WasmResult Context::closeStream(WasmStreamType /* stream_type */) { if (reenable_txn_) { TSError("[wasm][%s] transaction already reenabled", __FUNCTION__); return WasmResult::Ok; } if (txnp_ == nullptr) { TSError("[wasm][%s] Can't continue stream without a transaction", __FUNCTION__); return WasmResult::InternalFailure; } else { Dbg(dbg_ctl, "[%s] continue txn for context %d with error", __FUNCTION__, id()); reenable_txn_ = true; TSHttpTxnReenable(txnp_, TS_EVENT_HTTP_ERROR); return WasmResult::Ok; } } // send pre-made response WasmResult Context::sendLocalResponse(uint32_t response_code, std::string_view body_text, Pairs additional_headers, GrpcStatusCode /* grpc_status */, std::string_view details) { if (txnp_ == nullptr) { TSError("[wasm][%s] Can't send local response without a transaction", __FUNCTION__); return WasmResult::InternalFailure; } else { TSHttpTxnStatusSet(txnp_, static_cast<TSHttpStatus>(response_code)); if (body_text.size() > 0) { TSHttpTxnErrorBodySet(txnp_, TSstrndup(body_text.data(), body_text.size()), body_text.size(), nullptr); // Defaults to text/html } local_reply_headers_ = additional_headers; local_reply_details_ = details; local_reply_ = true; } return WasmResult::Ok; } WasmResult Context::getSharedData(std::string_view key, std::pair<std::string, uint32_t> *data) { return proxy_wasm::getGlobalSharedData().get(wasm_->vm_id(), key, data); } // Header/Trailer/Metadata Maps WasmResult Context::addHeaderMapValue(WasmHeaderMapType type, std::string_view key, std::string_view value) { auto map = getHeaderMap(type); if (map.bufp == nullptr) { TSError("[wasm][%s] Invalid type", __FUNCTION__); return WasmResult::BadArgument; } auto *field_loc = TSMimeHdrFieldFind(map.bufp, map.hdr_loc, key.data(), static_cast<int>(key.size())); if (TS_NULL_MLOC == field_loc) { if (TS_SUCCESS != TSMimeHdrFieldCreateNamed(map.bufp, map.hdr_loc, key.data(), static_cast<int>(key.size()), &field_loc)) { TSError("[wasm][%s] Cannot create named field", __FUNCTION__); return WasmResult::InternalFailure; } } if (TS_SUCCESS == TSMimeHdrFieldValueStringSet(map.bufp, map.hdr_loc, field_loc, -1, value.data(), static_cast<int>(value.size()))) { TSMimeHdrFieldAppend(map.bufp, map.hdr_loc, field_loc); TSHandleMLocRelease(map.bufp, map.hdr_loc, field_loc); return WasmResult::Ok; } else { TSError("[wasm][%s] Cannot set field value", __FUNCTION__); TSHandleMLocRelease(map.bufp, map.hdr_loc, field_loc); return WasmResult::InternalFailure; } } WasmResult Context::getHeaderMapValue(WasmHeaderMapType type, std::string_view key, std::string_view *result) { auto map = getHeaderMap(type); if (map.bufp == nullptr) { TSError("[wasm][%s] Invalid type", __FUNCTION__); return WasmResult::BadArgument; } if (map.bufp != nullptr) { auto *loc = TSMimeHdrFieldFind(map.bufp, map.hdr_loc, key.data(), static_cast<int>(key.size())); if (TS_NULL_MLOC != loc) { int vlen = 0; // TODO: add support for dups auto *v = TSMimeHdrFieldValueStringGet(map.bufp, map.hdr_loc, loc, 0, &vlen); std::string_view temp(v, vlen); *result = temp; TSHandleMLocRelease(map.bufp, map.hdr_loc, loc); return WasmResult::Ok; } else { *result = ""; return WasmResult::Ok; } } return WasmResult::NotFound; } WasmResult Context::getHeaderMapPairs(WasmHeaderMapType type, Pairs *result) { auto map = getHeaderMap(type); if (map.bufp == nullptr) { TSError("[wasm][%s] Invalid type", __FUNCTION__); return WasmResult::BadArgument; } int num = map.size(); result->reserve(num); for (int i = 0; i < num; i++) { auto *loc = TSMimeHdrFieldGet(map.bufp, map.hdr_loc, i); int nlen = 0; auto *n = TSMimeHdrFieldNameGet(map.bufp, map.hdr_loc, loc, &nlen); int vlen = 0; // TODO: add support for dups. auto *v = TSMimeHdrFieldValueStringGet(map.bufp, map.hdr_loc, loc, 0, &vlen); result->push_back( std::make_pair(std::string_view(n, static_cast<size_t>(nlen)), std::string_view(v, static_cast<size_t>(vlen)))); TSHandleMLocRelease(map.bufp, map.hdr_loc, loc); } return WasmResult::Ok; } WasmResult Context::setHeaderMapPairs(WasmHeaderMapType type, const Pairs &pairs) { auto map = getHeaderMap(type); if (map.bufp == nullptr) { TSError("[wasm][%s] Invalid type", __FUNCTION__); return WasmResult::BadArgument; } for (const auto &p : pairs) { std::string key(p.first); std::string value(p.second); auto *loc = TSMimeHdrFieldFind(map.bufp, map.hdr_loc, key.data(), static_cast<int>(key.size())); if (loc != TS_NULL_MLOC) { int first = 1; while (loc != TS_NULL_MLOC) { auto *tmp = TSMimeHdrFieldNextDup(map.bufp, map.hdr_loc, loc); if (first != 0) { first = 0; TSMimeHdrFieldValueStringSet(map.bufp, map.hdr_loc, loc, -1, value.data(), static_cast<int>(value.size())); } else { TSMimeHdrFieldDestroy(map.bufp, map.hdr_loc, loc); } TSHandleMLocRelease(map.bufp, map.hdr_loc, loc); loc = tmp; } } else if (TSMimeHdrFieldCreateNamed(map.bufp, map.hdr_loc, key.data(), static_cast<int>(key.size()), &loc) != TS_SUCCESS) { TSError("[wasm][%s] TSMimeHdrFieldCreateNamed error", __FUNCTION__); return WasmResult::InternalFailure; } else { TSMimeHdrFieldValueStringSet(map.bufp, map.hdr_loc, loc, -1, value.data(), static_cast<int>(value.size())); TSMimeHdrFieldAppend(map.bufp, map.hdr_loc, loc); } if (loc != TS_NULL_MLOC) { TSHandleMLocRelease(map.bufp, map.hdr_loc, loc); } } return WasmResult::Ok; } WasmResult Context::removeHeaderMapValue(WasmHeaderMapType type, std::string_view key) { auto map = getHeaderMap(type); if (map.bufp == nullptr) { TSError("[wasm][%s] Invalid type", __FUNCTION__); return WasmResult::BadArgument; } if (map.bufp != nullptr) { auto *loc = TSMimeHdrFieldFind(map.bufp, map.hdr_loc, key.data(), static_cast<int>(key.size())); while (loc != TS_NULL_MLOC) { auto *tmp = TSMimeHdrFieldNextDup(map.bufp, map.hdr_loc, loc); TSMimeHdrFieldDestroy(map.bufp, map.hdr_loc, loc); TSHandleMLocRelease(map.bufp, map.hdr_loc, loc); loc = tmp; } } return WasmResult::Ok; } WasmResult Context::replaceHeaderMapValue(WasmHeaderMapType type, std::string_view key, std::string_view value) { auto map = getHeaderMap(type); if (map.bufp == nullptr) { TSError("[wasm][%s] Invalid type", __FUNCTION__); return WasmResult::BadArgument; } if (map.bufp != nullptr) { auto *loc = TSMimeHdrFieldFind(map.bufp, map.hdr_loc, key.data(), static_cast<int>(key.size())); if (loc != TS_NULL_MLOC) { int first = 1; while (loc != TS_NULL_MLOC) { auto *tmp = TSMimeHdrFieldNextDup(map.bufp, map.hdr_loc, loc); if (first != 0) { first = 0; TSMimeHdrFieldValueStringSet(map.bufp, map.hdr_loc, loc, -1, value.data(), static_cast<int>(value.size())); } else { TSMimeHdrFieldDestroy(map.bufp, map.hdr_loc, loc); } TSHandleMLocRelease(map.bufp, map.hdr_loc, loc); loc = tmp; } } } return WasmResult::Ok; } WasmResult Context::getHeaderMapSize(WasmHeaderMapType type, uint32_t *result) { auto map = getHeaderMap(type); if (map.bufp == nullptr) { TSError("[wasm][%s] Invalid type", __FUNCTION__); return WasmResult::BadArgument; } *result = TSMimeHdrLengthGet(map.bufp, map.hdr_loc); return WasmResult::Ok; } HeaderMap Context::getHeaderMap(WasmHeaderMapType type) { HeaderMap map; switch (type) { case WasmHeaderMapType::RequestHeaders: if (txnp_ == nullptr) { return {}; } if (TSHttpTxnClientReqGet(txnp_, &map.bufp, &map.hdr_loc) == TS_SUCCESS) { return map; } return {}; case WasmHeaderMapType::RequestTrailers: return {}; case WasmHeaderMapType::ResponseHeaders: if (txnp_ == nullptr) { return {}; } if (TSHttpTxnServerRespGet(txnp_, &map.bufp, &map.hdr_loc) == TS_SUCCESS) { return map; } return {}; case WasmHeaderMapType::ResponseTrailers: return {}; case WasmHeaderMapType::HttpCallResponseHeaders: if (cr_hdr_buf_ == nullptr || cr_hdr_loc_ == nullptr) { return {}; } map.bufp = cr_hdr_buf_; map.hdr_loc = cr_hdr_loc_; return map; default: case WasmHeaderMapType::GrpcReceiveTrailingMetadata: case WasmHeaderMapType::GrpcReceiveInitialMetadata: case WasmHeaderMapType::HttpCallResponseTrailers: return {}; } } } // namespace ats_wasm