| /** @file | |
| Implements the Traffic Server C API functions. | |
| @section license License | |
| 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 <atomic> | |
| #include <charconv> | |
| #include <cstdint> | |
| #include <memory> | |
| #include <mutex> | |
| #include <tuple> | |
| #include <unordered_map> | |
| #include <string_view> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| #include "iocore/net/NetVConnection.h" | |
| #include "iocore/net/NetHandler.h" | |
| #include "iocore/net/UDPNet.h" | |
| #include "tscore/ink_config.h" | |
| #include "tscore/ink_error.h" | |
| #include "tscore/ink_platform.h" | |
| #include "tscore/ink_base64.h" | |
| #include "tscore/Encoding.h" | |
| #include "tscore/PluginUserArgs.h" | |
| #include "tscore/Layout.h" | |
| #include "tscore/Diags.h" | |
| #include "tsutil/Metrics.h" | |
| #include "api/InkAPIInternal.h" | |
| #include "api/HttpAPIHooks.h" | |
| #include "proxy/logging/Log.h" | |
| #include "proxy/hdrs/URL.h" | |
| #include "proxy/hdrs/MIME.h" | |
| #include "proxy/hdrs/HTTP.h" | |
| #include "proxy/ProxySession.h" | |
| #include "proxy/http2/Http2ClientSession.h" | |
| #include "proxy/PoolableSession.h" | |
| #include "proxy/http/HttpSM.h" | |
| #include "proxy/http/HttpConfig.h" | |
| #include "proxy/http/OverridableConfigDefs.h" | |
| #include "proxy/PluginHttpConnect.h" | |
| #include "../iocore/net/P_Net.h" | |
| #include "../iocore/net/P_UnixNet.h" | |
| #include "../iocore/net/P_SSLNetVConnection.h" | |
| #include "../iocore/cache/P_CacheHttp.h" | |
| #include "../iocore/cache/CacheVC.h" | |
| #include "records/RecCore.h" | |
| #include "../records/P_RecCore.h" | |
| #include "../records/P_RecDefs.h" | |
| #include "../iocore/net/P_SSLConfig.h" | |
| #include "../iocore/net/P_SSLClientUtils.h" | |
| #include "iocore/dns/DNSProcessor.h" | |
| #include "iocore/net/ConnectionTracker.h" | |
| #include "iocore/net/SSLAPIHooks.h" | |
| #include "iocore/net/SSLDiags.h" | |
| #include "iocore/net/TLSBasicSupport.h" | |
| #include "iocore/net/TLSSNISupport.h" | |
| #include "iocore/eventsystem/ConfigProcessor.h" | |
| #include "proxy/Plugin.h" | |
| #include "proxy/logging/LogObject.h" | |
| #include "proxy/logging/LogConfig.h" | |
| #include "proxy/PluginVC.h" | |
| #include "proxy/http/HttpSessionAccept.h" | |
| #include "proxy/PluginVC.h" | |
| #include "proxy/FetchSM.h" | |
| #include "api/LifecycleAPIHooks.h" | |
| #include "proxy/http/HttpDebugNames.h" | |
| #include "iocore/aio/AIO.h" | |
| #include "iocore/eventsystem/Tasks.h" | |
| #include "../iocore/net/P_OCSPStapling.h" | |
| #include "records/RecDefs.h" | |
| #include "records/RecCore.h" | |
| #include "records/RecYAMLDecoder.h" | |
| #include "iocore/utils/Machine.h" | |
| #include "proxy/http/HttpProxyServerMain.h" | |
| #include "shared/overridable_txn_vars.h" | |
| #include "mgmt/config/FileManager.h" | |
| #include "mgmt/config/ConfigRegistry.h" | |
| #include "mgmt/config/ConfigContext.h" | |
| #include "mgmt/config/ConfigContextDiags.h" | |
| #include "mgmt/rpc/jsonrpc/JsonRPC.h" | |
| #include <swoc/bwf_base.h> | |
| #include <swoc/IPRange.h> | |
| #include "ts/ts.h" | |
| /**************************************************************** | |
| * IMPORTANT - READ ME | |
| * Any plugin using the IO Core must enter | |
| * with a held mutex. SDK 1.0, 1.1 & 2.0 did not | |
| * have this restriction so we need to add a mutex | |
| * to Plugin's Continuation if it tries to use the IOCore | |
| * Not only does the plugin have to have a mutex | |
| * before entering the IO Core. The mutex needs to be held. | |
| * We now take out the mutex on each call to ensure it is | |
| * held for the entire duration of the IOCore call | |
| ***************************************************************/ | |
| // helper macro for setting HTTPHdr data | |
| #define SET_HTTP_HDR(_HDR, _BUF_PTR, _OBJ_PTR) \ | |
| _HDR.m_heap = ((HdrHeapSDKHandle *)_BUF_PTR)->m_heap; \ | |
| _HDR.m_http = (HTTPHdrImpl *)_OBJ_PTR; \ | |
| _HDR.m_mime = _HDR.m_http->m_fields_impl; | |
| // This assert is for internal API use only. | |
| #if TS_USE_FAST_SDK | |
| #define sdk_assert(EX) (void)(EX) | |
| #else | |
| #define sdk_assert(EX) ((void)((EX) ? (void)0 : _TSReleaseAssert(#EX, __FILE__, __LINE__))) | |
| #endif | |
| namespace rpc | |
| { | |
| extern std::mutex g_rpcHandlingMutex; | |
| extern std::condition_variable g_rpcHandlingCompletion; | |
| extern swoc::Rv<YAML::Node> g_rpcHandlerResponseData; | |
| extern bool g_rpcHandlerProcessingCompleted; | |
| } // namespace rpc | |
| static ts::Metrics &global_api_metrics = ts::Metrics::instance(); | |
| ConfigUpdateCbTable *global_config_cbs = nullptr; | |
| // Fetchpages SM | |
| extern ClassAllocator<FetchSM, false> FetchSMAllocator; | |
| /* From proxy/http/HttpProxyServerMain.c: */ | |
| extern bool ssl_register_protocol(const char *, Continuation *); | |
| // External converters. | |
| extern MgmtConverter const &HttpDownServerCacheTimeConv; | |
| extern HttpSessionAccept *plugin_http_accept; | |
| extern HttpSessionAccept *plugin_http_transparent_accept; | |
| extern thread_local PluginThreadContext *pluginThreadContext; | |
| extern ClassAllocator<INKContInternal, false> INKContAllocator; | |
| extern ClassAllocator<INKVConnInternal, false> INKVConnAllocator; | |
| static ClassAllocator<MIMEFieldSDKHandle, false> mHandleAllocator("MIMEFieldSDKHandle"); | |
| // forward declarations | |
| TSReturnCode sdk_sanity_check_null_ptr(void const *ptr); | |
| TSReturnCode sdk_sanity_check_mbuffer(TSMBuffer bufp); | |
| namespace | |
| { | |
| DbgCtl dbg_ctl_plugin{"plugin"}; | |
| DbgCtl dbg_ctl_parent_select{"parent_select"}; | |
| DbgCtl dbg_ctl_iocore_net{"iocore_net"}; | |
| DbgCtl dbg_ctl_cache_url{"cache_url"}; | |
| DbgCtl dbg_ctl_ssl{"ssl"}; | |
| DbgCtl dbg_ctl_ssl_cert_update{"ssl.cert_update"}; | |
| DbgCtl dbg_ctl_ssl_session_cache_insert{"ssl.session_cache.insert"}; | |
| DbgCtl dbg_ctl_rpc_api{"rpc.api"}; | |
| } // end anonymous namespace | |
| /******************************************************/ | |
| /* Allocators for field handles and standalone fields */ | |
| /******************************************************/ | |
| static MIMEFieldSDKHandle * | |
| sdk_alloc_field_handle(TSMBuffer /* bufp ATS_UNUSED */, MIMEHdrImpl *mh) | |
| { | |
| MIMEFieldSDKHandle *handle = THREAD_ALLOC(mHandleAllocator, this_thread()); | |
| // TODO: Should remove this when memory allocation can't fail. | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)handle) == TS_SUCCESS); | |
| obj_init_header(handle, HdrHeapObjType::FIELD_SDK_HANDLE, sizeof(MIMEFieldSDKHandle), 0); | |
| handle->mh = mh; | |
| return handle; | |
| } | |
| static void | |
| sdk_free_field_handle(TSMBuffer bufp, MIMEFieldSDKHandle *field_handle) | |
| { | |
| if (sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS) { | |
| THREAD_FREE(field_handle, mHandleAllocator, this_thread()); | |
| } | |
| } | |
| // Defines in InkAPIInternal.cc | |
| extern char traffic_server_version[128]; | |
| extern int ts_major_version; | |
| extern int ts_minor_version; | |
| extern int ts_patch_version; | |
| /** Reservation for a user arg. | |
| */ | |
| struct UserArg { | |
| TSUserArgType type; | |
| std::string name; ///< Name of reserving plugin. | |
| std::string description; ///< Description of use for this arg. | |
| }; | |
| // Managing the user args tables, and the global storage (which is assumed to be the biggest, by far). | |
| UserArg UserArgTable[TS_USER_ARGS_COUNT][MAX_USER_ARGS[TS_USER_ARGS_GLB]]; | |
| static PluginUserArgs<TS_USER_ARGS_GLB> global_user_args; | |
| std::atomic<int> UserArgIdx[TS_USER_ARGS_COUNT]; // Table of next reserved index. | |
| //////////////////////////////////////////////////////////////////// | |
| // | |
| // API error logging | |
| // | |
| //////////////////////////////////////////////////////////////////// | |
| void | |
| TSStatus(const char *fmt, ...) | |
| { | |
| va_list args; | |
| va_start(args, fmt); | |
| StatusV(fmt, args); | |
| va_end(args); | |
| } | |
| void | |
| TSNote(const char *fmt, ...) | |
| { | |
| va_list args; | |
| va_start(args, fmt); | |
| NoteV(fmt, args); | |
| va_end(args); | |
| } | |
| void | |
| TSWarning(const char *fmt, ...) | |
| { | |
| va_list args; | |
| va_start(args, fmt); | |
| WarningV(fmt, args); | |
| va_end(args); | |
| } | |
| void | |
| TSError(const char *fmt, ...) noexcept | |
| try { | |
| va_list args; | |
| va_start(args, fmt); | |
| ErrorV(fmt, args); | |
| va_end(args); | |
| } catch (...) { | |
| ink_abort("exception escaped %s", __func__); | |
| } | |
| void | |
| TSFatal(const char *fmt, ...) | |
| { | |
| va_list args; | |
| va_start(args, fmt); | |
| FatalV(fmt, args); | |
| va_end(args); | |
| } | |
| void | |
| TSAlert(const char *fmt, ...) | |
| { | |
| va_list args; | |
| va_start(args, fmt); | |
| AlertV(fmt, args); | |
| va_end(args); | |
| } | |
| void | |
| TSEmergency(const char *fmt, ...) | |
| { | |
| va_list args; | |
| va_start(args, fmt); | |
| EmergencyV(fmt, args); | |
| va_end(args); | |
| } | |
| // Assert in debug AND optim | |
| void | |
| _TSReleaseAssert(const char *text, const char *file, int line) | |
| { | |
| _ink_assert(text, file, line); | |
| } | |
| // Assert only in debug | |
| int | |
| #ifdef DEBUG | |
| _TSAssert(const char *text, const char *file, int line) | |
| { | |
| _ink_assert(text, file, line); | |
| return 0; | |
| } | |
| #else | |
| _TSAssert(const char *, const char *, int) | |
| { | |
| return 0; | |
| } | |
| #endif | |
| //////////////////////////////////////////////////////////////////// | |
| // | |
| // SDK Interoperability Support | |
| // | |
| // ---------------------------------------------------------------- | |
| // | |
| // Standalone Fields (SDK Version-Interoperability Hack) | |
| // | |
| // | |
| // A "standalone" field is an ugly hack for portability with old | |
| // versions of the SDK that mirrored the old header system. In | |
| // the old system, you could create arbitrary tiny little field | |
| // objects, distinct from MIME header objects, and link them | |
| // together. In the new header system, all fields are internal | |
| // constituents of the MIME header. To preserve the semantics of | |
| // the old SDK, we need to maintain the concept of fields that | |
| // are created outside of a MIME header. Whenever a field is | |
| // "attached" to a MIME header, it is copied into the MIME header | |
| // field's slot, and the handle to the field is updated to refer | |
| // to the new field. | |
| // | |
| // Hopefully, we can eliminate this old compatibility interface and | |
| // migrate users to the newer semantics quickly. | |
| // | |
| // ---------------------------------------------------------------- | |
| // | |
| // MIMEField SDK Handles (SDK Version-Interoperability Hack) | |
| // | |
| // MIMEField "handles" are used by the SDK as an indirect reference | |
| // to the MIMEField. Because versions 1 & 2 of the SDK allowed | |
| // standalone fields that existed without associated MIME headers, | |
| // and because the version 3 SDK requires an associated MIME header | |
| // for all field mutation operations (for presence bits, etc.) we | |
| // need a data structure that: | |
| // | |
| // * identifies standalone fields and stores field name/value | |
| // information for fields that are not yet in a header | |
| // * redirects the field to a real header field when the field | |
| // is inserted into a header | |
| // * maintains the associated MIMEHdrImpl when returning field | |
| // slots from lookup and create functions | |
| // | |
| // If the MIMEHdrImpl pointer is null, then the handle points | |
| // to a standalone field, otherwise the handle points to a field | |
| // within the MIME header. | |
| // | |
| //////////////////////////////////////////////////////////////////// | |
| /*****************************************************************/ | |
| /* Handles to headers are impls, but need to handle MIME or HTTP */ | |
| /*****************************************************************/ | |
| inline MIMEHdrImpl * | |
| _hdr_obj_to_mime_hdr_impl(HdrHeapObjImpl *obj) | |
| { | |
| MIMEHdrImpl *impl; | |
| if (static_cast<HdrHeapObjType>(obj->m_type) == HdrHeapObjType::HTTP_HEADER) { | |
| impl = static_cast<HTTPHdrImpl *>(obj)->m_fields_impl; | |
| } else if (static_cast<HdrHeapObjType>(obj->m_type) == HdrHeapObjType::MIME_HEADER) { | |
| impl = static_cast<MIMEHdrImpl *>(obj); | |
| } else { | |
| ink_release_assert(!"mloc not a header type"); | |
| impl = nullptr; /* gcc does not know about 'ink_release_assert' - make it happy */ | |
| } | |
| return impl; | |
| } | |
| inline MIMEHdrImpl * | |
| _hdr_mloc_to_mime_hdr_impl(TSMLoc mloc) | |
| { | |
| return _hdr_obj_to_mime_hdr_impl(reinterpret_cast<HdrHeapObjImpl *>(mloc)); | |
| } | |
| TSReturnCode | |
| sdk_sanity_check_field_handle(TSMLoc field, TSMLoc parent_hdr = nullptr) | |
| { | |
| if (field == TS_NULL_MLOC) { | |
| return TS_ERROR; | |
| } | |
| MIMEFieldSDKHandle *field_handle = reinterpret_cast<MIMEFieldSDKHandle *>(field); | |
| if (static_cast<HdrHeapObjType>(field_handle->m_type) != HdrHeapObjType::FIELD_SDK_HANDLE) { | |
| return TS_ERROR; | |
| } | |
| if (parent_hdr != nullptr) { | |
| MIMEHdrImpl *mh = _hdr_mloc_to_mime_hdr_impl(parent_hdr); | |
| if (field_handle->mh != mh) { | |
| return TS_ERROR; | |
| } | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| sdk_sanity_check_mbuffer(TSMBuffer bufp) | |
| { | |
| HdrHeapSDKHandle *handle = reinterpret_cast<HdrHeapSDKHandle *>(bufp); | |
| if ((handle == nullptr) || (handle->m_heap == nullptr) || (handle->m_heap->m_magic != HdrBufMagic::ALIVE)) { | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| sdk_sanity_check_mime_hdr_handle(TSMLoc field) | |
| { | |
| if (field == TS_NULL_MLOC) { | |
| return TS_ERROR; | |
| } | |
| MIMEFieldSDKHandle *field_handle = reinterpret_cast<MIMEFieldSDKHandle *>(field); | |
| if (static_cast<HdrHeapObjType>(field_handle->m_type) != HdrHeapObjType::MIME_HEADER) { | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| sdk_sanity_check_url_handle(TSMLoc field) | |
| { | |
| if (field == TS_NULL_MLOC) { | |
| return TS_ERROR; | |
| } | |
| MIMEFieldSDKHandle *field_handle = reinterpret_cast<MIMEFieldSDKHandle *>(field); | |
| if (static_cast<HdrHeapObjType>(field_handle->m_type) != HdrHeapObjType::URL) { | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| sdk_sanity_check_http_hdr_handle(TSMLoc field) | |
| { | |
| if (field == TS_NULL_MLOC) { | |
| return TS_ERROR; | |
| } | |
| HTTPHdrImpl *field_handle = reinterpret_cast<HTTPHdrImpl *>(field); | |
| if (static_cast<HdrHeapObjType>(field_handle->m_type) != HdrHeapObjType::HTTP_HEADER) { | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| sdk_sanity_check_continuation(TSCont cont) | |
| { | |
| if ((cont == nullptr) || (reinterpret_cast<INKContInternal *>(cont)->m_free_magic == INKCONT_INTERN_MAGIC_DEAD)) { | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| sdk_sanity_check_fetch_sm(TSFetchSM fetch_sm) | |
| { | |
| if (fetch_sm == nullptr) { | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| sdk_sanity_check_http_ssn(TSHttpSsn ssnp) | |
| { | |
| if (ssnp == nullptr) { | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| sdk_sanity_check_txn(TSHttpTxn txnp) | |
| { | |
| if ((txnp != nullptr) && ((reinterpret_cast<HttpSM *>(txnp))->magic == HttpSmMagic_t::ALIVE)) { | |
| return TS_SUCCESS; | |
| } | |
| return TS_ERROR; | |
| } | |
| TSReturnCode | |
| sdk_sanity_check_mime_parser(TSMimeParser parser) | |
| { | |
| if (parser == nullptr) { | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| sdk_sanity_check_http_parser(TSHttpParser parser) | |
| { | |
| if (parser == nullptr) { | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| sdk_sanity_check_alt_info(TSHttpAltInfo info) | |
| { | |
| if (info == nullptr) { | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| sdk_sanity_check_hook_id(TSHttpHookID id) | |
| { | |
| return HttpAPIHooks::is_valid(id) ? TS_SUCCESS : TS_ERROR; | |
| } | |
| TSReturnCode | |
| sdk_sanity_check_lifecycle_hook_id(TSLifecycleHookID id) | |
| { | |
| return LifecycleAPIHooks::is_valid(id) ? TS_SUCCESS : TS_ERROR; | |
| } | |
| TSReturnCode | |
| sdk_sanity_check_ssl_hook_id(TSHttpHookID id) | |
| { | |
| if (id < TS_SSL_FIRST_HOOK || id > TS_SSL_LAST_HOOK) { | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| sdk_sanity_check_null_ptr(void const *ptr) | |
| { | |
| return ptr == nullptr ? TS_ERROR : TS_SUCCESS; | |
| } | |
| // Plugin metric IDs index the plugin RSB, so bounds check against that. | |
| static TSReturnCode | |
| sdk_sanity_check_stat_id(int id) | |
| { | |
| return (global_api_metrics.valid(id) ? TS_SUCCESS : TS_ERROR); | |
| } | |
| static TSReturnCode | |
| sdk_sanity_check_rpc_handler_options(const TSRPCHandlerOptions *opt) | |
| { | |
| if (nullptr == opt) { | |
| return TS_ERROR; | |
| } | |
| if (opt->auth.restricted < 0 || opt->auth.restricted > 1) { | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| /** | |
| The function checks if the buffer is Modifiable and returns true if | |
| it is modifiable, else returns false. | |
| */ | |
| bool | |
| isWriteable(TSMBuffer bufp) | |
| { | |
| if (bufp != nullptr) { | |
| return (reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap->m_writeable; | |
| } | |
| return false; | |
| } | |
| //////////////////////////////////////////////////////////////////// | |
| // | |
| // API memory management | |
| // | |
| //////////////////////////////////////////////////////////////////// | |
| void * | |
| _TSmalloc(size_t size, const char * /* path ATS_UNUSED */) | |
| { | |
| return ats_malloc(size); | |
| } | |
| void * | |
| _TSrealloc(void *ptr, size_t size, const char * /* path ATS_UNUSED */) | |
| { | |
| return ats_realloc(ptr, size); | |
| } | |
| // length has to be int64_t and not size_t, since -1 means to call strlen() to get length | |
| char * | |
| _TSstrdup(const char *str, int64_t length, const char *path) | |
| { | |
| return _xstrdup(str, length, path); | |
| } | |
| size_t | |
| TSstrlcpy(char *dst, const char *str, size_t siz) | |
| { | |
| return ink_strlcpy(dst, str, siz); | |
| } | |
| size_t | |
| TSstrlcat(char *dst, const char *str, size_t siz) | |
| { | |
| return ink_strlcat(dst, str, siz); | |
| } | |
| void | |
| TSfree(void *ptr) | |
| { | |
| ats_free(ptr); | |
| } | |
| //////////////////////////////////////////////////////////////////// | |
| // | |
| // Encoding utility | |
| // | |
| //////////////////////////////////////////////////////////////////// | |
| TSReturnCode | |
| TSBase64Decode(const char *str, size_t str_len, unsigned char *dst, size_t dst_size, size_t *length) | |
| { | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)str) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)dst) == TS_SUCCESS); | |
| return ats_base64_decode(str, str_len, dst, dst_size, length) ? TS_SUCCESS : TS_ERROR; | |
| } | |
| TSReturnCode | |
| TSBase64Encode(const char *str, size_t str_len, char *dst, size_t dst_size, size_t *length) | |
| { | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)str) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)dst) == TS_SUCCESS); | |
| return ats_base64_encode(str, str_len, dst, dst_size, length) ? TS_SUCCESS : TS_ERROR; | |
| } | |
| //////////////////////////////////////////////////////////////////// | |
| // | |
| // API utility routines | |
| // | |
| //////////////////////////////////////////////////////////////////// | |
| ink_hrtime | |
| TShrtime() | |
| { | |
| return ink_get_hrtime(); | |
| } | |
| //////////////////////////////////////////////////////////////////// | |
| // | |
| // API install and plugin locations | |
| // | |
| //////////////////////////////////////////////////////////////////// | |
| const char * | |
| TSInstallDirGet() | |
| { | |
| static std::string prefix = Layout::get()->prefix; | |
| return prefix.c_str(); | |
| } | |
| const char * | |
| TSConfigDirGet() | |
| { | |
| static std::string sysconfdir = RecConfigReadConfigDir(); | |
| return sysconfdir.c_str(); | |
| } | |
| const char * | |
| TSRuntimeDirGet() | |
| { | |
| static std::string runtimedir = RecConfigReadRuntimeDir(); | |
| return runtimedir.c_str(); | |
| } | |
| const char * | |
| TSTrafficServerVersionGet() | |
| { | |
| return traffic_server_version; | |
| } | |
| int | |
| TSTrafficServerVersionGetMajor() | |
| { | |
| return ts_major_version; | |
| } | |
| int | |
| TSTrafficServerVersionGetMinor() | |
| { | |
| return ts_minor_version; | |
| } | |
| int | |
| TSTrafficServerVersionGetPatch() | |
| { | |
| return ts_patch_version; | |
| } | |
| const char * | |
| TSPluginDirGet() | |
| { | |
| static std::string path = RecConfigReadPluginDir(); | |
| return path.c_str(); | |
| } | |
| //////////////////////////////////////////////////////////////////// | |
| // | |
| // Plugin registration | |
| // | |
| //////////////////////////////////////////////////////////////////// | |
| TSReturnCode | |
| TSPluginRegister(const TSPluginRegistrationInfo *plugin_info) | |
| { | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)plugin_info) == TS_SUCCESS); | |
| if (!plugin_reg_current) { | |
| return TS_ERROR; | |
| } | |
| plugin_reg_current->plugin_registered = true; | |
| if (plugin_info->plugin_name) { | |
| plugin_reg_current->plugin_name = ats_strdup(plugin_info->plugin_name); | |
| } | |
| if (plugin_info->vendor_name) { | |
| plugin_reg_current->vendor_name = ats_strdup(plugin_info->vendor_name); | |
| } | |
| if (plugin_info->support_email) { | |
| plugin_reg_current->support_email = ats_strdup(plugin_info->support_email); | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSPluginDSOReloadEnable(int enabled) | |
| { | |
| TSReturnCode ret = TS_SUCCESS; | |
| if (!plugin_reg_current) { | |
| return TS_ERROR; | |
| } | |
| if (!enabled) { | |
| if (!PluginDso::loadedPlugins()->addPluginPathToDsoOptOutTable(plugin_reg_current->plugin_path)) { | |
| ret = TS_ERROR; | |
| } | |
| } | |
| return ret; | |
| } | |
| //////////////////////////////////////////////////////////////////// | |
| // | |
| // API file management | |
| // | |
| //////////////////////////////////////////////////////////////////// | |
| TSFile | |
| TSfopen(const char *filename, const char *mode) | |
| { | |
| FileImpl *file; | |
| file = new FileImpl; | |
| if (!file->fopen(filename, mode)) { | |
| delete file; | |
| return nullptr; | |
| } | |
| return reinterpret_cast<TSFile>(file); | |
| } | |
| void | |
| TSfclose(TSFile filep) | |
| { | |
| FileImpl *file = reinterpret_cast<FileImpl *>(filep); | |
| file->fclose(); | |
| delete file; | |
| } | |
| ssize_t | |
| TSfread(TSFile filep, void *buf, size_t length) | |
| { | |
| FileImpl *file = reinterpret_cast<FileImpl *>(filep); | |
| return file->fread(buf, length); | |
| } | |
| ssize_t | |
| TSfwrite(TSFile filep, const void *buf, size_t length) | |
| { | |
| FileImpl *file = reinterpret_cast<FileImpl *>(filep); | |
| return file->fwrite(buf, length); | |
| } | |
| void | |
| TSfflush(TSFile filep) | |
| { | |
| FileImpl *file = reinterpret_cast<FileImpl *>(filep); | |
| file->fflush(); | |
| } | |
| char * | |
| TSfgets(TSFile filep, char *buf, size_t length) | |
| { | |
| FileImpl *file = reinterpret_cast<FileImpl *>(filep); | |
| return file->fgets(buf, length); | |
| } | |
| //////////////////////////////////////////////////////////////////// | |
| // | |
| // Header component object handles | |
| // | |
| //////////////////////////////////////////////////////////////////// | |
| TSReturnCode | |
| TSHandleMLocRelease(TSMBuffer bufp, TSMLoc parent, TSMLoc mloc) noexcept | |
| try { | |
| MIMEFieldSDKHandle *field_handle; | |
| HdrHeapObjImpl *obj = reinterpret_cast<HdrHeapObjImpl *>(mloc); | |
| if (mloc == TS_NULL_MLOC) { | |
| return TS_SUCCESS; | |
| } | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| switch (static_cast<HdrHeapObjType>(obj->m_type)) { | |
| case HdrHeapObjType::URL: | |
| case HdrHeapObjType::HTTP_HEADER: | |
| case HdrHeapObjType::MIME_HEADER: | |
| return TS_SUCCESS; | |
| case HdrHeapObjType::FIELD_SDK_HANDLE: | |
| field_handle = static_cast<MIMEFieldSDKHandle *>(obj); | |
| if (sdk_sanity_check_field_handle(mloc, parent) != TS_SUCCESS) { | |
| return TS_ERROR; | |
| } | |
| sdk_free_field_handle(bufp, field_handle); | |
| return TS_SUCCESS; | |
| default: | |
| ink_release_assert(!"invalid mloc"); | |
| return TS_ERROR; | |
| } | |
| } catch (...) { | |
| ink_abort("exception escaped %s", __func__); | |
| } | |
| //////////////////////////////////////////////////////////////////// | |
| // | |
| // HdrHeaps (previously known as "Marshal Buffers") | |
| // | |
| //////////////////////////////////////////////////////////////////// | |
| // TSMBuffer: pointers to HdrHeapSDKHandle objects | |
| TSMBuffer | |
| TSMBufferCreate() | |
| { | |
| TSMBuffer bufp; | |
| HdrHeapSDKHandle *new_heap = new HdrHeapSDKHandle; | |
| new_heap->m_heap = new_HdrHeap(); | |
| bufp = reinterpret_cast<TSMBuffer>(new_heap); | |
| // TODO: Should remove this when memory allocation is guaranteed to fail. | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| return bufp; | |
| } | |
| TSReturnCode | |
| TSMBufferDestroy(TSMBuffer bufp) noexcept | |
| try { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If allowed, return TS_SUCCESS. Changed the | |
| // return value of function from void to TSReturnCode. | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| HdrHeapSDKHandle *sdk_heap = reinterpret_cast<HdrHeapSDKHandle *>(bufp); | |
| sdk_heap->m_heap->destroy(); | |
| delete sdk_heap; | |
| return TS_SUCCESS; | |
| } catch (...) { | |
| ink_abort("exception escaped %s", __func__); | |
| } | |
| //////////////////////////////////////////////////////////////////// | |
| // | |
| // URLs | |
| // | |
| //////////////////////////////////////////////////////////////////// | |
| // TSMBuffer: pointers to HdrHeapSDKHandle objects | |
| // TSMLoc: pointers to URLImpl objects | |
| TSReturnCode | |
| TSUrlCreate(TSMBuffer bufp, TSMLoc *locp) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr(locp) == TS_SUCCESS); | |
| if (isWriteable(bufp)) { | |
| HdrHeap *heap = reinterpret_cast<HdrHeapSDKHandle *>(bufp)->m_heap; | |
| *locp = reinterpret_cast<TSMLoc>(url_create(heap)); | |
| return TS_SUCCESS; | |
| } | |
| return TS_ERROR; | |
| } | |
| TSReturnCode | |
| TSUrlClone(TSMBuffer dest_bufp, TSMBuffer src_bufp, TSMLoc src_url, TSMLoc *locp) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(src_bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_mbuffer(dest_bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_url_handle(src_url) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr(locp) == TS_SUCCESS); | |
| if (!isWriteable(dest_bufp)) { | |
| return TS_ERROR; | |
| } | |
| HdrHeap *s_heap, *d_heap; | |
| URLImpl *s_url, *d_url; | |
| s_heap = reinterpret_cast<HdrHeapSDKHandle *>(src_bufp)->m_heap; | |
| d_heap = reinterpret_cast<HdrHeapSDKHandle *>(dest_bufp)->m_heap; | |
| s_url = reinterpret_cast<URLImpl *>(src_url); | |
| d_url = url_copy(s_url, s_heap, d_heap, (s_heap != d_heap)); | |
| *locp = reinterpret_cast<TSMLoc>(d_url); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSUrlCopy(TSMBuffer dest_bufp, TSMLoc dest_obj, TSMBuffer src_bufp, TSMLoc src_obj) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(src_bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_mbuffer(dest_bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_url_handle(src_obj) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_url_handle(dest_obj) == TS_SUCCESS); | |
| if (!isWriteable(dest_bufp)) { | |
| return TS_ERROR; | |
| } | |
| HdrHeap *s_heap, *d_heap; | |
| URLImpl *s_url, *d_url; | |
| s_heap = reinterpret_cast<HdrHeapSDKHandle *>(src_bufp)->m_heap; | |
| d_heap = reinterpret_cast<HdrHeapSDKHandle *>(dest_bufp)->m_heap; | |
| s_url = reinterpret_cast<URLImpl *>(src_obj); | |
| d_url = reinterpret_cast<URLImpl *>(dest_obj); | |
| url_copy_onto(s_url, s_heap, d_url, d_heap, (s_heap != d_heap)); | |
| return TS_SUCCESS; | |
| } | |
| void | |
| TSUrlPrint(TSMBuffer bufp, TSMLoc obj, TSIOBuffer iobufp) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_url_handle(obj) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_iocore_structure(iobufp) == TS_SUCCESS); | |
| MIOBuffer *b = reinterpret_cast<MIOBuffer *>(iobufp); | |
| IOBufferBlock *blk; | |
| int bufindex; | |
| int tmp, dumpoffset; | |
| int done; | |
| URL u; | |
| u.m_heap = (reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap; | |
| u.m_url_impl = reinterpret_cast<URLImpl *>(obj); | |
| dumpoffset = 0; | |
| do { | |
| blk = b->get_current_block(); | |
| if (!blk || blk->write_avail() == 0) { | |
| b->add_block(); | |
| blk = b->get_current_block(); | |
| } | |
| bufindex = 0; | |
| tmp = dumpoffset; | |
| done = u.print(blk->end(), blk->write_avail(), &bufindex, &tmp); | |
| dumpoffset += bufindex; | |
| b->fill(bufindex); | |
| } while (!done); | |
| } | |
| TSParseResult | |
| TSUrlParse(TSMBuffer bufp, TSMLoc obj, const char **start, const char *end) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_url_handle(obj) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)start) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)*start) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)end) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_PARSE_ERROR; | |
| } | |
| URL u; | |
| u.m_heap = (reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap; | |
| u.m_url_impl = reinterpret_cast<URLImpl *>(obj); | |
| url_clear(u.m_url_impl); | |
| return static_cast<TSParseResult>(u.parse(start, end)); | |
| } | |
| int | |
| TSUrlLengthGet(TSMBuffer bufp, TSMLoc obj) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_url_handle(obj) == TS_SUCCESS); | |
| URLImpl *url_impl = reinterpret_cast<URLImpl *>(obj); | |
| return url_length_get(url_impl); | |
| } | |
| char * | |
| TSUrlStringGet(TSMBuffer bufp, TSMLoc obj, int *length) | |
| { | |
| // bufp is not actually used anymore, so it can be null. | |
| if (bufp) { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| } | |
| sdk_assert(sdk_sanity_check_url_handle(obj) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)length) == TS_SUCCESS); | |
| URLImpl *url_impl = reinterpret_cast<URLImpl *>(obj); | |
| return url_string_get(url_impl, nullptr, length, nullptr); | |
| } | |
| using URLPartGetF = std::string_view (URL::*)() const noexcept; | |
| using URLPartSetF = void (URL::*)(std::string_view); | |
| static const std::string_view | |
| URLPartGet(TSMBuffer bufp, TSMLoc obj, URLPartGetF url_f) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_url_handle(obj) == TS_SUCCESS); | |
| URL u; | |
| u.m_heap = (reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap; | |
| u.m_url_impl = reinterpret_cast<URLImpl *>(obj); | |
| return (u.*url_f)(); | |
| } | |
| static TSReturnCode | |
| URLPartSet(TSMBuffer bufp, TSMLoc obj, const char *value, int length, URLPartSetF url_f) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_url_handle(obj) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| URL u; | |
| u.m_heap = (reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap; | |
| u.m_url_impl = reinterpret_cast<URLImpl *>(obj); | |
| if (!value) { | |
| length = 0; | |
| } else if (length < 0) { | |
| length = strlen(value); | |
| } | |
| (u.*url_f)(std::string_view{value, static_cast<std::string_view::size_type>(length)}); | |
| return TS_SUCCESS; | |
| } | |
| const char * | |
| TSUrlRawSchemeGet(TSMBuffer bufp, TSMLoc obj, int *length) | |
| { | |
| auto scheme{URLPartGet(bufp, obj, &URL::scheme_get)}; | |
| if (length) { | |
| *length = static_cast<int>(scheme.length()); | |
| } | |
| return scheme.data(); | |
| } | |
| const char * | |
| TSUrlSchemeGet(TSMBuffer bufp, TSMLoc obj, int *length) | |
| { | |
| char const *data = TSUrlRawSchemeGet(bufp, obj, length); | |
| if (data && *length) { | |
| return data; | |
| } | |
| switch (reinterpret_cast<URLImpl *>(obj)->m_url_type) { | |
| case URLType::HTTP: | |
| data = URL_SCHEME_HTTP.c_str(); | |
| *length = static_cast<int>(URL_SCHEME_HTTP.length()); | |
| break; | |
| case URLType::HTTPS: | |
| data = URL_SCHEME_HTTPS.c_str(); | |
| *length = static_cast<int>(URL_SCHEME_HTTPS.length()); | |
| break; | |
| default: | |
| *length = 0; | |
| data = nullptr; | |
| } | |
| return data; | |
| } | |
| TSReturnCode | |
| TSUrlSchemeSet(TSMBuffer bufp, TSMLoc obj, const char *value, int length) | |
| { | |
| return URLPartSet(bufp, obj, value, length, &URL::scheme_set); | |
| } | |
| /* Internet specific URLs */ | |
| const char * | |
| TSUrlUserGet(TSMBuffer bufp, TSMLoc obj, int *length) | |
| { | |
| auto user{URLPartGet(bufp, obj, &URL::user_get)}; | |
| if (length) { | |
| *length = static_cast<int>(user.length()); | |
| } | |
| return user.data(); | |
| } | |
| TSReturnCode | |
| TSUrlUserSet(TSMBuffer bufp, TSMLoc obj, const char *value, int length) | |
| { | |
| return URLPartSet(bufp, obj, value, length, &URL::user_set); | |
| } | |
| const char * | |
| TSUrlPasswordGet(TSMBuffer bufp, TSMLoc obj, int *length) | |
| { | |
| auto password{URLPartGet(bufp, obj, &URL::password_get)}; | |
| if (length) { | |
| *length = static_cast<int>(password.length()); | |
| } | |
| return password.data(); | |
| } | |
| TSReturnCode | |
| TSUrlPasswordSet(TSMBuffer bufp, TSMLoc obj, const char *value, int length) | |
| { | |
| return URLPartSet(bufp, obj, value, length, &URL::password_set); | |
| } | |
| const char * | |
| TSUrlHostGet(TSMBuffer bufp, TSMLoc obj, int *length) | |
| { | |
| auto host{URLPartGet(bufp, obj, &URL::host_get)}; | |
| if (length) { | |
| *length = static_cast<int>(host.length()); | |
| } | |
| return host.data(); | |
| } | |
| TSReturnCode | |
| TSUrlHostSet(TSMBuffer bufp, TSMLoc obj, const char *value, int length) | |
| { | |
| return URLPartSet(bufp, obj, value, length, &URL::host_set); | |
| } | |
| int | |
| TSUrlPortGet(TSMBuffer bufp, TSMLoc obj) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_url_handle(obj) == TS_SUCCESS); | |
| URL u; | |
| u.m_heap = (reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap; | |
| u.m_url_impl = reinterpret_cast<URLImpl *>(obj); | |
| return u.port_get(); | |
| } | |
| int | |
| TSUrlRawPortGet(TSMBuffer bufp, TSMLoc obj) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_url_handle(obj) == TS_SUCCESS); | |
| URL u; | |
| u.m_heap = (reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap; | |
| u.m_url_impl = reinterpret_cast<URLImpl *>(obj); | |
| return u.port_get_raw(); | |
| } | |
| TSReturnCode | |
| TSUrlPortSet(TSMBuffer bufp, TSMLoc obj, int port) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_url_handle(obj) == TS_SUCCESS); | |
| if (!isWriteable(bufp) || (port < 0)) { | |
| return TS_ERROR; | |
| } | |
| URL u; | |
| u.m_heap = (reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap; | |
| u.m_url_impl = reinterpret_cast<URLImpl *>(obj); | |
| u.port_set(port); | |
| return TS_SUCCESS; | |
| } | |
| /* FTP and HTTP specific URLs */ | |
| const char * | |
| TSUrlPathGet(TSMBuffer bufp, TSMLoc obj, int *length) | |
| { | |
| auto path{URLPartGet(bufp, obj, &URL::path_get)}; | |
| if (length) { | |
| *length = static_cast<int>(path.length()); | |
| } | |
| return path.data(); | |
| } | |
| TSReturnCode | |
| TSUrlPathSet(TSMBuffer bufp, TSMLoc obj, const char *value, int length) | |
| { | |
| return URLPartSet(bufp, obj, value, length, &URL::path_set); | |
| } | |
| /* FTP specific URLs */ | |
| int | |
| TSUrlFtpTypeGet(TSMBuffer bufp, TSMLoc obj) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_url_handle(obj) == TS_SUCCESS); | |
| URL u; | |
| u.m_heap = (reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap; | |
| u.m_url_impl = reinterpret_cast<URLImpl *>(obj); | |
| return u.type_code_get(); | |
| } | |
| TSReturnCode | |
| TSUrlFtpTypeSet(TSMBuffer bufp, TSMLoc obj, int type) | |
| { | |
| // The valid values are : 0, 65('A'), 97('a'), | |
| // 69('E'), 101('e'), 73 ('I') and 105('i'). | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_url_handle(obj) == TS_SUCCESS); | |
| if ((type == 0 || type == 'A' || type == 'E' || type == 'I' || type == 'a' || type == 'i' || type == 'e') && isWriteable(bufp)) { | |
| URL u; | |
| u.m_heap = (reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap; | |
| u.m_url_impl = reinterpret_cast<URLImpl *>(obj); | |
| u.type_code_set(type); | |
| return TS_SUCCESS; | |
| } | |
| return TS_ERROR; | |
| } | |
| /* HTTP specific URLs */ | |
| const char * | |
| TSUrlHttpQueryGet(TSMBuffer bufp, TSMLoc obj, int *length) | |
| { | |
| auto query{URLPartGet(bufp, obj, &URL::query_get)}; | |
| if (length) { | |
| *length = static_cast<int>(query.length()); | |
| } | |
| return query.data(); | |
| } | |
| TSReturnCode | |
| TSUrlHttpQuerySet(TSMBuffer bufp, TSMLoc obj, const char *value, int length) | |
| { | |
| return URLPartSet(bufp, obj, value, length, &URL::query_set); | |
| } | |
| const char * | |
| TSUrlHttpFragmentGet(TSMBuffer bufp, TSMLoc obj, int *length) | |
| { | |
| auto fragment{URLPartGet(bufp, obj, &URL::fragment_get)}; | |
| if (length) { | |
| *length = static_cast<int>(fragment.length()); | |
| } | |
| return fragment.data(); | |
| } | |
| TSReturnCode | |
| TSUrlHttpFragmentSet(TSMBuffer bufp, TSMLoc obj, const char *value, int length) | |
| { | |
| return URLPartSet(bufp, obj, value, length, &URL::fragment_set); | |
| } | |
| // URL percent encoding | |
| TSReturnCode | |
| TSStringPercentEncode(const char *str, int str_len, char *dst, size_t dst_size, size_t *length, const unsigned char *map) | |
| { | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)str) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)dst) == TS_SUCCESS); | |
| int new_len; // Unfortunately, a lot of the core uses "int" for length's internally... | |
| if (str_len < 0) { | |
| str_len = strlen(str); | |
| } | |
| sdk_assert(str_len < static_cast<int>(dst_size)); | |
| // TODO: Perhaps we should make escapify_url() deal with const properly... | |
| // You would think making escapify_url const correct for the source argument would be easy, but in the case where | |
| // No escaping is needed, the source argument is returned. If there is a destination argument, the source is copied over | |
| // However, if there is no destination argument, none is allocated. I don't understand the full possibility of calling cases. | |
| // It seems like we might want to review how this is being called and perhaps create a number of smaller accessor methods that | |
| // can be set up correctly. | |
| if (nullptr == Encoding::pure_escapify_url(nullptr, const_cast<char *>(str), str_len, &new_len, dst, dst_size, map)) { | |
| if (length) { | |
| *length = 0; | |
| } | |
| return TS_ERROR; | |
| } | |
| if (length) { | |
| *length = new_len; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSStringPercentDecode(const char *str, size_t str_len, char *dst, size_t dst_size, size_t *length) | |
| { | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)str) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)dst) == TS_SUCCESS); | |
| if (0 == str_len) { | |
| str_len = strlen(str); | |
| } | |
| // return unescapifyStr(str); | |
| char *buffer = dst; | |
| const char *src = str; | |
| int s = 0; // State, which we don't really use | |
| // TODO: We should check for "failures" here? | |
| unescape_str(buffer, buffer + dst_size, src, src + str_len, s); | |
| size_t data_written = std::min<size_t>(buffer - dst, dst_size - 1); | |
| *(dst + data_written) = '\0'; | |
| if (length) { | |
| *length = (data_written); | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSUrlPercentEncode(TSMBuffer bufp, TSMLoc obj, char *dst, size_t dst_size, size_t *length, const unsigned char *map) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_url_handle(obj) == TS_SUCCESS); | |
| char *url; | |
| int url_len; | |
| TSReturnCode ret; | |
| URLImpl *url_impl = reinterpret_cast<URLImpl *>(obj); | |
| // TODO: at some point, it might be nice to allow this to write to a pre-allocated buffer | |
| url = url_string_get(url_impl, nullptr, &url_len, nullptr); | |
| ret = TSStringPercentEncode(url, url_len, dst, dst_size, length, map); | |
| ats_free(url); | |
| return ret; | |
| } | |
| // pton | |
| TSReturnCode | |
| TSIpStringToAddr(const char *str, size_t str_len, sockaddr *addr) | |
| { | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)str) == TS_SUCCESS); | |
| if (0 != ats_ip_pton(std::string_view(str, str_len), addr)) { | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| //////////////////////////////////////////////////////////////////// | |
| // | |
| // MIME Headers | |
| // | |
| //////////////////////////////////////////////////////////////////// | |
| /**************/ | |
| /* MimeParser */ | |
| /**************/ | |
| TSMimeParser | |
| TSMimeParserCreate() | |
| { | |
| TSMimeParser parser = reinterpret_cast<TSMimeParser>(ats_malloc(sizeof(MIMEParser))); | |
| mime_parser_init(reinterpret_cast<MIMEParser *>(parser)); | |
| return parser; | |
| } | |
| void | |
| TSMimeParserClear(TSMimeParser parser) | |
| { | |
| sdk_assert(sdk_sanity_check_mime_parser(parser) == TS_SUCCESS); | |
| mime_parser_clear(reinterpret_cast<MIMEParser *>(parser)); | |
| } | |
| void | |
| TSMimeParserDestroy(TSMimeParser parser) | |
| { | |
| sdk_assert(sdk_sanity_check_mime_parser(parser) == TS_SUCCESS); | |
| mime_parser_clear(reinterpret_cast<MIMEParser *>(parser)); | |
| ats_free(parser); | |
| } | |
| /***********/ | |
| /* MimeHdr */ | |
| /***********/ | |
| // TSMBuffer: pointers to HdrHeapSDKHandle objects | |
| // TSMLoc: pointers to MIMEFieldSDKHandle objects | |
| TSReturnCode | |
| TSMimeHdrCreate(TSMBuffer bufp, TSMLoc *locp) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If not allowed, set *locp to TS_NULL_MLOC. | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)locp) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| *locp = reinterpret_cast<TSMLoc>(mime_hdr_create((reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap)); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSMimeHdrDestroy(TSMBuffer bufp, TSMLoc obj) noexcept | |
| try { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If allowed, return TS_SUCCESS. Changed the | |
| // return value of function from void to TSReturnCode. | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(obj) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(obj) == TS_SUCCESS)); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| MIMEHdrImpl *mh = _hdr_mloc_to_mime_hdr_impl(obj); | |
| mime_hdr_destroy((reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap, mh); | |
| return TS_SUCCESS; | |
| } catch (...) { | |
| ink_abort("exception escaped %s", __func__); | |
| } | |
| TSReturnCode | |
| TSMimeHdrClone(TSMBuffer dest_bufp, TSMBuffer src_bufp, TSMLoc src_hdr, TSMLoc *locp) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If not allowed, set *locp to TS_NULL_MLOC. | |
| sdk_assert(sdk_sanity_check_mbuffer(dest_bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_mbuffer(src_bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_mime_hdr_handle(src_hdr) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_http_hdr_handle(src_hdr) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)locp) == TS_SUCCESS); | |
| if (!isWriteable(dest_bufp)) { | |
| return TS_ERROR; | |
| } | |
| HdrHeap *s_heap, *d_heap; | |
| MIMEHdrImpl *s_mh, *d_mh; | |
| s_heap = (reinterpret_cast<HdrHeapSDKHandle *>(src_bufp))->m_heap; | |
| d_heap = (reinterpret_cast<HdrHeapSDKHandle *>(dest_bufp))->m_heap; | |
| s_mh = _hdr_mloc_to_mime_hdr_impl(src_hdr); | |
| d_mh = mime_hdr_clone(s_mh, s_heap, d_heap, (s_heap != d_heap)); | |
| *locp = reinterpret_cast<TSMLoc>(d_mh); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSMimeHdrCopy(TSMBuffer dest_bufp, TSMLoc dest_obj, TSMBuffer src_bufp, TSMLoc src_obj) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If allowed, return TS_SUCCESS. Changed the | |
| // return value of function from void to TSReturnCode. | |
| sdk_assert(sdk_sanity_check_mbuffer(src_bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_mbuffer(dest_bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(src_obj) == TS_SUCCESS) || | |
| (sdk_sanity_check_http_hdr_handle(src_obj) == TS_SUCCESS)); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(dest_obj) == TS_SUCCESS) || | |
| (sdk_sanity_check_http_hdr_handle(dest_obj) == TS_SUCCESS)); | |
| if (!isWriteable(dest_bufp)) { | |
| return TS_ERROR; | |
| } | |
| HdrHeap *s_heap, *d_heap; | |
| MIMEHdrImpl *s_mh, *d_mh; | |
| s_heap = (reinterpret_cast<HdrHeapSDKHandle *>(src_bufp))->m_heap; | |
| d_heap = (reinterpret_cast<HdrHeapSDKHandle *>(dest_bufp))->m_heap; | |
| s_mh = _hdr_mloc_to_mime_hdr_impl(src_obj); | |
| d_mh = _hdr_mloc_to_mime_hdr_impl(dest_obj); | |
| mime_hdr_fields_clear(d_heap, d_mh); | |
| mime_hdr_copy_onto(s_mh, s_heap, d_mh, d_heap, (s_heap != d_heap)); | |
| return TS_SUCCESS; | |
| } | |
| void | |
| TSMimeHdrPrint(TSMLoc obj, TSIOBuffer iobufp) | |
| { | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(obj) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(obj) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_iocore_structure(iobufp) == TS_SUCCESS); | |
| MIMEHdrImpl const *mh = _hdr_mloc_to_mime_hdr_impl(obj); | |
| MIOBuffer *b = reinterpret_cast<MIOBuffer *>(iobufp); | |
| IOBufferBlock *blk; | |
| int bufindex; | |
| int tmp, dumpoffset = 0; | |
| int done; | |
| do { | |
| blk = b->get_current_block(); | |
| if (!blk || blk->write_avail() == 0) { | |
| b->add_block(); | |
| blk = b->get_current_block(); | |
| } | |
| bufindex = 0; | |
| tmp = dumpoffset; | |
| done = mime_hdr_print(mh, blk->end(), blk->write_avail(), &bufindex, &tmp); | |
| dumpoffset += bufindex; | |
| b->fill(bufindex); | |
| } while (!done); | |
| } | |
| TSParseResult | |
| TSMimeHdrParse(TSMimeParser parser, TSMBuffer bufp, TSMLoc obj, const char **start, const char *end) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(obj) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(obj) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)start) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)*start) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)end) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_PARSE_ERROR; | |
| } | |
| MIMEHdrImpl *mh = _hdr_mloc_to_mime_hdr_impl(obj); | |
| return static_cast<TSParseResult>(mime_parser_parse(reinterpret_cast<MIMEParser *>(parser), | |
| (reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap, mh, start, end, false, | |
| false, false)); | |
| } | |
| int | |
| TSMimeHdrLengthGet(TSMBuffer bufp, TSMLoc obj) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(obj) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(obj) == TS_SUCCESS)); | |
| MIMEHdrImpl *mh = _hdr_mloc_to_mime_hdr_impl(obj); | |
| return mime_hdr_length_get(mh); | |
| } | |
| TSReturnCode | |
| TSMimeHdrFieldsClear(TSMBuffer bufp, TSMLoc obj) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(obj) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(obj) == TS_SUCCESS)); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| MIMEHdrImpl *mh = _hdr_mloc_to_mime_hdr_impl(obj); | |
| mime_hdr_fields_clear((reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap, mh); | |
| return TS_SUCCESS; | |
| } | |
| int | |
| TSMimeHdrFieldsCount(TSMBuffer bufp, TSMLoc obj) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(obj) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(obj) == TS_SUCCESS)); | |
| MIMEHdrImpl *mh = _hdr_mloc_to_mime_hdr_impl(obj); | |
| return mime_hdr_fields_count(mh); | |
| } | |
| // The following three helper functions should not be used in plugins! Since they are not used | |
| // by plugins, there's no need to validate the input. | |
| static const char * | |
| TSMimeFieldValueGet(TSMBuffer /* bufp ATS_UNUSED */, TSMLoc field_obj, int idx, int *value_len_ptr) | |
| { | |
| MIMEFieldSDKHandle *handle = reinterpret_cast<MIMEFieldSDKHandle *>(field_obj); | |
| if (idx >= 0) { | |
| return mime_field_value_get_comma_val(handle->field_ptr, value_len_ptr, idx); | |
| } else { | |
| auto value{handle->field_ptr->value_get()}; | |
| *value_len_ptr = static_cast<int>(value.length()); | |
| return value.data(); | |
| } | |
| } | |
| // Returns false when the value exceeds the uint16_t field-length limit and was | |
| // rejected by mime_field_value_set, so callers can surface TS_ERROR. | |
| static bool | |
| TSMimeFieldValueSet(TSMBuffer bufp, TSMLoc field_obj, int idx, const char *value, int length) | |
| { | |
| MIMEFieldSDKHandle *handle = reinterpret_cast<MIMEFieldSDKHandle *>(field_obj); | |
| HdrHeap *heap = (reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap; | |
| if (length == -1) { | |
| length = strlen(value); | |
| } | |
| if (idx >= 0) { | |
| mime_field_value_set_comma_val(heap, handle->mh, handle->field_ptr, idx, | |
| std::string_view{value, static_cast<std::string_view::size_type>(length)}); | |
| return true; | |
| } | |
| return mime_field_value_set(heap, handle->mh, handle->field_ptr, | |
| std::string_view{value, static_cast<std::string_view::size_type>(length)}, true); | |
| } | |
| static void | |
| TSMimeFieldValueInsert(TSMBuffer bufp, TSMLoc field_obj, const char *value, int length, int idx) | |
| { | |
| MIMEFieldSDKHandle *handle = reinterpret_cast<MIMEFieldSDKHandle *>(field_obj); | |
| HdrHeap *heap = (reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap; | |
| if (length == -1) { | |
| length = strlen(value); | |
| } | |
| mime_field_value_insert_comma_val(heap, handle->mh, handle->field_ptr, idx, | |
| std::string_view{value, static_cast<std::string_view::size_type>(length)}); | |
| } | |
| /****************/ | |
| /* MimeHdrField */ | |
| /****************/ | |
| // TSMBuffer: pointers to HdrHeapSDKHandle objects | |
| // TSMLoc: pointers to MIMEFieldSDKHandle objects | |
| TSMLoc | |
| TSMimeHdrFieldGet(TSMBuffer bufp, TSMLoc hdr_obj, int idx) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(hdr_obj) == TS_SUCCESS) || | |
| (sdk_sanity_check_http_hdr_handle(hdr_obj) == TS_SUCCESS)); | |
| sdk_assert(idx >= 0); | |
| MIMEHdrImpl *mh = _hdr_mloc_to_mime_hdr_impl(hdr_obj); | |
| MIMEField *f = mime_hdr_field_get(mh, idx); | |
| if (f == nullptr) { | |
| return TS_NULL_MLOC; | |
| } | |
| MIMEFieldSDKHandle *h = sdk_alloc_field_handle(bufp, mh); | |
| h->field_ptr = f; | |
| return reinterpret_cast<TSMLoc>(h); | |
| } | |
| TSMLoc | |
| TSMimeHdrFieldFind(TSMBuffer bufp, TSMLoc hdr_obj, const char *name, int length) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(hdr_obj) == TS_SUCCESS) || | |
| (sdk_sanity_check_http_hdr_handle(hdr_obj) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)name) == TS_SUCCESS); | |
| if (length == -1) { | |
| length = strlen(name); | |
| } | |
| MIMEHdrImpl *mh = _hdr_mloc_to_mime_hdr_impl(hdr_obj); | |
| MIMEField *f = mime_hdr_field_find(mh, std::string_view{name, static_cast<std::string_view::size_type>(length)}); | |
| if (f == nullptr) { | |
| return TS_NULL_MLOC; | |
| } | |
| MIMEFieldSDKHandle *h = sdk_alloc_field_handle(bufp, mh); | |
| h->field_ptr = f; | |
| return reinterpret_cast<TSMLoc>(h); | |
| } | |
| TSReturnCode | |
| TSMimeHdrFieldAppend(TSMBuffer bufp, TSMLoc mh_mloc, TSMLoc field_mloc) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If allowed, return TS_SUCCESS. Changed the | |
| // return value of function from void to TSReturnCode. | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(mh_mloc) == TS_SUCCESS) || | |
| (sdk_sanity_check_http_hdr_handle(mh_mloc) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field_mloc) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| MIMEField *mh_field; | |
| MIMEHdrImpl *mh = _hdr_mloc_to_mime_hdr_impl(mh_mloc); | |
| MIMEFieldSDKHandle *field_handle = reinterpret_cast<MIMEFieldSDKHandle *>(field_mloc); | |
| ////////////////////////////////////////////////////////////////////// | |
| // The field passed in field_mloc might have been allocated from // | |
| // inside a MIME header (the correct way), or it might have been // | |
| // created in isolation as a "standalone field" (the old way). // | |
| // // | |
| // If it's a standalone field (the associated mime header is null), // | |
| // then we need to now allocate a real field inside the header, // | |
| // copy over the data, and convert the standalone field into a // | |
| // forwarding pointer to the real field, in case it's used again // | |
| ////////////////////////////////////////////////////////////////////// | |
| if (field_handle->mh == nullptr) { | |
| HdrHeap *heap = ((reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap); | |
| // allocate a new hdr field and copy any pre-set info | |
| mh_field = mime_field_create_for_name(heap, mh, field_handle->field_ptr->name_get()); | |
| // FIX: is it safe to copy everything over? | |
| memcpy(mh_field, field_handle->field_ptr, sizeof(MIMEField)); | |
| // now set up the forwarding ptr from standalone field to hdr field | |
| field_handle->mh = mh; | |
| field_handle->field_ptr = mh_field; | |
| } | |
| ink_assert(field_handle->mh == mh); | |
| ink_assert(field_handle->field_ptr->m_ptr_name); | |
| mime_hdr_field_attach(mh, field_handle->field_ptr, 1, nullptr); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSMimeHdrFieldRemove(TSMBuffer bufp, TSMLoc mh_mloc, TSMLoc field_mloc) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If allowed, return TS_SUCCESS. Changed the | |
| // return value of function from void to TSReturnCode. | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(mh_mloc) == TS_SUCCESS) || | |
| (sdk_sanity_check_http_hdr_handle(mh_mloc) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field_mloc, mh_mloc) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| MIMEFieldSDKHandle *field_handle = reinterpret_cast<MIMEFieldSDKHandle *>(field_mloc); | |
| if (field_handle->mh != nullptr) { | |
| MIMEHdrImpl *mh = _hdr_mloc_to_mime_hdr_impl(mh_mloc); | |
| ink_assert(mh == field_handle->mh); | |
| sdk_sanity_check_field_handle(field_mloc, mh_mloc); | |
| mime_hdr_field_detach(mh, field_handle->field_ptr, false); // only detach this dup | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSMimeHdrFieldDestroy(TSMBuffer bufp, TSMLoc mh_mloc, TSMLoc field_mloc) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If allowed, return TS_SUCCESS. Changed the | |
| // return value of function from void to TSReturnCode. | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(mh_mloc) == TS_SUCCESS) || | |
| (sdk_sanity_check_http_hdr_handle(mh_mloc) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field_mloc, mh_mloc) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| MIMEFieldSDKHandle *field_handle = reinterpret_cast<MIMEFieldSDKHandle *>(field_mloc); | |
| if (field_handle->mh == nullptr) { // NOT SUPPORTED!! | |
| ink_release_assert(!"Failed MH"); | |
| } else { | |
| MIMEHdrImpl *mh = _hdr_mloc_to_mime_hdr_impl(mh_mloc); | |
| HdrHeap *heap = ((reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap); | |
| ink_assert(mh == field_handle->mh); | |
| if (sdk_sanity_check_field_handle(field_mloc, mh_mloc) != TS_SUCCESS) { | |
| return TS_ERROR; | |
| } | |
| // detach and delete this field, but not all dups | |
| mime_hdr_field_delete(heap, mh, field_handle->field_ptr, false); | |
| } | |
| // for consistence, the handle will not be released here. | |
| // users will be required to do it. | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSMimeHdrFieldCreate(TSMBuffer bufp, TSMLoc mh_mloc, TSMLoc *locp) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If not allowed, set *locp to TS_NULL_MLOC. | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(mh_mloc) == TS_SUCCESS) || | |
| (sdk_sanity_check_http_hdr_handle(mh_mloc) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)locp) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| MIMEHdrImpl *mh = _hdr_mloc_to_mime_hdr_impl(mh_mloc); | |
| HdrHeap *heap = ((reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap); | |
| MIMEFieldSDKHandle *h = sdk_alloc_field_handle(bufp, mh); | |
| h->field_ptr = mime_field_create(heap, mh); | |
| *locp = reinterpret_cast<TSMLoc>(h); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSMimeHdrFieldCreateNamed(TSMBuffer bufp, TSMLoc mh_mloc, const char *name, int name_len, TSMLoc *locp) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(mh_mloc) == TS_SUCCESS) || | |
| (sdk_sanity_check_http_hdr_handle(mh_mloc) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)name) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)locp) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| if (name_len == -1) { | |
| name_len = strlen(name); | |
| } | |
| MIMEHdrImpl *mh = _hdr_mloc_to_mime_hdr_impl(mh_mloc); | |
| HdrHeap *heap = ((reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap); | |
| MIMEFieldSDKHandle *h = sdk_alloc_field_handle(bufp, mh); | |
| h->field_ptr = mime_field_create_named(heap, mh, std::string_view{name, static_cast<std::string_view::size_type>(name_len)}); | |
| if (h->field_ptr == nullptr) { | |
| // The name exceeds the uint16_t field-length limit; nothing was created. | |
| sdk_free_field_handle(bufp, h); | |
| *locp = nullptr; | |
| return TS_ERROR; | |
| } | |
| *locp = reinterpret_cast<TSMLoc>(h); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSMimeHdrFieldCopy(TSMBuffer dest_bufp, TSMLoc dest_hdr, TSMLoc dest_field, TSMBuffer src_bufp, TSMLoc src_hdr, TSMLoc src_field) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If allowed, return TS_SUCCESS. Changed the | |
| // return value of function from void to TSReturnCode. | |
| sdk_assert(sdk_sanity_check_mbuffer(src_bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_mbuffer(dest_bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(src_hdr) == TS_SUCCESS) || | |
| (sdk_sanity_check_http_hdr_handle(src_hdr) == TS_SUCCESS)); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(dest_hdr) == TS_SUCCESS) || | |
| (sdk_sanity_check_http_hdr_handle(dest_hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(src_field, src_hdr) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_field_handle(dest_field, dest_hdr) == TS_SUCCESS); | |
| if (!isWriteable(dest_bufp)) { | |
| return TS_ERROR; | |
| } | |
| bool dest_attached; | |
| MIMEFieldSDKHandle *s_handle = reinterpret_cast<MIMEFieldSDKHandle *>(src_field); | |
| MIMEFieldSDKHandle *d_handle = reinterpret_cast<MIMEFieldSDKHandle *>(dest_field); | |
| HdrHeap *d_heap = (reinterpret_cast<HdrHeapSDKHandle *>(dest_bufp))->m_heap; | |
| // FIX: This tortuous detach/change/attach algorithm is due to the | |
| // fact that we can't change the name of an attached header (assertion) | |
| // TODO: This is never used ... is_live() has no side effects, so this should be ok | |
| // to not call, so commented out | |
| // src_attached = (s_handle->mh && s_handle->field_ptr->is_live()); | |
| dest_attached = (d_handle->mh && d_handle->field_ptr->is_live()); | |
| if (dest_attached) { | |
| mime_hdr_field_detach(d_handle->mh, d_handle->field_ptr, false); | |
| } | |
| mime_field_name_value_set( | |
| d_heap, d_handle->mh, d_handle->field_ptr, s_handle->field_ptr->m_wks_idx, | |
| std::string_view{s_handle->field_ptr->m_ptr_name, static_cast<std::string_view::size_type>(s_handle->field_ptr->m_len_name)}, | |
| std::string_view{s_handle->field_ptr->m_ptr_value, static_cast<std::string_view::size_type>(s_handle->field_ptr->m_len_value)}, | |
| 0, 0, true); | |
| if (dest_attached) { | |
| mime_hdr_field_attach(d_handle->mh, d_handle->field_ptr, 1, nullptr); | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSMimeHdrFieldClone(TSMBuffer dest_bufp, TSMLoc dest_hdr, TSMBuffer src_bufp, TSMLoc src_hdr, TSMLoc src_field, TSMLoc *locp) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If not allowed, set *locp to TS_NULL_MLOC. | |
| sdk_assert(sdk_sanity_check_mbuffer(dest_bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_mbuffer(src_bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(dest_hdr) == TS_SUCCESS) || | |
| (sdk_sanity_check_http_hdr_handle(dest_hdr) == TS_SUCCESS)); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(src_hdr) == TS_SUCCESS) || | |
| (sdk_sanity_check_http_hdr_handle(src_hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(src_field, src_hdr) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)locp) == TS_SUCCESS); | |
| if (!isWriteable(dest_bufp)) { | |
| return TS_ERROR; | |
| } | |
| // This is sort of sub-optimal, since we'll check the args again. TODO. | |
| if (TSMimeHdrFieldCreate(dest_bufp, dest_hdr, locp) == TS_SUCCESS) { | |
| TSMimeHdrFieldCopy(dest_bufp, dest_hdr, *locp, src_bufp, src_hdr, src_field); | |
| return TS_SUCCESS; | |
| } | |
| // TSMimeHdrFieldCreate() failed for some reason. | |
| return TS_ERROR; | |
| } | |
| TSReturnCode | |
| TSMimeHdrFieldCopyValues(TSMBuffer dest_bufp, TSMLoc dest_hdr, TSMLoc dest_field, TSMBuffer src_bufp, TSMLoc src_hdr, | |
| TSMLoc src_field) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If allowed, return TS_SUCCESS. Changed the | |
| // return value of function from void to TSReturnCode. | |
| sdk_assert(sdk_sanity_check_mbuffer(src_bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_mbuffer(dest_bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(src_hdr) == TS_SUCCESS) || | |
| (sdk_sanity_check_http_hdr_handle(src_hdr) == TS_SUCCESS)); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(dest_hdr) == TS_SUCCESS) || | |
| (sdk_sanity_check_http_hdr_handle(dest_hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(src_field, src_hdr) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_field_handle(dest_field, dest_hdr) == TS_SUCCESS); | |
| if (!isWriteable(dest_bufp)) { | |
| return TS_ERROR; | |
| } | |
| MIMEFieldSDKHandle *s_handle = reinterpret_cast<MIMEFieldSDKHandle *>(src_field); | |
| MIMEFieldSDKHandle *d_handle = reinterpret_cast<MIMEFieldSDKHandle *>(dest_field); | |
| HdrHeap *d_heap = (reinterpret_cast<HdrHeapSDKHandle *>(dest_bufp))->m_heap; | |
| MIMEField *s_field, *d_field; | |
| s_field = s_handle->field_ptr; | |
| d_field = d_handle->field_ptr; | |
| mime_field_value_set(d_heap, d_handle->mh, d_field, | |
| std::string_view{s_field->m_ptr_value, static_cast<std::string_view::size_type>(s_field->m_len_value)}, | |
| true); | |
| return TS_SUCCESS; | |
| } | |
| TSMLoc | |
| TSMimeHdrFieldNext(TSMBuffer bufp, TSMLoc hdr, TSMLoc field) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(hdr) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field, hdr) == TS_SUCCESS); | |
| if (auto handle = reinterpret_cast<MIMEFieldSDKHandle *>(field); handle->mh != nullptr) { | |
| if (auto spot = handle->mh->find(handle->field_ptr); spot != handle->mh->end()) { | |
| if (++spot != handle->mh->end()) { | |
| MIMEFieldSDKHandle *h = sdk_alloc_field_handle(bufp, handle->mh); | |
| h->field_ptr = &*spot; | |
| return reinterpret_cast<TSMLoc>(h); | |
| } | |
| } | |
| } | |
| return TS_NULL_MLOC; | |
| } | |
| TSMLoc | |
| TSMimeHdrFieldNextDup(TSMBuffer bufp, TSMLoc hdr, TSMLoc field) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(hdr) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field, hdr) == TS_SUCCESS); | |
| MIMEHdrImpl *mh = _hdr_mloc_to_mime_hdr_impl(hdr); | |
| MIMEFieldSDKHandle *field_handle = reinterpret_cast<MIMEFieldSDKHandle *>(field); | |
| MIMEField *next = field_handle->field_ptr->m_next_dup; | |
| if (next == nullptr) { | |
| return TS_NULL_MLOC; | |
| } | |
| MIMEFieldSDKHandle *next_handle = sdk_alloc_field_handle(bufp, mh); | |
| next_handle->field_ptr = next; | |
| return reinterpret_cast<TSMLoc>(next_handle); | |
| } | |
| int | |
| TSMimeHdrFieldLengthGet(TSMBuffer bufp, TSMLoc hdr, TSMLoc field) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(hdr) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field, hdr) == TS_SUCCESS); | |
| MIMEFieldSDKHandle *handle = reinterpret_cast<MIMEFieldSDKHandle *>(field); | |
| return mime_field_length_get(handle->field_ptr); | |
| } | |
| const char * | |
| TSMimeHdrFieldNameGet(TSMBuffer bufp, TSMLoc hdr, TSMLoc field, int *length) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(hdr) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field, hdr) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)length) == TS_SUCCESS); | |
| MIMEFieldSDKHandle *handle = reinterpret_cast<MIMEFieldSDKHandle *>(field); | |
| auto name{handle->field_ptr->name_get()}; | |
| *length = static_cast<int>(name.length()); | |
| return name.data(); | |
| } | |
| TSReturnCode | |
| TSMimeHdrFieldNameSet(TSMBuffer bufp, TSMLoc hdr, TSMLoc field, const char *name, int length) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If allowed, return TS_SUCCESS. Changed the | |
| // return value of function from void to TSReturnCode. | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(hdr) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field, hdr) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)name) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| if (length == -1) { | |
| length = strlen(name); | |
| } | |
| MIMEFieldSDKHandle *handle = reinterpret_cast<MIMEFieldSDKHandle *>(field); | |
| HdrHeap *heap = (reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap; | |
| int attached = (handle->mh && handle->field_ptr->is_live()); | |
| if (attached) { | |
| mime_hdr_field_detach(handle->mh, handle->field_ptr, false); | |
| } | |
| bool const stored = | |
| handle->field_ptr->name_set(heap, handle->mh, std::string_view{name, static_cast<std::string_view::size_type>(length)}); | |
| if (attached) { | |
| mime_hdr_field_attach(handle->mh, handle->field_ptr, 1, nullptr); | |
| } | |
| // A rejected oversized name leaves the field's prior name intact; report the | |
| // failure so the plugin knows the set did not take effect. | |
| return stored ? TS_SUCCESS : TS_ERROR; | |
| } | |
| TSReturnCode | |
| TSMimeHdrFieldValuesClear(TSMBuffer bufp, TSMLoc hdr, TSMLoc field) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If allowed, return TS_SUCCESS. Changed the | |
| // return value of function from void to TSReturnCode. | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(hdr) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field, hdr) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| MIMEFieldSDKHandle *handle = reinterpret_cast<MIMEFieldSDKHandle *>(field); | |
| HdrHeap *heap = (reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap; | |
| /** | |
| * Modified the string value passed from an empty string ("") to null. | |
| * An empty string is also considered to be a token. The correct value of | |
| * the field after this function should be null. | |
| */ | |
| mime_field_value_set(heap, handle->mh, handle->field_ptr, std::string_view{nullptr, 0}, true); | |
| return TS_SUCCESS; | |
| } | |
| int | |
| TSMimeHdrFieldValuesCount(TSMBuffer bufp, TSMLoc hdr, TSMLoc field) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(hdr) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field, hdr) == TS_SUCCESS); | |
| MIMEFieldSDKHandle *handle = reinterpret_cast<MIMEFieldSDKHandle *>(field); | |
| return mime_field_value_get_comma_val_count(handle->field_ptr); | |
| } | |
| const char * | |
| TSMimeHdrFieldValueStringGet(TSMBuffer bufp, TSMLoc hdr, TSMLoc field, int idx, int *value_len_ptr) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(hdr) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field, hdr) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)value_len_ptr) == TS_SUCCESS); | |
| return TSMimeFieldValueGet(bufp, field, idx, value_len_ptr); | |
| } | |
| time_t | |
| TSMimeHdrFieldValueDateGet(TSMBuffer bufp, TSMLoc hdr, TSMLoc field) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(hdr) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field, hdr) == TS_SUCCESS); | |
| int value_len; | |
| const char *value_str = TSMimeFieldValueGet(bufp, field, -1, &value_len); | |
| if (value_str == nullptr) { | |
| return static_cast<time_t>(0); | |
| } | |
| return mime_parse_date(value_str, value_str + value_len); | |
| } | |
| time_t | |
| TSMimeParseDate(char const *const value_str, int const value_len) | |
| { | |
| if (value_str == nullptr) { | |
| return static_cast<time_t>(0); | |
| } | |
| return mime_parse_date(value_str, value_str + value_len); | |
| } | |
| int | |
| TSMimeHdrFieldValueIntGet(TSMBuffer bufp, TSMLoc hdr, TSMLoc field, int idx) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(hdr) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field, hdr) == TS_SUCCESS); | |
| int value_len; | |
| const char *value_str = TSMimeFieldValueGet(bufp, field, idx, &value_len); | |
| if (value_str == nullptr) { | |
| return 0; | |
| } | |
| return mime_parse_int(value_str, value_str + value_len); | |
| } | |
| int64_t | |
| TSMimeHdrFieldValueInt64Get(TSMBuffer bufp, TSMLoc hdr, TSMLoc field, int idx) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(hdr) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field, hdr) == TS_SUCCESS); | |
| int value_len; | |
| const char *value_str = TSMimeFieldValueGet(bufp, field, idx, &value_len); | |
| if (value_str == nullptr) { | |
| return 0; | |
| } | |
| return mime_parse_int64(value_str, value_str + value_len); | |
| } | |
| unsigned int | |
| TSMimeHdrFieldValueUintGet(TSMBuffer bufp, TSMLoc hdr, TSMLoc field, int idx) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(hdr) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field, hdr) == TS_SUCCESS); | |
| int value_len; | |
| const char *value_str = TSMimeFieldValueGet(bufp, field, idx, &value_len); | |
| if (value_str == nullptr) { | |
| return 0; | |
| } | |
| return mime_parse_uint(value_str, value_str + value_len); | |
| } | |
| TSReturnCode | |
| TSMimeHdrFieldValueStringSet(TSMBuffer bufp, TSMLoc hdr, TSMLoc field, int idx, const char *value, int length) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If allowed, return TS_SUCCESS. Changed the | |
| // return value of function from void to TSReturnCode. | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(hdr) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field, hdr) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)value) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| if (length == -1) { | |
| length = strlen(value); | |
| } | |
| return TSMimeFieldValueSet(bufp, field, idx, value, length) ? TS_SUCCESS : TS_ERROR; | |
| } | |
| TSReturnCode | |
| TSMimeHdrFieldValueDateSet(TSMBuffer bufp, TSMLoc hdr, TSMLoc field, time_t value) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If allowed, return TS_SUCCESS. Changed the | |
| // return value of function from void to TSReturnCode. | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(hdr) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field, hdr) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| char tmp[33]; | |
| int len = mime_format_date(tmp, value); | |
| // idx is ignored and we overwrite all existing values | |
| // TSMimeFieldValueSet(bufp, field_obj, idx, tmp, len); | |
| TSMimeFieldValueSet(bufp, field, -1, tmp, len); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSMimeFormatDate(time_t const value_time, char *const value_str, int *const value_length) | |
| { | |
| if (value_length == nullptr) { | |
| return TS_ERROR; | |
| } | |
| if (*value_length < 33) { | |
| return TS_ERROR; | |
| } | |
| *value_length = mime_format_date(value_str, value_time); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSMimeHdrFieldValueIntSet(TSMBuffer bufp, TSMLoc hdr, TSMLoc field, int idx, int value) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If allowed, return TS_SUCCESS. Changed the | |
| // return value of function from void to TSReturnCode. | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(hdr) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field, hdr) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| char tmp[16]; | |
| int len = mime_format_int(tmp, value, sizeof(tmp)); | |
| TSMimeFieldValueSet(bufp, field, idx, tmp, len); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSMimeHdrFieldValueInt64Set(TSMBuffer bufp, TSMLoc hdr, TSMLoc field, int idx, int64_t value) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If allowed, return TS_SUCCESS. Changed the | |
| // return value of function from void to TSReturnCode. | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(hdr) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field, hdr) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| char tmp[20]; | |
| int len = mime_format_int64(tmp, value, sizeof(tmp)); | |
| TSMimeFieldValueSet(bufp, field, idx, tmp, len); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSMimeHdrFieldValueUintSet(TSMBuffer bufp, TSMLoc hdr, TSMLoc field, int idx, unsigned int value) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If allowed, return TS_SUCCESS. Changed the | |
| // return value of function from void to TSReturnCode. | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(hdr) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field, hdr) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| char tmp[16]; | |
| int len = mime_format_uint(tmp, value, sizeof(tmp)); | |
| TSMimeFieldValueSet(bufp, field, idx, tmp, len); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSMimeHdrFieldValueAppend(TSMBuffer bufp, TSMLoc hdr, TSMLoc field, int idx, const char *value, int length) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If allowed, return TS_SUCCESS. Changed the | |
| // return value of function from void to TSReturnCode. | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(hdr) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field, hdr) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)value) == TS_SUCCESS); | |
| sdk_assert(idx >= 0); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| MIMEFieldSDKHandle *handle = reinterpret_cast<MIMEFieldSDKHandle *>(field); | |
| HdrHeap *heap = (reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap; | |
| if (length == -1) { | |
| length = strlen(value); | |
| } | |
| mime_field_value_extend_comma_val(heap, handle->mh, handle->field_ptr, idx, | |
| std::string_view{value, static_cast<std::string_view::size_type>(length)}); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSMimeHdrFieldValueStringInsert(TSMBuffer bufp, TSMLoc hdr, TSMLoc field, int idx, const char *value, int length) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR, else return TS_SUCCESS. | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(hdr) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field, hdr) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)value) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| if (length == -1) { | |
| length = strlen(value); | |
| } | |
| TSMimeFieldValueInsert(bufp, field, value, length, idx); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSMimeHdrFieldValueIntInsert(TSMBuffer bufp, TSMLoc hdr, TSMLoc field, int idx, int value) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR, else return TS_SUCCESS. | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(hdr) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field, hdr) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| char tmp[16]; | |
| int len = mime_format_int(tmp, value, sizeof(tmp)); | |
| TSMimeFieldValueInsert(bufp, field, tmp, len, idx); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSMimeHdrFieldValueUintInsert(TSMBuffer bufp, TSMLoc hdr, TSMLoc field, int idx, unsigned int value) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR, else return TS_SUCCESS. | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(hdr) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field, hdr) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| char tmp[16]; | |
| int len = mime_format_uint(tmp, value, sizeof(tmp)); | |
| TSMimeFieldValueInsert(bufp, field, tmp, len, idx); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSMimeHdrFieldValueDateInsert(TSMBuffer bufp, TSMLoc hdr, TSMLoc field, time_t value) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR, else return TS_SUCCESS | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(hdr) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field, hdr) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| if (TSMimeHdrFieldValuesClear(bufp, hdr, field) == TS_ERROR) { | |
| return TS_ERROR; | |
| } | |
| char tmp[33]; | |
| int len = mime_format_date(tmp, value); | |
| // idx ignored, overwrite all existing values | |
| // (void)TSMimeFieldValueInsert(bufp, field_obj, tmp, len, idx); | |
| (void)TSMimeFieldValueSet(bufp, field, -1, tmp, len); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSMimeHdrFieldValueDelete(TSMBuffer bufp, TSMLoc hdr, TSMLoc field, int idx) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If allowed, return TS_SUCCESS. Changed the | |
| // return value of function from void to TSReturnCode. | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert((sdk_sanity_check_mime_hdr_handle(hdr) == TS_SUCCESS) || (sdk_sanity_check_http_hdr_handle(hdr) == TS_SUCCESS)); | |
| sdk_assert(sdk_sanity_check_field_handle(field, hdr) == TS_SUCCESS); | |
| sdk_assert(idx >= 0); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| MIMEFieldSDKHandle *handle = reinterpret_cast<MIMEFieldSDKHandle *>(field); | |
| HdrHeap *heap = (reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap; | |
| mime_field_value_delete_comma_val(heap, handle->mh, handle->field_ptr, idx); | |
| return TS_SUCCESS; | |
| } | |
| const char * | |
| TSMimeHdrStringToWKS(const char *str, int length) | |
| { | |
| if (length < 0) { | |
| return hdrtoken_string_to_wks(str); | |
| } else { | |
| return hdrtoken_string_to_wks(str, length); | |
| } | |
| } | |
| /**************/ | |
| /* HttpParser */ | |
| /**************/ | |
| TSHttpParser | |
| TSHttpParserCreate() | |
| { | |
| TSHttpParser parser = reinterpret_cast<TSHttpParser>(ats_malloc(sizeof(HTTPParser))); | |
| http_parser_init(reinterpret_cast<HTTPParser *>(parser)); | |
| return parser; | |
| } | |
| void | |
| TSHttpParserClear(TSHttpParser parser) | |
| { | |
| sdk_assert(sdk_sanity_check_http_parser(parser) == TS_SUCCESS); | |
| http_parser_clear(reinterpret_cast<HTTPParser *>(parser)); | |
| } | |
| void | |
| TSHttpParserDestroy(TSHttpParser parser) noexcept | |
| try { | |
| sdk_assert(sdk_sanity_check_http_parser(parser) == TS_SUCCESS); | |
| http_parser_clear(reinterpret_cast<HTTPParser *>(parser)); | |
| ats_free(parser); | |
| } catch (...) { | |
| ink_abort("exception escaped %s", __func__); | |
| } | |
| /***********/ | |
| /* HttpHdr */ | |
| /***********/ | |
| TSMLoc | |
| TSHttpHdrCreate(TSMBuffer bufp) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| HTTPHdr h; | |
| h.m_heap = (reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap; | |
| h.create(HTTPType::UNKNOWN); | |
| return reinterpret_cast<TSMLoc>(h.m_http); | |
| } | |
| void | |
| TSHttpHdrDestroy(TSMBuffer bufp, TSMLoc obj) noexcept | |
| try { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_http_hdr_handle(obj) == TS_SUCCESS); | |
| // No more objects counts in heap or deallocation | |
| // so do nothing! | |
| // HDR FIX ME - Did this free the MBuffer in Pete's old system | |
| } catch (...) { | |
| ink_abort("exception escaped %s", __func__); | |
| } | |
| TSReturnCode | |
| TSHttpHdrClone(TSMBuffer dest_bufp, TSMBuffer src_bufp, TSMLoc src_hdr, TSMLoc *locp) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If not allowed, set *locp to TS_NULL_MLOC. | |
| sdk_assert(sdk_sanity_check_mbuffer(dest_bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_mbuffer(src_bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_http_hdr_handle(src_hdr) == TS_SUCCESS); | |
| if (!isWriteable(dest_bufp)) { | |
| return TS_ERROR; | |
| } | |
| HdrHeap *s_heap, *d_heap; | |
| HTTPHdrImpl *s_hh, *d_hh; | |
| s_heap = (reinterpret_cast<HdrHeapSDKHandle *>(src_bufp))->m_heap; | |
| d_heap = (reinterpret_cast<HdrHeapSDKHandle *>(dest_bufp))->m_heap; | |
| s_hh = reinterpret_cast<HTTPHdrImpl *>(src_hdr); | |
| if (static_cast<HdrHeapObjType>(s_hh->m_type) != HdrHeapObjType::HTTP_HEADER) { | |
| return TS_ERROR; | |
| } | |
| // TODO: This is never used | |
| // inherit_strs = (s_heap != d_heap ? true : false); | |
| d_hh = http_hdr_clone(s_hh, s_heap, d_heap); | |
| *locp = reinterpret_cast<TSMLoc>(d_hh); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpHdrCopy(TSMBuffer dest_bufp, TSMLoc dest_obj, TSMBuffer src_bufp, TSMLoc src_obj) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If allowed, return TS_SUCCESS. Changed the | |
| // return value of function from void to TSReturnCode. | |
| sdk_assert(sdk_sanity_check_mbuffer(src_bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_mbuffer(dest_bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_http_hdr_handle(dest_obj) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_http_hdr_handle(src_obj) == TS_SUCCESS); | |
| if (!isWriteable(dest_bufp)) { | |
| return TS_ERROR; | |
| } | |
| bool inherit_strs; | |
| HdrHeap *s_heap, *d_heap; | |
| HTTPHdrImpl *s_hh, *d_hh; | |
| s_heap = (reinterpret_cast<HdrHeapSDKHandle *>(src_bufp))->m_heap; | |
| d_heap = (reinterpret_cast<HdrHeapSDKHandle *>(dest_bufp))->m_heap; | |
| s_hh = reinterpret_cast<HTTPHdrImpl *>(src_obj); | |
| d_hh = reinterpret_cast<HTTPHdrImpl *>(dest_obj); | |
| if ((static_cast<HdrHeapObjType>(s_hh->m_type) != HdrHeapObjType::HTTP_HEADER) || | |
| (static_cast<HdrHeapObjType>(d_hh->m_type) != HdrHeapObjType::HTTP_HEADER)) { | |
| return TS_ERROR; | |
| } | |
| inherit_strs = (s_heap != d_heap ? true : false); | |
| TSHttpHdrTypeSet(dest_bufp, dest_obj, static_cast<TSHttpType>(s_hh->m_polarity)); | |
| http_hdr_copy_onto(s_hh, s_heap, d_hh, d_heap, inherit_strs); | |
| return TS_SUCCESS; | |
| } | |
| void | |
| TSHttpHdrPrint(TSMBuffer bufp, TSMLoc obj, TSIOBuffer iobufp) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_http_hdr_handle(obj) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_iocore_structure(iobufp) == TS_SUCCESS); | |
| MIOBuffer *b = reinterpret_cast<MIOBuffer *>(iobufp); | |
| IOBufferBlock *blk; | |
| HTTPHdr h; | |
| int bufindex; | |
| int tmp, dumpoffset; | |
| int done; | |
| SET_HTTP_HDR(h, bufp, obj); | |
| ink_assert(static_cast<HdrHeapObjType>(h.m_http->m_type) == HdrHeapObjType::HTTP_HEADER); | |
| dumpoffset = 0; | |
| do { | |
| blk = b->get_current_block(); | |
| if (!blk || blk->write_avail() == 0) { | |
| b->add_block(); | |
| blk = b->get_current_block(); | |
| } | |
| bufindex = 0; | |
| tmp = dumpoffset; | |
| done = h.print(blk->end(), blk->write_avail(), &bufindex, &tmp); | |
| dumpoffset += bufindex; | |
| b->fill(bufindex); | |
| } while (!done); | |
| } | |
| TSParseResult | |
| TSHttpHdrParseReq(TSHttpParser parser, TSMBuffer bufp, TSMLoc obj, const char **start, const char *end) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_http_hdr_handle(obj) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)start) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)*start) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)end) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_PARSE_ERROR; | |
| } | |
| HTTPHdr h; | |
| SET_HTTP_HDR(h, bufp, obj); | |
| ink_assert(static_cast<HdrHeapObjType>(h.m_http->m_type) == HdrHeapObjType::HTTP_HEADER); | |
| TSHttpHdrTypeSet(bufp, obj, TS_HTTP_TYPE_REQUEST); | |
| return static_cast<TSParseResult>(h.parse_req(reinterpret_cast<HTTPParser *>(parser), start, end, false)); | |
| } | |
| TSParseResult | |
| TSHttpHdrParseResp(TSHttpParser parser, TSMBuffer bufp, TSMLoc obj, const char **start, const char *end) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_http_hdr_handle(obj) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)start) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)*start) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)end) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_PARSE_ERROR; | |
| } | |
| HTTPHdr h; | |
| SET_HTTP_HDR(h, bufp, obj); | |
| ink_assert(static_cast<HdrHeapObjType>(h.m_http->m_type) == HdrHeapObjType::HTTP_HEADER); | |
| TSHttpHdrTypeSet(bufp, obj, TS_HTTP_TYPE_RESPONSE); | |
| return static_cast<TSParseResult>(h.parse_resp(reinterpret_cast<HTTPParser *>(parser), start, end, false)); | |
| } | |
| int | |
| TSHttpHdrLengthGet(TSMBuffer bufp, TSMLoc obj) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_http_hdr_handle(obj) == TS_SUCCESS); | |
| HTTPHdr h; | |
| SET_HTTP_HDR(h, bufp, obj); | |
| ink_assert(static_cast<HdrHeapObjType>(h.m_http->m_type) == HdrHeapObjType::HTTP_HEADER); | |
| return h.length_get(); | |
| } | |
| TSHttpType | |
| TSHttpHdrTypeGet(TSMBuffer bufp, TSMLoc obj) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_http_hdr_handle(obj) == TS_SUCCESS); | |
| HTTPHdr h; | |
| SET_HTTP_HDR(h, bufp, obj); | |
| /* Don't need the assert as the check is done in sdk_sanity_check_http_hdr_handle | |
| ink_assert(h.m_http->m_type == HdrHeapObjType::HTTP_HEADER); | |
| */ | |
| return static_cast<TSHttpType>(h.type_get()); | |
| } | |
| TSReturnCode | |
| TSHttpHdrTypeSet(TSMBuffer bufp, TSMLoc obj, TSHttpType type) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If allowed, return TS_SUCCESS. Changed the | |
| // return value of function from void to TSReturnCode. | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_http_hdr_handle(obj) == TS_SUCCESS); | |
| sdk_assert((type >= TS_HTTP_TYPE_UNKNOWN) && (type <= TS_HTTP_TYPE_RESPONSE)); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| HTTPHdr h; | |
| SET_HTTP_HDR(h, bufp, obj); | |
| ink_assert(static_cast<HdrHeapObjType>(h.m_http->m_type) == HdrHeapObjType::HTTP_HEADER); | |
| // FIX: why are we using an HTTPHdr here? why can't we | |
| // just manipulate the impls directly? | |
| // In Pete's MBuffer system you can change the type | |
| // at will. Not so anymore. We need to try to | |
| // fake the difference. We not going to let | |
| // people change the types of a header. If they | |
| // try, too bad. | |
| if (h.m_http->m_polarity == HTTPType::UNKNOWN) { | |
| if (type == static_cast<TSHttpType>(HTTPType::REQUEST)) { | |
| h.m_http->u.req.m_url_impl = url_create(h.m_heap); | |
| h.m_http->m_polarity = static_cast<HTTPType>(type); | |
| } else if (type == static_cast<TSHttpType>(HTTPType::RESPONSE)) { | |
| h.m_http->m_polarity = static_cast<HTTPType>(type); | |
| } | |
| } | |
| return TS_SUCCESS; | |
| } | |
| int | |
| TSHttpHdrVersionGet(TSMBuffer bufp, TSMLoc obj) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_http_hdr_handle(obj) == TS_SUCCESS); | |
| HTTPHdr h; | |
| SET_HTTP_HDR(h, bufp, obj); | |
| HTTPVersion ver = h.version_get(); | |
| return ver.get_flat_version(); | |
| } | |
| TSReturnCode | |
| TSHttpHdrVersionSet(TSMBuffer bufp, TSMLoc obj, int ver) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If allowed, return TS_SUCCESS. Changed the | |
| // return value of function from void to TSReturnCode. | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_http_hdr_handle(obj) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| HTTPHdr h; | |
| HTTPVersion version{ver}; | |
| SET_HTTP_HDR(h, bufp, obj); | |
| ink_assert(static_cast<HdrHeapObjType>(h.m_http->m_type) == HdrHeapObjType::HTTP_HEADER); | |
| h.version_set(version); | |
| return TS_SUCCESS; | |
| } | |
| const char * | |
| TSHttpHdrMethodGet(TSMBuffer bufp, TSMLoc obj, int *length) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_http_hdr_handle(obj) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)length) == TS_SUCCESS); | |
| HTTPHdr h; | |
| SET_HTTP_HDR(h, bufp, obj); | |
| auto method{h.method_get()}; | |
| *length = static_cast<int>(method.length()); | |
| return method.data(); | |
| } | |
| TSReturnCode | |
| TSHttpHdrMethodSet(TSMBuffer bufp, TSMLoc obj, const char *value, int length) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If allowed, return TS_SUCCESS. Changed the | |
| // return value of function from void to TSReturnCode. | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_http_hdr_handle(obj) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)value) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| HTTPHdr h; | |
| SET_HTTP_HDR(h, bufp, obj); | |
| if (length < 0) { | |
| length = strlen(value); | |
| } | |
| h.method_set(std::string_view{value, static_cast<std::string_view::size_type>(length)}); | |
| return TS_SUCCESS; | |
| } | |
| const char * | |
| TSHttpHdrHostGet(TSMBuffer bufp, TSMLoc obj, int *length) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_http_hdr_handle(obj) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)length) == TS_SUCCESS); | |
| HTTPHdr h; | |
| SET_HTTP_HDR(h, bufp, obj); | |
| auto host{h.host_get()}; | |
| *length = static_cast<int>(host.length()); | |
| return host.data(); | |
| } | |
| TSReturnCode | |
| TSHttpHdrUrlGet(TSMBuffer bufp, TSMLoc obj, TSMLoc *locp) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_http_hdr_handle(obj) == TS_SUCCESS); | |
| HTTPHdrImpl *hh = reinterpret_cast<HTTPHdrImpl *>(obj); | |
| if (hh->m_polarity != HTTPType::REQUEST) { | |
| return TS_ERROR; | |
| } | |
| *locp = (reinterpret_cast<TSMLoc>(hh->u.req.m_url_impl)); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpHdrUrlSet(TSMBuffer bufp, TSMLoc obj, TSMLoc url) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If allowed, return TS_SUCCESS. Changed the | |
| // return value of function from void to TSReturnCode. | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_http_hdr_handle(obj) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_url_handle(url) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| HdrHeap *heap = (reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap; | |
| HTTPHdrImpl *hh = reinterpret_cast<HTTPHdrImpl *>(obj); | |
| if (static_cast<HdrHeapObjType>(hh->m_type) != HdrHeapObjType::HTTP_HEADER) { | |
| return TS_ERROR; | |
| } | |
| URLImpl *url_impl = reinterpret_cast<URLImpl *>(url); | |
| http_hdr_url_set(heap, hh, url_impl); | |
| return TS_SUCCESS; | |
| } | |
| TSHttpStatus | |
| TSHttpHdrStatusGet(TSMBuffer bufp, TSMLoc obj) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_http_hdr_handle(obj) == TS_SUCCESS); | |
| HTTPHdr h; | |
| SET_HTTP_HDR(h, bufp, obj); | |
| return static_cast<TSHttpStatus>(h.status_get()); | |
| } | |
| TSReturnCode | |
| TSHttpHdrStatusSet(TSMBuffer bufp, TSMLoc obj, TSHttpStatus status, TSHttpTxn txnp, std::string_view setter) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If allowed, return TS_SUCCESS. Changed the | |
| // return value of function from void to TSReturnCode. | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_http_hdr_handle(obj) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| HTTPHdr h; | |
| SET_HTTP_HDR(h, bufp, obj); | |
| ink_assert(static_cast<HdrHeapObjType>(h.m_http->m_type) == HdrHeapObjType::HTTP_HEADER); | |
| h.status_set(static_cast<HTTPStatus>(status)); | |
| if (txnp != nullptr && !setter.empty()) { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| sm->t_state.http_return_code_setter_name = setter; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpHdrStatusSet(TSMBuffer bufp, TSMLoc obj, TSHttpStatus status) | |
| { | |
| return TSHttpHdrStatusSet(bufp, obj, status, nullptr, std::string_view{}); | |
| } | |
| const char * | |
| TSHttpHdrReasonGet(TSMBuffer bufp, TSMLoc obj, int *length) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_http_hdr_handle(obj) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)length) == TS_SUCCESS); | |
| HTTPHdr h; | |
| SET_HTTP_HDR(h, bufp, obj); | |
| auto reason{h.reason_get()}; | |
| *length = static_cast<int>(reason.length()); | |
| return reason.data(); | |
| } | |
| TSReturnCode | |
| TSHttpHdrReasonSet(TSMBuffer bufp, TSMLoc obj, const char *value, int length) | |
| { | |
| // Allow to modify the buffer only | |
| // if bufp is modifiable. If bufp is not modifiable return | |
| // TS_ERROR. If allowed, return TS_SUCCESS. Changed the | |
| // return value of function from void to TSReturnCode. | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_http_hdr_handle(obj) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)value) == TS_SUCCESS); | |
| if (!isWriteable(bufp)) { | |
| return TS_ERROR; | |
| } | |
| HTTPHdr h; | |
| SET_HTTP_HDR(h, bufp, obj); | |
| /* Don't need the assert as the check is done in sdk_sanity_check_http_hdr_handle | |
| ink_assert(h.m_http->m_type == HdrHeapObjType::HTTP_HEADER); | |
| */ | |
| if (length < 0) { | |
| length = strlen(value); | |
| } | |
| h.reason_set(std::string_view{value, static_cast<std::string_view::size_type>(length)}); | |
| return TS_SUCCESS; | |
| } | |
| const char * | |
| TSHttpHdrReasonLookup(TSHttpStatus status) | |
| { | |
| return http_hdr_reason_lookup(static_cast<HTTPStatus>(status)); | |
| } | |
| //////////////////////////////////////////////////////////////////// | |
| // | |
| // Cache | |
| // | |
| //////////////////////////////////////////////////////////////////// | |
| inline TSReturnCode | |
| sdk_sanity_check_cachekey(TSCacheKey key) | |
| { | |
| if (nullptr == key) { | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSCacheKey | |
| TSCacheKeyCreate() | |
| { | |
| TSCacheKey key = reinterpret_cast<TSCacheKey>(new CacheInfo()); | |
| // TODO: Probably remove this when we can be use "NEW" can't fail. | |
| sdk_assert(sdk_sanity_check_cachekey(key) == TS_SUCCESS); | |
| return key; | |
| } | |
| TSReturnCode | |
| TSCacheKeyDigestSet(TSCacheKey key, const char *input, int length) | |
| { | |
| sdk_assert(sdk_sanity_check_cachekey(key) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_iocore_structure((void *)input) == TS_SUCCESS); | |
| sdk_assert(length > 0); | |
| CacheInfo *ci = reinterpret_cast<CacheInfo *>(key); | |
| if (ci->magic != CACHE_INFO_MAGIC_ALIVE) { | |
| return TS_ERROR; | |
| } | |
| CryptoContext().hash_immediate(ci->cache_key, input, length); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSCacheKeyDigestFromUrlSet(TSCacheKey key, TSMLoc url) | |
| { | |
| sdk_assert(sdk_sanity_check_cachekey(key) == TS_SUCCESS); | |
| if ((reinterpret_cast<CacheInfo *>(key))->magic != CACHE_INFO_MAGIC_ALIVE) { | |
| return TS_ERROR; | |
| } | |
| url_CryptoHash_get(reinterpret_cast<URLImpl *>(url), &(reinterpret_cast<CacheInfo *>(key))->cache_key); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSCacheKeyDataTypeSet(TSCacheKey key, TSCacheDataType type) | |
| { | |
| sdk_assert(sdk_sanity_check_cachekey(key) == TS_SUCCESS); | |
| if ((reinterpret_cast<CacheInfo *>(key))->magic != CACHE_INFO_MAGIC_ALIVE) { | |
| return TS_ERROR; | |
| } | |
| switch (type) { | |
| case TS_CACHE_DATA_TYPE_NONE: | |
| (reinterpret_cast<CacheInfo *>(key))->frag_type = CACHE_FRAG_TYPE_NONE; | |
| break; | |
| case TS_CACHE_DATA_TYPE_OTHER: /* other maps to http */ | |
| case TS_CACHE_DATA_TYPE_HTTP: | |
| (reinterpret_cast<CacheInfo *>(key))->frag_type = CACHE_FRAG_TYPE_HTTP; | |
| break; | |
| default: | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSCacheKeyHostNameSet(TSCacheKey key, const char *hostname, int host_len) | |
| { | |
| sdk_assert(sdk_sanity_check_cachekey(key) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)hostname) == TS_SUCCESS); | |
| sdk_assert(host_len > 0); | |
| if ((reinterpret_cast<CacheInfo *>(key))->magic != CACHE_INFO_MAGIC_ALIVE) { | |
| return TS_ERROR; | |
| } | |
| CacheInfo *i = reinterpret_cast<CacheInfo *>(key); | |
| /* need to make a copy of the hostname. The caller | |
| might deallocate it anytime in the future */ | |
| i->hostname = static_cast<char *>(ats_malloc(host_len)); | |
| memcpy(i->hostname, hostname, host_len); | |
| i->len = host_len; | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSCacheKeyPinnedSet(TSCacheKey key, time_t pin_in_cache) | |
| { | |
| sdk_assert(sdk_sanity_check_cachekey(key) == TS_SUCCESS); | |
| if ((reinterpret_cast<CacheInfo *>(key))->magic != CACHE_INFO_MAGIC_ALIVE) { | |
| return TS_ERROR; | |
| } | |
| CacheInfo *i = reinterpret_cast<CacheInfo *>(key); | |
| i->pin_in_cache = pin_in_cache; | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSCacheKeyDestroy(TSCacheKey key) noexcept | |
| try { | |
| sdk_assert(sdk_sanity_check_cachekey(key) == TS_SUCCESS); | |
| if ((reinterpret_cast<CacheInfo *>(key))->magic != CACHE_INFO_MAGIC_ALIVE) { | |
| return TS_ERROR; | |
| } | |
| CacheInfo *i = reinterpret_cast<CacheInfo *>(key); | |
| ats_free(i->hostname); | |
| i->magic = CACHE_INFO_MAGIC_DEAD; | |
| delete i; | |
| return TS_SUCCESS; | |
| } catch (...) { | |
| ink_abort("exception escaped %s", __func__); | |
| } | |
| TSCacheHttpInfo | |
| TSCacheHttpInfoCopy(TSCacheHttpInfo infop) | |
| { | |
| CacheHTTPInfo *new_info = new CacheHTTPInfo; | |
| new_info->copy(reinterpret_cast<CacheHTTPInfo *>(infop)); | |
| return reinterpret_cast<TSCacheHttpInfo>(new_info); | |
| } | |
| void | |
| TSCacheHttpInfoReqGet(TSCacheHttpInfo infop, TSMBuffer *bufp, TSMLoc *obj) | |
| { | |
| CacheHTTPInfo *info = reinterpret_cast<CacheHTTPInfo *>(infop); | |
| *(reinterpret_cast<HTTPHdr **>(bufp)) = info->request_get(); | |
| *obj = reinterpret_cast<TSMLoc>(info->request_get()->m_http); | |
| sdk_assert(sdk_sanity_check_mbuffer(*bufp) == TS_SUCCESS); | |
| } | |
| void | |
| TSCacheHttpInfoRespGet(TSCacheHttpInfo infop, TSMBuffer *bufp, TSMLoc *obj) | |
| { | |
| CacheHTTPInfo *info = reinterpret_cast<CacheHTTPInfo *>(infop); | |
| *(reinterpret_cast<HTTPHdr **>(bufp)) = info->response_get(); | |
| *obj = reinterpret_cast<TSMLoc>(info->response_get()->m_http); | |
| sdk_assert(sdk_sanity_check_mbuffer(*bufp) == TS_SUCCESS); | |
| } | |
| time_t | |
| TSCacheHttpInfoReqSentTimeGet(TSCacheHttpInfo infop) | |
| { | |
| CacheHTTPInfo *info = reinterpret_cast<CacheHTTPInfo *>(infop); | |
| return info->request_sent_time_get(); | |
| } | |
| time_t | |
| TSCacheHttpInfoRespReceivedTimeGet(TSCacheHttpInfo infop) | |
| { | |
| CacheHTTPInfo *info = reinterpret_cast<CacheHTTPInfo *>(infop); | |
| return info->response_received_time_get(); | |
| } | |
| int64_t | |
| TSCacheHttpInfoSizeGet(TSCacheHttpInfo infop) | |
| { | |
| CacheHTTPInfo *info = reinterpret_cast<CacheHTTPInfo *>(infop); | |
| return info->object_size_get(); | |
| } | |
| void | |
| TSCacheHttpInfoReqSet(TSCacheHttpInfo infop, TSMBuffer bufp, TSMLoc obj) | |
| { | |
| HTTPHdr h; | |
| SET_HTTP_HDR(h, bufp, obj); | |
| CacheHTTPInfo *info = reinterpret_cast<CacheHTTPInfo *>(infop); | |
| info->request_set(&h); | |
| } | |
| void | |
| TSCacheHttpInfoRespSet(TSCacheHttpInfo infop, TSMBuffer bufp, TSMLoc obj) | |
| { | |
| HTTPHdr h; | |
| SET_HTTP_HDR(h, bufp, obj); | |
| CacheHTTPInfo *info = reinterpret_cast<CacheHTTPInfo *>(infop); | |
| info->response_set(&h); | |
| } | |
| int | |
| TSCacheHttpInfoVector(TSCacheHttpInfo infop, void *data, int length) | |
| { | |
| CacheHTTPInfo *info = reinterpret_cast<CacheHTTPInfo *>(infop); | |
| CacheHTTPInfoVector vector; | |
| vector.insert(info); | |
| int size = vector.marshal_length(); | |
| if (size > length) { | |
| // error | |
| return 0; | |
| } | |
| return vector.marshal(static_cast<char *>(data), length); | |
| } | |
| void | |
| TSCacheHttpInfoDestroy(TSCacheHttpInfo infop) | |
| { | |
| (reinterpret_cast<CacheHTTPInfo *>(infop))->destroy(); | |
| } | |
| TSCacheHttpInfo | |
| TSCacheHttpInfoCreate() | |
| { | |
| CacheHTTPInfo *info = new CacheHTTPInfo; | |
| info->create(); | |
| return reinterpret_cast<TSCacheHttpInfo>(info); | |
| } | |
| //////////////////////////////////////////////////////////////////// | |
| // | |
| // Configuration | |
| // | |
| //////////////////////////////////////////////////////////////////// | |
| unsigned int | |
| TSConfigSet(unsigned int id, void *data, TSConfigDestroyFunc funcp) | |
| { | |
| INKConfigImpl *config = new INKConfigImpl; | |
| config->mdata = data; | |
| config->m_destroy_func = funcp; | |
| return configProcessor.set(id, config); | |
| } | |
| TSConfig | |
| TSConfigGet(unsigned int id) | |
| { | |
| return reinterpret_cast<TSConfig>(configProcessor.get(id)); | |
| } | |
| void | |
| TSConfigRelease(unsigned int id, TSConfig configp) | |
| { | |
| configProcessor.release(id, reinterpret_cast<ConfigInfo *>(configp)); | |
| } | |
| void * | |
| TSConfigDataGet(TSConfig configp) | |
| { | |
| INKConfigImpl *config = reinterpret_cast<INKConfigImpl *>(configp); | |
| return config->mdata; | |
| } | |
| //////////////////////////////////////////////////////////////////// | |
| // | |
| // Management | |
| // | |
| //////////////////////////////////////////////////////////////////// | |
| void | |
| TSMgmtUpdateRegister(TSCont contp, const char *plugin_name, const char *plugin_file_name) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(contp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)plugin_name) == TS_SUCCESS); | |
| global_config_cbs->insert(reinterpret_cast<INKContInternal *>(contp), plugin_name, plugin_file_name); | |
| } | |
| //////////////////////////////////////////////////////////////////// | |
| // | |
| // Config Registry - plugin config reload registration | |
| // | |
| //////////////////////////////////////////////////////////////////// | |
| namespace | |
| { | |
| // Handle behind the opaque TSCfgLoadCtx. One is created per handler invocation | |
| // (and per subtask) and owned by PluginCtxRegistry. Caches the strings/nodes the | |
| // getters need to return as stable pointers for the lifetime of the handle. | |
| struct PluginConfigContext { | |
| ConfigContext ctx; | |
| std::string filename; | |
| std::string reload_token; | |
| YAML::Node supplied_yaml; | |
| YAML::Node reload_directives; | |
| }; | |
| DbgCtl dbg_ctl_plugin_config{"config.reload"}; | |
| // Process-lifetime registry of live TSCfgLoadCtx handles. | |
| // | |
| // Handles handed to plugins are opaque monotonically-increasing ids, never raw | |
| // pointers, so a stale handle can never alias a freshly-allocated context (no | |
| // ABA). Every accessor validates the id under the registry lock, so a call made | |
| // after Complete/Fail - or on a bogus handle - is a genuine no-op instead of a | |
| // use-after-free read. Complete/Fail unregister the context under the same lock, | |
| // so a second finalize can never double-free. Accessors hold shared ownership for | |
| // the duration of the call, so a Complete/Fail racing on another thread cannot | |
| // free the context mid-call. | |
| class PluginCtxRegistry | |
| { | |
| public: | |
| static PluginCtxRegistry & | |
| instance() | |
| { | |
| static PluginCtxRegistry r; | |
| return r; | |
| } | |
| /// Take ownership of @a pctx and return its opaque handle. | |
| TSCfgLoadCtx | |
| create(std::unique_ptr<PluginConfigContext> pctx) | |
| { | |
| std::lock_guard lock(_mutex); | |
| uint64_t id = _next_id++; | |
| _live.emplace(id, std::move(pctx)); | |
| return reinterpret_cast<TSCfgLoadCtx>(static_cast<uintptr_t>(id)); | |
| } | |
| /// Look up a live context by handle. Returns @c nullptr when the handle was | |
| /// already finalized or was never valid. The caller keeps shared ownership for | |
| /// the duration of its call, so a concurrent Complete/Fail unregisters the | |
| /// context without freeing it out from under the caller. | |
| std::shared_ptr<PluginConfigContext> | |
| lookup(TSCfgLoadCtx handle) | |
| { | |
| uint64_t id = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(handle)); | |
| std::lock_guard lock(_mutex); | |
| auto it = _live.find(id); | |
| return it != _live.end() ? it->second : nullptr; | |
| } | |
| /// Remove a context from the registry, transferring ownership to the caller. | |
| /// Returns @c nullptr when the handle is unknown or already finalized. | |
| std::shared_ptr<PluginConfigContext> | |
| extract(TSCfgLoadCtx handle) | |
| { | |
| uint64_t id = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(handle)); | |
| std::lock_guard lock(_mutex); | |
| auto it = _live.find(id); | |
| if (it == _live.end()) { | |
| return nullptr; | |
| } | |
| auto pctx = std::move(it->second); | |
| _live.erase(it); | |
| return pctx; | |
| } | |
| private: | |
| std::mutex _mutex; | |
| std::unordered_map<uint64_t, std::shared_ptr<PluginConfigContext>> _live; | |
| uint64_t _next_id{1}; | |
| }; | |
| /// Resolve a handle to its live context, logging when the handle is invalid or | |
| /// already finalized. Returns @c nullptr in that case so callers can no-op. | |
| std::shared_ptr<PluginConfigContext> | |
| resolve_plugin_ctx(TSCfgLoadCtx handle, const char *api_fn) | |
| { | |
| auto pctx = PluginCtxRegistry::instance().lookup(handle); | |
| if (pctx == nullptr) { | |
| Warning("%s called on an invalid or already-finalized TSCfgLoadCtx; ignoring", api_fn); | |
| ink_assert(!"TSCfgLoadCtx used after Complete/Fail or otherwise invalid"); | |
| } | |
| return pctx; | |
| } | |
| /// Verify we're inside TSPluginInit() after a successful TSPluginRegister(). | |
| /// Returns the active plugin name on success, nullptr on failure (after logging). | |
| const char * | |
| validate_plugin_init(const char *api_fn) | |
| { | |
| if (!plugin_reg_current) { | |
| Error("[unknown-plugin] %s must be called from TSPluginInit()", api_fn); | |
| return nullptr; | |
| } | |
| if (!plugin_reg_current->plugin_registered || plugin_reg_current->plugin_name == nullptr) { | |
| Error("[%s] %s must be called after TSPluginRegister() with a non-null plugin_name", | |
| plugin_reg_current->plugin_path ? plugin_reg_current->plugin_path : "?", api_fn); | |
| return nullptr; | |
| } | |
| return plugin_reg_current->plugin_name; | |
| } | |
| } // anonymous namespace | |
| TSReturnCode | |
| TSCfgRegister(const TSCfgRegistrationInfo *info) | |
| { | |
| const char *plugin_name = validate_plugin_init("TSCfgRegister"); | |
| if (!plugin_name) { | |
| return TS_ERROR; | |
| } | |
| if (info == nullptr) { | |
| Error("[%s] TSCfgRegister: info is null", plugin_name); | |
| return TS_ERROR; | |
| } | |
| if (info->key.empty() || info->config_path.empty() || info->handler == nullptr) { | |
| Error("[%s] TSCfgRegister: missing required fields (key/config_path/handler)", plugin_name); | |
| return TS_ERROR; | |
| } | |
| std::string key_str{info->key}; | |
| std::string config_path_str{info->config_path}; | |
| std::string filename_record_str{info->filename_record}; | |
| config::ConfigSource cfg_source = | |
| (info->source == TS_CFG_SOURCE_FILE_AND_RPC) ? config::ConfigSource::FileAndRpc : config::ConfigSource::FileOnly; | |
| auto cb = info->handler; | |
| auto udata = info->data; | |
| // Lambda captures the function pointer, opaque user data, and the registry key | |
| // (so it never has to rely on ctx.get_description() matching the key). The | |
| // handle is owned by PluginCtxRegistry for the duration of the reload. | |
| config::ConfigReloadHandler wrapper = [cb, udata, key_str](ConfigContext ctx) { | |
| auto pctx = std::make_unique<PluginConfigContext>(); | |
| pctx->reload_token = ctx.get_reload_token(); | |
| pctx->supplied_yaml = ctx.supplied_yaml(); | |
| pctx->reload_directives = ctx.reload_directives(); | |
| if (auto const *entry = config::ConfigRegistry::Get_Instance().find(key_str); entry != nullptr) { | |
| pctx->filename = entry->resolve_filename(); | |
| } | |
| pctx->ctx = std::move(ctx); | |
| TSCfgLoadCtx handle = PluginCtxRegistry::instance().create(std::move(pctx)); | |
| Dbg(dbg_ctl_plugin_config, "Invoking plugin config handler for '%s'", key_str.c_str()); | |
| cb(handle, udata); | |
| // On synchronous completion the plugin has already finalized (and freed) the | |
| // handle. On deferred completion the handle stays live in the registry until | |
| // the plugin calls Complete/Fail. If the plugin never finalizes, the entry | |
| // persists (bounded: one per un-finalized reload) but core's progress | |
| // timeout still unblocks the reload cycle. | |
| }; | |
| // Duplicate-key / invalid registrations are dropped by register_plugin_config; | |
| // surface that to the plugin as TS_ERROR rather than a false success. Callers | |
| // can also probe up front with TSCfgIsRegistered. | |
| if (!config::ConfigRegistry::Get_Instance().register_plugin_config(key_str, plugin_name, config_path_str, filename_record_str, | |
| std::move(wrapper), cfg_source, {}, info->is_required)) { | |
| Error("[%s] TSCfgRegister: registration dropped for key '%s' (duplicate key or invalid plugin state)", plugin_name, | |
| key_str.c_str()); | |
| return TS_ERROR; | |
| } | |
| Dbg(dbg_ctl_plugin_config, "[%s] TSCfgRegister: registered '%s' (file: %s)", plugin_name, key_str.c_str(), | |
| config_path_str.c_str()); | |
| return TS_SUCCESS; | |
| } | |
| bool | |
| TSCfgIsRegistered(std::string_view key) | |
| { | |
| if (key.empty()) { | |
| return false; | |
| } | |
| return config::ConfigRegistry::Get_Instance().contains(std::string{key}); | |
| } | |
| TSReturnCode | |
| TSCfgAttachReloadTrigger(std::string_view key, std::string_view record_name) | |
| { | |
| const char *plugin_name = validate_plugin_init("TSCfgAttachReloadTrigger"); | |
| if (!plugin_name) { | |
| return TS_ERROR; | |
| } | |
| if (key.empty() || record_name.empty()) { | |
| Error("[%s] TSCfgAttachReloadTrigger: key and record_name required", plugin_name); | |
| return TS_ERROR; | |
| } | |
| std::string key_str{key}; | |
| std::string record_str{record_name}; | |
| if (config::ConfigRegistry::Get_Instance().attach(key_str, record_str.c_str()) != 0) { | |
| Dbg(dbg_ctl_plugin_config, "[%s] TSCfgAttachReloadTrigger: attach failed (key='%s', record='%s')", plugin_name, key_str.c_str(), | |
| record_str.c_str()); | |
| return TS_ERROR; | |
| } | |
| Dbg(dbg_ctl_plugin_config, "[%s] TSCfgAttachReloadTrigger: attached record '%s' to config '%s'", plugin_name, record_str.c_str(), | |
| key_str.c_str()); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSCfgAddFileDependency(const TSCfgFileDependencyInfo *info) | |
| { | |
| const char *plugin_name = validate_plugin_init("TSCfgAddFileDependency"); | |
| if (!plugin_name) { | |
| return TS_ERROR; | |
| } | |
| if (info == nullptr) { | |
| Error("[%s] TSCfgAddFileDependency: info is null", plugin_name); | |
| return TS_ERROR; | |
| } | |
| if (info->key.empty() || info->config_path.empty()) { | |
| Error("[%s] TSCfgAddFileDependency: missing required fields (key/config_path)", plugin_name); | |
| return TS_ERROR; | |
| } | |
| auto ®istry = config::ConfigRegistry::Get_Instance(); | |
| std::string key_str = std::string{info->key}; | |
| std::string path_str = std::string{info->config_path}; | |
| std::string filename_record_str = std::string{info->filename_record}; | |
| std::string dep_key_str = std::string{info->dep_key}; | |
| bool const has_dep_key = !dep_key_str.empty(); | |
| // filename_record_str may be empty; add_file_dependency / resolve_config_filename treat empty as "no record". | |
| const char *filename_record_arg = filename_record_str.empty() ? nullptr : filename_record_str.c_str(); | |
| int rc = has_dep_key ? | |
| registry.add_file_and_node_dependency(key_str, dep_key_str, filename_record_arg, path_str.c_str(), info->is_required) : | |
| registry.add_file_dependency(key_str, filename_record_arg, path_str.c_str(), info->is_required); | |
| if (rc != 0) { | |
| Dbg(dbg_ctl_plugin_config, "[%s] TSCfgAddFileDependency: registration failed (key='%s', dep_key='%s', file='%s')", plugin_name, | |
| key_str.c_str(), dep_key_str.c_str(), path_str.c_str()); | |
| return TS_ERROR; | |
| } | |
| if (has_dep_key) { | |
| Dbg(dbg_ctl_plugin_config, "[%s] TSCfgAddFileDependency: added file '%s' (dep_key '%s') to config '%s'", plugin_name, | |
| path_str.c_str(), dep_key_str.c_str(), key_str.c_str()); | |
| } else { | |
| Dbg(dbg_ctl_plugin_config, "[%s] TSCfgAddFileDependency: added file '%s' to config '%s'", plugin_name, path_str.c_str(), | |
| key_str.c_str()); | |
| } | |
| return TS_SUCCESS; | |
| } | |
| namespace | |
| { | |
| /// Finalize a handle with Complete or Fail. The context is extracted (and thus | |
| /// freed) under the registry lock, so a second finalize - or any later accessor | |
| /// call - safely finds nothing and no-ops instead of touching freed memory. | |
| void | |
| finalize_plugin_ctx(TSCfgLoadCtx handle, std::string_view msg, bool complete) | |
| { | |
| auto pctx = PluginCtxRegistry::instance().extract(handle); | |
| if (pctx == nullptr) { | |
| Warning("TSCfgLoadCtx%s called on an invalid or already-finalized handle; ignoring", complete ? "Complete" : "Fail"); | |
| ink_assert(!"TSCfgLoadCtx finalized more than once or otherwise invalid"); | |
| return; | |
| } | |
| if (complete) { | |
| if (!msg.empty()) { | |
| CfgLoadComplete(pctx->ctx, "%.*s", static_cast<int>(msg.size()), msg.data()); | |
| } else { | |
| pctx->ctx.complete(); | |
| } | |
| } else { | |
| if (!msg.empty()) { | |
| CfgLoadFail(pctx->ctx, "%.*s", static_cast<int>(msg.size()), msg.data()); | |
| } else { | |
| pctx->ctx.fail(); | |
| } | |
| } | |
| // pctx frees here, or when the last concurrent accessor releases its reference. | |
| } | |
| } // anonymous namespace | |
| void | |
| TSCfgLoadCtxInProgress(TSCfgLoadCtx ctx, std::string_view msg) | |
| { | |
| auto pctx = resolve_plugin_ctx(ctx, "TSCfgLoadCtxInProgress"); | |
| if (pctx == nullptr) { | |
| return; | |
| } | |
| if (!msg.empty()) { | |
| CfgLoadInProgress(pctx->ctx, "%.*s", static_cast<int>(msg.size()), msg.data()); | |
| } else { | |
| pctx->ctx.in_progress(); | |
| } | |
| } | |
| void | |
| TSCfgLoadCtxComplete(TSCfgLoadCtx ctx, std::string_view msg) | |
| { | |
| finalize_plugin_ctx(ctx, msg, /*complete=*/true); | |
| } | |
| void | |
| TSCfgLoadCtxFail(TSCfgLoadCtx ctx, std::string_view msg) | |
| { | |
| finalize_plugin_ctx(ctx, msg, /*complete=*/false); | |
| } | |
| void | |
| TSCfgLoadCtxAddLog(TSCfgLoadCtx ctx, TSCfgLogLevel level, std::string_view msg) | |
| { | |
| auto pctx = resolve_plugin_ctx(ctx, "TSCfgLoadCtxAddLog"); | |
| if (pctx == nullptr || msg.empty()) { | |
| return; | |
| } | |
| DiagsLevel diags_level = DL_Note; | |
| switch (level) { | |
| case TS_CFG_LOG_WARNING: | |
| diags_level = DL_Warning; | |
| break; | |
| case TS_CFG_LOG_ERROR: | |
| diags_level = DL_Error; | |
| break; | |
| case TS_CFG_LOG_NOTE: | |
| default: | |
| diags_level = DL_Note; | |
| break; | |
| } | |
| CfgLoadLog(pctx->ctx, diags_level, "%.*s", static_cast<int>(msg.size()), msg.data()); | |
| } | |
| TSCfgLoadCtx | |
| TSCfgLoadCtxAddSubtask(TSCfgLoadCtx ctx, std::string_view description) | |
| { | |
| auto pctx = resolve_plugin_ctx(ctx, "TSCfgLoadCtxAddSubtask"); | |
| if (pctx == nullptr) { | |
| return nullptr; | |
| } | |
| ConfigContext child_ctx = pctx->ctx.add_dependent_ctx(description); | |
| if (!child_ctx) { | |
| return nullptr; | |
| } | |
| auto child_handle = std::make_unique<PluginConfigContext>(); | |
| child_handle->filename = pctx->filename; | |
| child_handle->reload_token = pctx->reload_token; | |
| child_handle->supplied_yaml = child_ctx.supplied_yaml(); | |
| child_handle->reload_directives = child_ctx.reload_directives(); | |
| child_handle->ctx = std::move(child_ctx); | |
| return PluginCtxRegistry::instance().create(std::move(child_handle)); | |
| } | |
| std::string_view | |
| TSCfgLoadCtxGetFilename(TSCfgLoadCtx ctx) | |
| { | |
| auto pctx = resolve_plugin_ctx(ctx, "TSCfgLoadCtxGetFilename"); | |
| if (pctx == nullptr) { | |
| return {}; | |
| } | |
| return pctx->filename; | |
| } | |
| std::string_view | |
| TSCfgLoadCtxGetReloadToken(TSCfgLoadCtx ctx) | |
| { | |
| auto pctx = resolve_plugin_ctx(ctx, "TSCfgLoadCtxGetReloadToken"); | |
| if (pctx == nullptr) { | |
| return {}; | |
| } | |
| return pctx->reload_token; | |
| } | |
| TSYaml | |
| TSCfgLoadCtxGetSuppliedYaml(TSCfgLoadCtx ctx) | |
| { | |
| auto pctx = resolve_plugin_ctx(ctx, "TSCfgLoadCtxGetSuppliedYaml"); | |
| if (pctx == nullptr || !pctx->supplied_yaml.IsDefined()) { | |
| return nullptr; | |
| } | |
| return reinterpret_cast<TSYaml>(&pctx->supplied_yaml); | |
| } | |
| TSYaml | |
| TSCfgLoadCtxGetReloadDirectives(TSCfgLoadCtx ctx) | |
| { | |
| auto pctx = resolve_plugin_ctx(ctx, "TSCfgLoadCtxGetReloadDirectives"); | |
| if (pctx == nullptr || !pctx->reload_directives.IsDefined()) { | |
| return nullptr; | |
| } | |
| return reinterpret_cast<TSYaml>(&pctx->reload_directives); | |
| } | |
| TSReturnCode | |
| TSMgmtIntGet(const char *var_name, TSMgmtInt *result) | |
| { | |
| auto tmp{RecGetRecordInt(var_name)}; | |
| // Try the old librecords first | |
| if (!tmp) { | |
| int id = global_api_metrics.lookup(var_name); | |
| if (id == ts::Metrics::NOT_FOUND) { | |
| return TS_ERROR; | |
| } else { | |
| *result = global_api_metrics[id].load(); | |
| } | |
| } else { | |
| *result = tmp.value(); | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSMgmtCounterGet(const char *var_name, TSMgmtCounter *result) | |
| { | |
| auto tmp{RecGetRecordCounter(var_name)}; | |
| // Try the old librecords first | |
| if (!tmp) { | |
| int id = global_api_metrics.lookup(var_name); | |
| if (id == ts::Metrics::NOT_FOUND) { | |
| return TS_ERROR; | |
| } else { | |
| *result = global_api_metrics[id].load(); | |
| } | |
| } else { | |
| *result = tmp.value(); | |
| } | |
| return TS_SUCCESS; | |
| } | |
| // ToDo: These don't have the new metrics, only librecords. | |
| TSReturnCode | |
| TSMgmtFloatGet(const char *var_name, TSMgmtFloat *result) | |
| { | |
| auto tmp{RecGetRecordFloat(const_cast<char *>(var_name))}; | |
| if (tmp) { | |
| *result = tmp.value(); | |
| return TS_SUCCESS; | |
| } | |
| return TS_ERROR; | |
| } | |
| TSReturnCode | |
| TSMgmtStringGet(const char *var_name, TSMgmtString *result) | |
| { | |
| auto tmp_str{RecGetRecordStringAlloc(const_cast<char *>(var_name))}; | |
| auto tmp{ats_as_c_str(tmp_str)}; | |
| if (tmp) { | |
| *result = ats_strdup(tmp); | |
| return TS_SUCCESS; | |
| } | |
| return TS_ERROR; | |
| } | |
| TSReturnCode | |
| TSMgmtSourceGet(const char *var_name, TSMgmtSource *source) | |
| { | |
| return REC_ERR_OKAY == RecGetRecordSource(var_name, reinterpret_cast<RecSourceT *>(source)) ? TS_SUCCESS : TS_ERROR; | |
| } | |
| TSReturnCode | |
| TSMgmtDataTypeGet(const char *var_name, TSRecordDataType *result) | |
| { | |
| return REC_ERR_OKAY == RecGetRecordDataType(var_name, reinterpret_cast<RecDataT *>(result)) ? TS_SUCCESS : TS_ERROR; | |
| } | |
| //////////////////////////////////////////////////////////////////// | |
| // | |
| // Continuations | |
| // | |
| //////////////////////////////////////////////////////////////////// | |
| TSCont | |
| TSContCreate(TSEventFunc funcp, TSMutex mutexp) | |
| { | |
| // mutexp can be null | |
| if (mutexp != nullptr) { | |
| sdk_assert(sdk_sanity_check_mutex(mutexp) == TS_SUCCESS); | |
| } | |
| if (pluginThreadContext) { | |
| pluginThreadContext->acquire(); | |
| } | |
| INKContInternal *i = THREAD_ALLOC(INKContAllocator, this_thread()); | |
| i->init(funcp, mutexp, pluginThreadContext); | |
| return reinterpret_cast<TSCont>(i); | |
| } | |
| void | |
| TSContDestroy(TSCont contp) noexcept | |
| try { | |
| sdk_assert(sdk_sanity_check_iocore_structure(contp) == TS_SUCCESS); | |
| INKContInternal *i = reinterpret_cast<INKContInternal *>(contp); | |
| if (i->m_context) { | |
| reinterpret_cast<PluginThreadContext *>(i->m_context)->release(); | |
| } | |
| i->destroy(); | |
| } catch (...) { | |
| ink_abort("exception escaped %s", __func__); | |
| } | |
| void | |
| TSContDataSet(TSCont contp, void *data) noexcept | |
| try { | |
| sdk_assert(sdk_sanity_check_iocore_structure(contp) == TS_SUCCESS); | |
| INKContInternal *i = reinterpret_cast<INKContInternal *>(contp); | |
| i->mdata = data; | |
| } catch (...) { | |
| ink_abort("exception escaped %s", __func__); | |
| } | |
| void * | |
| TSContDataGet(TSCont contp) noexcept | |
| try { | |
| sdk_assert(sdk_sanity_check_iocore_structure(contp) == TS_SUCCESS); | |
| INKContInternal *i = reinterpret_cast<INKContInternal *>(contp); | |
| return i->mdata; | |
| } catch (...) { | |
| ink_abort("exception escaped %s", __func__); | |
| } | |
| TSAction | |
| TSContScheduleOnPool(TSCont contp, TSHRTime timeout, TSThreadPool tp) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(contp) == TS_SUCCESS); | |
| /* ensure we are on a EThread */ | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)this_ethread()) == TS_SUCCESS); | |
| FORCE_PLUGIN_SCOPED_MUTEX(contp); | |
| INKContInternal *i = reinterpret_cast<INKContInternal *>(contp); | |
| if (ink_atomic_increment(static_cast<int *>(&i->m_event_count), 1) < 0) { | |
| ink_assert(!"not reached"); | |
| } | |
| EventType etype; | |
| switch (tp) { | |
| case TS_THREAD_POOL_NET: | |
| etype = ET_NET; | |
| break; | |
| case TS_THREAD_POOL_TASK: | |
| etype = ET_TASK; | |
| break; | |
| case TS_THREAD_POOL_DNS: | |
| etype = ET_DNS; | |
| break; | |
| case TS_THREAD_POOL_UDP: | |
| etype = ET_UDP; | |
| break; | |
| default: | |
| etype = ET_TASK; | |
| break; | |
| } | |
| TSAction action; | |
| if (timeout == 0) { | |
| action = reinterpret_cast<TSAction>(eventProcessor.schedule_imm(i, etype)); | |
| } else { | |
| action = reinterpret_cast<TSAction>(eventProcessor.schedule_in(i, HRTIME_MSECONDS(timeout), etype)); | |
| } | |
| /* This is a hack. Should be handled in ink_types */ | |
| action = reinterpret_cast<TSAction>(reinterpret_cast<uintptr_t>(action) | 0x1); | |
| return action; | |
| } | |
| TSAction | |
| TSContScheduleOnThread(TSCont contp, TSHRTime timeout, TSEventThread ethread) | |
| { | |
| ink_release_assert(ethread != nullptr); | |
| sdk_assert(sdk_sanity_check_iocore_structure(contp) == TS_SUCCESS); | |
| FORCE_PLUGIN_SCOPED_MUTEX(contp); | |
| INKContInternal *i = reinterpret_cast<INKContInternal *>(contp); | |
| if (ink_atomic_increment(static_cast<int *>(&i->m_event_count), 1) < 0) { | |
| ink_assert(!"not reached"); | |
| } | |
| EThread *eth = reinterpret_cast<EThread *>(ethread); | |
| if (i->getThreadAffinity() == nullptr) { | |
| i->setThreadAffinity(eth); | |
| } | |
| TSAction action; | |
| if (timeout == 0) { | |
| action = reinterpret_cast<TSAction>(eth->schedule_imm(i)); | |
| } else { | |
| action = reinterpret_cast<TSAction>(eth->schedule_in(i, HRTIME_MSECONDS(timeout))); | |
| } | |
| /* This is a hack. Should be handled in ink_types */ | |
| action = reinterpret_cast<TSAction>(reinterpret_cast<uintptr_t>(action) | 0x1); | |
| return action; | |
| } | |
| std::vector<TSAction> | |
| TSContScheduleOnEntirePool(TSCont contp, TSHRTime timeout, TSThreadPool tp) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(contp) == TS_SUCCESS); | |
| /* ensure we are on a EThread */ | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)this_ethread()) == TS_SUCCESS); | |
| INKContInternal *i = reinterpret_cast<INKContInternal *>(contp); | |
| // This is to allow the continuation to be scheduled on multiple threads | |
| sdk_assert(i->mutex == nullptr); | |
| EventType etype; | |
| switch (tp) { | |
| case TS_THREAD_POOL_NET: | |
| etype = ET_NET; | |
| break; | |
| case TS_THREAD_POOL_TASK: | |
| etype = ET_TASK; | |
| break; | |
| case TS_THREAD_POOL_DNS: | |
| etype = ET_DNS; | |
| break; | |
| case TS_THREAD_POOL_UDP: | |
| etype = ET_UDP; | |
| break; | |
| default: | |
| etype = ET_TASK; | |
| break; | |
| } | |
| if (ink_atomic_increment(static_cast<int *>(&i->m_event_count), eventProcessor.thread_group[etype]._count) < 0) { | |
| ink_assert(!"not reached"); | |
| } | |
| return eventProcessor.schedule_entire(i, HRTIME_MSECONDS(timeout), 0, etype, timeout == 0 ? EVENT_IMMEDIATE : EVENT_INTERVAL); | |
| } | |
| TSAction | |
| TSContScheduleEveryOnPool(TSCont contp, TSHRTime every, TSThreadPool tp) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(contp) == TS_SUCCESS); | |
| /* ensure we are on a EThread */ | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)this_ethread()) == TS_SUCCESS); | |
| FORCE_PLUGIN_SCOPED_MUTEX(contp); | |
| INKContInternal *i = reinterpret_cast<INKContInternal *>(contp); | |
| if (ink_atomic_increment(static_cast<int *>(&i->m_event_count), 1) < 0) { | |
| ink_assert(!"not reached"); | |
| } | |
| EventType etype; | |
| switch (tp) { | |
| case TS_THREAD_POOL_NET: | |
| etype = ET_NET; | |
| break; | |
| case TS_THREAD_POOL_TASK: | |
| etype = ET_TASK; | |
| break; | |
| case TS_THREAD_POOL_DNS: | |
| etype = ET_DNS; | |
| break; | |
| case TS_THREAD_POOL_UDP: | |
| etype = ET_UDP; | |
| break; | |
| default: | |
| etype = ET_TASK; | |
| break; | |
| } | |
| TSAction action = reinterpret_cast<TSAction>(eventProcessor.schedule_every(i, HRTIME_MSECONDS(every), etype)); | |
| /* This is a hack. Should be handled in ink_types */ | |
| action = reinterpret_cast<TSAction>(reinterpret_cast<uintptr_t>(action) | 0x1); | |
| return action; | |
| } | |
| TSAction | |
| TSContScheduleEveryOnThread(TSCont contp, TSHRTime every, TSEventThread ethread) | |
| { | |
| ink_release_assert(ethread != nullptr); | |
| sdk_assert(sdk_sanity_check_iocore_structure(contp) == TS_SUCCESS); | |
| FORCE_PLUGIN_SCOPED_MUTEX(contp); | |
| INKContInternal *i = reinterpret_cast<INKContInternal *>(contp); | |
| if (ink_atomic_increment(static_cast<int *>(&i->m_event_count), 1) < 0) { | |
| ink_assert(!"not reached"); | |
| } | |
| EThread *eth = reinterpret_cast<EThread *>(ethread); | |
| if (i->getThreadAffinity() == nullptr) { | |
| i->setThreadAffinity(eth); | |
| } | |
| TSAction action = reinterpret_cast<TSAction>(eth->schedule_every(i, HRTIME_MSECONDS(every))); | |
| /* This is a hack. Should be handled in ink_types */ | |
| action = reinterpret_cast<TSAction>(reinterpret_cast<uintptr_t>(action) | 0x1); | |
| return action; | |
| } | |
| std::vector<TSAction> | |
| TSContScheduleEveryOnEntirePool(TSCont contp, TSHRTime every, TSThreadPool tp) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(contp) == TS_SUCCESS); | |
| /* ensure we are on a EThread */ | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)this_ethread()) == TS_SUCCESS); | |
| sdk_assert(every != 0); | |
| INKContInternal *i = reinterpret_cast<INKContInternal *>(contp); | |
| // This is to allow the continuation to be scheduled on multiple threads | |
| sdk_assert(i->mutex == nullptr); | |
| EventType etype; | |
| switch (tp) { | |
| case TS_THREAD_POOL_NET: | |
| etype = ET_NET; | |
| break; | |
| case TS_THREAD_POOL_TASK: | |
| etype = ET_TASK; | |
| break; | |
| case TS_THREAD_POOL_DNS: | |
| etype = ET_DNS; | |
| break; | |
| case TS_THREAD_POOL_UDP: | |
| etype = ET_UDP; | |
| break; | |
| default: | |
| etype = ET_TASK; | |
| break; | |
| } | |
| if (ink_atomic_increment(static_cast<int *>(&i->m_event_count), eventProcessor.thread_group[etype]._count) < 0) { | |
| ink_assert(!"not reached"); | |
| } | |
| return eventProcessor.schedule_entire(i, 0, HRTIME_MSECONDS(every), etype, EVENT_INTERVAL); | |
| } | |
| TSReturnCode | |
| TSContThreadAffinitySet(TSCont contp, TSEventThread ethread) | |
| { | |
| ink_release_assert(ethread != nullptr); | |
| sdk_assert(sdk_sanity_check_iocore_structure(contp) == TS_SUCCESS); | |
| FORCE_PLUGIN_SCOPED_MUTEX(contp); | |
| INKContInternal *i = reinterpret_cast<INKContInternal *>(contp); | |
| EThread *thread_affinity = reinterpret_cast<EThread *>(ethread); | |
| if (i->setThreadAffinity(thread_affinity)) { | |
| return TS_SUCCESS; | |
| } | |
| return TS_ERROR; | |
| } | |
| TSEventThread | |
| TSContThreadAffinityGet(TSCont contp) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(contp) == TS_SUCCESS); | |
| FORCE_PLUGIN_SCOPED_MUTEX(contp); | |
| INKContInternal *i = reinterpret_cast<INKContInternal *>(contp); | |
| return reinterpret_cast<TSEventThread>(i->getThreadAffinity()); | |
| } | |
| void | |
| TSContThreadAffinityClear(TSCont contp) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(contp) == TS_SUCCESS); | |
| FORCE_PLUGIN_SCOPED_MUTEX(contp); | |
| INKContInternal *i = reinterpret_cast<INKContInternal *>(contp); | |
| i->clearThreadAffinity(); | |
| } | |
| TSAction | |
| TSHttpSchedule(TSCont contp, TSHttpTxn txnp, TSHRTime timeout) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(contp) == TS_SUCCESS); | |
| FORCE_PLUGIN_SCOPED_MUTEX(contp); | |
| INKContInternal *i = reinterpret_cast<INKContInternal *>(contp); | |
| if (ink_atomic_increment(&i->m_event_count, 1) < 0) { | |
| ink_assert(!"not reached"); | |
| } | |
| TSAction action; | |
| Continuation *cont = reinterpret_cast<Continuation *>(contp); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| sm->set_http_schedule(cont); | |
| if (timeout == 0) { | |
| action = reinterpret_cast<TSAction>(eventProcessor.schedule_imm(sm, ET_NET)); | |
| } else { | |
| action = reinterpret_cast<TSAction>(eventProcessor.schedule_in(sm, HRTIME_MSECONDS(timeout), ET_NET)); | |
| } | |
| action = reinterpret_cast<TSAction>(reinterpret_cast<uintptr_t>(action) | 0x1); | |
| return action; | |
| } | |
| int | |
| TSContCall(TSCont contp, TSEvent event, void *edata) | |
| { | |
| Continuation *c = reinterpret_cast<Continuation *>(contp); | |
| WEAK_MUTEX_TRY_LOCK(lock, c->mutex, this_ethread()); | |
| if (!lock.is_locked()) { | |
| // If we cannot get the lock, the caller needs to restructure to handle rescheduling | |
| ink_release_assert(0); | |
| } | |
| return c->handleEvent(static_cast<int>(event), edata); | |
| } | |
| TSMutex | |
| TSContMutexGet(TSCont contp) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(contp) == TS_SUCCESS); | |
| Continuation *c = reinterpret_cast<Continuation *>(contp); | |
| return reinterpret_cast<TSMutex>(c->mutex.get()); | |
| } | |
| /* HTTP hooks */ | |
| void | |
| TSHttpHookAdd(TSHttpHookID id, TSCont contp) | |
| { | |
| INKContInternal *icontp; | |
| sdk_assert(sdk_sanity_check_continuation(contp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_hook_id(id) == TS_SUCCESS); | |
| icontp = reinterpret_cast<INKContInternal *>(contp); | |
| TSSslHookInternalID internalId{id}; | |
| if (internalId.is_in_bounds()) { | |
| SSLAPIHooks::instance()->append(internalId, icontp); | |
| } else { // Follow through the regular HTTP hook framework | |
| http_global_hooks->append(id, icontp); | |
| } | |
| } | |
| void | |
| TSLifecycleHookAdd(TSLifecycleHookID id, TSCont contp) | |
| { | |
| sdk_assert(sdk_sanity_check_continuation(contp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_lifecycle_hook_id(id) == TS_SUCCESS); | |
| g_lifecycle_hooks->append(id, reinterpret_cast<INKContInternal *>(contp)); | |
| } | |
| /* HTTP sessions */ | |
| void | |
| TSHttpSsnHookAdd(TSHttpSsn ssnp, TSHttpHookID id, TSCont contp) | |
| { | |
| sdk_assert(sdk_sanity_check_http_ssn(ssnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_continuation(contp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_hook_id(id) == TS_SUCCESS); | |
| ProxySession *cs = reinterpret_cast<ProxySession *>(ssnp); | |
| cs->hook_add(id, reinterpret_cast<INKContInternal *>(contp)); | |
| } | |
| int | |
| TSHttpSsnTransactionCount(TSHttpSsn ssnp) | |
| { | |
| sdk_assert(sdk_sanity_check_http_ssn(ssnp) == TS_SUCCESS); | |
| ProxySession *cs = reinterpret_cast<ProxySession *>(ssnp); | |
| return cs->get_transact_count(); | |
| } | |
| TSVConn | |
| TSHttpSsnClientVConnGet(TSHttpSsn ssnp) | |
| { | |
| ProxySession *cs = reinterpret_cast<ProxySession *>(ssnp); | |
| return reinterpret_cast<TSVConn>(cs->get_netvc()); | |
| } | |
| TSVConn | |
| TSHttpSsnServerVConnGet(TSHttpSsn ssnp) | |
| { | |
| PoolableSession *ss = reinterpret_cast<PoolableSession *>(ssnp); | |
| if (ss != nullptr) { | |
| return reinterpret_cast<TSVConn>(ss->get_netvc()); | |
| } | |
| return nullptr; | |
| } | |
| TSVConn | |
| TSHttpTxnServerVConnGet(TSHttpTxn txnp) | |
| { | |
| TSVConn vconn = nullptr; | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| if (sm != nullptr) { | |
| ProxyTransaction *st = sm->get_server_txn(); | |
| if (st != nullptr) { | |
| vconn = reinterpret_cast<TSVConn>(st->get_netvc()); | |
| } | |
| } | |
| return vconn; | |
| } | |
| class TSHttpSsnCallback : public Continuation | |
| { | |
| public: | |
| TSHttpSsnCallback(ProxySession *cs, Ptr<ProxyMutex> m, TSEvent event) : Continuation(m), m_cs(cs), m_event(event) | |
| { | |
| SET_HANDLER(&TSHttpSsnCallback::event_handler); | |
| } | |
| int | |
| event_handler(int, void *) | |
| { | |
| // The current continuation is associated with the nethandler mutex. | |
| // We need to hold the nethandler mutex because the later Session logic may | |
| // activate the nethandler add_to_queue logic | |
| // Need to make sure we have the ProxySession mutex as well. | |
| EThread *eth = this_ethread(); | |
| MUTEX_TRY_LOCK(trylock, m_cs->mutex, eth); | |
| if (!trylock.is_locked()) { | |
| eth->schedule_imm(this); | |
| } else { | |
| m_cs->handleEvent(static_cast<int>(m_event), nullptr); | |
| delete this; | |
| } | |
| return 0; | |
| } | |
| private: | |
| ProxySession *m_cs; | |
| TSEvent m_event; | |
| }; | |
| void | |
| TSHttpSsnReenable(TSHttpSsn ssnp, TSEvent event) | |
| { | |
| sdk_assert(sdk_sanity_check_http_ssn(ssnp) == TS_SUCCESS); | |
| ProxySession *cs = reinterpret_cast<ProxySession *>(ssnp); | |
| EThread *eth = this_ethread(); | |
| // If this function is being executed on a thread created by the API | |
| // which is DEDICATED, the continuation needs to be called back on a | |
| // REGULAR thread. Specially an ET_NET thread | |
| if (!eth->is_event_type(ET_NET)) { | |
| EThread *affinity_thread = cs->getThreadAffinity(); | |
| if (affinity_thread && affinity_thread->is_event_type(ET_NET)) { | |
| NetHandler *nh = get_NetHandler(affinity_thread); | |
| affinity_thread->schedule_imm(new TSHttpSsnCallback(cs, nh->mutex, event), ET_NET); | |
| } else { | |
| eventProcessor.schedule_imm(new TSHttpSsnCallback(cs, cs->mutex, event), ET_NET); | |
| } | |
| } else { | |
| MUTEX_TRY_LOCK(trylock, cs->mutex, eth); | |
| if (!trylock.is_locked()) { | |
| EThread *affinity_thread = cs->getThreadAffinity(); | |
| if (affinity_thread && affinity_thread->is_event_type(ET_NET)) { | |
| NetHandler *nh = get_NetHandler(affinity_thread); | |
| affinity_thread->schedule_imm(new TSHttpSsnCallback(cs, nh->mutex, event), ET_NET); | |
| } else { | |
| eventProcessor.schedule_imm(new TSHttpSsnCallback(cs, cs->mutex, event), ET_NET); | |
| } | |
| } else { | |
| cs->handleEvent(static_cast<int>(event), nullptr); | |
| } | |
| } | |
| } | |
| /* HTTP transactions */ | |
| void | |
| TSHttpTxnHookAdd(TSHttpTxn txnp, TSHttpHookID id, TSCont contp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_continuation(contp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_hook_id(id) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| APIHook *hook = sm->txn_hook_get(id); | |
| // Traverse list of hooks and add a particular hook only once | |
| while (hook != nullptr) { | |
| if (hook->m_cont == reinterpret_cast<INKContInternal *>(contp)) { | |
| return; | |
| } | |
| hook = hook->m_link.next; | |
| } | |
| sm->txn_hook_add(id, reinterpret_cast<INKContInternal *>(contp)); | |
| } | |
| TSHttpSsn | |
| TSHttpTxnSsnGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| return reinterpret_cast<TSHttpSsn>(sm->get_ua_txn() ? reinterpret_cast<TSHttpSsn>(sm->get_ua_txn()->get_proxy_ssn()) : nullptr); | |
| } | |
| TSReturnCode | |
| TSHttpTxnClientReqGet(TSHttpTxn txnp, TSMBuffer *bufp, TSMLoc *obj) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)obj) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| HTTPHdr *hptr = &(sm->t_state.hdr_info.client_request); | |
| if (hptr->valid()) { | |
| *(reinterpret_cast<HTTPHdr **>(bufp)) = hptr; | |
| *obj = reinterpret_cast<TSMLoc>(hptr->m_http); | |
| if (sdk_sanity_check_mbuffer(*bufp) == TS_SUCCESS) { | |
| hptr->mark_target_dirty(); | |
| return TS_SUCCESS; | |
| ; | |
| } | |
| } | |
| return TS_ERROR; | |
| } | |
| // pristine url is the url before remap | |
| TSReturnCode | |
| TSHttpTxnPristineUrlGet(TSHttpTxn txnp, TSMBuffer *bufp, TSMLoc *url_loc) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)url_loc) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| HTTPHdr *hptr = &(sm->t_state.hdr_info.client_request); | |
| if (hptr->valid()) { | |
| *(reinterpret_cast<HTTPHdr **>(bufp)) = hptr; | |
| *url_loc = reinterpret_cast<TSMLoc>(sm->t_state.unmapped_url.m_url_impl); | |
| if (sdk_sanity_check_mbuffer(*bufp) == TS_SUCCESS) { | |
| if (*url_loc == nullptr) { | |
| *url_loc = reinterpret_cast<TSMLoc>(hptr->m_http->u.req.m_url_impl); | |
| } | |
| if (*url_loc) { | |
| return TS_SUCCESS; | |
| } | |
| } | |
| } | |
| return TS_ERROR; | |
| } | |
| int | |
| TSHttpTxnServerSsnTransactionCount(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| // Any value greater than zero indicates connection reuse. | |
| return sm->server_transact_count; | |
| } | |
| // Shortcut to just get the URL. | |
| // The caller is responsible to free memory that is allocated for the string | |
| // that is returned. | |
| char * | |
| TSHttpTxnEffectiveUrlStringGet(TSHttpTxn txnp, int *length) | |
| { | |
| sdk_assert(TS_SUCCESS == sdk_sanity_check_txn(txnp)); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)length) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| return sm->t_state.hdr_info.client_request.url_string_get(nullptr, length); | |
| } | |
| TSReturnCode | |
| TSHttpHdrEffectiveUrlBufGet(TSMBuffer hdr_buf, TSMLoc hdr_loc, char *buf, int64_t size, int64_t *length) | |
| { | |
| sdk_assert(sdk_sanity_check_mbuffer(hdr_buf) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_http_hdr_handle(hdr_loc) == TS_SUCCESS); | |
| if (size) { | |
| sdk_assert(sdk_sanity_check_null_ptr(buf) == TS_SUCCESS); | |
| } | |
| sdk_assert(sdk_sanity_check_null_ptr(length) == TS_SUCCESS); | |
| auto buf_handle = reinterpret_cast<HTTPHdr *>(hdr_buf); | |
| auto hdr_handle = reinterpret_cast<HTTPHdrImpl *>(hdr_loc); | |
| if (hdr_handle->m_polarity != HTTPType::REQUEST) { | |
| Dbg(dbg_ctl_plugin, "Trying to get a URL from response header %p", hdr_loc); | |
| return TS_ERROR; | |
| } | |
| int url_length = buf_handle->url_printed_length(URLNormalize::LC_SCHEME_HOST | URLNormalize::IMPLIED_SCHEME); | |
| sdk_assert(url_length >= 0); | |
| *length = url_length; | |
| // If the user-provided buffer is too small to hold the URL string, do not put anything in it. This is not considered | |
| // an error case. | |
| // | |
| if (url_length <= size) { | |
| int index = 0; | |
| int offset = 0; | |
| buf_handle->url_print(buf, size, &index, &offset, URLNormalize::LC_SCHEME_HOST | URLNormalize::IMPLIED_SCHEME); | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnClientRespGet(TSHttpTxn txnp, TSMBuffer *bufp, TSMLoc *obj) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)obj) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| HTTPHdr *hptr = &(sm->t_state.hdr_info.client_response); | |
| if (hptr->valid()) { | |
| *(reinterpret_cast<HTTPHdr **>(bufp)) = hptr; | |
| *obj = reinterpret_cast<TSMLoc>(hptr->m_http); | |
| sdk_assert(sdk_sanity_check_mbuffer(*bufp) == TS_SUCCESS); | |
| return TS_SUCCESS; | |
| } | |
| return TS_ERROR; | |
| } | |
| TSReturnCode | |
| TSHttpTxnServerReqGet(TSHttpTxn txnp, TSMBuffer *bufp, TSMLoc *obj) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)obj) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| HTTPHdr *hptr = &(sm->t_state.hdr_info.server_request); | |
| if (hptr->valid()) { | |
| *(reinterpret_cast<HTTPHdr **>(bufp)) = hptr; | |
| *obj = reinterpret_cast<TSMLoc>(hptr->m_http); | |
| sdk_assert(sdk_sanity_check_mbuffer(*bufp) == TS_SUCCESS); | |
| return TS_SUCCESS; | |
| } | |
| return TS_ERROR; | |
| } | |
| TSReturnCode | |
| TSHttpTxnServerRespGet(TSHttpTxn txnp, TSMBuffer *bufp, TSMLoc *obj) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)obj) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| HTTPHdr *hptr = &(sm->t_state.hdr_info.server_response); | |
| if (hptr->valid()) { | |
| *(reinterpret_cast<HTTPHdr **>(bufp)) = hptr; | |
| *obj = reinterpret_cast<TSMLoc>(hptr->m_http); | |
| sdk_assert(sdk_sanity_check_mbuffer(*bufp) == TS_SUCCESS); | |
| return TS_SUCCESS; | |
| } | |
| return TS_ERROR; | |
| } | |
| TSReturnCode | |
| TSHttpTxnCachedReqGet(TSHttpTxn txnp, TSMBuffer *bufp, TSMLoc *obj) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)obj) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| HTTPInfo *cached_obj = sm->t_state.cache_info.object_read; | |
| // The following check is need to prevent the HttpSM handle copy from going bad | |
| // Since the cache manages the header buffer, sm->t_state.cache_info.object_read | |
| // is the only way to tell if handle has gone bad. | |
| if ((!cached_obj) || (!cached_obj->valid())) { | |
| return TS_ERROR; | |
| } | |
| HTTPHdr *cached_hdr = sm->t_state.cache_info.object_read->request_get(); | |
| if (!cached_hdr->valid()) { | |
| return TS_ERROR; | |
| } | |
| // We can't use the HdrHeapSDKHandle structure in the RamCache since multiple | |
| // threads can access. We need to create our own for the transaction and return that. | |
| HdrHeapSDKHandle **handle = &(sm->t_state.cache_req_hdr_heap_handle); | |
| if (*handle == nullptr) { | |
| *handle = static_cast<HdrHeapSDKHandle *>(sm->t_state.arena.alloc(sizeof(HdrHeapSDKHandle))); | |
| (*handle)->m_heap = cached_hdr->m_heap; | |
| } | |
| *(reinterpret_cast<HdrHeapSDKHandle **>(bufp)) = *handle; | |
| *obj = reinterpret_cast<TSMLoc>(cached_hdr->m_http); | |
| sdk_assert(sdk_sanity_check_mbuffer(*bufp) == TS_SUCCESS); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnCachedRespGet(TSHttpTxn txnp, TSMBuffer *bufp, TSMLoc *obj) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)obj) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| HTTPInfo *cached_obj = sm->t_state.cache_info.object_read; | |
| // The following check is need to prevent the HttpSM handle copy from going bad | |
| // Since the cache manages the header buffer, sm->t_state.cache_info.object_read | |
| // is the only way to tell if handle has gone bad. | |
| if ((!cached_obj) || (!cached_obj->valid())) { | |
| return TS_ERROR; | |
| } | |
| HTTPHdr *cached_hdr = sm->t_state.cache_info.object_read->response_get(); | |
| if (!cached_hdr->valid()) { | |
| return TS_ERROR; | |
| } | |
| // We can't use the HdrHeapSDKHandle structure in the RamCache since multiple | |
| // threads can access. We need to create our own for the transaction and return that. | |
| HdrHeapSDKHandle **handle = &(sm->t_state.cache_resp_hdr_heap_handle); | |
| if (*handle == nullptr) { | |
| *handle = static_cast<HdrHeapSDKHandle *>(sm->t_state.arena.alloc(sizeof(HdrHeapSDKHandle))); | |
| } | |
| // Always reset the m_heap to make sure the heap is not stale | |
| (*handle)->m_heap = cached_hdr->m_heap; | |
| *(reinterpret_cast<HdrHeapSDKHandle **>(bufp)) = *handle; | |
| *obj = reinterpret_cast<TSMLoc>(cached_hdr->m_http); | |
| sdk_assert(sdk_sanity_check_mbuffer(*bufp) == TS_SUCCESS); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnCachedRespModifiableGet(TSHttpTxn txnp, TSMBuffer *bufp, TSMLoc *obj) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)obj) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| HttpTransact::State *s = &(sm->t_state); | |
| HTTPHdr *c_resp = nullptr; | |
| HTTPInfo *cached_obj = sm->t_state.cache_info.object_read; | |
| HTTPInfo *cached_obj_store = &(sm->t_state.cache_info.object_store); | |
| if ((!cached_obj) || (!cached_obj->valid())) { | |
| return TS_ERROR; | |
| } | |
| if (!cached_obj_store->valid()) { | |
| cached_obj_store->create(); | |
| } | |
| c_resp = cached_obj_store->response_get(); | |
| if (!c_resp->valid()) { | |
| cached_obj_store->response_set(cached_obj->response_get()); | |
| } | |
| c_resp = cached_obj_store->response_get(); | |
| s->api_modifiable_cached_resp = true; | |
| ink_assert(c_resp != nullptr && c_resp->valid()); | |
| *(reinterpret_cast<HTTPHdr **>(bufp)) = c_resp; | |
| *obj = reinterpret_cast<TSMLoc>(c_resp->m_http); | |
| sdk_assert(sdk_sanity_check_mbuffer(*bufp) == TS_SUCCESS); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnCacheLookupStatusGet(TSHttpTxn txnp, int *lookup_status) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)lookup_status) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| switch (sm->t_state.cache_lookup_result) { | |
| case HttpTransact::CacheLookupResult_t::MISS: | |
| case HttpTransact::CacheLookupResult_t::DOC_BUSY: | |
| *lookup_status = TS_CACHE_LOOKUP_MISS; | |
| break; | |
| case HttpTransact::CacheLookupResult_t::HIT_STALE: | |
| *lookup_status = TS_CACHE_LOOKUP_HIT_STALE; | |
| break; | |
| case HttpTransact::CacheLookupResult_t::HIT_WARNING: | |
| case HttpTransact::CacheLookupResult_t::HIT_FRESH: | |
| *lookup_status = TS_CACHE_LOOKUP_HIT_FRESH; | |
| break; | |
| case HttpTransact::CacheLookupResult_t::SKIPPED: | |
| *lookup_status = TS_CACHE_LOOKUP_SKIPPED; | |
| break; | |
| case HttpTransact::CacheLookupResult_t::NONE: | |
| default: | |
| return TS_ERROR; | |
| }; | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnCacheLookupCountGet(TSHttpTxn txnp, int *lookup_count) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)lookup_count) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| *lookup_count = sm->t_state.cache_info.lookup_count; | |
| return TS_SUCCESS; | |
| } | |
| /* two hooks this function may gets called: | |
| TS_HTTP_READ_CACHE_HDR_HOOK & | |
| TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK | |
| */ | |
| TSReturnCode | |
| TSHttpTxnCacheLookupStatusSet(TSHttpTxn txnp, int cachelookup) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| HttpTransact::CacheLookupResult_t *sm_status = &(sm->t_state.cache_lookup_result); | |
| // converting from a miss to a hit is not allowed | |
| if (*sm_status == HttpTransact::CacheLookupResult_t::MISS && cachelookup != TS_CACHE_LOOKUP_MISS) { | |
| return TS_ERROR; | |
| } | |
| // here is to handle converting a hit to a miss | |
| if (cachelookup == TS_CACHE_LOOKUP_MISS && *sm_status != HttpTransact::CacheLookupResult_t::MISS) { | |
| sm->t_state.api_cleanup_cache_read = true; | |
| ink_assert(sm->t_state.transact_return_point != nullptr); | |
| sm->t_state.transact_return_point = HttpTransact::HandleCacheOpenRead; | |
| } | |
| switch (cachelookup) { | |
| case TS_CACHE_LOOKUP_MISS: | |
| *sm_status = HttpTransact::CacheLookupResult_t::MISS; | |
| break; | |
| case TS_CACHE_LOOKUP_HIT_STALE: | |
| *sm_status = HttpTransact::CacheLookupResult_t::HIT_STALE; | |
| break; | |
| case TS_CACHE_LOOKUP_HIT_FRESH: | |
| *sm_status = HttpTransact::CacheLookupResult_t::HIT_FRESH; | |
| break; | |
| default: | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnVerifiedAddrSet(TSHttpTxn txnp, const struct sockaddr *addr) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)addr) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| ProxyTransaction *prxtxn = sm->get_ua_txn(); | |
| prxtxn->set_verified_client_addr(addr); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnVerifiedAddrGet(TSHttpTxn txnp, const struct sockaddr **addr) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)addr) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| ProxyTransaction *prxtxn = sm->get_ua_txn(); | |
| *addr = prxtxn->get_verified_client_addr(); | |
| if ((*addr)->sa_family == AF_UNSPEC) { | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnInfoIntGet(TSHttpTxn txnp, TSHttpTxnInfoKey key, TSMgmtInt *value) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)value) == TS_SUCCESS); | |
| HttpSM *s = reinterpret_cast<HttpSM *>(txnp); | |
| HttpCacheSM *c_sm = &(s->get_cache_sm()); | |
| switch (key) { | |
| case TS_TXN_INFO_CACHE_HIT_RAM: | |
| *value = (static_cast<TSMgmtInt>(c_sm->is_ram_cache_hit())); | |
| break; | |
| case TS_TXN_INFO_CACHE_COMPRESSED_IN_RAM: | |
| *value = (static_cast<TSMgmtInt>(c_sm->is_compressed_in_ram())); | |
| break; | |
| case TS_TXN_INFO_CACHE_HIT_RWW: | |
| *value = (static_cast<TSMgmtInt>(c_sm->is_readwhilewrite_inprogress())); | |
| break; | |
| case TS_TXN_INFO_CACHE_OPEN_READ_TRIES: | |
| *value = (static_cast<TSMgmtInt>(c_sm->get_open_read_tries())); | |
| break; | |
| case TS_TXN_INFO_CACHE_OPEN_WRITE_TRIES: | |
| *value = (static_cast<TSMgmtInt>(c_sm->get_open_write_tries())); | |
| break; | |
| case TS_TXN_INFO_CACHE_VOLUME: | |
| *value = (static_cast<TSMgmtInt>(c_sm->get_volume_number())); | |
| break; | |
| default: | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpSsnInfoIntGet(TSHttpSsn ssnp, TSHttpSsnInfoKey key, TSMgmtInt *value, uint64_t sub_key) | |
| { | |
| sdk_assert(sdk_sanity_check_http_ssn(ssnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)value) == TS_SUCCESS); | |
| ProxySession *ssn = reinterpret_cast<ProxySession *>(ssnp); | |
| switch (key) { | |
| case TS_SSN_INFO_TRANSACTION_COUNT: | |
| *value = ssn->get_transact_count(); | |
| break; | |
| case TS_SSN_INFO_RECEIVED_FRAME_COUNT: | |
| if (!ssn->is_protocol_framed()) { | |
| return TS_ERROR; | |
| } | |
| *value = ssn->get_received_frame_count(sub_key); | |
| break; | |
| default: | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| int | |
| TSHttpTxnIsWebsocket(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| return sm->t_state.is_websocket; | |
| } | |
| const char * | |
| TSHttpTxnCacheDiskPathGet(TSHttpTxn txnp, int *length) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| char const *path = nullptr; | |
| if (HttpCacheSM *c_sm = &(sm->get_cache_sm()); c_sm) { | |
| path = c_sm->get_disk_path(); | |
| } | |
| if (length) { | |
| *length = path ? strlen(path) : 0; | |
| } | |
| return path; | |
| } | |
| TSReturnCode | |
| TSHttpTxnCacheLookupUrlGet(TSHttpTxn txnp, TSMBuffer bufp, TSMLoc obj) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_url_handle(obj) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| URL u, *l_url; | |
| u.m_heap = (reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap; | |
| u.m_url_impl = reinterpret_cast<URLImpl *>(obj); | |
| if (!u.valid()) { | |
| return TS_ERROR; | |
| } | |
| l_url = sm->t_state.cache_info.lookup_url; | |
| if (l_url && l_url->valid()) { | |
| u.copy(l_url); | |
| return TS_SUCCESS; | |
| } | |
| return TS_ERROR; | |
| } | |
| TSReturnCode | |
| TSHttpTxnCacheLookupUrlSet(TSHttpTxn txnp, TSMBuffer bufp, TSMLoc obj) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_url_handle(obj) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| URL u, *l_url; | |
| u.m_heap = (reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap; | |
| u.m_url_impl = reinterpret_cast<URLImpl *>(obj); | |
| if (!u.valid()) { | |
| return TS_ERROR; | |
| } | |
| l_url = sm->t_state.cache_info.lookup_url; | |
| if (!l_url) { | |
| sm->t_state.cache_info.lookup_url_storage.create(nullptr); | |
| sm->t_state.cache_info.lookup_url = &(sm->t_state.cache_info.lookup_url_storage); | |
| l_url = sm->t_state.cache_info.lookup_url; | |
| } | |
| if (!l_url || !l_url->valid()) { | |
| return TS_ERROR; | |
| } else { | |
| l_url->copy(&u); | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnCacheKeyDigestGet(TSHttpTxn txnp, char *buffer, int *length) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(length != nullptr); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| const CryptoHash &hash = sm->get_cache_sm().get_cache_key().hash; | |
| constexpr int size = CRYPTO_HASH_SIZE; | |
| int provided_length = *length; | |
| *length = size; | |
| if (hash.is_zero()) { | |
| return TS_ERROR; | |
| } | |
| if (buffer == nullptr) { | |
| return TS_SUCCESS; | |
| } | |
| if (provided_length < size) { | |
| return TS_ERROR; | |
| } | |
| memcpy(buffer, hash.u8, size); | |
| return TS_SUCCESS; | |
| } | |
| /** | |
| * timeout is in msec | |
| * overrides as proxy.config.http.transaction_active_timeout_out | |
| **/ | |
| void | |
| TSHttpTxnActiveTimeoutSet(TSHttpTxn txnp, int timeout) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpTransact::State *s = &((reinterpret_cast<HttpSM *>(txnp))->t_state); | |
| s->api_txn_active_timeout_value = timeout; | |
| } | |
| /** | |
| * timeout is in msec | |
| * overrides as proxy.config.http.connect_attempts_timeout | |
| **/ | |
| void | |
| TSHttpTxnConnectTimeoutSet(TSHttpTxn txnp, int timeout) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpTransact::State *s = &((reinterpret_cast<HttpSM *>(txnp))->t_state); | |
| s->api_txn_connect_timeout_value = timeout; | |
| } | |
| /** | |
| * timeout is in msec | |
| * overrides as proxy.config.dns.lookup_timeout | |
| **/ | |
| void | |
| TSHttpTxnDNSTimeoutSet(TSHttpTxn txnp, int timeout) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpTransact::State *s = &((reinterpret_cast<HttpSM *>(txnp))->t_state); | |
| s->api_txn_dns_timeout_value = timeout; | |
| } | |
| /** | |
| * timeout is in msec | |
| * overrides as proxy.config.http.transaction_no_activity_timeout_out | |
| **/ | |
| void | |
| TSHttpTxnNoActivityTimeoutSet(TSHttpTxn txnp, int timeout) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpTransact::State *s = &((reinterpret_cast<HttpSM *>(txnp))->t_state); | |
| s->api_txn_no_activity_timeout_value = timeout; | |
| } | |
| TSReturnCode | |
| TSHttpTxnServerRespNoStoreSet(TSHttpTxn txnp, int flag) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpTransact::State *s = &((reinterpret_cast<HttpSM *>(txnp))->t_state); | |
| s->api_server_response_no_store = (flag != 0); | |
| return TS_SUCCESS; | |
| } | |
| bool | |
| TSHttpTxnServerRespNoStoreGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpTransact::State *s = &((reinterpret_cast<HttpSM *>(txnp))->t_state); | |
| return s->api_server_response_no_store; | |
| } | |
| TSReturnCode | |
| TSHttpTxnServerRespIgnore(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpTransact::State *s = &((reinterpret_cast<HttpSM *>(txnp))->t_state); | |
| HTTPInfo *cached_obj = s->cache_info.object_read; | |
| HTTPHdr *cached_resp; | |
| if (cached_obj == nullptr || !cached_obj->valid()) { | |
| return TS_ERROR; | |
| } | |
| cached_resp = cached_obj->response_get(); | |
| if (cached_resp == nullptr || !cached_resp->valid()) { | |
| return TS_ERROR; | |
| } | |
| s->api_server_response_ignore = true; | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnShutDown(TSHttpTxn txnp, TSEvent event) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| if (event == TS_EVENT_HTTP_TXN_CLOSE) { | |
| return TS_ERROR; | |
| } | |
| HttpTransact::State *s = &((reinterpret_cast<HttpSM *>(txnp))->t_state); | |
| s->api_http_sm_shutdown = true; | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnAborted(TSHttpTxn txnp, bool *client_abort) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(client_abort != nullptr); | |
| *client_abort = false; | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| switch (sm->t_state.squid_codes.log_code) { | |
| case SquidLogCode::ERR_CLIENT_ABORT: | |
| case SquidLogCode::ERR_CLIENT_READ_ERROR: | |
| case SquidLogCode::TCP_SWAPFAIL: | |
| // check for client abort and cache read error | |
| *client_abort = true; | |
| return TS_SUCCESS; | |
| default: | |
| break; | |
| } | |
| if (sm->t_state.current.server && sm->t_state.current.server->abort == HttpTransact::ABORTED) { | |
| // check for the server abort | |
| return TS_SUCCESS; | |
| } | |
| // there can be the case of transformation error. | |
| return TS_ERROR; | |
| } | |
| void | |
| TSHttpTxnReqCacheableSet(TSHttpTxn txnp, int flag) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| sm->t_state.api_req_cacheable = (flag != 0); | |
| } | |
| void | |
| TSHttpTxnRespCacheableSet(TSHttpTxn txnp, int flag) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| sm->t_state.api_resp_cacheable = (flag != 0); | |
| } | |
| int | |
| TSHttpTxnClientReqIsServerStyle(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| return (sm->t_state.hdr_info.client_req_is_server_style ? 1 : 0); | |
| } | |
| TSReturnCode | |
| TSHttpTxnUpdateCachedObject(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| HttpTransact::State *s = &(sm->t_state); | |
| HTTPInfo *cached_obj_store = &(sm->t_state.cache_info.object_store); | |
| HTTPHdr *client_request = &(sm->t_state.hdr_info.client_request); | |
| if (!cached_obj_store->valid() || !cached_obj_store->response_get()) { | |
| return TS_ERROR; | |
| } | |
| if (!cached_obj_store->request_get() && !client_request->valid()) { | |
| return TS_ERROR; | |
| } | |
| if (s->cache_info.write_lock_state == HttpTransact::CacheWriteLock_t::READ_RETRY) { | |
| return TS_ERROR; | |
| } | |
| s->api_update_cached_object = HttpTransact::UpdateCachedObject_t::PREPARE; | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnTransformRespGet(TSHttpTxn txnp, TSMBuffer *bufp, TSMLoc *obj) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| HTTPHdr *hptr = &(sm->t_state.hdr_info.transform_response); | |
| if (hptr->valid()) { | |
| *(reinterpret_cast<HTTPHdr **>(bufp)) = hptr; | |
| *obj = reinterpret_cast<TSMLoc>(hptr->m_http); | |
| return sdk_sanity_check_mbuffer(*bufp); | |
| } | |
| return TS_ERROR; | |
| } | |
| sockaddr const * | |
| TSHttpSsnClientAddrGet(TSHttpSsn ssnp) | |
| { | |
| ProxySession *cs = reinterpret_cast<ProxySession *>(ssnp); | |
| if (cs == nullptr) { | |
| return nullptr; | |
| } | |
| return cs->get_client_addr(); | |
| } | |
| sockaddr const * | |
| TSHttpTxnClientAddrGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| TSHttpSsn ssnp = TSHttpTxnSsnGet(txnp); | |
| return TSHttpSsnClientAddrGet(ssnp); | |
| } | |
| sockaddr const * | |
| TSHttpSsnIncomingAddrGet(TSHttpSsn ssnp) | |
| { | |
| ProxySession *cs = reinterpret_cast<ProxySession *>(ssnp); | |
| if (cs == nullptr) { | |
| return nullptr; | |
| } | |
| return cs->get_local_addr(); | |
| } | |
| sockaddr const * | |
| TSHttpTxnIncomingAddrGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| TSHttpSsn ssnp = TSHttpTxnSsnGet(txnp); | |
| return TSHttpSsnIncomingAddrGet(ssnp); | |
| } | |
| sockaddr const * | |
| TSHttpTxnOutgoingAddrGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| const sockaddr *retval = nullptr; | |
| NetVConnection *vc = nullptr; | |
| ProxyTransaction *ssn = sm->get_server_txn(); | |
| if (ssn == nullptr) { | |
| vc = sm->get_server_vc(); | |
| } else { | |
| vc = ssn->get_netvc(); | |
| } | |
| if (vc != nullptr) { | |
| retval = vc->get_local_addr(); | |
| } | |
| return retval; | |
| } | |
| sockaddr const * | |
| TSHttpTxnServerAddrGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| return &sm->t_state.server_info.dst_addr.sa; | |
| } | |
| TSReturnCode | |
| TSHttpTxnServerAddrSet(TSHttpTxn txnp, struct sockaddr const *addr) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| if (sm->t_state.dns_info.set_upstream_address(addr)) { | |
| sm->t_state.dns_info.os_addr_style = ResolveInfo::OS_Addr::USE_API; | |
| return TS_SUCCESS; | |
| } | |
| return TS_ERROR; | |
| } | |
| void | |
| TSHttpTxnClientIncomingPortSet(TSHttpTxn txnp, int port) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| sm->t_state.client_info.dst_addr.network_order_port() = htons(port); | |
| } | |
| // [amc] This might use the port. The code path should do that but it | |
| // hasn't been tested. | |
| TSReturnCode | |
| TSHttpTxnOutgoingAddrSet(TSHttpTxn txnp, const struct sockaddr *addr) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| sm->get_ua_txn()->upstream_outbound_options.outbound_port = ats_ip_port_host_order(addr); | |
| sm->get_ua_txn()->set_outbound_ip(swoc::IPAddr(addr)); | |
| return TS_SUCCESS; | |
| } | |
| sockaddr const * | |
| TSHttpTxnNextHopAddrGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| /** | |
| * Return zero if the server structure is not yet constructed. | |
| */ | |
| if (sm->t_state.current.server == nullptr) { | |
| return nullptr; | |
| } | |
| return &sm->t_state.current.server->dst_addr.sa; | |
| } | |
| const char * | |
| TSHttpTxnNextHopNameGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| /** | |
| * Return zero if the server structure is not yet constructed. | |
| */ | |
| if (sm->t_state.current.server == nullptr) { | |
| return nullptr; | |
| } | |
| return sm->t_state.current.server->name; | |
| } | |
| int | |
| TSHttpTxnNextHopPortGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| auto sm = reinterpret_cast<HttpSM const *>(txnp); | |
| /** | |
| * Return -1 if the server structure is not yet constructed. | |
| */ | |
| if (nullptr == sm->t_state.current.server) { | |
| return -1; | |
| } | |
| return sm->t_state.current.server->dst_addr.host_order_port(); | |
| } | |
| TSReturnCode | |
| TSHttpTxnOutgoingTransparencySet(TSHttpTxn txnp, int flag) | |
| { | |
| if (TS_SUCCESS != sdk_sanity_check_txn(txnp)) { | |
| return TS_ERROR; | |
| } | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| if (nullptr == sm || nullptr == sm->get_ua_txn()) { | |
| return TS_ERROR; | |
| } | |
| sm->get_ua_txn()->set_outbound_transparent(flag); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnClientPacketMarkSet(TSHttpTxn txnp, int mark) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| if (nullptr == sm->get_ua_txn()) { | |
| return TS_ERROR; | |
| } | |
| NetVConnection *vc = sm->get_ua_txn()->get_netvc(); | |
| if (nullptr == vc) { | |
| return TS_ERROR; | |
| } | |
| vc->options.packet_mark = static_cast<uint32_t>(mark); | |
| vc->apply_options(); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnServerPacketMarkSet(TSHttpTxn txnp, int mark) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| // change the mark on an active server session | |
| ProxyTransaction *ssn = sm->get_server_txn(); | |
| if (nullptr != ssn) { | |
| NetVConnection *vc = ssn->get_netvc(); | |
| if (vc != nullptr) { | |
| vc->options.packet_mark = static_cast<uint32_t>(mark); | |
| vc->apply_options(); | |
| } | |
| } | |
| // update the transactions mark config for future connections | |
| TSHttpTxnConfigIntSet(txnp, TS_CONFIG_NET_SOCK_PACKET_MARK_OUT, mark); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnClientPacketDscpSet(TSHttpTxn txnp, int dscp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| if (nullptr == sm->get_ua_txn()) { | |
| return TS_ERROR; | |
| } | |
| NetVConnection *vc = sm->get_ua_txn()->get_netvc(); | |
| if (nullptr == vc) { | |
| return TS_ERROR; | |
| } | |
| vc->options.packet_tos = static_cast<uint32_t>(dscp) << 2; | |
| vc->apply_options(); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnServerPacketDscpSet(TSHttpTxn txnp, int dscp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| // change the tos on an active server session | |
| ProxyTransaction *ssn = sm->get_server_txn(); | |
| if (nullptr != ssn) { | |
| NetVConnection *vc = ssn->get_netvc(); | |
| if (vc != nullptr) { | |
| vc->options.packet_tos = static_cast<uint32_t>(dscp) << 2; | |
| vc->apply_options(); | |
| } | |
| } | |
| // update the transactions mark config for future connections | |
| TSHttpTxnConfigIntSet(txnp, TS_CONFIG_NET_SOCK_PACKET_TOS_OUT, dscp << 2); | |
| return TS_SUCCESS; | |
| } | |
| // Set the body, or, if you provide a null buffer, clear the body message | |
| void | |
| TSHttpTxnErrorBodySet(TSHttpTxn txnp, char *buf, size_t buflength, char *mimetype) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| HttpTransact::State *s = &(sm->t_state); | |
| // Cleanup anything already set. | |
| s->free_internal_msg_buffer(); | |
| ats_free(s->internal_msg_buffer_type); | |
| s->internal_msg_buffer = buf; | |
| s->internal_msg_buffer_size = buf ? buflength : 0; | |
| s->internal_msg_buffer_fast_allocator_size = -1; | |
| s->internal_msg_buffer_type = mimetype; | |
| } | |
| char * | |
| TSHttpTxnErrorBodyGet(TSHttpTxn txnp, size_t *buflength, char **mimetype) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| HttpTransact::State *s = &(sm->t_state); | |
| if (buflength) { | |
| *buflength = s->internal_msg_buffer_size; | |
| } | |
| if (mimetype) { | |
| *mimetype = s->internal_msg_buffer_type; | |
| } | |
| return s->internal_msg_buffer; | |
| } | |
| void | |
| TSHttpTxnServerRequestBodySet(TSHttpTxn txnp, char *buf, int64_t buflength) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| HttpTransact::State *s = &(sm->t_state); | |
| // Cleanup anything already set. | |
| s->free_internal_msg_buffer(); | |
| if (buf) { | |
| s->api_server_request_body_set = true; | |
| s->internal_msg_buffer = buf; | |
| s->internal_msg_buffer_size = buflength; | |
| } else { | |
| s->api_server_request_body_set = false; | |
| s->internal_msg_buffer = nullptr; | |
| s->internal_msg_buffer_size = 0; | |
| } | |
| s->internal_msg_buffer_fast_allocator_size = -1; | |
| } | |
| void const * | |
| TSHttpTxnNextHopStrategyGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| auto sm = reinterpret_cast<HttpSM const *>(txnp); | |
| return static_cast<void *>(sm->t_state.next_hop_strategy); | |
| } | |
| void | |
| TSHttpTxnNextHopStrategySet(TSHttpTxn txnp, void const *stratptr) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| // null strategy falls back to parent.config | |
| // sdk_assert(sdk_sanity_check_null_ptr(strategy) == TS_SUCCESS); | |
| auto sm = reinterpret_cast<HttpSM *>(txnp); | |
| auto strategy = reinterpret_cast<NextHopSelectionStrategy const *>(stratptr); | |
| sm->t_state.next_hop_strategy = const_cast<NextHopSelectionStrategy *>(strategy); | |
| } | |
| char const * | |
| TSHttpNextHopStrategyNameGet(void const *stratptr) | |
| { | |
| char const *name = nullptr; | |
| if (nullptr != stratptr) { | |
| auto strategy = reinterpret_cast<NextHopSelectionStrategy const *>(stratptr); | |
| name = strategy->strategy_name.c_str(); | |
| } | |
| return name; | |
| } | |
| void const * | |
| TSHttpTxnNextHopNamedStrategyGet(TSHttpTxn txnp, const char *name) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)name) == TS_SUCCESS); | |
| auto sm = reinterpret_cast<HttpSM const *>(txnp); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)sm->m_remap.get()) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)sm->m_remap->strategyFactory) == TS_SUCCESS); | |
| // HttpSM has a reference count handle to UrlRewrite which has a | |
| // pointer to NextHopStrategyFactory | |
| NextHopSelectionStrategy const *const strat = sm->m_remap->strategyFactory->strategyInstance(name); | |
| return static_cast<void const *>(strat); | |
| } | |
| TSReturnCode | |
| TSHttpTxnParentProxyGet(TSHttpTxn txnp, const char **hostname, int *port) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| *hostname = sm->t_state.api_info.parent_proxy_name; | |
| *port = sm->t_state.api_info.parent_proxy_port; | |
| return TS_SUCCESS; | |
| } | |
| void | |
| TSHttpTxnParentProxySet(TSHttpTxn txnp, const char *hostname, int port) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)hostname) == TS_SUCCESS); | |
| sdk_assert(port > 0); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| sm->t_state.api_info.parent_proxy_name = sm->t_state.arena.str_store(hostname, strlen(hostname)); | |
| sm->t_state.api_info.parent_proxy_port = port; | |
| } | |
| TSReturnCode | |
| TSHttpTxnParentSelectionUrlGet(TSHttpTxn txnp, TSMBuffer bufp, TSMLoc obj) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_url_handle(obj) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| URL u, *l_url; | |
| u.m_heap = (reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap; | |
| u.m_url_impl = reinterpret_cast<URLImpl *>(obj); | |
| if (!u.valid()) { | |
| return TS_ERROR; | |
| } | |
| l_url = sm->t_state.cache_info.parent_selection_url; | |
| if (l_url && l_url->valid()) { | |
| u.copy(l_url); | |
| return TS_SUCCESS; | |
| } | |
| return TS_ERROR; | |
| } | |
| TSReturnCode | |
| TSHttpTxnParentSelectionUrlSet(TSHttpTxn txnp, TSMBuffer bufp, TSMLoc obj) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_mbuffer(bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_url_handle(obj) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| URL u, *l_url; | |
| u.m_heap = (reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap; | |
| u.m_url_impl = reinterpret_cast<URLImpl *>(obj); | |
| if (!u.valid()) { | |
| return TS_ERROR; | |
| } | |
| l_url = sm->t_state.cache_info.parent_selection_url; | |
| if (!l_url) { | |
| sm->t_state.cache_info.parent_selection_url_storage.create(nullptr); | |
| sm->t_state.cache_info.parent_selection_url = &(sm->t_state.cache_info.parent_selection_url_storage); | |
| l_url = sm->t_state.cache_info.parent_selection_url; | |
| } | |
| if (!l_url || !l_url->valid()) { | |
| return TS_ERROR; | |
| } else { | |
| l_url->copy(&u); | |
| } | |
| Dbg(dbg_ctl_parent_select, "TSHttpTxnParentSelectionUrlSet() parent_selection_url : addr = %p val = %p", | |
| &(sm->t_state.cache_info.parent_selection_url), sm->t_state.cache_info.parent_selection_url); | |
| return TS_SUCCESS; | |
| } | |
| void | |
| TSHttpTxnUntransformedRespCache(TSHttpTxn txnp, int on) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| sm->t_state.api_info.cache_untransformed = (on ? true : false); | |
| } | |
| void | |
| TSHttpTxnTransformedRespCache(TSHttpTxn txnp, int on) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| sm->t_state.api_info.cache_transformed = (on ? true : false); | |
| } | |
| class TSHttpSMCallback : public Continuation | |
| { | |
| public: | |
| TSHttpSMCallback(HttpSM *sm, TSEvent event) : Continuation(sm->mutex), m_sm(sm), m_event(event) | |
| { | |
| SET_HANDLER(&TSHttpSMCallback::event_handler); | |
| } | |
| int | |
| event_handler(int, void *) | |
| { | |
| m_sm->state_api_callback(static_cast<int>(m_event), nullptr); | |
| delete this; | |
| return 0; | |
| } | |
| private: | |
| HttpSM *m_sm; | |
| TSEvent m_event; | |
| }; | |
| //---------------------------------------------------------------------------- | |
| void | |
| TSHttpTxnReenable(TSHttpTxn txnp, TSEvent event) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| EThread *eth = this_ethread(); | |
| // TS-2271: If this function is being executed on a thread which was not | |
| // created using the ATS EThread API, eth will be null, and the | |
| // continuation needs to be called back on a REGULAR thread. | |
| // | |
| // If we are not coming from the thread associated with the state machine, | |
| // reschedule. Also reschedule if we cannot get the state machine lock. | |
| if (eth != nullptr && sm->getThreadAffinity() == eth) { | |
| MUTEX_TRY_LOCK(trylock, sm->mutex, eth); | |
| if (trylock.is_locked()) { | |
| ink_assert(eth->is_event_type(ET_NET)); | |
| sm->state_api_callback(static_cast<int>(event), nullptr); | |
| return; | |
| } | |
| } | |
| // Couldn't call the handler directly, schedule to the original SM thread | |
| TSHttpSMCallback *cb = new TSHttpSMCallback(sm, event); | |
| cb->setThreadAffinity(sm->getThreadAffinity()); | |
| eventProcessor.schedule_imm(cb, ET_NET); | |
| } | |
| TSReturnCode TSUserArgIndexNameLookup(TSUserArgType type, const char *name, int *arg_idx, const char **description); | |
| TSReturnCode | |
| TSUserArgIndexReserve(TSUserArgType type, const char *name, const char *description, int *ptr_idx) | |
| { | |
| sdk_assert(sdk_sanity_check_null_ptr(ptr_idx) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr(name) == TS_SUCCESS); | |
| sdk_assert(0 <= type && type < TS_USER_ARGS_COUNT); | |
| int idx; | |
| /* Since this function is meant to be called during plugin initialization we could end up "leaking" indices during plugins | |
| * reload. Make sure we allocate 1 index per name, also current TSUserArgIndexNameLookup() implementation assumes 1-1 | |
| * relationship as well. */ | |
| const char *desc; | |
| if (TS_SUCCESS == TSUserArgIndexNameLookup(type, name, &idx, &desc)) { | |
| // Found existing index. | |
| // No need to add get_user_arg_offset(type) here since | |
| // TSUserArgIndexNameLookup already does so. | |
| *ptr_idx = idx; | |
| return TS_SUCCESS; | |
| } | |
| idx = UserArgIdx[type]++; | |
| int limit = MAX_USER_ARGS[type]; | |
| if (idx < limit) { | |
| UserArg &arg(UserArgTable[type][idx]); | |
| arg.name = name; | |
| if (description) { | |
| arg.description = description; | |
| } | |
| *ptr_idx = idx + get_user_arg_offset(type); | |
| return TS_SUCCESS; | |
| } | |
| return TS_ERROR; | |
| } | |
| TSReturnCode | |
| TSUserArgIndexLookup(TSUserArgType type, int idx, const char **name, const char **description) | |
| { | |
| sdk_assert(0 <= type && type < TS_USER_ARGS_COUNT); | |
| sdk_assert(SanityCheckUserIndex(type, idx)); | |
| idx -= get_user_arg_offset(type); | |
| if (sdk_sanity_check_null_ptr(name) == TS_SUCCESS) { | |
| if (idx < UserArgIdx[type]) { | |
| UserArg &arg(UserArgTable[type][idx]); | |
| *name = arg.name.c_str(); | |
| if (description) { | |
| *description = arg.description.c_str(); | |
| } | |
| return TS_SUCCESS; | |
| } | |
| } | |
| return TS_ERROR; | |
| } | |
| // Not particularly efficient, but good enough for now. | |
| TSReturnCode | |
| TSUserArgIndexNameLookup(TSUserArgType type, const char *name, int *arg_idx, const char **description) | |
| { | |
| sdk_assert(sdk_sanity_check_null_ptr(arg_idx) == TS_SUCCESS); | |
| sdk_assert(0 <= type && type < TS_USER_ARGS_COUNT); | |
| std::string_view n{name}; | |
| for (UserArg *arg = UserArgTable[type], *limit = arg + UserArgIdx[type]; arg < limit; ++arg) { | |
| if (arg->name == n) { | |
| if (description) { | |
| *description = arg->description.c_str(); | |
| } | |
| *arg_idx = arg - UserArgTable[type] + get_user_arg_offset(type); | |
| return TS_SUCCESS; | |
| } | |
| } | |
| return TS_ERROR; | |
| } | |
| // ------------- | |
| void | |
| TSUserArgSet(void *data, int arg_idx, void *arg) | |
| { | |
| if (nullptr != data) { | |
| PluginUserArgsMixin *user_args = dynamic_cast<PluginUserArgsMixin *>(static_cast<Continuation *>(data)); | |
| sdk_assert(user_args); | |
| user_args->set_user_arg(arg_idx, arg); | |
| } else { | |
| global_user_args.set_user_arg(arg_idx, arg); | |
| } | |
| } | |
| void * | |
| TSUserArgGet(void *data, int arg_idx) | |
| { | |
| if (nullptr != data) { | |
| PluginUserArgsMixin *user_args = dynamic_cast<PluginUserArgsMixin *>(static_cast<Continuation *>(data)); | |
| sdk_assert(user_args); | |
| return user_args->get_user_arg(arg_idx); | |
| } else { | |
| return global_user_args.get_user_arg(arg_idx); | |
| } | |
| } | |
| void | |
| TSHttpTxnStatusSet(TSHttpTxn txnp, TSHttpStatus status, std::string_view setter) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| sm->t_state.http_return_code = static_cast<HTTPStatus>(status); | |
| if (!setter.empty()) { | |
| sm->t_state.http_return_code_setter_name = setter; | |
| } | |
| } | |
| void | |
| TSHttpTxnStatusSet(TSHttpTxn txnp, TSHttpStatus status) | |
| { | |
| TSHttpTxnStatusSet(txnp, status, std::string_view{}); | |
| } | |
| TSHttpStatus | |
| TSHttpTxnStatusGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| return static_cast<TSHttpStatus>(sm->t_state.http_return_code); | |
| } | |
| TSReturnCode | |
| TSHttpTxnCntlSet(TSHttpTxn txnp, TSHttpCntlType cntl, bool data) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| switch (cntl) { | |
| case TS_HTTP_CNTL_LOGGING_MODE: | |
| sm->t_state.api_info.logging_enabled = data; | |
| break; | |
| case TS_HTTP_CNTL_INTERCEPT_RETRY_MODE: | |
| sm->t_state.api_info.retry_intercept_failures = data; | |
| break; | |
| case TS_HTTP_CNTL_RESPONSE_CACHEABLE: | |
| sm->t_state.api_resp_cacheable = data; | |
| break; | |
| case TS_HTTP_CNTL_REQUEST_CACHEABLE: | |
| sm->t_state.api_req_cacheable = data; | |
| break; | |
| case TS_HTTP_CNTL_SERVER_NO_STORE: | |
| sm->t_state.api_server_response_no_store = data; | |
| break; | |
| case TS_HTTP_CNTL_TXN_DEBUG: | |
| sm->debug_on = data; | |
| break; | |
| case TS_HTTP_CNTL_SKIP_REMAPPING: | |
| sm->t_state.api_skip_all_remapping = data; | |
| break; | |
| default: | |
| return TS_ERROR; | |
| break; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| bool | |
| TSHttpTxnCntlGet(TSHttpTxn txnp, TSHttpCntlType ctrl) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| switch (ctrl) { | |
| case TS_HTTP_CNTL_LOGGING_MODE: | |
| return sm->t_state.api_info.logging_enabled; | |
| break; | |
| case TS_HTTP_CNTL_INTERCEPT_RETRY_MODE: | |
| return sm->t_state.api_info.retry_intercept_failures; | |
| break; | |
| case TS_HTTP_CNTL_RESPONSE_CACHEABLE: | |
| return sm->t_state.api_resp_cacheable; | |
| break; | |
| case TS_HTTP_CNTL_REQUEST_CACHEABLE: | |
| return sm->t_state.api_req_cacheable; | |
| break; | |
| case TS_HTTP_CNTL_SERVER_NO_STORE: | |
| return sm->t_state.api_server_response_no_store; | |
| break; | |
| case TS_HTTP_CNTL_TXN_DEBUG: | |
| return sm->debug_on; | |
| break; | |
| case TS_HTTP_CNTL_SKIP_REMAPPING: | |
| return sm->t_state.api_skip_all_remapping; | |
| break; | |
| default: | |
| break; | |
| } | |
| return false; // Unknown here, but oh well. | |
| } | |
| /* This is kinda horky, we have to use TSServerState instead of | |
| HttpTransact::ServerState_t, otherwise we have a prototype | |
| mismatch in the public ts/ts.h interfaces. */ | |
| TSServerState | |
| TSHttpTxnServerStateGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpTransact::State *s = &((reinterpret_cast<HttpSM *>(txnp))->t_state); | |
| return static_cast<TSServerState>(s->current.state); | |
| } | |
| void | |
| TSHttpTxnDebugSet(TSHttpTxn txnp, int on) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| (reinterpret_cast<HttpSM *>(txnp))->debug_on = on; | |
| } | |
| int | |
| TSHttpTxnDebugGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| return (reinterpret_cast<HttpSM *>(txnp))->debug_on; | |
| } | |
| void | |
| TSHttpSsnDebugSet(TSHttpSsn ssnp, int on) | |
| { | |
| sdk_assert(sdk_sanity_check_http_ssn(ssnp) == TS_SUCCESS); | |
| (reinterpret_cast<ProxySession *>(ssnp))->set_debug(0 != on); | |
| } | |
| int | |
| TSHttpSsnDebugGet(TSHttpSsn ssnp, int *on) | |
| { | |
| sdk_assert(sdk_sanity_check_http_ssn(ssnp) == TS_SUCCESS); | |
| sdk_assert(on != nullptr); | |
| *on = (reinterpret_cast<ProxySession *>(ssnp))->debug(); | |
| return TS_SUCCESS; | |
| } | |
| int | |
| TSHttpTxnClientReqHdrBytesGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| return sm->client_request_hdr_bytes; | |
| } | |
| int64_t | |
| TSHttpTxnClientReqBodyBytesGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| return sm->client_request_body_bytes; | |
| } | |
| int | |
| TSHttpTxnServerReqHdrBytesGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| return sm->server_request_hdr_bytes; | |
| } | |
| int64_t | |
| TSHttpTxnServerReqBodyBytesGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| return sm->server_request_body_bytes; | |
| } | |
| int | |
| TSHttpTxnServerRespHdrBytesGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| return sm->server_response_hdr_bytes; | |
| } | |
| int64_t | |
| TSHttpTxnServerRespBodyBytesGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| return sm->server_response_body_bytes; | |
| } | |
| int | |
| TSHttpTxnClientRespHdrBytesGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| return sm->client_response_hdr_bytes; | |
| } | |
| int64_t | |
| TSHttpTxnClientRespBodyBytesGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| return sm->client_response_body_bytes; | |
| } | |
| int | |
| TSVConnIsSslReused(TSVConn sslp) | |
| { | |
| NetVConnection *vc = reinterpret_cast<NetVConnection *>(sslp); | |
| SSLNetVConnection *ssl_vc = dynamic_cast<SSLNetVConnection *>(vc); | |
| return ssl_vc ? ssl_vc->getIsResumedSSLSession() : 0; | |
| } | |
| const char * | |
| TSVConnSslCipherGet(TSVConn sslp) | |
| { | |
| NetVConnection *vc = reinterpret_cast<NetVConnection *>(sslp); | |
| TLSBasicSupport *tlsbs = vc->get_service<TLSBasicSupport>(); | |
| return tlsbs ? tlsbs->get_tls_cipher_suite() : nullptr; | |
| } | |
| const char * | |
| TSVConnSslProtocolGet(TSVConn sslp) | |
| { | |
| NetVConnection *vc = reinterpret_cast<NetVConnection *>(sslp); | |
| TLSBasicSupport *tlsbs = vc->get_service<TLSBasicSupport>(); | |
| return tlsbs ? tlsbs->get_tls_protocol_name() : nullptr; | |
| } | |
| const char * | |
| TSVConnSslCurveGet(TSVConn sslp) | |
| { | |
| NetVConnection *vc = reinterpret_cast<NetVConnection *>(sslp); | |
| TLSBasicSupport *tlsbs = vc->get_service<TLSBasicSupport>(); | |
| return tlsbs ? tlsbs->get_tls_curve() : nullptr; | |
| } | |
| int | |
| TSHttpTxnPushedRespHdrBytesGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| return sm->pushed_response_hdr_bytes; | |
| } | |
| int64_t | |
| TSHttpTxnPushedRespBodyBytesGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| return sm->pushed_response_body_bytes; | |
| } | |
| // Get a particular milestone hrtime'r. Note that this can return 0, which means it has not | |
| // been set yet. | |
| TSReturnCode | |
| TSHttpTxnMilestoneGet(TSHttpTxn txnp, TSMilestonesType milestone, ink_hrtime *time) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr(time) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| TSReturnCode ret = TS_SUCCESS; | |
| if ((milestone < TS_MILESTONE_UA_BEGIN) || (milestone >= TS_MILESTONE_LAST_ENTRY)) { | |
| *time = -1; | |
| ret = TS_ERROR; | |
| } else { | |
| *time = sm->milestones[milestone]; | |
| } | |
| return ret; | |
| } | |
| TSReturnCode | |
| TSHttpTxnCachedRespTimeGet(TSHttpTxn txnp, time_t *resp_time) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| HTTPInfo *cached_obj = sm->t_state.cache_info.object_read; | |
| if (cached_obj == nullptr || !cached_obj->valid()) { | |
| return TS_ERROR; | |
| } | |
| *resp_time = cached_obj->response_received_time_get(); | |
| return TS_SUCCESS; | |
| } | |
| int | |
| TSHttpCurrentClientConnectionsGet() | |
| { | |
| return Metrics::Gauge::load(http_rsb.current_client_connections); | |
| } | |
| int | |
| TSHttpCurrentActiveClientConnectionsGet() | |
| { | |
| return Metrics::Gauge::load(http_rsb.current_active_client_connections); | |
| } | |
| int | |
| TSHttpCurrentIdleClientConnectionsGet() | |
| { | |
| int64_t total = Metrics::Gauge::load(http_rsb.current_client_connections); | |
| int64_t active = Metrics::Gauge::load(http_rsb.current_active_client_connections); | |
| if (total >= active) { | |
| return static_cast<int>(total - active); | |
| } | |
| return 0; | |
| } | |
| int | |
| TSHttpCurrentCacheConnectionsGet() | |
| { | |
| return Metrics::Gauge::load(http_rsb.current_cache_connections); | |
| } | |
| int | |
| TSHttpCurrentServerConnectionsGet() | |
| { | |
| return Metrics::Gauge::load(http_rsb.current_server_connections); | |
| } | |
| /* HTTP alternate selection */ | |
| TSReturnCode | |
| TSHttpAltInfoClientReqGet(TSHttpAltInfo infop, TSMBuffer *bufp, TSMLoc *obj) | |
| { | |
| sdk_assert(sdk_sanity_check_alt_info(infop) == TS_SUCCESS); | |
| HttpAltInfo *info = reinterpret_cast<HttpAltInfo *>(infop); | |
| *(reinterpret_cast<HTTPHdr **>(bufp)) = &info->m_client_req; | |
| *obj = reinterpret_cast<TSMLoc>(info->m_client_req.m_http); | |
| return sdk_sanity_check_mbuffer(*bufp); | |
| } | |
| TSReturnCode | |
| TSHttpAltInfoCachedReqGet(TSHttpAltInfo infop, TSMBuffer *bufp, TSMLoc *obj) | |
| { | |
| sdk_assert(sdk_sanity_check_alt_info(infop) == TS_SUCCESS); | |
| HttpAltInfo *info = reinterpret_cast<HttpAltInfo *>(infop); | |
| *(reinterpret_cast<HTTPHdr **>(bufp)) = &info->m_cached_req; | |
| *obj = reinterpret_cast<TSMLoc>(info->m_cached_req.m_http); | |
| return sdk_sanity_check_mbuffer(*bufp); | |
| } | |
| TSReturnCode | |
| TSHttpAltInfoCachedRespGet(TSHttpAltInfo infop, TSMBuffer *bufp, TSMLoc *obj) | |
| { | |
| sdk_assert(sdk_sanity_check_alt_info(infop) == TS_SUCCESS); | |
| HttpAltInfo *info = reinterpret_cast<HttpAltInfo *>(infop); | |
| *(reinterpret_cast<HTTPHdr **>(bufp)) = &info->m_cached_resp; | |
| *obj = reinterpret_cast<TSMLoc>(info->m_cached_resp.m_http); | |
| return sdk_sanity_check_mbuffer(*bufp); | |
| } | |
| void | |
| TSHttpAltInfoQualitySet(TSHttpAltInfo infop, float quality) | |
| { | |
| sdk_assert(sdk_sanity_check_alt_info(infop) == TS_SUCCESS); | |
| HttpAltInfo *info = reinterpret_cast<HttpAltInfo *>(infop); | |
| info->m_qvalue = quality; | |
| } | |
| const char * | |
| TSHttpTxnPluginTagGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| return sm->plugin_tag; | |
| } | |
| TSHttpConnectOptions | |
| TSHttpConnectOptionsGet(TSConnectType connect_type) | |
| { | |
| sdk_assert(connect_type > TS_CONNECT_UNDEFINED); | |
| sdk_assert(connect_type < TS_CONNECT_LAST_ENTRY); | |
| return TSHttpConnectOptions{.connect_type = connect_type, | |
| .addr = nullptr, | |
| .tag = nullptr, | |
| .id = 0, | |
| .buffer_index = TS_IOBUFFER_SIZE_INDEX_32K, | |
| .buffer_water_mark = TS_IOBUFFER_WATER_MARK_PLUGIN_VC_DEFAULT}; | |
| } | |
| TSVConn | |
| TSHttpConnectWithPluginId(sockaddr const *addr, const char *tag, int64_t id) | |
| { | |
| TSHttpConnectOptions options = TSHttpConnectOptionsGet(TS_CONNECT_PLUGIN); | |
| options.addr = addr; | |
| options.tag = tag; | |
| options.id = id; | |
| return TSHttpConnectPlugin(&options); | |
| } | |
| TSVConn | |
| TSHttpConnectPlugin(TSHttpConnectOptions *options) | |
| { | |
| sdk_assert(options != nullptr); | |
| sdk_assert(options->connect_type == TS_CONNECT_PLUGIN); | |
| sdk_assert(options->addr); | |
| sdk_assert(ats_is_unix(options->addr) || ats_is_ip(options->addr)); | |
| sdk_assert(ats_is_unix(options->addr) || ats_ip_port_cast(options->addr)); | |
| return reinterpret_cast<TSVConn>(PluginHttpConnectInternal(options)); | |
| } | |
| TSVConn | |
| TSHttpConnect(sockaddr const *addr) | |
| { | |
| return TSHttpConnectWithPluginId(addr, "plugin", 0); | |
| } | |
| TSVConn | |
| TSHttpConnectTransparent(sockaddr const *client_addr, sockaddr const *server_addr) | |
| { | |
| sdk_assert(ats_is_ip(client_addr)); | |
| sdk_assert(ats_is_ip(server_addr)); | |
| sdk_assert(!ats_is_ip_any(client_addr)); | |
| sdk_assert(ats_ip_port_cast(client_addr)); | |
| sdk_assert(!ats_is_ip_any(server_addr)); | |
| sdk_assert(ats_ip_port_cast(server_addr)); | |
| if (plugin_http_transparent_accept) { | |
| PluginVCCore *new_pvc = PluginVCCore::alloc(plugin_http_transparent_accept); | |
| // set active address expects host ordering and the above casts do not | |
| // swap when it is required | |
| new_pvc->set_active_addr(client_addr); | |
| new_pvc->set_passive_addr(server_addr); | |
| new_pvc->set_transparent(true, true); | |
| PluginVC *return_vc = new_pvc->connect(); | |
| if (return_vc != nullptr) { | |
| PluginVC *other_side = return_vc->get_other_side(); | |
| if (other_side != nullptr) { | |
| other_side->set_is_internal_request(true); | |
| } | |
| } | |
| return reinterpret_cast<TSVConn>(return_vc); | |
| } | |
| return nullptr; | |
| } | |
| /* Actions */ | |
| void | |
| TSActionCancel(TSAction actionp) noexcept | |
| try { | |
| Action *thisaction; | |
| INKContInternal *i; | |
| // Nothing to cancel | |
| if (actionp == nullptr) { | |
| return; | |
| } | |
| /* This is a hack. Should be handled in ink_types */ | |
| if (reinterpret_cast<uintptr_t>(actionp) & 0x1) { | |
| thisaction = reinterpret_cast<Action *>(reinterpret_cast<uintptr_t>(actionp) - 1); | |
| if (thisaction) { | |
| i = static_cast<INKContInternal *>(thisaction->continuation); | |
| i->handle_event_count(EVENT_IMMEDIATE); | |
| } else { // The action pointer for an INKContInternal was effectively null, just go away | |
| return; | |
| } | |
| } else { | |
| thisaction = reinterpret_cast<Action *>(actionp); | |
| } | |
| thisaction->cancel(); | |
| } catch (...) { | |
| ink_abort("exception escaped %s", __func__); | |
| } | |
| // Currently no error handling necessary, actionp can be anything. | |
| int | |
| TSActionDone(TSAction actionp) | |
| { | |
| return (reinterpret_cast<Action *>(actionp) == ACTION_RESULT_DONE) ? 1 : 0; | |
| } | |
| /* Connections */ | |
| TSVConn | |
| TSVConnCreate(TSEventFunc event_funcp, TSMutex mutexp) | |
| { | |
| if (mutexp == nullptr) { | |
| mutexp = reinterpret_cast<TSMutex>(new_ProxyMutex()); | |
| } | |
| // TODO: probably don't need this if memory allocations fails properly | |
| sdk_assert(sdk_sanity_check_mutex(mutexp) == TS_SUCCESS); | |
| if (pluginThreadContext) { | |
| pluginThreadContext->acquire(); | |
| } | |
| INKVConnInternal *i = THREAD_ALLOC(INKVConnAllocator, this_thread()); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)i) == TS_SUCCESS); | |
| i->init(event_funcp, mutexp, pluginThreadContext); | |
| return reinterpret_cast<TSVConn>(i); | |
| } | |
| struct ActionSink : public Continuation { | |
| ActionSink() : Continuation(nullptr) { SET_HANDLER(&ActionSink::mainEvent); } | |
| int | |
| mainEvent(int event, void *edata) | |
| { | |
| // Just sink the event ... | |
| Dbg(dbg_ctl_iocore_net, "sinking event=%d (%s), edata=%p", event, HttpDebugNames::get_event_name(event), edata); | |
| return EVENT_CONT; | |
| } | |
| }; | |
| static ActionSink a; | |
| TSVConn | |
| TSVConnFdCreate(int fd) | |
| { | |
| UnixNetVConnection *vc; | |
| EThread *t = this_ethread(); | |
| if (unlikely(fd == NO_FD)) { | |
| return nullptr; | |
| } | |
| vc = static_cast<UnixNetVConnection *>(netProcessor.allocate_vc(t)); | |
| if (vc == nullptr) { | |
| return nullptr; | |
| } | |
| // We need to set an Action to handle NET_EVENT_OPEN* events. Since we have a | |
| // socket already, we don't need to do anything in those events, so we can just | |
| // sink them. It's better to sink them here, than to make the NetVC code more | |
| // complex. | |
| vc->action_ = &a; | |
| vc->id = net_next_connection_number(); | |
| vc->submit_time = ink_get_hrtime(); | |
| vc->mutex = new_ProxyMutex(); | |
| vc->set_is_transparent(false); | |
| vc->set_context(NetVConnectionContext_t::NET_VCONNECTION_OUT); | |
| // We should take the nh's lock and vc's lock before we get into the connectUp | |
| SCOPED_MUTEX_LOCK(lock, get_NetHandler(t)->mutex, t); | |
| SCOPED_MUTEX_LOCK(lock2, vc->mutex, t); | |
| if (vc->connectUp(t, fd) != CONNECT_SUCCESS) { | |
| return nullptr; | |
| } | |
| return reinterpret_cast<TSVConn>(vc); | |
| } | |
| TSVIO | |
| TSVConnReadVIOGet(TSVConn connp) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(connp) == TS_SUCCESS); | |
| VConnection *vc = reinterpret_cast<VConnection *>(connp); | |
| TSVIO data; | |
| if (vc->get_data(TS_API_DATA_READ_VIO, &data)) { | |
| return data; | |
| } | |
| return nullptr; | |
| } | |
| TSVIO | |
| TSVConnWriteVIOGet(TSVConn connp) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(connp) == TS_SUCCESS); | |
| VConnection *vc = reinterpret_cast<VConnection *>(connp); | |
| TSVIO data; | |
| if (vc->get_data(TS_API_DATA_WRITE_VIO, &data)) { | |
| return data; | |
| } | |
| return nullptr; | |
| } | |
| int | |
| TSVConnClosedGet(TSVConn connp) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(connp) == TS_SUCCESS); | |
| VConnection *vc = reinterpret_cast<VConnection *>(connp); | |
| int data = 0; | |
| bool f = vc->get_data(TS_API_DATA_CLOSED, &data); | |
| ink_assert(f); // This can fail in some cases, we need to track those down. | |
| return data; | |
| } | |
| TSVIO | |
| TSVConnRead(TSVConn connp, TSCont contp, TSIOBuffer bufp, int64_t nbytes) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(connp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_iocore_structure(contp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_iocore_structure(bufp) == TS_SUCCESS); | |
| sdk_assert(nbytes >= 0); | |
| FORCE_PLUGIN_SCOPED_MUTEX(contp); | |
| VConnection *vc = reinterpret_cast<VConnection *>(connp); | |
| return reinterpret_cast<TSVIO>( | |
| vc->do_io_read(reinterpret_cast<INKContInternal *>(contp), nbytes, reinterpret_cast<MIOBuffer *>(bufp))); | |
| } | |
| TSVIO | |
| TSVConnWrite(TSVConn connp, TSCont contp, TSIOBufferReader readerp, int64_t nbytes) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(connp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_iocore_structure(contp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_iocore_structure(readerp) == TS_SUCCESS); | |
| sdk_assert(nbytes >= 0); | |
| FORCE_PLUGIN_SCOPED_MUTEX(contp); | |
| VConnection *vc = reinterpret_cast<VConnection *>(connp); | |
| return reinterpret_cast<TSVIO>( | |
| vc->do_io_write(reinterpret_cast<INKContInternal *>(contp), nbytes, reinterpret_cast<IOBufferReader *>(readerp))); | |
| } | |
| void | |
| TSVConnClose(TSVConn connp) noexcept | |
| try { | |
| sdk_assert(sdk_sanity_check_iocore_structure(connp) == TS_SUCCESS); | |
| VConnection *vc = reinterpret_cast<VConnection *>(connp); | |
| vc->do_io_close(); | |
| } catch (...) { | |
| ink_abort("exception escaped %s", __func__); | |
| } | |
| void | |
| TSVConnAbort(TSVConn connp, int error) noexcept | |
| try { | |
| sdk_assert(sdk_sanity_check_iocore_structure(connp) == TS_SUCCESS); | |
| VConnection *vc = reinterpret_cast<VConnection *>(connp); | |
| vc->do_io_close(error); | |
| } catch (...) { | |
| ink_abort("exception escaped %s", __func__); | |
| } | |
| void | |
| TSVConnShutdown(TSVConn connp, int read, int write) noexcept | |
| try { | |
| sdk_assert(sdk_sanity_check_iocore_structure(connp) == TS_SUCCESS); | |
| VConnection *vc = reinterpret_cast<VConnection *>(connp); | |
| if (read && write) { | |
| vc->do_io_shutdown(IO_SHUTDOWN_READWRITE); | |
| } else if (read) { | |
| vc->do_io_shutdown(IO_SHUTDOWN_READ); | |
| } else if (write) { | |
| vc->do_io_shutdown(IO_SHUTDOWN_WRITE); | |
| } | |
| } catch (...) { | |
| ink_abort("exception escaped %s", __func__); | |
| } | |
| int64_t | |
| TSVConnCacheObjectSizeGet(TSVConn connp) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(connp) == TS_SUCCESS); | |
| CacheVC *vc = reinterpret_cast<CacheVC *>(connp); | |
| return vc->get_object_size(); | |
| } | |
| void | |
| TSVConnCacheHttpInfoSet(TSVConn connp, TSCacheHttpInfo infop) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(connp) == TS_SUCCESS); | |
| CacheVC *vc = reinterpret_cast<CacheVC *>(connp); | |
| if (static_cast<CacheOpType>(vc->op_type) == CacheOpType::Scan) { | |
| vc->set_http_info(reinterpret_cast<CacheHTTPInfo *>(infop)); | |
| } | |
| } | |
| /* Transformations */ | |
| TSVConn | |
| TSTransformCreate(TSEventFunc event_funcp, TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| return TSVConnCreate(event_funcp, | |
| reinterpret_cast<TSMutex>(static_cast<Continuation *>(reinterpret_cast<HttpSM *>(txnp))->getMutex())); | |
| } | |
| TSVConn | |
| TSTransformOutputVConnGet(TSVConn connp) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(connp) == TS_SUCCESS); | |
| VConnection *vc = reinterpret_cast<VConnection *>(connp); | |
| TSVConn data; | |
| vc->get_data(TS_API_DATA_OUTPUT_VC, &data); // This case can't fail. | |
| return data; | |
| } | |
| void | |
| TSHttpTxnServerIntercept(TSCont contp, TSHttpTxn txnp) | |
| { | |
| HttpSM *http_sm = reinterpret_cast<HttpSM *>(txnp); | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_continuation(contp) == TS_SUCCESS); | |
| TSIOBufferSizeIndex buffer_index = TSPluginVCIOBufferIndexGet(txnp); | |
| TSIOBufferWaterMark buffer_water_mark = TSPluginVCIOBufferWaterMarkGet(txnp); | |
| http_sm->plugin_tunnel_type = HttpPluginTunnel_t::AS_SERVER; | |
| http_sm->plugin_tunnel = PluginVCCore::alloc(reinterpret_cast<INKContInternal *>(contp), buffer_index, buffer_water_mark); | |
| } | |
| void | |
| TSHttpTxnIntercept(TSCont contp, TSHttpTxn txnp) | |
| { | |
| HttpSM *http_sm = reinterpret_cast<HttpSM *>(txnp); | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_continuation(contp) == TS_SUCCESS); | |
| TSIOBufferSizeIndex buffer_index = TSPluginVCIOBufferIndexGet(txnp); | |
| TSIOBufferWaterMark buffer_water_mark = TSPluginVCIOBufferWaterMarkGet(txnp); | |
| http_sm->plugin_tunnel_type = HttpPluginTunnel_t::AS_INTERCEPT; | |
| http_sm->plugin_tunnel = PluginVCCore::alloc(reinterpret_cast<INKContInternal *>(contp), buffer_index, buffer_water_mark); | |
| } | |
| TSIOBufferSizeIndex | |
| TSPluginVCIOBufferIndexGet(TSHttpTxn txnp) | |
| { | |
| TSMgmtInt index; | |
| if (TSHttpTxnConfigIntGet(txnp, TS_CONFIG_PLUGIN_VC_DEFAULT_BUFFER_INDEX, &index) == TS_SUCCESS && | |
| index >= TS_IOBUFFER_SIZE_INDEX_128 && index <= MAX_BUFFER_SIZE_INDEX) { | |
| return static_cast<TSIOBufferSizeIndex>(index); | |
| } | |
| return TS_IOBUFFER_SIZE_INDEX_32K; | |
| } | |
| TSIOBufferWaterMark | |
| TSPluginVCIOBufferWaterMarkGet(TSHttpTxn txnp) | |
| { | |
| TSMgmtInt water_mark; | |
| if (TSHttpTxnConfigIntGet(txnp, TS_CONFIG_PLUGIN_VC_DEFAULT_BUFFER_WATER_MARK, &water_mark) == TS_SUCCESS && | |
| water_mark > TS_IOBUFFER_WATER_MARK_UNDEFINED) { | |
| return static_cast<TSIOBufferWaterMark>(water_mark); | |
| } | |
| return TS_IOBUFFER_WATER_MARK_PLUGIN_VC_DEFAULT; | |
| } | |
| /* Net VConnections */ | |
| void | |
| TSVConnInactivityTimeoutSet(TSVConn connp, TSHRTime timeout) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(connp) == TS_SUCCESS); | |
| NetVConnection *vc = reinterpret_cast<NetVConnection *>(connp); | |
| vc->set_inactivity_timeout(timeout); | |
| } | |
| void | |
| TSVConnInactivityTimeoutCancel(TSVConn connp) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(connp) == TS_SUCCESS); | |
| NetVConnection *vc = reinterpret_cast<NetVConnection *>(connp); | |
| vc->cancel_inactivity_timeout(); | |
| } | |
| void | |
| TSVConnActiveTimeoutSet(TSVConn connp, TSHRTime timeout) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(connp) == TS_SUCCESS); | |
| NetVConnection *vc = reinterpret_cast<NetVConnection *>(connp); | |
| vc->set_active_timeout(timeout); | |
| } | |
| void | |
| TSVConnActiveTimeoutCancel(TSVConn connp) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(connp) == TS_SUCCESS); | |
| NetVConnection *vc = reinterpret_cast<NetVConnection *>(connp); | |
| vc->cancel_active_timeout(); | |
| } | |
| sockaddr const * | |
| TSNetVConnLocalAddrGet(TSVConn connp) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(connp) == TS_SUCCESS); | |
| NetVConnection *vc = reinterpret_cast<NetVConnection *>(connp); | |
| return vc->get_local_addr(); | |
| } | |
| sockaddr const * | |
| TSNetVConnRemoteAddrGet(TSVConn connp) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(connp) == TS_SUCCESS); | |
| NetVConnection *vc = reinterpret_cast<NetVConnection *>(connp); | |
| return vc->get_remote_addr(); | |
| } | |
| sockaddr const * | |
| TSNetVConnClientAddrGet(TSVConn connp) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(connp) == TS_SUCCESS); | |
| NetVConnection *vc = reinterpret_cast<NetVConnection *>(connp); | |
| return vc->get_effective_remote_addr(); | |
| } | |
| TSAction | |
| TSNetConnect(TSCont contp, sockaddr const *addr) | |
| { | |
| sdk_assert(sdk_sanity_check_continuation(contp) == TS_SUCCESS); | |
| sdk_assert(ats_is_ip(addr)); | |
| HttpConfigParams *http_config_param = HttpConfig::acquire(); | |
| NetVCOptions opt; | |
| if (http_config_param) { | |
| opt.set_sock_param(http_config_param->oride.sock_recv_buffer_size_out, http_config_param->oride.sock_send_buffer_size_out, | |
| http_config_param->oride.sock_option_flag_out, http_config_param->oride.sock_packet_mark_out, | |
| http_config_param->oride.sock_packet_tos_out); | |
| HttpConfig::release(http_config_param); | |
| } | |
| FORCE_PLUGIN_SCOPED_MUTEX(contp); | |
| return reinterpret_cast<TSAction>(netProcessor.connect_re(reinterpret_cast<INKContInternal *>(contp), addr, opt)); | |
| } | |
| TSAction | |
| TSNetConnectTransparent(TSCont contp, sockaddr const *client_addr, sockaddr const *server_addr) | |
| { | |
| sdk_assert(sdk_sanity_check_continuation(contp) == TS_SUCCESS); | |
| sdk_assert(ats_is_ip(server_addr)); | |
| sdk_assert(ats_ip_are_compatible(client_addr, server_addr)); | |
| NetVCOptions opt; | |
| opt.addr_binding = NetVCOptions::FOREIGN_ADDR; | |
| opt.local_ip.assign(client_addr); | |
| opt.local_port = ats_ip_port_host_order(client_addr); | |
| FORCE_PLUGIN_SCOPED_MUTEX(contp); | |
| return reinterpret_cast<TSAction>(netProcessor.connect_re(reinterpret_cast<INKContInternal *>(contp), server_addr, opt)); | |
| } | |
| TSCont | |
| TSNetInvokingContGet(TSVConn conn) | |
| { | |
| NetVConnection *vc = reinterpret_cast<NetVConnection *>(conn); | |
| UnixNetVConnection *net_vc = dynamic_cast<UnixNetVConnection *>(vc); | |
| TSCont ret = nullptr; | |
| if (net_vc) { | |
| const Action *action = net_vc->get_action(); | |
| ret = reinterpret_cast<TSCont>(action->continuation); | |
| } | |
| return ret; | |
| } | |
| TSHttpTxn | |
| TSNetInvokingTxnGet(TSVConn conn) | |
| { | |
| TSCont cont = TSNetInvokingContGet(conn); | |
| TSHttpTxn ret = nullptr; | |
| if (cont) { | |
| Continuation *contobj = reinterpret_cast<Continuation *>(cont); | |
| HttpSM *sm = dynamic_cast<HttpSM *>(contobj); | |
| if (sm) { | |
| ret = reinterpret_cast<TSHttpTxn>(sm); | |
| } | |
| } | |
| return ret; | |
| } | |
| TSAction | |
| TSNetAccept(TSCont contp, int port, int domain, int accept_threads) | |
| { | |
| NetProcessor::AcceptOptions opt; | |
| sdk_assert(sdk_sanity_check_continuation(contp) == TS_SUCCESS); | |
| sdk_assert(port > 0); | |
| sdk_assert(accept_threads >= -1); | |
| // TODO: Does this imply that only one "accept thread" could be | |
| // doing an accept at any time? | |
| FORCE_PLUGIN_SCOPED_MUTEX(contp); | |
| opt = make_net_accept_options(nullptr, accept_threads); | |
| // If it's not IPv6, force to IPv4. | |
| opt.ip_family = domain == AF_INET6 ? AF_INET6 : AF_INET; | |
| opt.local_port = port; | |
| opt.frequent_accept = false; | |
| INKContInternal *i = reinterpret_cast<INKContInternal *>(contp); | |
| return reinterpret_cast<TSAction>(netProcessor.accept(i, opt)); | |
| } | |
| TSReturnCode | |
| TSNetAcceptNamedProtocol(TSCont contp, const char *protocol) | |
| { | |
| sdk_assert(protocol != nullptr); | |
| sdk_assert(contp != nullptr); | |
| sdk_assert(sdk_sanity_check_continuation(contp) == TS_SUCCESS); | |
| if (!ssl_register_protocol(protocol, reinterpret_cast<INKContInternal *>(contp))) { | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| /* DNS Lookups */ | |
| /// Context structure for the lookup callback to the plugin. | |
| struct TSResolveInfo { | |
| IpEndpoint addr; ///< Lookup result. | |
| HostDBRecord *record = nullptr; ///< Record for the FQDN. | |
| }; | |
| static int | |
| TSHostLookupTrampoline(TSCont contp, TSEvent ev, void *data) | |
| { | |
| auto c = reinterpret_cast<INKContInternal *>(contp); | |
| // Set up the local context. | |
| TSResolveInfo ri; | |
| ri.record = static_cast<HostDBRecord *>(data); | |
| if (ri.record) { | |
| ri.record->rr_info()[0].data.ip.toSockAddr(ri.addr); | |
| } | |
| auto *target = reinterpret_cast<INKContInternal *>(c->mdata); | |
| // Deliver the message. | |
| target->handleEvent(ev, &ri); | |
| // Cleanup. | |
| c->destroy(); | |
| return TS_SUCCESS; | |
| }; | |
| TSAction | |
| TSHostLookup(TSCont contp, const char *hostname, size_t namelen) | |
| { | |
| sdk_assert(sdk_sanity_check_continuation(contp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)hostname) == TS_SUCCESS); | |
| sdk_assert(namelen > 0); | |
| FORCE_PLUGIN_SCOPED_MUTEX(contp); | |
| // There is no place to store the actual sockaddr to which a pointer should be returned. | |
| // therefore an intermediate continuation is created to intercept the reply from HostDB. | |
| // Its handler can create the required sockaddr context on the stack and then forward | |
| // the event to the plugin continuation. The sockaddr cannot be placed in the HostDB | |
| // record because that is a shared object. | |
| auto bouncer = INKContAllocator.alloc(); | |
| bouncer->init(&TSHostLookupTrampoline, reinterpret_cast<TSMutex>(reinterpret_cast<INKContInternal *>(contp)->mutex.get())); | |
| bouncer->mdata = contp; | |
| return reinterpret_cast<TSAction>(hostDBProcessor.getbyname_re(bouncer, hostname, namelen)); | |
| } | |
| sockaddr const * | |
| TSHostLookupResultAddrGet(TSHostLookupResult lookup_result) | |
| { | |
| sdk_assert(sdk_sanity_check_hostlookup_structure(lookup_result) == TS_SUCCESS); | |
| auto ri{reinterpret_cast<TSResolveInfo *>(lookup_result)}; | |
| return ri->addr.isValid() ? &ri->addr.sa : nullptr; | |
| } | |
| /* | |
| * checks if the cache is ready | |
| */ | |
| /* Cache VConnections */ | |
| TSAction | |
| TSCacheRead(TSCont contp, TSCacheKey key) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(contp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_cachekey(key) == TS_SUCCESS); | |
| FORCE_PLUGIN_SCOPED_MUTEX(contp); | |
| CacheInfo *info = reinterpret_cast<CacheInfo *>(key); | |
| Continuation *i = reinterpret_cast<INKContInternal *>(contp); | |
| return reinterpret_cast<TSAction>(cacheProcessor.open_read( | |
| i, &info->cache_key, info->frag_type, std::string_view{info->hostname, static_cast<std::string_view::size_type>(info->len)})); | |
| } | |
| TSAction | |
| TSCacheWrite(TSCont contp, TSCacheKey key) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(contp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_cachekey(key) == TS_SUCCESS); | |
| FORCE_PLUGIN_SCOPED_MUTEX(contp); | |
| CacheInfo *info = reinterpret_cast<CacheInfo *>(key); | |
| Continuation *i = reinterpret_cast<INKContInternal *>(contp); | |
| return reinterpret_cast<TSAction>( | |
| cacheProcessor.open_write(i, &info->cache_key, info->frag_type, 0, false, info->pin_in_cache, | |
| std::string_view{info->hostname, static_cast<std::string_view::size_type>(info->len)})); | |
| } | |
| TSAction | |
| TSCacheRemove(TSCont contp, TSCacheKey key) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(contp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_cachekey(key) == TS_SUCCESS); | |
| FORCE_PLUGIN_SCOPED_MUTEX(contp); | |
| CacheInfo *info = reinterpret_cast<CacheInfo *>(key); | |
| INKContInternal *i = reinterpret_cast<INKContInternal *>(contp); | |
| return reinterpret_cast<TSAction>(cacheProcessor.remove( | |
| i, &info->cache_key, info->frag_type, std::string_view{info->hostname, static_cast<std::string_view::size_type>(info->len)})); | |
| } | |
| TSAction | |
| TSCacheScan(TSCont contp, TSCacheKey key, int KB_per_second) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(contp) == TS_SUCCESS); | |
| // NOTE: key can be NULl here, so don't check for it. | |
| FORCE_PLUGIN_SCOPED_MUTEX(contp); | |
| INKContInternal *i = reinterpret_cast<INKContInternal *>(contp); | |
| if (key) { | |
| CacheInfo *info = reinterpret_cast<CacheInfo *>(key); | |
| return reinterpret_cast<TSAction>( | |
| cacheProcessor.scan(i, std::string_view{info->hostname, static_cast<std::string_view::size_type>(info->len)}, KB_per_second)); | |
| } | |
| return reinterpret_cast<TSAction>(cacheProcessor.scan(i, std::string_view{}, KB_per_second)); | |
| } | |
| /************************ REC Stats API **************************/ | |
| int | |
| TSStatCreate(const char *the_name, TSRecordDataType /* the_type ATS_UNUSED */, TSStatPersistence /* persist ATS_UNUSED */, | |
| TSStatSync /* sync ATS_UNUSED */) | |
| { | |
| int id = Metrics::Gauge::create(the_name); // Gauges allows for all "int" operations | |
| if (id == ts::Metrics::NOT_FOUND) { | |
| return TS_ERROR; | |
| } | |
| return id; | |
| } | |
| void | |
| TSStatIntIncrement(int id, TSMgmtInt amount) | |
| { | |
| sdk_assert(sdk_sanity_check_stat_id(id) == TS_SUCCESS); | |
| global_api_metrics.increment(id, amount); | |
| } | |
| void | |
| TSStatIntDecrement(int id, TSMgmtInt amount) | |
| { | |
| sdk_assert(sdk_sanity_check_stat_id(id) == TS_SUCCESS); | |
| global_api_metrics.decrement(id, amount); | |
| } | |
| TSMgmtInt | |
| TSStatIntGet(int id) | |
| { | |
| sdk_assert(sdk_sanity_check_stat_id(id) == TS_SUCCESS); | |
| return global_api_metrics[id].load(); | |
| } | |
| void | |
| TSStatIntSet(int id, TSMgmtInt value) | |
| { | |
| sdk_assert(sdk_sanity_check_stat_id(id) == TS_SUCCESS); | |
| global_api_metrics[id].store(value); | |
| } | |
| TSReturnCode | |
| TSStatFindName(const char *name, int *idp) | |
| { | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)name) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)idp) == TS_SUCCESS); | |
| int id = global_api_metrics.lookup(name); | |
| if (id == ts::Metrics::NOT_FOUND) { | |
| return TS_ERROR; | |
| } else { | |
| *idp = id; | |
| return TS_SUCCESS; | |
| } | |
| } | |
| bool | |
| TSStatIdIsValid(int the_stat) | |
| { | |
| return sdk_sanity_check_stat_id(the_stat) == TS_SUCCESS; | |
| } | |
| /************************** Logging API ****************************/ | |
| TSReturnCode | |
| TSTextLogObjectCreate(const char *filename, int mode, TSTextLogObject *new_object) | |
| { | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)filename) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)new_object) == TS_SUCCESS); | |
| if (mode < 0 || mode >= TS_LOG_MODE_INVALID_FLAG) { | |
| *new_object = nullptr; | |
| return TS_ERROR; | |
| } | |
| TextLogObject *tlog = new TextLogObject( | |
| filename, Log::config->logfile_dir, static_cast<bool>(mode) & TS_LOG_MODE_ADD_TIMESTAMP, nullptr, Log::config->rolling_enabled, | |
| Log::config->preproc_threads, Log::config->rolling_interval_sec, Log::config->rolling_offset_hr, Log::config->rolling_size_mb, | |
| Log::config->rolling_max_count, Log::config->rolling_min_count, Log::config->rolling_allow_empty); | |
| if (tlog == nullptr) { | |
| *new_object = nullptr; | |
| return TS_ERROR; | |
| } | |
| int err = (mode & TS_LOG_MODE_DO_NOT_RENAME ? Log::config->log_object_manager.manage_api_object(tlog, 0) : | |
| Log::config->log_object_manager.manage_api_object(tlog)); | |
| if (err != LogObjectManager::NO_FILENAME_CONFLICTS) { | |
| delete tlog; | |
| *new_object = nullptr; | |
| return TS_ERROR; | |
| } | |
| *new_object = reinterpret_cast<TSTextLogObject>(tlog); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSTextLogObjectWrite(TSTextLogObject the_object, const char *format, ...) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(the_object) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)format) == TS_SUCCESS); | |
| TSReturnCode retVal = TS_SUCCESS; | |
| va_list ap; | |
| va_start(ap, format); | |
| switch ((reinterpret_cast<TextLogObject *>(the_object))->va_write(format, ap)) { | |
| case (Log::LOG_OK): | |
| case (Log::SKIP): | |
| case (Log::AGGR): | |
| break; | |
| case (Log::FULL): | |
| retVal = TS_ERROR; | |
| break; | |
| case (Log::FAIL): | |
| retVal = TS_ERROR; | |
| break; | |
| default: | |
| ink_assert(!"invalid return code"); | |
| } | |
| va_end(ap); | |
| return retVal; | |
| } | |
| void | |
| TSTextLogObjectFlush(TSTextLogObject the_object) noexcept | |
| try { | |
| sdk_assert(sdk_sanity_check_iocore_structure(the_object) == TS_SUCCESS); | |
| (reinterpret_cast<TextLogObject *>(the_object))->force_new_buffer(); | |
| } catch (...) { | |
| ink_abort("exception escaped %s", __func__); | |
| } | |
| TSReturnCode | |
| TSTextLogObjectDestroy(TSTextLogObject the_object) noexcept | |
| try { | |
| sdk_assert(sdk_sanity_check_iocore_structure(the_object) == TS_SUCCESS); | |
| if (Log::config->log_object_manager.unmanage_api_object(reinterpret_cast<TextLogObject *>(the_object))) { | |
| return TS_SUCCESS; | |
| } | |
| return TS_ERROR; | |
| } catch (...) { | |
| ink_abort("exception escaped %s", __func__); | |
| } | |
| void | |
| TSTextLogObjectHeaderSet(TSTextLogObject the_object, const char *header) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(the_object) == TS_SUCCESS); | |
| (reinterpret_cast<TextLogObject *>(the_object))->set_log_file_header(header); | |
| } | |
| TSReturnCode | |
| TSTextLogObjectRollingEnabledSet(TSTextLogObject the_object, int rolling_enabled) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(the_object) == TS_SUCCESS); | |
| if (LogRollingEnabledIsValid(rolling_enabled)) { | |
| (reinterpret_cast<TextLogObject *>(the_object))->set_rolling_enabled(static_cast<Log::RollingEnabledValues>(rolling_enabled)); | |
| return TS_SUCCESS; | |
| } | |
| return TS_ERROR; | |
| } | |
| void | |
| TSTextLogObjectRollingIntervalSecSet(TSTextLogObject the_object, int rolling_interval_sec) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(the_object) == TS_SUCCESS); | |
| (reinterpret_cast<TextLogObject *>(the_object))->set_rolling_interval_sec(rolling_interval_sec); | |
| } | |
| void | |
| TSTextLogObjectRollingOffsetHrSet(TSTextLogObject the_object, int rolling_offset_hr) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(the_object) == TS_SUCCESS); | |
| (reinterpret_cast<TextLogObject *>(the_object))->set_rolling_offset_hr(rolling_offset_hr); | |
| } | |
| void | |
| TSTextLogObjectRollingSizeMbSet(TSTextLogObject the_object, int rolling_size_mb) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(the_object) == TS_SUCCESS); | |
| (reinterpret_cast<TextLogObject *>(the_object))->set_rolling_size_mb(rolling_size_mb); | |
| } | |
| TSReturnCode | |
| TSHttpSsnClientFdGet(TSHttpSsn ssnp, int *fdp) | |
| { | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)fdp) == TS_SUCCESS); | |
| VConnection *basecs = reinterpret_cast<VConnection *>(ssnp); | |
| ProxySession *cs = dynamic_cast<ProxySession *>(basecs); | |
| if (cs == nullptr) { | |
| return TS_ERROR; | |
| } | |
| NetVConnection *vc = cs->get_netvc(); | |
| if (vc == nullptr) { | |
| return TS_ERROR; | |
| } | |
| *fdp = vc->get_socket(); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnClientFdGet(TSHttpTxn txnp, int *fdp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)fdp) == TS_SUCCESS); | |
| TSHttpSsn ssnp = TSHttpTxnSsnGet(txnp); | |
| return TSHttpSsnClientFdGet(ssnp, fdp); | |
| } | |
| TSReturnCode | |
| TSHttpTxnServerFdGet(TSHttpTxn txnp, int *fdp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)fdp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| *fdp = -1; | |
| TSReturnCode retval = TS_ERROR; | |
| ProxyTransaction *ss = sm->get_server_txn(); | |
| if (ss != nullptr) { | |
| NetVConnection *vc = ss->get_netvc(); | |
| if (vc != nullptr) { | |
| *fdp = vc->get_socket(); | |
| retval = TS_SUCCESS; | |
| } | |
| } | |
| return retval; | |
| } | |
| void | |
| load_config_file_callback(const char *parent_file, const char *remap_file) | |
| { | |
| FileManager::instance().configFileChild(parent_file, remap_file); | |
| } | |
| /* Config file name setting */ | |
| TSReturnCode | |
| TSMgmtConfigFileAdd(const char *parent, const char *fileName) | |
| { | |
| load_config_file_callback(parent, fileName); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSCacheUrlSet(TSHttpTxn txnp, const char *url, int length) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| Dbg(dbg_ctl_cache_url, "[TSCacheUrlSet]"); | |
| if (sm->t_state.cache_info.lookup_url == nullptr) { | |
| Dbg(dbg_ctl_cache_url, "[TSCacheUrlSet] changing the cache url to: %s", url); | |
| if (length == -1) { | |
| length = strlen(url); | |
| } | |
| sm->t_state.cache_info.lookup_url_storage.create(nullptr); | |
| sm->t_state.cache_info.lookup_url = &(sm->t_state.cache_info.lookup_url_storage); | |
| sm->t_state.cache_info.lookup_url->parse(url, length); | |
| return TS_SUCCESS; | |
| } | |
| return TS_ERROR; | |
| } | |
| void | |
| TSCacheHttpInfoKeySet(TSCacheHttpInfo infop, TSCacheKey keyp) | |
| { | |
| // TODO: Check input ? | |
| CacheHTTPInfo *info = reinterpret_cast<CacheHTTPInfo *>(infop); | |
| CryptoHash *key = reinterpret_cast<CryptoHash *>(keyp); | |
| info->object_key_set(*key); | |
| } | |
| void | |
| TSCacheHttpInfoSizeSet(TSCacheHttpInfo infop, int64_t size) | |
| { | |
| // TODO: Check input ? | |
| CacheHTTPInfo *info = reinterpret_cast<CacheHTTPInfo *>(infop); | |
| info->object_size_set(size); | |
| } | |
| // this function should be called at TS_EVENT_HTTP_READ_RESPONSE_HDR | |
| void | |
| TSHttpTxnRedirectUrlSet(TSHttpTxn txnp, const char *url, const int url_len) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)url) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| ats_free(sm->redirect_url); | |
| sm->redirect_url = nullptr; | |
| sm->redirect_url_len = 0; | |
| sm->redirect_url = const_cast<char *>(url); | |
| sm->redirect_url_len = url_len; | |
| sm->enable_redirection = true; | |
| // Don't reset HttpSM::redirection_tries here: a per-hop reset defeats the number_of_redirections | |
| // limit that HttpSM enforces, allowing an unbounded redirect chain. | |
| // Make sure we allow for at least one redirection. | |
| if (sm->t_state.txn_conf->number_of_redirections <= 0) { | |
| sm->t_state.setup_per_txn_configs(); | |
| sm->t_state.my_txn_conf().number_of_redirections = 1; | |
| } | |
| } | |
| const char * | |
| TSHttpTxnRedirectUrlGet(TSHttpTxn txnp, int *url_len_ptr) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| *url_len_ptr = sm->redirect_url_len; | |
| return sm->redirect_url; | |
| } | |
| int | |
| TSHttpTxnRedirectRetries(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| return sm->redirection_tries; | |
| } | |
| char * | |
| TSFetchRespGet(TSHttpTxn txnp, int *length) | |
| { | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)length) == TS_SUCCESS); | |
| FetchSM *fetch_sm = reinterpret_cast<FetchSM *>(txnp); | |
| return fetch_sm->resp_get(length); | |
| } | |
| TSReturnCode | |
| TSFetchPageRespGet(TSHttpTxn txnp, TSMBuffer *bufp, TSMLoc *obj) | |
| { | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)bufp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)obj) == TS_SUCCESS); | |
| HTTPHdr *hptr = reinterpret_cast<HTTPHdr *>(txnp); | |
| if (hptr->valid()) { | |
| *(reinterpret_cast<HTTPHdr **>(bufp)) = hptr; | |
| *obj = reinterpret_cast<TSMLoc>(hptr->m_http); | |
| return sdk_sanity_check_mbuffer(*bufp); | |
| } | |
| return TS_ERROR; | |
| } | |
| void | |
| TSFetchPages(TSFetchUrlParams_t *params) | |
| { | |
| TSFetchUrlParams_t *myparams = params; | |
| while (myparams != nullptr) { | |
| FetchSM *fetch_sm = FetchSMAllocator.alloc(); | |
| sockaddr *addr = ats_ip_sa_cast(&myparams->ip); | |
| fetch_sm->init(reinterpret_cast<Continuation *>(myparams->contp), myparams->options, myparams->events, myparams->request, | |
| myparams->request_len, addr); | |
| fetch_sm->httpConnect(); | |
| myparams = myparams->next; | |
| } | |
| } | |
| TSFetchSM | |
| TSFetchUrl(const char *headers, int request_len, sockaddr const *ip, TSCont contp, TSFetchWakeUpOptions callback_options, | |
| TSFetchEvent events) | |
| { | |
| if (callback_options != NO_CALLBACK) { | |
| sdk_assert(sdk_sanity_check_continuation(contp) == TS_SUCCESS); | |
| } | |
| FetchSM *fetch_sm = FetchSMAllocator.alloc(); | |
| fetch_sm->init(reinterpret_cast<Continuation *>(contp), callback_options, events, headers, request_len, ip); | |
| fetch_sm->httpConnect(); | |
| return reinterpret_cast<TSFetchSM>(fetch_sm); | |
| } | |
| void | |
| TSFetchFlagSet(TSFetchSM fetch_sm, int flags) | |
| { | |
| sdk_assert(sdk_sanity_check_fetch_sm(fetch_sm) == TS_SUCCESS); | |
| (reinterpret_cast<FetchSM *>(fetch_sm))->set_fetch_flags(flags); | |
| } | |
| TSFetchSM | |
| TSFetchCreate(TSCont contp, const char *method, const char *url, const char *version, struct sockaddr const *client_addr, int flags) | |
| { | |
| sdk_assert(sdk_sanity_check_continuation(contp) == TS_SUCCESS); | |
| sdk_assert(ats_is_ip(client_addr)); | |
| FetchSM *fetch_sm = FetchSMAllocator.alloc(); | |
| fetch_sm->ext_init(reinterpret_cast<Continuation *>(contp), method, url, version, client_addr, flags); | |
| return reinterpret_cast<TSFetchSM>(fetch_sm); | |
| } | |
| void | |
| TSFetchHeaderAdd(TSFetchSM fetch_sm, const char *name, int name_len, const char *value, int value_len) | |
| { | |
| sdk_assert(sdk_sanity_check_fetch_sm(fetch_sm) == TS_SUCCESS); | |
| (reinterpret_cast<FetchSM *>(fetch_sm))->ext_add_header(name, name_len, value, value_len); | |
| } | |
| void | |
| TSFetchWriteData(TSFetchSM fetch_sm, const void *data, size_t len) | |
| { | |
| sdk_assert(sdk_sanity_check_fetch_sm(fetch_sm) == TS_SUCCESS); | |
| (reinterpret_cast<FetchSM *>(fetch_sm))->ext_write_data(data, len); | |
| } | |
| ssize_t | |
| TSFetchReadData(TSFetchSM fetch_sm, void *buf, size_t len) | |
| { | |
| sdk_assert(sdk_sanity_check_fetch_sm(fetch_sm) == TS_SUCCESS); | |
| return (reinterpret_cast<FetchSM *>(fetch_sm))->ext_read_data(static_cast<char *>(buf), len); | |
| } | |
| void | |
| TSFetchLaunch(TSFetchSM fetch_sm) | |
| { | |
| sdk_assert(sdk_sanity_check_fetch_sm(fetch_sm) == TS_SUCCESS); | |
| (reinterpret_cast<FetchSM *>(fetch_sm))->ext_launch(); | |
| } | |
| void | |
| TSFetchDestroy(TSFetchSM fetch_sm) noexcept | |
| try { | |
| sdk_assert(sdk_sanity_check_fetch_sm(fetch_sm) == TS_SUCCESS); | |
| (reinterpret_cast<FetchSM *>(fetch_sm))->ext_destroy(); | |
| } catch (...) { | |
| ink_abort("exception escaped %s", __func__); | |
| } | |
| void | |
| TSFetchUserDataSet(TSFetchSM fetch_sm, void *data) | |
| { | |
| sdk_assert(sdk_sanity_check_fetch_sm(fetch_sm) == TS_SUCCESS); | |
| (reinterpret_cast<FetchSM *>(fetch_sm))->ext_set_user_data(data); | |
| } | |
| void * | |
| TSFetchUserDataGet(TSFetchSM fetch_sm) | |
| { | |
| sdk_assert(sdk_sanity_check_fetch_sm(fetch_sm) == TS_SUCCESS); | |
| return (reinterpret_cast<FetchSM *>(fetch_sm))->ext_get_user_data(); | |
| } | |
| TSMBuffer | |
| TSFetchRespHdrMBufGet(TSFetchSM fetch_sm) | |
| { | |
| sdk_assert(sdk_sanity_check_fetch_sm(fetch_sm) == TS_SUCCESS); | |
| return (reinterpret_cast<FetchSM *>(fetch_sm))->resp_hdr_bufp(); | |
| } | |
| TSMLoc | |
| TSFetchRespHdrMLocGet(TSFetchSM fetch_sm) | |
| { | |
| sdk_assert(sdk_sanity_check_fetch_sm(fetch_sm) == TS_SUCCESS); | |
| return (reinterpret_cast<FetchSM *>(fetch_sm))->resp_hdr_mloc(); | |
| } | |
| int | |
| TSHttpSsnIsInternal(TSHttpSsn ssnp) | |
| { | |
| ProxySession *cs = reinterpret_cast<ProxySession *>(ssnp); | |
| if (!cs) { | |
| return 0; | |
| } | |
| NetVConnection *vc = cs->get_netvc(); | |
| if (!vc) { | |
| return 0; | |
| } | |
| return vc->get_is_internal_request() ? 1 : 0; | |
| } | |
| int | |
| TSHttpTxnIsInternal(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| return TSHttpSsnIsInternal(TSHttpTxnSsnGet(txnp)); | |
| } | |
| static void | |
| txn_error_get(TSHttpTxn txnp, bool client, bool sent, uint32_t &error_class, uint64_t &error_code) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| HttpTransact::ConnectionAttributes *connection_attributes = nullptr; | |
| if (client == true) { | |
| // client | |
| connection_attributes = &sm->t_state.client_info; | |
| } else { | |
| // server | |
| connection_attributes = &sm->t_state.server_info; | |
| } | |
| if (sent == true) { | |
| // sent | |
| error_code = connection_attributes->tx_error_code.code; | |
| error_class = static_cast<uint32_t>(connection_attributes->tx_error_code.cls); | |
| } else { | |
| // received | |
| error_code = connection_attributes->rx_error_code.code; | |
| error_class = static_cast<uint32_t>(connection_attributes->rx_error_code.cls); | |
| } | |
| } | |
| void | |
| TSHttpSsnClientReceivedErrorGet(TSHttpSsn ssnp, uint32_t *error_class, uint64_t *error_code) | |
| { | |
| sdk_assert(sdk_sanity_check_http_ssn(ssnp) == TS_SUCCESS); | |
| sdk_assert(error_class != nullptr && error_code != nullptr); | |
| auto *session = dynamic_cast<Http2ClientSession *>(reinterpret_cast<ProxySession *>(ssnp)); | |
| ProxyError error = session ? session->connection_state.rx_error_code : ProxyError{}; | |
| *error_class = static_cast<uint32_t>(error.cls); | |
| *error_code = error.code; | |
| } | |
| void | |
| TSHttpSsnClientSentErrorGet(TSHttpSsn ssnp, uint32_t *error_class, uint64_t *error_code) | |
| { | |
| sdk_assert(sdk_sanity_check_http_ssn(ssnp) == TS_SUCCESS); | |
| sdk_assert(error_class != nullptr && error_code != nullptr); | |
| auto *session = dynamic_cast<Http2ClientSession *>(reinterpret_cast<ProxySession *>(ssnp)); | |
| ProxyError error = session ? session->connection_state.tx_error_code : ProxyError{}; | |
| *error_class = static_cast<uint32_t>(error.cls); | |
| *error_code = error.code; | |
| } | |
| void | |
| TSHttpTxnClientReceivedErrorGet(TSHttpTxn txnp, uint32_t *error_class, uint64_t *error_code) | |
| { | |
| txn_error_get(txnp, true, false, *error_class, *error_code); | |
| } | |
| void | |
| TSHttpTxnClientSentErrorGet(TSHttpTxn txnp, uint32_t *error_class, uint64_t *error_code) | |
| { | |
| txn_error_get(txnp, true, true, *error_class, *error_code); | |
| } | |
| void | |
| TSHttpTxnServerReceivedErrorGet(TSHttpTxn txnp, uint32_t *error_class, uint64_t *error_code) | |
| { | |
| txn_error_get(txnp, false, false, *error_class, *error_code); | |
| } | |
| void | |
| TSHttpTxnServerSentErrorGet(TSHttpTxn txnp, uint32_t *error_class, uint64_t *error_code) | |
| { | |
| txn_error_get(txnp, false, true, *error_class, *error_code); | |
| } | |
| TSReturnCode | |
| TSHttpTxnServerPush(TSHttpTxn txnp, const char *url, int url_len) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(url != nullptr); | |
| if (url_len < 0) { | |
| url_len = strlen(url); | |
| } | |
| URL url_obj; | |
| url_obj.create(nullptr); | |
| if (url_obj.parse(url, url_len) == ParseResult::ERROR) { | |
| url_obj.destroy(); | |
| return TS_ERROR; | |
| } | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| Http2Stream *stream = dynamic_cast<Http2Stream *>(sm->get_ua_txn()); | |
| if (stream == nullptr) { | |
| url_obj.destroy(); | |
| return TS_ERROR; | |
| } | |
| Http2ClientSession *ua_session = static_cast<Http2ClientSession *>(stream->get_proxy_ssn()); | |
| SCOPED_MUTEX_LOCK(lock, ua_session->mutex, this_ethread()); | |
| if (ua_session->connection_state.is_state_closed() || ua_session->is_url_pushed(url, url_len)) { | |
| url_obj.destroy(); | |
| return TS_ERROR; | |
| } | |
| HTTPHdr *hptr = &(sm->t_state.hdr_info.client_request); | |
| TSMLoc obj = reinterpret_cast<TSMLoc>(hptr->m_http); | |
| MIMEHdrImpl *mh = _hdr_mloc_to_mime_hdr_impl(obj); | |
| MIMEField *f = mime_hdr_field_find(mh, static_cast<std::string_view>(MIME_FIELD_ACCEPT_ENCODING)); | |
| if (!stream->push_promise(url_obj, f)) { | |
| url_obj.destroy(); | |
| return TS_ERROR; | |
| } | |
| ua_session->add_url_to_pushed_table(url, url_len); | |
| url_obj.destroy(); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnClientStreamIdGet(TSHttpTxn txnp, uint64_t *stream_id) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(stream_id != nullptr); | |
| auto *sm = reinterpret_cast<HttpSM *>(txnp); | |
| auto *stream = dynamic_cast<Http2Stream *>(sm->get_ua_txn()); | |
| if (stream == nullptr) { | |
| return TS_ERROR; | |
| } | |
| *stream_id = stream->get_id(); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnClientStreamPriorityGet(TSHttpTxn txnp, TSHttpPriority *priority) | |
| { | |
| static_assert(sizeof(TSHttpPriority) >= sizeof(TSHttp2Priority), | |
| "TSHttpPriorityType is incorrectly smaller than TSHttp2Priority."); | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(priority != nullptr); | |
| auto *sm = reinterpret_cast<HttpSM *>(txnp); | |
| auto *stream = dynamic_cast<Http2Stream *>(sm->get_ua_txn()); | |
| if (stream == nullptr) { | |
| return TS_ERROR; | |
| } | |
| auto *priority_out = reinterpret_cast<TSHttp2Priority *>(priority); | |
| priority_out->priority_type = HTTP_PRIORITY_TYPE_HTTP_2; | |
| priority_out->stream_dependency = stream->get_transaction_priority_dependence(); | |
| priority_out->weight = stream->get_transaction_priority_weight(); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSAIORead(int fd, off_t offset, char *buf, size_t buffSize, TSCont contp) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(contp) == TS_SUCCESS); | |
| Continuation *pCont = reinterpret_cast<Continuation *>(contp); | |
| AIOCallback *pAIO = new_AIOCallback(); | |
| if (pAIO == nullptr) { | |
| return TS_ERROR; | |
| } | |
| pAIO->aiocb.aio_fildes = fd; | |
| pAIO->aiocb.aio_offset = offset; | |
| pAIO->aiocb.aio_nbytes = buffSize; | |
| pAIO->aiocb.aio_buf = buf; | |
| pAIO->action = pCont; | |
| pAIO->thread = pCont->mutex->thread_holding; | |
| pAIO->from_ts_api = true; | |
| if (ink_aio_read(pAIO, 1) == 1) { | |
| return TS_SUCCESS; | |
| } | |
| delete pAIO; | |
| return TS_ERROR; | |
| } | |
| char * | |
| TSAIOBufGet(TSAIOCallback data) | |
| { | |
| AIOCallback *pAIO = reinterpret_cast<AIOCallback *>(data); | |
| return static_cast<char *>(pAIO->aiocb.aio_buf); | |
| } | |
| int | |
| TSAIONBytesGet(TSAIOCallback data) | |
| { | |
| AIOCallback *pAIO = reinterpret_cast<AIOCallback *>(data); | |
| return static_cast<int>(pAIO->aio_result); | |
| } | |
| TSReturnCode | |
| TSAIOWrite(int fd, off_t offset, char *buf, const size_t bufSize, TSCont contp) | |
| { | |
| sdk_assert(sdk_sanity_check_iocore_structure(contp) == TS_SUCCESS); | |
| Continuation *pCont = reinterpret_cast<Continuation *>(contp); | |
| AIOCallback *pAIO = new_AIOCallback(); | |
| // TODO: Might be able to remove this when allocations can never fail. | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)pAIO) == TS_SUCCESS); | |
| pAIO->aiocb.aio_fildes = fd; | |
| pAIO->aiocb.aio_offset = offset; | |
| pAIO->aiocb.aio_buf = buf; | |
| pAIO->aiocb.aio_nbytes = bufSize; | |
| pAIO->action = pCont; | |
| pAIO->thread = pCont->mutex->thread_holding; | |
| pAIO->from_ts_api = true; | |
| if (ink_aio_write(pAIO, 1) == 1) { | |
| return TS_SUCCESS; | |
| } | |
| delete pAIO; | |
| return TS_ERROR; | |
| } | |
| TSReturnCode | |
| TSAIOThreadNumSet(int thread_num) | |
| { | |
| if (ink_aio_thread_num_set(thread_num)) { | |
| return TS_SUCCESS; | |
| } | |
| return TS_ERROR; | |
| } | |
| void | |
| TSRecordDump(int rec_type, TSRecordDumpCb callback, void *edata) | |
| { | |
| RecDumpRecords(static_cast<RecT>(rec_type), reinterpret_cast<RecDumpEntryCb>(callback), edata); | |
| } | |
| /* ability to skip the remap phase of the State Machine | |
| this only really makes sense in TS_HTTP_READ_REQUEST_HDR_HOOK | |
| */ | |
| void | |
| TSSkipRemappingSet(TSHttpTxn txnp, int flag) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| sm->t_state.api_skip_all_remapping = (flag != 0); | |
| } | |
| /* These are the default converter function sets for management data types. If those are used the | |
| * proper converters can be determined here. For other types the converters must be explicitly | |
| * specified. | |
| * | |
| * The purpose of these are to allow configuration elements to not be management types but more | |
| * natural types (e.g., an enumeration can be the actual enumeration, not an @c MgmtInt that needs | |
| * frequent casting). In effect the converter does the casting for the plugin API, isolating that | |
| * to this API handling, with the rest of the code base using the natural types. | |
| */ | |
| /// Unhandled API conversions. | |
| /// Because the code around the specially handled types still uses this in the default case, | |
| /// it must compile for those cases. To indicate unhandled, return @c nullptr for @a conv. | |
| /// @internal This should be a temporary state, eventually the other cases should be handled | |
| /// via specializations here. | |
| /// @internal C++ note - THIS MUST BE FIRST IN THE DECLARATIONS or it might be falsely used. | |
| template <typename T> | |
| inline void * | |
| _memberp_to_generic(T *ptr, MgmtConverter const *&conv) | |
| { | |
| conv = nullptr; | |
| return ptr; | |
| } | |
| /// API conversion for @c MgmtInt, identify conversion as integer. | |
| inline void * | |
| _memberp_to_generic(MgmtInt *ptr, MgmtConverter const *&conv) | |
| { | |
| static const MgmtConverter converter([](const void *data) -> MgmtInt { return *static_cast<const MgmtInt *>(data); }, | |
| [](void *data, MgmtInt i) -> void { *static_cast<MgmtInt *>(data) = i; }); | |
| conv = &converter; | |
| return ptr; | |
| } | |
| /// API conversion for @c MgmtByte, handles integer / byte size differences. | |
| inline void * | |
| _memberp_to_generic(MgmtByte *ptr, MgmtConverter const *&conv) | |
| { | |
| static const MgmtConverter converter{[](const void *data) -> MgmtInt { return *static_cast<const MgmtByte *>(data); }, | |
| [](void *data, MgmtInt i) -> void { *static_cast<MgmtByte *>(data) = i; }}; | |
| conv = &converter; | |
| return ptr; | |
| } | |
| /// API conversion for @c MgmtFloat, identity conversion as float. | |
| inline void * | |
| _memberp_to_generic(MgmtFloat *ptr, MgmtConverter const *&conv) | |
| { | |
| static const MgmtConverter converter{[](const void *data) -> MgmtFloat { return *static_cast<const MgmtFloat *>(data); }, | |
| [](void *data, MgmtFloat f) -> void { *static_cast<MgmtFloat *>(data) = f; }}; | |
| conv = &converter; | |
| return ptr; | |
| } | |
| /// API conversion for arbitrary enum. | |
| /// Handle casting to and from the enum type @a E. | |
| template <typename E> | |
| inline auto | |
| _memberp_to_generic(MgmtFloat *ptr, MgmtConverter const *&conv) -> typename std::enable_if<std::is_enum<E>::value, void *>::type | |
| { | |
| static const MgmtConverter converter{ | |
| [](const void *data) -> MgmtInt { return static_cast<MgmtInt>(*static_cast<const E *>(data)); }, | |
| [](void *data, MgmtInt i) -> void { *static_cast<E *>(data) = static_cast<E>(i); }}; | |
| conv = &converter; | |
| return ptr; | |
| } | |
| /** | |
| * X-Macro Dispatch for _conf_to_memberp() | |
| * | |
| * Don't be intimidated by the macros below - they're simpler than they look! | |
| * The end result is just a switch statement with one case per config, like: | |
| * | |
| * switch (conf) { | |
| * case TS_CONFIG_HTTP_CHUNKING_ENABLED: | |
| * ret = _memberp_to_generic(&overridableHttpConfig->chunking_enabled, conv); | |
| * break; | |
| * case TS_CONFIG_HTTP_DOWN_SERVER_CACHE_TIME: | |
| * conv = &HttpDownServerCacheTimeConv; | |
| * ret = &overridableHttpConfig->down_server_timeout; | |
| * break; | |
| * // ... ~130 more cases, one per overridable config ... | |
| * } | |
| * | |
| * The macros just auto-generate these cases from OverridableConfigDefs.h so we | |
| * don't have to maintain them by hand. Here's how an entry becomes a case: | |
| * | |
| * Example 1 - Standard config (GENERIC converter): | |
| * Entry in OverridableConfigDefs.h: | |
| * X(HTTP_CHUNKING_ENABLED, chunking_enabled, "...", INT, GENERIC) | |
| * Generates this case: | |
| * case TS_CONFIG_HTTP_CHUNKING_ENABLED: | |
| * ret = _memberp_to_generic(&overridableHttpConfig->chunking_enabled, conv); | |
| * break; | |
| * | |
| * Example 2 - Custom converter: | |
| * Entry in OverridableConfigDefs.h: | |
| * X(HTTP_DOWN_SERVER_CACHE_TIME, down_server_timeout, "...", INT, HttpDownServerCacheTimeConv) | |
| * Generates this case: | |
| * case TS_CONFIG_HTTP_DOWN_SERVER_CACHE_TIME: | |
| * conv = &HttpDownServerCacheTimeConv; | |
| * ret = &overridableHttpConfig->down_server_timeout; | |
| * break; | |
| * | |
| * The magic is in _CONF_CASE_DISPATCH which uses the CONV parameter (5th field) | |
| * to select which _CONF_CASE_* macro generates the case. Token pasting (##) | |
| * turns "GENERIC" into _CONF_CASE_GENERIC, "NONE" into _CONF_CASE_NONE, etc. | |
| * | |
| * Built-in converter types: | |
| * - GENERIC: Auto-selects converter based on member type (most common). | |
| * - NONE: No-op for configs handled elsewhere (e.g., SSL strings). | |
| * - Custom: Any other name maps to a _CONF_CASE_<name> macro defined below. | |
| * | |
| * To add a new custom converter: | |
| * 1. Define your MgmtConverter (e.g., MyConv with load/store lambdas). | |
| * 2. Add a macro here following this pattern: | |
| * #define _CONF_CASE_MyConv(KEY, MEMBER) \ | |
| * case TS_CONFIG_##KEY: conv = &MyConv; \ | |
| * ret = &overridableHttpConfig->MEMBER; break; | |
| * 3. Add #undef _CONF_CASE_MyConv after _conf_to_memberp(). | |
| * 4. Use "MyConv" as the CONV parameter in OverridableConfigDefs.h. | |
| */ | |
| // clang-format off | |
| // GENERIC: Use _memberp_to_generic() which selects converter based on member type. | |
| #define _CONF_CASE_GENERIC(KEY, MEMBER) \ | |
| case TS_CONFIG_##KEY: ret = _memberp_to_generic(&overridableHttpConfig->MEMBER, conv); break; | |
| // NONE: No-op case for configs handled specially elsewhere. | |
| #define _CONF_CASE_NONE(KEY, MEMBER) \ | |
| case TS_CONFIG_##KEY: break; | |
| // Custom converter: Converts ts_seconds to/from integer seconds. | |
| #define _CONF_CASE_HttpDownServerCacheTimeConv(KEY, MEMBER) \ | |
| case TS_CONFIG_##KEY: conv = &HttpDownServerCacheTimeConv; ret = &overridableHttpConfig->MEMBER; break; | |
| // Custom converter: Parses/formats HTTP status code lists (e.g., "404 500"). | |
| #define _CONF_CASE_HttpStatusCodeList_Conv(KEY, MEMBER) \ | |
| case TS_CONFIG_##KEY: ret = &overridableHttpConfig->MEMBER; conv = &HttpStatusCodeList::Conv; break; | |
| // Custom converters for ConnectionTracker config variables. | |
| #define _CONF_CASE_ConnectionTracker_MIN_SERVER_CONV(KEY, MEMBER) \ | |
| case TS_CONFIG_##KEY: ret = &overridableHttpConfig->MEMBER; conv = &ConnectionTracker::MIN_SERVER_CONV; break; | |
| #define _CONF_CASE_ConnectionTracker_MAX_SERVER_CONV(KEY, MEMBER) \ | |
| case TS_CONFIG_##KEY: ret = &overridableHttpConfig->MEMBER; conv = &ConnectionTracker::MAX_SERVER_CONV; break; | |
| #define _CONF_CASE_ConnectionTracker_SERVER_MATCH_CONV(KEY, MEMBER) \ | |
| case TS_CONFIG_##KEY: ret = &overridableHttpConfig->MEMBER; conv = &ConnectionTracker::SERVER_MATCH_CONV; break; | |
| #define _CONF_CASE_ConnectionTracker_METRIC_ENABLED_CONV(KEY, MEMBER) \ | |
| case TS_CONFIG_##KEY: ret = &overridableHttpConfig->MEMBER; conv = &ConnectionTracker::METRIC_ENABLED_CONV; break; | |
| #define _CONF_CASE_ConnectionTracker_METRIC_AGGREGATE_CONV(KEY, MEMBER) \ | |
| case TS_CONFIG_##KEY: ret = &overridableHttpConfig->MEMBER; conv = &ConnectionTracker::METRIC_AGGREGATE_CONV; break; | |
| // Custom converter: Parses/formats host resolution preference strings. | |
| #define _CONF_CASE_HttpTransact_HOST_RES_CONV(KEY, MEMBER) \ | |
| case TS_CONFIG_##KEY: ret = &overridableHttpConfig->MEMBER; conv = &HttpTransact::HOST_RES_CONV; break; | |
| // Custom converter: Parses/formats targeted cache control header lists. | |
| #define _CONF_CASE_TargetedCacheControlHeaders_Conv(KEY, MEMBER) \ | |
| case TS_CONFIG_##KEY: ret = &overridableHttpConfig->MEMBER; conv = &TargetedCacheControlHeaders::Conv; break; | |
| // Dispatcher: Routes to _CONF_CASE_<CONV> based on the CONV parameter. | |
| #define _CONF_CASE_DISPATCH(KEY, MEMBER, RECORD_NAME, DATA_TYPE, CONV) _CONF_CASE_##CONV(KEY, MEMBER) | |
| // clang-format on | |
| /** | |
| * Map a TSOverridableConfigKey to its corresponding struct member pointer. | |
| * | |
| * @param[in] conf The config key to look up. | |
| * @param[in] overridableHttpConfig Pointer to the config struct. | |
| * @param[out] conv set to the appropriate MgmtConverter. | |
| * @return Pointer to the struct member, or nullptr if not found/applicable. | |
| */ | |
| static void * | |
| _conf_to_memberp(TSOverridableConfigKey conf, OverridableHttpConfigParams *overridableHttpConfig, MgmtConverter const *&conv) | |
| { | |
| void *ret = nullptr; | |
| conv = nullptr; | |
| switch (conf) { | |
| // This uses OVERRIDABLE_CONFIGS to generate cases for each config. | |
| // For example: | |
| // | |
| // case TS_CONFIG_HTTP_CHUNKING_ENABLED: | |
| // ret = _memberp_to_generic(&overridableHttpConfig->chunking_enabled, conv); | |
| // break; | |
| // case TS_CONFIG_HTTP_DOWN_SERVER_CACHE_TIME: | |
| // conv = &HttpDownServerCacheTimeConv; | |
| // ret = &overridableHttpConfig->down_server_timeout; | |
| // break; | |
| // | |
| // ... ~130 more cases, one per overridable config ... | |
| // | |
| OVERRIDABLE_CONFIGS(_CONF_CASE_DISPATCH) | |
| case TS_CONFIG_NULL: | |
| case TS_CONFIG_LAST_ENTRY: | |
| break; | |
| } | |
| return ret; | |
| } | |
| #undef _CONF_CASE_GENERIC | |
| #undef _CONF_CASE_NONE | |
| #undef _CONF_CASE_HttpDownServerCacheTimeConv | |
| #undef _CONF_CASE_HttpStatusCodeList_Conv | |
| #undef _CONF_CASE_ConnectionTracker_MIN_SERVER_CONV | |
| #undef _CONF_CASE_ConnectionTracker_MAX_SERVER_CONV | |
| #undef _CONF_CASE_ConnectionTracker_SERVER_MATCH_CONV | |
| #undef _CONF_CASE_ConnectionTracker_METRIC_ENABLED_CONV | |
| #undef _CONF_CASE_ConnectionTracker_METRIC_AGGREGATE_CONV | |
| #undef _CONF_CASE_HttpTransact_HOST_RES_CONV | |
| #undef _CONF_CASE_TargetedCacheControlHeaders_Conv | |
| #undef _CONF_CASE_DISPATCH | |
| // 2nd little helper function to find the struct member for getting. | |
| static const void * | |
| _conf_to_memberp(TSOverridableConfigKey conf, const OverridableHttpConfigParams *overridableHttpConfig, MgmtConverter const *&conv) | |
| { | |
| return _conf_to_memberp(conf, const_cast<OverridableHttpConfigParams *>(overridableHttpConfig), conv); | |
| } | |
| // 3rd little helper function to find and eval MgmtConverter | |
| static TSReturnCode | |
| _eval_conv(OverridableHttpConfigParams *target, TSOverridableConfigKey conf, const char *value, int length) | |
| { | |
| MgmtConverter const *conv; | |
| void *dest = _conf_to_memberp(conf, target, conv); | |
| if (dest != nullptr && conv != nullptr && conv->store_string) { | |
| conv->store_string(dest, std::string_view(value, length)); | |
| } else { | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| /* APIs to manipulate the overridable configuration options. | |
| */ | |
| TSReturnCode | |
| TSHttpTxnConfigIntSet(TSHttpTxn txnp, TSOverridableConfigKey conf, TSMgmtInt value) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *s = reinterpret_cast<HttpSM *>(txnp); | |
| MgmtConverter const *conv; | |
| s->t_state.setup_per_txn_configs(); | |
| void *dest = _conf_to_memberp(conf, &(s->t_state.my_txn_conf()), conv); | |
| if (!dest || !conv->store_int) { | |
| return TS_ERROR; | |
| } | |
| conv->store_int(dest, value); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnConfigIntGet(TSHttpTxn txnp, TSOverridableConfigKey conf, TSMgmtInt *value) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)value) == TS_SUCCESS); | |
| HttpSM *s = reinterpret_cast<HttpSM *>(txnp); | |
| MgmtConverter const *conv; | |
| const void *src = _conf_to_memberp(conf, s->t_state.txn_conf, conv); | |
| if (!src || !conv->load_int) { | |
| return TS_ERROR; | |
| } | |
| *value = conv->load_int(src); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnConfigFloatSet(TSHttpTxn txnp, TSOverridableConfigKey conf, TSMgmtFloat value) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *s = reinterpret_cast<HttpSM *>(txnp); | |
| MgmtConverter const *conv; | |
| s->t_state.setup_per_txn_configs(); | |
| void *dest = _conf_to_memberp(conf, &(s->t_state.my_txn_conf()), conv); | |
| if (!dest || !conv->store_float) { | |
| return TS_ERROR; | |
| } | |
| conv->store_float(dest, value); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnConfigFloatGet(TSHttpTxn txnp, TSOverridableConfigKey conf, TSMgmtFloat *value) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr(static_cast<void *>(value)) == TS_SUCCESS); | |
| MgmtConverter const *conv; | |
| const void *src = _conf_to_memberp(conf, reinterpret_cast<HttpSM *>(txnp)->t_state.txn_conf, conv); | |
| if (!src || !conv->load_float) { | |
| return TS_ERROR; | |
| } | |
| *value = conv->load_float(src); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnConfigStringSet(TSHttpTxn txnp, TSOverridableConfigKey conf, const char *value, int length) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| if (length == -1) { | |
| length = strlen(value); | |
| } | |
| HttpSM *s = reinterpret_cast<HttpSM *>(txnp); | |
| s->t_state.setup_per_txn_configs(); | |
| auto set_txn_string = [s, value, length](char *&destination, size_t &destination_length) { | |
| if (value && length > 0) { | |
| destination = s->t_state.arena.str_store(value, length); | |
| destination_length = length; | |
| } else { | |
| destination = nullptr; | |
| destination_length = 0; | |
| } | |
| }; | |
| switch (conf) { | |
| case TS_CONFIG_HTTP_RESPONSE_SERVER_STR: | |
| if (value && length > 0) { | |
| s->t_state.my_txn_conf().proxy_response_server_string = const_cast<char *>(value); // The "core" likes non-const char* | |
| s->t_state.my_txn_conf().proxy_response_server_string_len = length; | |
| } else { | |
| s->t_state.my_txn_conf().proxy_response_server_string = nullptr; | |
| s->t_state.my_txn_conf().proxy_response_server_string_len = 0; | |
| } | |
| break; | |
| case TS_CONFIG_HTTP_GLOBAL_USER_AGENT_HEADER: | |
| if (value && length > 0) { | |
| s->t_state.my_txn_conf().global_user_agent_header = const_cast<char *>(value); // The "core" likes non-const char* | |
| s->t_state.my_txn_conf().global_user_agent_header_size = length; | |
| } else { | |
| s->t_state.my_txn_conf().global_user_agent_header = nullptr; | |
| s->t_state.my_txn_conf().global_user_agent_header_size = 0; | |
| } | |
| break; | |
| case TS_CONFIG_BODY_FACTORY_TEMPLATE_BASE: | |
| if (value && length > 0) { | |
| s->t_state.my_txn_conf().body_factory_template_base = const_cast<char *>(value); | |
| s->t_state.my_txn_conf().body_factory_template_base_len = length; | |
| } else { | |
| s->t_state.my_txn_conf().body_factory_template_base = nullptr; | |
| s->t_state.my_txn_conf().body_factory_template_base_len = 0; | |
| } | |
| break; | |
| case TS_CONFIG_HTTP_INSERT_FORWARDED: | |
| if (value && length > 0) { | |
| auto &parsed = ParsedConfigCache::lookup(conf, std::string_view(value, length)); | |
| s->t_state.my_txn_conf().insert_forwarded = std::get<HttpForwarded::OptionBitSet>(parsed.parsed); | |
| } | |
| break; | |
| case TS_CONFIG_HTTP_SERVER_SESSION_SHARING_MATCH: | |
| if (value && length > 0) { | |
| auto &parsed = ParsedConfigCache::lookup(conf, std::string_view(value, length)); | |
| s->t_state.my_txn_conf().server_session_sharing_match = std::get<MgmtByte>(parsed.parsed); | |
| s->t_state.my_txn_conf().server_session_sharing_match_str = const_cast<char *>(parsed.conf_value_storage.data()); | |
| } | |
| break; | |
| case TS_CONFIG_SSL_CLIENT_VERIFY_SERVER_POLICY: | |
| set_txn_string(s->t_state.my_txn_conf().ssl_client_verify_server_policy, | |
| s->t_state.my_txn_conf().ssl_client_verify_server_policy_len); | |
| break; | |
| case TS_CONFIG_SSL_CLIENT_VERIFY_SERVER_PROPERTIES: | |
| set_txn_string(s->t_state.my_txn_conf().ssl_client_verify_server_properties, | |
| s->t_state.my_txn_conf().ssl_client_verify_server_properties_len); | |
| break; | |
| case TS_CONFIG_SSL_CLIENT_SNI_POLICY: | |
| set_txn_string(s->t_state.my_txn_conf().ssl_client_sni_policy, s->t_state.my_txn_conf().ssl_client_sni_policy_len); | |
| break; | |
| case TS_CONFIG_SSL_CLIENT_CERT_FILENAME: | |
| set_txn_string(s->t_state.my_txn_conf().ssl_client_cert_filename, s->t_state.my_txn_conf().ssl_client_cert_filename_len); | |
| break; | |
| case TS_CONFIG_SSL_CLIENT_PRIVATE_KEY_FILENAME: | |
| set_txn_string(s->t_state.my_txn_conf().ssl_client_private_key_filename, | |
| s->t_state.my_txn_conf().ssl_client_private_key_filename_len); | |
| break; | |
| case TS_CONFIG_SSL_CLIENT_CA_CERT_FILENAME: | |
| set_txn_string(s->t_state.my_txn_conf().ssl_client_ca_cert_filename, s->t_state.my_txn_conf().ssl_client_ca_cert_filename_len); | |
| break; | |
| case TS_CONFIG_SSL_CLIENT_CA_CERT_PATH: | |
| set_txn_string(s->t_state.my_txn_conf().ssl_client_ca_cert_path, s->t_state.my_txn_conf().ssl_client_ca_cert_path_len); | |
| break; | |
| case TS_CONFIG_SSL_CLIENT_ALPN_PROTOCOLS: | |
| set_txn_string(s->t_state.my_txn_conf().ssl_client_alpn_protocols, s->t_state.my_txn_conf().ssl_client_alpn_protocols_len); | |
| break; | |
| case TS_CONFIG_SSL_CERT_FILEPATH: | |
| /* noop */ | |
| break; | |
| case TS_CONFIG_HTTP_NEGATIVE_CACHING_LIST: | |
| if (value && length > 0) { | |
| auto &parsed = ParsedConfigCache::lookup(conf, std::string_view(value, length)); | |
| s->t_state.my_txn_conf().negative_caching_list = std::get<HttpStatusCodeList>(parsed.parsed); | |
| } | |
| break; | |
| case TS_CONFIG_HTTP_NEGATIVE_REVALIDATING_LIST: | |
| if (value && length > 0) { | |
| auto &parsed = ParsedConfigCache::lookup(conf, std::string_view(value, length)); | |
| s->t_state.my_txn_conf().negative_revalidating_list = std::get<HttpStatusCodeList>(parsed.parsed); | |
| } | |
| break; | |
| case TS_CONFIG_HTTP_HOST_RESOLUTION_PREFERENCE: | |
| if (value && length > 0) { | |
| auto &parsed = ParsedConfigCache::lookup(conf, std::string_view(value, length)); | |
| s->t_state.my_txn_conf().host_res_data = std::get<HostResData>(parsed.parsed); | |
| } | |
| break; | |
| case TS_CONFIG_HTTP_CACHE_TARGETED_CACHE_CONTROL_HEADERS: | |
| if (value && length > 0) { | |
| auto &parsed = ParsedConfigCache::lookup(conf, std::string_view(value, length)); | |
| // This is intentionally a non-owning copy of the parsed representation. | |
| // ParsedConfigCache::ParsedValue owns the backing string in conf_value_storage, | |
| // and TargetedCacheControlHeaders stores string_view entries into that stable | |
| // storage. The per-transaction override struct in HttpTransact::State is raw | |
| // storage (not an owning/destructed HttpConfigParams object), so this path | |
| // does not free conf_value. Reusing the cached parsed object avoids reparsing | |
| // and avoids allocating/duplicating a second backing string on every txn | |
| // override update, while preserving valid lifetimes for all string_view data. | |
| s->t_state.my_txn_conf().targeted_cache_control_headers = std::get<TargetedCacheControlHeaders>(parsed.parsed); | |
| } else { | |
| s->t_state.my_txn_conf().targeted_cache_control_headers = TargetedCacheControlHeaders{}; | |
| } | |
| break; | |
| default: { | |
| if (value && length > 0) { | |
| return _eval_conv(&(s->t_state.my_txn_conf()), conf, value, length); | |
| } | |
| break; | |
| } | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnConfigStringGet(TSHttpTxn txnp, TSOverridableConfigKey conf, const char **value, int *length) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void **)value) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)length) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| switch (conf) { | |
| case TS_CONFIG_HTTP_RESPONSE_SERVER_STR: | |
| *value = sm->t_state.txn_conf->proxy_response_server_string; | |
| *length = sm->t_state.txn_conf->proxy_response_server_string_len; | |
| break; | |
| case TS_CONFIG_HTTP_GLOBAL_USER_AGENT_HEADER: | |
| *value = sm->t_state.txn_conf->global_user_agent_header; | |
| *length = sm->t_state.txn_conf->global_user_agent_header_size; | |
| break; | |
| case TS_CONFIG_BODY_FACTORY_TEMPLATE_BASE: | |
| *value = sm->t_state.txn_conf->body_factory_template_base; | |
| *length = sm->t_state.txn_conf->body_factory_template_base_len; | |
| break; | |
| case TS_CONFIG_HTTP_SERVER_SESSION_SHARING_MATCH: | |
| *value = sm->t_state.txn_conf->server_session_sharing_match_str; | |
| *length = *value ? strlen(*value) : 0; | |
| break; | |
| case TS_CONFIG_SSL_CLIENT_VERIFY_SERVER_POLICY: | |
| *value = sm->t_state.txn_conf->ssl_client_verify_server_policy; | |
| *length = sm->t_state.txn_conf->ssl_client_verify_server_policy_len; | |
| break; | |
| case TS_CONFIG_SSL_CLIENT_VERIFY_SERVER_PROPERTIES: | |
| *value = sm->t_state.txn_conf->ssl_client_verify_server_properties; | |
| *length = sm->t_state.txn_conf->ssl_client_verify_server_properties_len; | |
| break; | |
| case TS_CONFIG_SSL_CLIENT_SNI_POLICY: | |
| *value = sm->t_state.txn_conf->ssl_client_sni_policy; | |
| *length = sm->t_state.txn_conf->ssl_client_sni_policy_len; | |
| break; | |
| case TS_CONFIG_SSL_CLIENT_CERT_FILENAME: | |
| *value = sm->t_state.txn_conf->ssl_client_cert_filename; | |
| *length = sm->t_state.txn_conf->ssl_client_cert_filename_len; | |
| break; | |
| case TS_CONFIG_SSL_CLIENT_PRIVATE_KEY_FILENAME: | |
| *value = sm->t_state.txn_conf->ssl_client_private_key_filename; | |
| *length = sm->t_state.txn_conf->ssl_client_private_key_filename_len; | |
| break; | |
| case TS_CONFIG_SSL_CLIENT_CA_CERT_FILENAME: | |
| *value = sm->t_state.txn_conf->ssl_client_ca_cert_filename; | |
| *length = sm->t_state.txn_conf->ssl_client_ca_cert_filename_len; | |
| break; | |
| case TS_CONFIG_SSL_CLIENT_CA_CERT_PATH: | |
| *value = sm->t_state.txn_conf->ssl_client_ca_cert_path; | |
| *length = sm->t_state.txn_conf->ssl_client_ca_cert_path_len; | |
| break; | |
| case TS_CONFIG_SSL_CLIENT_ALPN_PROTOCOLS: | |
| *value = sm->t_state.txn_conf->ssl_client_alpn_protocols; | |
| *length = sm->t_state.txn_conf->ssl_client_alpn_protocols_len; | |
| break; | |
| default: { | |
| MgmtConverter const *conv; | |
| const void *src = _conf_to_memberp(conf, sm->t_state.txn_conf, conv); | |
| if (src != nullptr && conv != nullptr && conv->load_string) { | |
| auto sv = conv->load_string(src); | |
| *value = sv.data(); | |
| *length = sv.size(); | |
| } else { | |
| return TS_ERROR; | |
| } | |
| break; | |
| } | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnConfigFind(const char *name, int length, TSOverridableConfigKey *conf, TSRecordDataType *type) | |
| { | |
| sdk_assert(sdk_sanity_check_null_ptr(name) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr(conf) == TS_SUCCESS); | |
| std::string_view name_sv(name, length < 0 ? strlen(name) : length); | |
| if (auto config = ts::Overridable_Txn_Vars.find(name_sv); config != ts::Overridable_Txn_Vars.end()) { | |
| std::tie(*conf, *type) = config->second; | |
| return TS_SUCCESS; | |
| } | |
| return TS_ERROR; | |
| } | |
| TSReturnCode | |
| TSHttpTxnPrivateSessionSet(TSHttpTxn txnp, int private_session) | |
| { | |
| if (sdk_sanity_check_txn(txnp) != TS_SUCCESS) { | |
| return TS_ERROR; | |
| } | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| if (sm->set_server_session_private(private_session)) { | |
| return TS_SUCCESS; | |
| } | |
| return TS_ERROR; | |
| } | |
| // APIs to register new Mgmt (records) entries. | |
| TSReturnCode | |
| TSMgmtStringCreate(TSRecordType rec_type, const char *name, const TSMgmtString data_default, TSRecordUpdateType update_type, | |
| TSRecordCheckType check_type, const char *check_regex, TSRecordAccessType access_type) | |
| { | |
| if (check_regex == nullptr && check_type != TS_RECORDCHECK_NULL) { | |
| return TS_ERROR; | |
| } | |
| if (REC_ERR_OKAY != RecRegisterConfigString(static_cast<enum RecT>(rec_type), name, data_default, | |
| static_cast<enum RecUpdateT>(update_type), static_cast<enum RecCheckT>(check_type), | |
| check_regex, REC_SOURCE_PLUGIN, static_cast<enum RecAccessT>(access_type))) { | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSMgmtIntCreate(TSRecordType rec_type, const char *name, TSMgmtInt data_default, TSRecordUpdateType update_type, | |
| TSRecordCheckType check_type, const char *check_regex, TSRecordAccessType access_type) | |
| { | |
| if (check_regex == nullptr && check_type != TS_RECORDCHECK_NULL) { | |
| return TS_ERROR; | |
| } | |
| if (REC_ERR_OKAY != RecRegisterConfigInt(static_cast<enum RecT>(rec_type), name, static_cast<RecInt>(data_default), | |
| static_cast<enum RecUpdateT>(update_type), static_cast<enum RecCheckT>(check_type), | |
| check_regex, REC_SOURCE_PLUGIN, static_cast<enum RecAccessT>(access_type))) { | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpTxnCloseAfterResponse(TSHttpTxn txnp, int should_close) | |
| { | |
| if (sdk_sanity_check_txn(txnp) != TS_SUCCESS) { | |
| return TS_ERROR; | |
| } | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| if (should_close) { | |
| sm->t_state.client_info.keep_alive = HTTPKeepAlive::NO_KEEPALIVE; | |
| if (sm->get_ua_txn()) { | |
| sm->set_ua_half_close_flag(); | |
| } | |
| } | |
| // Don't change if PIPELINE is set... | |
| else if (sm->t_state.client_info.keep_alive == HTTPKeepAlive::NO_KEEPALIVE) { | |
| sm->t_state.client_info.keep_alive = HTTPKeepAlive::KEEPALIVE; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| namespace | |
| { | |
| bool | |
| is_usable_port_descriptor(const HttpProxyPort *port) | |
| { | |
| return port != nullptr && | |
| (port->m_family == AF_UNIX || ((port->m_family == AF_INET || port->m_family == AF_INET6) && port->m_port != 0)); | |
| } | |
| } // namespace | |
| // Parse a port descriptor for the proxy.config.http.server_ports descriptor format. | |
| TSPortDescriptor | |
| TSPortDescriptorParse(const char *descriptor) | |
| { | |
| auto *port = new HttpProxyPort(); | |
| if (descriptor != nullptr && port->processOptions(descriptor) && is_usable_port_descriptor(port)) { | |
| return reinterpret_cast<TSPortDescriptor>(port); | |
| } | |
| delete port; | |
| return nullptr; | |
| } | |
| TSReturnCode | |
| TSPortDescriptorAccept(TSPortDescriptor descp, TSCont contp) | |
| { | |
| if (descp == nullptr || contp == nullptr) { | |
| return TS_ERROR; | |
| } | |
| const auto *port = reinterpret_cast<const HttpProxyPort *>(descp); | |
| if (!is_usable_port_descriptor(port)) { | |
| return TS_ERROR; | |
| } | |
| Action *action = nullptr; | |
| NetProcessor::AcceptOptions net(make_net_accept_options(port, -1 /* nthreads */)); | |
| if (port->isSSL()) { | |
| action = sslNetProcessor.main_accept(reinterpret_cast<INKContInternal *>(contp), port->m_fd, net); | |
| } else { | |
| action = netProcessor.main_accept(reinterpret_cast<INKContInternal *>(contp), port->m_fd, net); | |
| } | |
| return action ? TS_SUCCESS : TS_ERROR; | |
| } | |
| void | |
| TSPortDescriptorDestroy(TSPortDescriptor descp) | |
| { | |
| delete reinterpret_cast<HttpProxyPort *>(descp); | |
| } | |
| TSReturnCode | |
| TSPluginDescriptorAccept(TSCont contp) | |
| { | |
| Action *action = nullptr; | |
| HttpProxyPort::Group &proxy_ports = HttpProxyPort::global(); | |
| for (auto &port : proxy_ports) { | |
| if (port.isPlugin()) { | |
| NetProcessor::AcceptOptions net(make_net_accept_options(&port, -1 /* nthreads */)); | |
| action = netProcessor.main_accept(reinterpret_cast<INKContInternal *>(contp), port.m_fd, net); | |
| } | |
| } | |
| return action ? TS_SUCCESS : TS_ERROR; | |
| } | |
| int | |
| TSHttpTxnBackgroundFillStarted(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *s = reinterpret_cast<HttpSM *>(txnp); | |
| return (s->background_fill == BackgroundFill_t::STARTED); | |
| } | |
| int | |
| TSHttpTxnIsCacheable(TSHttpTxn txnp, TSMBuffer request, TSMBuffer response) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| HTTPHdr *req, *resp; | |
| // We allow for either request or response to be empty (or both), in | |
| // which case we default to the transactions request or response. | |
| if (request) { | |
| sdk_assert(sdk_sanity_check_mbuffer(request) == TS_SUCCESS); | |
| req = reinterpret_cast<HTTPHdr *>(request); | |
| } else { | |
| req = &(sm->t_state.hdr_info.client_request); | |
| } | |
| if (response) { | |
| sdk_assert(sdk_sanity_check_mbuffer(response) == TS_SUCCESS); | |
| resp = reinterpret_cast<HTTPHdr *>(response); | |
| } else { | |
| resp = &(sm->t_state.hdr_info.server_response); | |
| } | |
| // Make sure these are valid response / requests, then verify if it's cacheable. | |
| return (req->valid() && resp->valid() && HttpTransact::is_response_cacheable(&(sm->t_state), req, resp)) ? 1 : 0; | |
| } | |
| int | |
| TSHttpTxnGetMaxAge(TSHttpTxn txnp, TSMBuffer response) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| HTTPHdr *resp; | |
| if (response) { | |
| // Make sure the response we got as a parameter is valid | |
| sdk_assert(sdk_sanity_check_mbuffer(response) == TS_SUCCESS); | |
| resp = reinterpret_cast<HTTPHdr *>(response); | |
| } else { | |
| // Use the transactions origin response if the user passed null | |
| resp = &(sm->t_state.hdr_info.server_response); | |
| } | |
| if (!resp || !resp->valid()) { | |
| return -1; | |
| } | |
| // We have a valid response, return max_age | |
| return HttpTransact::get_max_age(resp); | |
| } | |
| // Lookup various debug names for common HTTP types. | |
| const char * | |
| TSHttpServerStateNameLookup(TSServerState state) | |
| { | |
| return HttpDebugNames::get_server_state_name(static_cast<HttpTransact::ServerState_t>(state)); | |
| } | |
| const char * | |
| TSHttpHookNameLookup(TSHttpHookID hook) | |
| { | |
| return HttpDebugNames::get_api_hook_name(static_cast<TSHttpHookID>(hook)); | |
| } | |
| const char * | |
| TSHttpEventNameLookup(TSEvent event) | |
| { | |
| return HttpDebugNames::get_event_name(static_cast<int>(event)); | |
| } | |
| /// Re-enable NetVC that has TLSEventSupport. | |
| class TSSslCallback : public Continuation | |
| { | |
| public: | |
| TSSslCallback(TLSEventSupport *tes, TSEvent event) : Continuation(tes->getMutexForTLSEvents().get()), m_tes(tes), m_event(event) | |
| { | |
| SET_HANDLER(&TSSslCallback::event_handler); | |
| } | |
| int | |
| event_handler(int /* event ATS_UNUSED */, void *) | |
| { | |
| m_tes->reenable(m_event); | |
| delete this; | |
| return 0; | |
| } | |
| private: | |
| TLSEventSupport *m_tes; | |
| TSEvent m_event; | |
| }; | |
| /// SSL Hooks | |
| TSReturnCode | |
| TSVConnTunnel(TSVConn sslp) | |
| { | |
| NetVConnection *vc = reinterpret_cast<NetVConnection *>(sslp); | |
| SSLNetVConnection *ssl_vc = dynamic_cast<SSLNetVConnection *>(vc); | |
| TSReturnCode zret = TS_SUCCESS; | |
| if (nullptr != ssl_vc) { | |
| ssl_vc->hookOpRequested = SslVConnOp::SSL_HOOK_OP_TUNNEL; | |
| } else { | |
| zret = TS_ERROR; | |
| } | |
| return zret; | |
| } | |
| TSSslConnection | |
| TSVConnSslConnectionGet(TSVConn sslp) | |
| { | |
| TSSslConnection ssl = nullptr; | |
| NetVConnection *netvc = reinterpret_cast<NetVConnection *>(sslp); | |
| if (auto tbs = netvc->get_service<TLSBasicSupport>(); tbs) { | |
| ssl = reinterpret_cast<TSSslConnection>(tbs->get_tls_handle()); | |
| } | |
| return ssl; | |
| } | |
| int | |
| TSVConnFdGet(TSVConn vconnp) | |
| { | |
| sdk_assert(sdk_sanity_check_null_ptr(vconnp) == TS_SUCCESS); | |
| NetVConnection *vc = reinterpret_cast<NetVConnection *>(vconnp); | |
| return vc->get_socket(); | |
| } | |
| const char * | |
| TSVConnSslSniGet(TSVConn sslp, int *length) | |
| { | |
| if (sslp == nullptr) { | |
| if (length) { | |
| *length = 0; | |
| } | |
| return nullptr; | |
| } | |
| char const *server_name = nullptr; | |
| NetVConnection *vc = reinterpret_cast<NetVConnection *>(sslp); | |
| if (auto snis = vc->get_service<TLSSNISupport>(); snis) { | |
| server_name = snis->get_sni_server_name(); | |
| if (length) { | |
| *length = server_name ? strlen(server_name) : 0; | |
| } | |
| } | |
| return server_name; | |
| } | |
| TSClientHello | |
| TSVConnClientHelloGet(TSVConn sslp) | |
| { | |
| NetVConnection *netvc = reinterpret_cast<NetVConnection *>(sslp); | |
| if (netvc == nullptr) { | |
| return nullptr; | |
| } | |
| if (auto snis = netvc->get_service<TLSSNISupport>(); snis) { | |
| TLSSNISupport::ClientHello *client_hello = snis->get_client_hello(); | |
| if (client_hello == nullptr) { | |
| return nullptr; | |
| } | |
| // Wrap the raw object in the accessor and return | |
| return TSClientHello(client_hello); | |
| } | |
| return nullptr; | |
| } | |
| TSReturnCode | |
| TSClientHelloExtensionGet(TSClientHello ch, unsigned int type, const unsigned char **out, size_t *outlen) | |
| { | |
| if (static_cast<TLSSNISupport::ClientHello *>(ch._get_internal())->getExtension(type, out, outlen) == 1) { | |
| return TS_SUCCESS; | |
| } | |
| return TS_ERROR; | |
| } | |
| TSSslVerifyCTX | |
| TSVConnSslVerifyCTXGet(TSVConn sslp) | |
| { | |
| NetVConnection *vc = reinterpret_cast<NetVConnection *>(sslp); | |
| TLSBasicSupport *tlsbs = vc->get_service<TLSBasicSupport>(); | |
| if (tlsbs != nullptr) { | |
| return reinterpret_cast<TSSslVerifyCTX>(tlsbs->get_tls_cert_to_verify()); | |
| } | |
| return nullptr; | |
| } | |
| TSSslContext | |
| TSSslContextFindByName(const char *name) | |
| { | |
| if (nullptr == name || 0 == strlen(name)) { | |
| // an empty name is an invalid input | |
| return nullptr; | |
| } | |
| TSSslContext ret = nullptr; | |
| SSLCertLookup *lookup = SSLCertificateConfig::acquire(); | |
| if (lookup != nullptr) { | |
| SSLCertContext *cc = lookup->find(name); | |
| if (cc) { | |
| shared_SSL_CTX ctx = cc->getCtx(); | |
| if (ctx) { | |
| ret = reinterpret_cast<TSSslContext>(ctx.get()); | |
| } | |
| } | |
| SSLCertificateConfig::release(lookup); | |
| } | |
| return ret; | |
| } | |
| TSSslContext | |
| TSSslContextFindByAddr(struct sockaddr const *addr) | |
| { | |
| TSSslContext ret = nullptr; | |
| SSLCertLookup *lookup = SSLCertificateConfig::acquire(); | |
| if (lookup != nullptr) { | |
| IpEndpoint ip; | |
| ip.assign(addr); | |
| SSLCertContext *cc = lookup->find(ip); | |
| if (cc) { | |
| shared_SSL_CTX ctx = cc->getCtx(); | |
| if (ctx) { | |
| ret = reinterpret_cast<TSSslContext>(ctx.get()); | |
| } | |
| } | |
| SSLCertificateConfig::release(lookup); | |
| } | |
| return ret; | |
| } | |
| /** | |
| * This function sets the secret cache value for a given secret name. This allows | |
| * plugins to load cert/key PEM information on for use by the TLS core | |
| */ | |
| TSReturnCode | |
| TSSslSecretSet(const char *secret_name, int secret_name_length, const char *secret_data, int secret_data_len) | |
| { | |
| TSReturnCode retval = TS_SUCCESS; | |
| std::string const secret_name_str{secret_name, static_cast<unsigned>(secret_name_length)}; | |
| SSLConfigParams *load_params = SSLConfig::load_acquire(); | |
| SSLConfigParams *params = SSLConfig::acquire(); | |
| if (load_params != nullptr) { // Update the current data structure | |
| Dbg(dbg_ctl_ssl_cert_update, "Setting secrets in SSLConfig load for: %.*s", secret_name_length, secret_name); | |
| load_params->secrets.setSecret(secret_name_str, std::string_view(secret_data, secret_data_len)); | |
| load_params->updateCTX(secret_name_str); | |
| SSLConfig::load_release(load_params); | |
| } | |
| if (params != nullptr) { | |
| Dbg(dbg_ctl_ssl_cert_update, "Setting secrets in SSLConfig for: %.*s", secret_name_length, secret_name); | |
| params->secrets.setSecret(secret_name_str, std::string_view(secret_data, secret_data_len)); | |
| params->updateCTX(secret_name_str); | |
| SSLConfig::release(params); | |
| } | |
| return retval; | |
| } | |
| TSReturnCode | |
| TSSslSecretUpdate(const char *secret_name, int secret_name_length) | |
| { | |
| TSReturnCode retval = TS_SUCCESS; | |
| SSLConfigParams *params = SSLConfig::acquire(); | |
| if (params != nullptr) { | |
| params->updateCTX(std::string(secret_name, secret_name_length)); | |
| } | |
| SSLConfig::release(params); | |
| return retval; | |
| } | |
| char * | |
| TSSslSecretGet(const char *secret_name, int secret_name_length, int *secret_data_length) | |
| { | |
| sdk_assert(secret_name != nullptr); | |
| sdk_assert(secret_data_length != nullptr); | |
| bool loading = true; | |
| SSLConfigParams *params = SSLConfig::load_acquire(); | |
| if (params == nullptr) { | |
| params = SSLConfig::acquire(); | |
| loading = false; | |
| } | |
| std::string const secret_data = params->secrets.getSecret(std::string(secret_name, secret_name_length)); | |
| char *data{nullptr}; | |
| if (secret_data.empty()) { | |
| *secret_data_length = 0; | |
| } else { | |
| data = static_cast<char *>(ats_malloc(secret_data.size())); | |
| memcpy(data, secret_data.data(), secret_data.size()); | |
| *secret_data_length = secret_data.size(); | |
| } | |
| if (loading) { | |
| SSLConfig::load_release(params); | |
| } else { | |
| SSLConfig::release(params); | |
| } | |
| return data; | |
| } | |
| /** | |
| * This function retrieves an array of lookup keys for client contexts loaded in | |
| * traffic server. Given a 2-level mapping for client contexts, every 2 lookup keys | |
| * can be used to locate and identify 1 context. | |
| * @param n Allocated size for result array. | |
| * @param result Const char pointer arrays to be filled with lookup keys. | |
| * @param actual Total number of lookup keys. | |
| */ | |
| TSReturnCode | |
| TSSslClientContextsNamesGet(int n, const char **result, int *actual) | |
| { | |
| sdk_assert(n == 0 || result != nullptr); | |
| int idx = 0, count = 0; | |
| SSLConfigParams *params = SSLConfig::acquire(); | |
| if (params) { | |
| auto &ctx_map_lock = params->ctxMapLock; | |
| auto &ca_map = params->top_level_ctx_map; | |
| auto mem = static_cast<std::string_view *>(alloca(sizeof(std::string_view) * n)); | |
| ink_mutex_acquire(&ctx_map_lock); | |
| for (auto &ca_pair : ca_map) { | |
| // Populate mem array with 2 strings each time | |
| for (auto &ctx_pair : ca_pair.second) { | |
| if (idx + 1 < n) { | |
| mem[idx++] = ca_pair.first; | |
| mem[idx++] = ctx_pair.first; | |
| } | |
| count += 2; | |
| } | |
| } | |
| ink_mutex_release(&ctx_map_lock); | |
| for (int i = 0; i < idx; i++) { | |
| result[i] = mem[i].data(); | |
| } | |
| } | |
| if (actual) { | |
| *actual = count; | |
| } | |
| SSLConfig::release(params); | |
| return TS_SUCCESS; | |
| } | |
| /** | |
| * This function returns the client context corresponding to the lookup keys provided. | |
| * User should call TSSslClientContextsGet() first to determine which lookup keys are | |
| * present before querying for them. User will need to release the context returned | |
| * from this function. | |
| * Returns valid TSSslContext on success and nullptr on failure. | |
| * @param first_key Key string for the top level. | |
| * @param second_key Key string for the second level. | |
| */ | |
| TSSslContext | |
| TSSslClientContextFindByName(const char *ca_paths, const char *ck_paths) | |
| { | |
| if (!ca_paths || !ck_paths || ca_paths[0] == '\0' || ck_paths[0] == '\0') { | |
| return nullptr; | |
| } | |
| SSLConfigParams *params = SSLConfig::acquire(); | |
| TSSslContext retval = nullptr; | |
| if (params) { | |
| ink_mutex_acquire(¶ms->ctxMapLock); | |
| auto ca_iter = params->top_level_ctx_map.find(ca_paths); | |
| if (ca_iter != params->top_level_ctx_map.end()) { | |
| auto ctx_iter = ca_iter->second.find(ck_paths); | |
| if (ctx_iter != ca_iter->second.end()) { | |
| SSL_CTX_up_ref(ctx_iter->second.get()); | |
| retval = reinterpret_cast<TSSslContext>(ctx_iter->second.get()); | |
| } | |
| } | |
| ink_mutex_release(¶ms->ctxMapLock); | |
| } | |
| SSLConfig::release(params); | |
| return retval; | |
| } | |
| TSSslContext | |
| TSSslServerContextCreate(TSSslX509 cert, const char *certname, const char *rsp_file) | |
| { | |
| TSSslContext ret = nullptr; | |
| SSLConfigParams *config = SSLConfig::acquire(); | |
| if (config != nullptr) { | |
| ret = reinterpret_cast<TSSslContext>(SSLCreateServerContext(config, nullptr)); | |
| if (ret && SSLConfigParams::ssl_ocsp_enabled && cert && certname) { | |
| if (SSL_CTX_set_tlsext_status_cb(reinterpret_cast<SSL_CTX *>(ret), ssl_callback_ocsp_stapling)) { | |
| if (!ssl_stapling_init_cert(reinterpret_cast<SSL_CTX *>(ret), reinterpret_cast<X509 *>(cert), certname, rsp_file)) { | |
| Warning("failed to configure SSL_CTX for OCSP Stapling info for certificate at %s", certname); | |
| } | |
| } | |
| } | |
| SSLConfig::release(config); | |
| } | |
| return ret; | |
| } | |
| void | |
| TSSslContextDestroy(TSSslContext ctx) noexcept | |
| try { | |
| SSLReleaseContext(reinterpret_cast<SSL_CTX *>(ctx)); | |
| } catch (...) { | |
| ink_abort("exception escaped %s", __func__); | |
| } | |
| TSReturnCode | |
| TSSslClientCertUpdate(const char *cert_path, const char *key_path) | |
| { | |
| if (nullptr == cert_path) { | |
| return TS_ERROR; | |
| } | |
| // --- Pin the active SSL configuration --- | |
| // | |
| // Keep this configuration generation alive across every early return and | |
| // release it automatically when the update finishes. | |
| std::string key{cert_path}; | |
| SSLConfig::scoped_config params; | |
| // The client context map is keyed by the resolved certificate path. | |
| Dbg(dbg_ctl_ssl_cert_update, "TSSslClientCertUpdate(): Use %.*s as key for lookup", static_cast<int>(key.size()), key.data()); | |
| if (!params) { | |
| return TS_ERROR; | |
| } | |
| auto &ca_paths_map = params->top_level_ctx_map; | |
| auto &map_lock = params->ctxMapLock; | |
| std::vector<std::string> ca_paths_keys; | |
| // --- Find every matching CA bucket --- | |
| // | |
| // A certificate can be used with more than one CA configuration. Snapshot | |
| // all matching bucket keys while holding the map lock, then release it | |
| // before performing the expensive context construction. | |
| ink_mutex_acquire(&map_lock); | |
| for (auto const &[ca_paths_key, ctx_map] : ca_paths_map) { | |
| if (ctx_map.contains(key)) { | |
| ca_paths_keys.push_back(ca_paths_key); | |
| } | |
| } | |
| ink_mutex_release(&map_lock); | |
| if (ca_paths_keys.empty()) { | |
| return TS_ERROR; | |
| } | |
| // --- Drop the cached certificate data --- | |
| // | |
| // getCTX() builds contexts from the cached secret data rather than from the | |
| // files. Drop the cached copies so that a context built later for a CA | |
| // bucket that does not exist yet also picks up the updated certificate | |
| // instead of the pre-update PEM. | |
| params->secrets.invalidateSecret(key); | |
| if (key_path != nullptr && key_path[0] != '\0') { | |
| params->secrets.invalidateSecret(key_path); | |
| } | |
| std::vector<std::pair<std::string, shared_SSL_CTX>> client_contexts; | |
| // --- Build every replacement context --- | |
| // | |
| // Build all replacements before changing the live map. If any construction | |
| // fails, the existing working contexts remain installed. | |
| client_contexts.reserve(ca_paths_keys.size()); | |
| for (auto const &ca_paths_key : ca_paths_keys) { | |
| size_t sep = ca_paths_key.find(':'); | |
| std::string ca_bundle_file = ca_paths_key.substr(0, sep); | |
| std::string ca_bundle_path = ca_paths_key.substr(sep + 1); | |
| shared_SSL_CTX client_ctx(SSLCreateClientContext(params, ca_bundle_file.empty() ? nullptr : ca_bundle_file.c_str(), | |
| ca_bundle_path.empty() ? nullptr : ca_bundle_path.c_str(), cert_path, | |
| key_path), | |
| SSL_CTX_free); | |
| if (!client_ctx) { | |
| return TS_ERROR; | |
| } | |
| client_contexts.emplace_back(ca_paths_key, std::move(client_ctx)); | |
| } | |
| std::vector<shared_SSL_CTX *> targets; | |
| // --- Install all replacement contexts --- | |
| // | |
| // Reacquire the map lock and locate every target before overwriting any of | |
| // them, so that the live map is either updated completely or left untouched. | |
| targets.reserve(client_contexts.size()); | |
| ink_mutex_acquire(&map_lock); | |
| for (auto const &client_context : client_contexts) { | |
| auto ca_iter = ca_paths_map.find(client_context.first); | |
| if (ca_iter == ca_paths_map.end()) { | |
| break; | |
| } | |
| auto ctx_iter = ca_iter->second.find(key); | |
| if (ctx_iter == ca_iter->second.end()) { | |
| break; | |
| } | |
| targets.push_back(&ctx_iter->second); | |
| } | |
| bool const updated_all = targets.size() == client_contexts.size(); | |
| if (updated_all) { | |
| for (std::size_t i = 0; i < targets.size(); ++i) { | |
| *targets[i] = std::move(client_contexts[i].second); | |
| } | |
| } | |
| ink_mutex_release(&map_lock); | |
| return updated_all ? TS_SUCCESS : TS_ERROR; | |
| } | |
| TSReturnCode | |
| TSSslServerCertUpdate(const char *cert_path, const char *key_path) | |
| { | |
| if (nullptr == cert_path) { | |
| return TS_ERROR; | |
| } | |
| if (!key_path || key_path[0] == '\0') { | |
| key_path = cert_path; | |
| } | |
| SSLCertContext *cc = nullptr; | |
| shared_SSL_CTX test_ctx = nullptr; | |
| std::shared_ptr<X509> cert = nullptr; | |
| SSLConfig::scoped_config config; | |
| SSLCertificateConfig::scoped_config lookup; | |
| if (lookup && config) { | |
| // Read cert from path to extract lookup key (common name) | |
| scoped_BIO bio(BIO_new_file(cert_path, "r")); | |
| if (bio) { | |
| cert = std::shared_ptr<X509>(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr), X509_free); | |
| } | |
| if (!bio || !cert) { | |
| SSLError("Failed to load certificate/key from %s", cert_path); | |
| return TS_ERROR; | |
| } | |
| // Extract common name | |
| const int pos = X509_NAME_get_index_by_NID(X509_get_subject_name(cert.get()), NID_commonName, -1); | |
| const X509_NAME_ENTRY *common_name = X509_NAME_get_entry(X509_get_subject_name(cert.get()), pos); | |
| const ASN1_STRING *common_name_asn1 = X509_NAME_ENTRY_get_data(common_name); | |
| char *common_name_str = reinterpret_cast<char *>(const_cast<unsigned char *>(ASN1_STRING_get0_data(common_name_asn1))); | |
| if (ASN1_STRING_length(common_name_asn1) != static_cast<int>(strlen(common_name_str))) { | |
| // Embedded null char | |
| return TS_ERROR; | |
| } | |
| Dbg(dbg_ctl_ssl_cert_update, "Updating from %s with common name %s", cert_path, common_name_str); | |
| // Update context to use cert | |
| cc = lookup->find(common_name_str); | |
| if (cc && cc->getCtx()) { | |
| test_ctx = shared_SSL_CTX(SSLCreateServerContext(config, cc->userconfig.get(), cert_path, key_path), SSLReleaseContext); | |
| if (!test_ctx) { | |
| return TS_ERROR; | |
| } | |
| // Atomic Swap | |
| cc->setCtx(std::move(test_ctx)); | |
| return TS_SUCCESS; | |
| } | |
| } | |
| return TS_ERROR; | |
| } | |
| TSReturnCode | |
| TSSslTicketKeyUpdate(char *ticketData, int ticketDataLen) | |
| { | |
| return SSLTicketKeyConfig::reconfigure_data(ticketData, ticketDataLen) ? TS_SUCCESS : TS_ERROR; | |
| } | |
| TSReturnCode | |
| TSVConnProtocolEnable(TSVConn connp, const char *protocol_name) | |
| { | |
| TSReturnCode retval = TS_ERROR; | |
| int protocol_idx = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{protocol_name}); | |
| auto net_vc = reinterpret_cast<UnixNetVConnection *>(connp); | |
| if (auto alpn = net_vc->get_service<ALPNSupport>(); alpn) { | |
| alpn->enableProtocol(protocol_idx); | |
| retval = TS_SUCCESS; | |
| } | |
| return retval; | |
| } | |
| TSReturnCode | |
| TSVConnProtocolDisable(TSVConn connp, const char *protocol_name) | |
| { | |
| TSReturnCode retval = TS_ERROR; | |
| int protocol_idx = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{protocol_name}); | |
| auto net_vc = reinterpret_cast<UnixNetVConnection *>(connp); | |
| if (auto alpn = net_vc->get_service<ALPNSupport>(); alpn) { | |
| alpn->disableProtocol(protocol_idx); | |
| retval = TS_SUCCESS; | |
| } | |
| return retval; | |
| } | |
| TSAcceptor | |
| TSAcceptorGet(TSVConn sslp) | |
| { | |
| NetVConnection *vc = reinterpret_cast<NetVConnection *>(sslp); | |
| SSLNetVConnection *ssl_vc = dynamic_cast<SSLNetVConnection *>(vc); | |
| return ssl_vc ? reinterpret_cast<TSAcceptor>(ssl_vc->accept_object) : nullptr; | |
| } | |
| TSAcceptor | |
| TSAcceptorGetbyID(int ID) | |
| { | |
| SCOPED_MUTEX_LOCK(lock, naVecMutex, this_ethread()); | |
| auto ret = naVec.at(ID); | |
| Dbg(dbg_ctl_ssl, "getNetAccept in INK API.cc %p", ret); | |
| return reinterpret_cast<TSAcceptor>(ret); | |
| } | |
| int | |
| TSAcceptorIDGet(TSAcceptor acceptor) | |
| { | |
| NetAccept *na = reinterpret_cast<NetAccept *>(acceptor); | |
| return na ? na->id : -1; | |
| } | |
| int | |
| TSAcceptorCount() | |
| { | |
| SCOPED_MUTEX_LOCK(lock, naVecMutex, this_ethread()); | |
| return naVec.size(); | |
| } | |
| int | |
| TSVConnIsSsl(TSVConn sslp) | |
| { | |
| NetVConnection *vc = reinterpret_cast<NetVConnection *>(sslp); | |
| SSLNetVConnection *ssl_vc = dynamic_cast<SSLNetVConnection *>(vc); | |
| return ssl_vc != nullptr; | |
| } | |
| int | |
| TSVConnProvidedSslCert(TSVConn sslp) | |
| { | |
| NetVConnection *vc = reinterpret_cast<NetVConnection *>(sslp); | |
| return vc->provided_cert(); | |
| } | |
| void | |
| TSVConnReenable(TSVConn vconn) | |
| { | |
| TSVConnReenableEx(vconn, TS_EVENT_CONTINUE); | |
| } | |
| void | |
| TSVConnReenableEx(TSVConn vconn, TSEvent event) | |
| { | |
| NetVConnection *vc = reinterpret_cast<NetVConnection *>(vconn); | |
| if (auto tes = vc->get_service<TLSEventSupport>(); tes) { | |
| EThread *eth = this_ethread(); | |
| // We use the mutex of VC's NetHandler so we can put the VC into ready_list by reenable() | |
| Ptr<ProxyMutex> m = tes->getMutexForTLSEvents(); | |
| MUTEX_TRY_LOCK(trylock, m, eth); | |
| if (trylock.is_locked()) { | |
| tes->reenable(event); | |
| } else { | |
| // We schedule the reenable to the home thread of ssl_vc. | |
| tes->getThreadForTLSEvents()->schedule_imm(new TSSslCallback(tes, event)); | |
| } | |
| } | |
| } | |
| TSReturnCode | |
| TSVConnPPInfoGet(TSVConn vconn, uint16_t key, const char **value, int *length) | |
| { | |
| NetVConnection *vc = reinterpret_cast<NetVConnection *>(vconn); | |
| if (key < 0x100) { | |
| auto &tlv = vc->get_proxy_protocol_info().tlv; | |
| if (auto ite = tlv.find(key); ite != tlv.end()) { | |
| *value = ite->second.data(); | |
| *length = ite->second.length(); | |
| } else { | |
| return TS_ERROR; | |
| } | |
| } else { | |
| switch (key) { | |
| case TS_PP_INFO_SRC_ADDR: | |
| *value = reinterpret_cast<const char *>(vc->get_proxy_protocol_src_addr()); | |
| if (*value == nullptr) { | |
| return TS_ERROR; | |
| } | |
| *length = ats_ip_size(reinterpret_cast<const sockaddr *>(*value)); | |
| break; | |
| case TS_PP_INFO_DST_ADDR: | |
| *value = reinterpret_cast<const char *>(vc->get_proxy_protocol_dst_addr()); | |
| if (*value == nullptr) { | |
| return TS_ERROR; | |
| } | |
| *length = ats_ip_size(reinterpret_cast<const sockaddr *>(*value)); | |
| break; | |
| default: | |
| return TS_ERROR; | |
| } | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSVConnPPInfoIntGet(TSVConn vconn, uint16_t key, TSMgmtInt *value) | |
| { | |
| NetVConnection *vc = reinterpret_cast<NetVConnection *>(vconn); | |
| if (key < 0x100) { | |
| // Unknown type value cannot be returned as an integer | |
| return TS_ERROR; | |
| } else { | |
| switch (key) { | |
| case TS_PP_INFO_VERSION: | |
| *value = static_cast<TSMgmtInt>(vc->get_proxy_protocol_version()); | |
| break; | |
| case TS_PP_INFO_SRC_PORT: | |
| *value = static_cast<TSMgmtInt>(vc->get_proxy_protocol_src_port()); | |
| break; | |
| case TS_PP_INFO_DST_PORT: | |
| *value = static_cast<TSMgmtInt>(vc->get_proxy_protocol_dst_port()); | |
| break; | |
| case TS_PP_INFO_PROTOCOL: | |
| *value = static_cast<TSMgmtInt>(vc->get_proxy_protocol_info().ip_family); | |
| break; | |
| case TS_PP_INFO_SOCK_TYPE: | |
| *value = static_cast<TSMgmtInt>(vc->get_proxy_protocol_info().type); | |
| break; | |
| default: | |
| return TS_ERROR; | |
| } | |
| } | |
| return TS_SUCCESS; | |
| } | |
| // APIs for managing and using UUIDs. | |
| TSUuid | |
| TSUuidCreate() | |
| { | |
| ATSUuid *uuid = new ATSUuid(); | |
| return reinterpret_cast<TSUuid>(uuid); | |
| } | |
| void | |
| TSUuidDestroy(TSUuid uuid) | |
| { | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)uuid) == TS_SUCCESS); | |
| delete reinterpret_cast<ATSUuid *>(uuid); | |
| } | |
| TSReturnCode | |
| TSUuidCopy(TSUuid dest, const TSUuid src) | |
| { | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)dest) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)src) == TS_SUCCESS); | |
| ATSUuid *d = reinterpret_cast<ATSUuid *>(dest); | |
| ATSUuid *s = reinterpret_cast<ATSUuid *>(src); | |
| if (s->valid()) { | |
| *d = *s; | |
| return TS_SUCCESS; | |
| } | |
| return TS_ERROR; | |
| } | |
| TSReturnCode | |
| TSUuidInitialize(TSUuid uuid, TSUuidVersion v) | |
| { | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)uuid) == TS_SUCCESS); | |
| ATSUuid *u = reinterpret_cast<ATSUuid *>(uuid); | |
| u->initialize(v); | |
| return u->valid() ? TS_SUCCESS : TS_ERROR; | |
| } | |
| TSUuid | |
| TSProcessUuidGet() | |
| { | |
| Machine *machine = Machine::instance(); | |
| return reinterpret_cast<TSUuid>(&machine->process_uuid); | |
| } | |
| const char * | |
| TSUuidStringGet(const TSUuid uuid) | |
| { | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)uuid) == TS_SUCCESS); | |
| ATSUuid *u = reinterpret_cast<ATSUuid *>(uuid); | |
| if (u->valid()) { | |
| return u->getString(); | |
| } | |
| return nullptr; | |
| } | |
| TSReturnCode | |
| TSClientRequestUuidGet(TSHttpTxn txnp, char *uuid_str) | |
| { | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)uuid_str) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| const char *machine = const_cast<char *>(Machine::instance()->process_uuid.getString()); | |
| int len; | |
| len = snprintf(uuid_str, TS_CRUUID_STRING_LEN + 1, "%s-%" PRId64 "", machine, sm->sm_id); | |
| if (len > TS_CRUUID_STRING_LEN) { | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSUuidStringParse(TSUuid uuid, const char *str) | |
| { | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)uuid) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)str) == TS_SUCCESS); | |
| ATSUuid *u = reinterpret_cast<ATSUuid *>(uuid); | |
| if (u->parseString(str)) { | |
| return TS_SUCCESS; | |
| } | |
| return TS_ERROR; | |
| } | |
| TSUuidVersion | |
| TSUuidVersionGet(TSUuid uuid) | |
| { | |
| sdk_assert(sdk_sanity_check_null_ptr((void *)uuid) == TS_SUCCESS); | |
| ATSUuid *u = reinterpret_cast<ATSUuid *>(uuid); | |
| return u->version(); | |
| } | |
| // Expose the HttpSM's sequence number (ID) | |
| uint64_t | |
| TSHttpTxnIdGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| return static_cast<uint64_t>(sm->sm_id); | |
| } | |
| // Returns unique client session identifier | |
| int64_t | |
| TSHttpSsnIdGet(TSHttpSsn ssnp) | |
| { | |
| sdk_assert(sdk_sanity_check_http_ssn(ssnp) == TS_SUCCESS); | |
| ProxySession const *cs = reinterpret_cast<ProxySession *>(ssnp); | |
| return cs->connection_id(); | |
| } | |
| // Return information about the protocols used by the client | |
| TSReturnCode | |
| TSHttpTxnClientProtocolStackGet(TSHttpTxn txnp, int count, const char **result, int *actual) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(count == 0 || result != nullptr); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| int new_count = 0; | |
| if (sm && count > 0) { | |
| auto mem = static_cast<std::string_view *>(alloca(sizeof(std::string_view) * count)); | |
| new_count = sm->populate_client_protocol(mem, count); | |
| for (int i = 0; i < new_count; ++i) { | |
| result[i] = mem[i].data(); | |
| } | |
| } | |
| if (actual) { | |
| *actual = new_count; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSHttpSsnClientProtocolStackGet(TSHttpSsn ssnp, int count, const char **result, int *actual) | |
| { | |
| sdk_assert(sdk_sanity_check_http_ssn(ssnp) == TS_SUCCESS); | |
| sdk_assert(count == 0 || result != nullptr); | |
| auto const *cs = reinterpret_cast<ProxySession *>(ssnp); | |
| int new_count = 0; | |
| if (cs && count > 0) { | |
| auto mem = static_cast<std::string_view *>(alloca(sizeof(std::string_view) * count)); | |
| new_count = cs->populate_protocol(mem, count); | |
| for (int i = 0; i < new_count; ++i) { | |
| result[i] = mem[i].data(); | |
| } | |
| } | |
| if (actual) { | |
| *actual = new_count; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| // Return information about the protocols used by the server | |
| TSReturnCode | |
| TSHttpTxnServerProtocolStackGet(TSHttpTxn txnp, int count, const char **result, int *actual) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(count == 0 || result != nullptr); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| int new_count = 0; | |
| if (sm && count > 0) { | |
| auto mem = static_cast<std::string_view *>(alloca(sizeof(std::string_view) * count)); | |
| new_count = sm->populate_server_protocol(mem, count); | |
| for (int i = 0; i < new_count; ++i) { | |
| result[i] = mem[i].data(); | |
| } | |
| } | |
| if (actual) { | |
| *actual = new_count; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| const char * | |
| TSNormalizedProtocolTag(const char *tag) | |
| { | |
| return RecNormalizeProtoTag(tag); | |
| } | |
| const char * | |
| TSHttpTxnClientProtocolStackContains(TSHttpTxn txnp, const char *tag) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| return sm->client_protocol_contains(std::string_view{tag}); | |
| } | |
| const char * | |
| TSHttpSsnClientProtocolStackContains(TSHttpSsn ssnp, const char *tag) | |
| { | |
| sdk_assert(sdk_sanity_check_http_ssn(ssnp) == TS_SUCCESS); | |
| ProxySession *cs = reinterpret_cast<ProxySession *>(ssnp); | |
| return cs->protocol_contains(std::string_view{tag}); | |
| } | |
| const char * | |
| TSHttpTxnServerProtocolStackContains(TSHttpTxn txnp, const char *tag) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| return sm->server_protocol_contains(std::string_view{tag}); | |
| } | |
| const char * | |
| TSRegisterProtocolTag(const char * /* tag ATS_UNUSED */) | |
| { | |
| return nullptr; | |
| } | |
| TSReturnCode | |
| TSHttpTxnRedoCacheLookup(TSHttpTxn txnp, const char *url, int length) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| HttpTransact::State *s = &(sm->t_state); | |
| sdk_assert(s->next_action == HttpTransact::StateMachineAction_t::CACHE_LOOKUP); | |
| // Because of where this is in the state machine, the storage for the cache_info URL must | |
| // have already been initialized and @a lookup_url must be valid. | |
| auto result = s->cache_info.lookup_url->parse(url, length < 0 ? strlen(url) : length); | |
| if (ParseResult::DONE == result) { | |
| s->transact_return_point = nullptr; | |
| sm->rewind_state_machine(); | |
| return TS_SUCCESS; | |
| } | |
| return TS_ERROR; | |
| } | |
| namespace | |
| { | |
| // Function that contains the common logic for TSRemapFrom/ToUrlGet(). | |
| // | |
| TSReturnCode | |
| remapUrlGet(TSHttpTxn txnp, TSMLoc *urlLocp, URL *(UrlMappingContainer::*mfp)() const) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr(urlLocp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| URL *url = (sm->t_state.url_map.*mfp)(); | |
| if (url == nullptr) { | |
| return TS_ERROR; | |
| } | |
| auto urlImpl = url->m_url_impl; | |
| if (urlImpl == nullptr) { | |
| return TS_ERROR; | |
| } | |
| *urlLocp = reinterpret_cast<TSMLoc>(urlImpl); | |
| return TS_SUCCESS; | |
| } | |
| } // end anonymous namespace | |
| TSReturnCode | |
| TSRemapFromUrlGet(TSHttpTxn txnp, TSMLoc *urlLocp) | |
| { | |
| return remapUrlGet(txnp, urlLocp, &UrlMappingContainer::getFromURL); | |
| } | |
| TSReturnCode | |
| TSRemapToUrlGet(TSHttpTxn txnp, TSMLoc *urlLocp) | |
| { | |
| return remapUrlGet(txnp, urlLocp, &UrlMappingContainer::getToURL); | |
| } | |
| void * | |
| TSRemapDLHandleGet(TSRemapPluginInfo plugin_info) | |
| { | |
| sdk_assert(sdk_sanity_check_null_ptr(plugin_info) == TS_SUCCESS); | |
| RemapPluginInfo *info = reinterpret_cast<RemapPluginInfo *>(plugin_info); | |
| return info->dlh(); | |
| } | |
| TSReturnCode | |
| TSHostnameIsSelf(const char *hostname, size_t hostname_len) | |
| { | |
| return Machine::instance()->is_self(std::string_view{hostname, hostname_len}) ? TS_SUCCESS : TS_ERROR; | |
| } | |
| TSReturnCode | |
| TSHostStatusGet(const char *hostname, const size_t hostname_len, TSHostStatus *status, unsigned int *reason) | |
| { | |
| HostStatRec *hst = HostStatus::instance().getHostStatus(std::string_view(hostname, hostname_len)); | |
| if (hst == nullptr) { | |
| return TS_ERROR; | |
| } | |
| if (status != nullptr) { | |
| *status = hst->status; | |
| } | |
| if (reason != nullptr) { | |
| *reason = hst->reasons; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| void | |
| TSHostStatusSet(const char *hostname, const size_t hostname_len, TSHostStatus status, const unsigned int down_time, | |
| const unsigned int reason) | |
| { | |
| HostStatus::instance().setHostStatus(std::string_view(hostname, hostname_len), status, down_time, reason); | |
| } | |
| // TSHttpTxnResponseActionSet takes a ResponseAction and sets it as the behavior for finding the next parent. | |
| // Be aware ATS will never change this outside a plugin. Therefore, plugins which set the ResponseAction | |
| // to retry must also un-set it after the subsequent success or failure, or ATS will retry forever! | |
| // | |
| // The passed *action must not be null, and is copied and may be destroyed after this call returns. | |
| // Callers must maintain owernship of action.hostname, and its lifetime must exceed the transaction. | |
| void | |
| TSHttpTxnResponseActionSet(TSHttpTxn txnp, TSResponseAction *action) | |
| { | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| HttpTransact::State *s = &(sm->t_state); | |
| s->response_action.handled = true; | |
| s->response_action.action = *action; | |
| } | |
| // Get the ResponseAction set by a plugin. | |
| // | |
| // The action is an out-param and must point to a valid location. | |
| // The returned action.hostname must not be modified, and is owned by some plugin if not null. | |
| // | |
| // The action members will always be zero, if no plugin has called TSHttpTxnResponseActionSet. | |
| // | |
| void | |
| TSHttpTxnResponseActionGet(TSHttpTxn txnp, TSResponseAction *action) | |
| { | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| HttpTransact::State *s = &(sm->t_state); | |
| if (!s->response_action.handled) { | |
| memset(action, 0, sizeof(TSResponseAction)); // because {0} gives a C++ warning. Ugh. | |
| } else { | |
| *action = s->response_action.action; | |
| } | |
| } | |
| TSIOBufferReader | |
| TSHttpTxnPostBufferReaderGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| return reinterpret_cast<TSIOBufferReader>(sm->get_postbuf_clone_reader()); | |
| } | |
| TSRPCProviderHandle | |
| TSRPCRegister(const char *provider_name, size_t provider_len, const char *yaml_version, size_t yamlcpp_lib_len) | |
| { | |
| sdk_assert(sdk_sanity_check_null_ptr(yaml_version) == TS_SUCCESS); | |
| sdk_assert(sdk_sanity_check_null_ptr(provider_name) == TS_SUCCESS); | |
| // We want to make sure that plugins are using the same yaml library version as we use internally. Plugins have to cast the | |
| // TSYaml to the YAML::Node, in order for them to make sure the version compatibility they need to register here and make sure | |
| // the version is the same. | |
| if (std::string_view{yaml_version, yamlcpp_lib_len} != YAMLCPP_LIB_VERSION) { | |
| Dbg(dbg_ctl_rpc_api, "[%.*s] YAML version check failed. Passed='%.*s', expected='%s'", static_cast<int>(provider_len), | |
| provider_name, static_cast<int>(yamlcpp_lib_len), yaml_version, YAMLCPP_LIB_VERSION); | |
| return nullptr; | |
| } | |
| ::rpc::RPCRegistryInfo *info = new ::rpc::RPCRegistryInfo(); | |
| info->provider = {provider_name, provider_len}; | |
| return reinterpret_cast<TSRPCProviderHandle>(info); | |
| } | |
| TSReturnCode | |
| TSRPCRegisterMethodHandler(const char *name, size_t name_len, TSRPCMethodCb callback, TSRPCProviderHandle info, | |
| const TSRPCHandlerOptions *opt) | |
| { | |
| sdk_assert(sdk_sanity_check_rpc_handler_options(opt) == TS_SUCCESS); | |
| if (!::rpc::add_method_handler_from_plugin( | |
| {name, name_len}, | |
| [callback](std::string_view const &id, const YAML::Node ¶ms) -> void { | |
| std::string msgId{id.data(), id.size()}; | |
| callback(msgId.c_str(), reinterpret_cast<TSYaml>(const_cast<YAML::Node *>(¶ms))); | |
| }, | |
| reinterpret_cast<const ::rpc::RPCRegistryInfo *>(info), *opt)) { | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSRPCRegisterNotificationHandler(const char *name, size_t name_len, TSRPCNotificationCb callback, TSRPCProviderHandle info, | |
| const TSRPCHandlerOptions *opt) | |
| { | |
| sdk_assert(sdk_sanity_check_rpc_handler_options(opt) == TS_SUCCESS); | |
| if (!::rpc::add_notification_handler( | |
| {name, name_len}, | |
| [callback](const YAML::Node ¶ms) -> void { callback(reinterpret_cast<TSYaml>(const_cast<YAML::Node *>(¶ms))); }, | |
| reinterpret_cast<const ::rpc::RPCRegistryInfo *>(info), *opt)) { | |
| return TS_ERROR; | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSRPCHandlerDone(TSYaml resp) | |
| { | |
| Dbg(dbg_ctl_rpc_api, ">> Handler seems to be done"); | |
| std::lock_guard<std::mutex> lock(::rpc::g_rpcHandlingMutex); | |
| auto const &data = *reinterpret_cast<YAML::Node const *>(resp); | |
| ::rpc::g_rpcHandlerResponseData = data; | |
| ::rpc::g_rpcHandlerProcessingCompleted = true; | |
| ::rpc::g_rpcHandlingCompletion.notify_one(); | |
| Dbg(dbg_ctl_rpc_api, ">> all set."); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSRPCHandlerError(int ec, const char *descr, size_t descr_len) | |
| { | |
| Dbg(dbg_ctl_rpc_api, ">> Handler seems to be done with an error"); | |
| std::lock_guard<std::mutex> lock(rpc::g_rpcHandlingMutex); | |
| ::rpc::g_rpcHandlerResponseData = swoc::Errata(ts::make_errno_code(ec), "{}", swoc::TextView{descr, descr_len}); | |
| ::rpc::g_rpcHandlerProcessingCompleted = true; | |
| ::rpc::g_rpcHandlingCompletion.notify_one(); | |
| Dbg(dbg_ctl_rpc_api, ">> error flagged."); | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSRecYAMLConfigParse(TSYaml node, TSYAMLRecNodeHandler handler, void *data) | |
| { | |
| swoc::Errata err; | |
| try { | |
| err = ParseRecordsFromYAML( | |
| *reinterpret_cast<YAML::Node *>(node), | |
| [handler, data](const CfgNode &field, swoc::Errata &) -> void { | |
| // Errors from the handler should be reported and handled by the handler. | |
| // RecYAMLConfigFileParse will report any YAML parsing error. | |
| TSYAMLRecCfgFieldData cfg; | |
| auto const &field_str = field.node.as<std::string>(); | |
| cfg.field_name = field_str.c_str(); | |
| cfg.record_name = field.get_record_name().data(); | |
| cfg.value_node = reinterpret_cast<TSYaml>(const_cast<YAML::Node *>(&field.value_node)); | |
| handler(&cfg, data); | |
| }, | |
| true /* lock */); | |
| } catch (std::exception const &ex) { | |
| err.note(ERRATA_ERROR, "RecYAMLConfigParse error cought: {}", ex.what()); | |
| } | |
| // Drop API logs in case of an error. | |
| if (!err.empty()) { | |
| std::string buf; | |
| Dbg(dbg_ctl_plugin, "%s", swoc::bwprint(buf, "{}", err).c_str()); | |
| } | |
| return err.empty() ? TS_SUCCESS : TS_ERROR; | |
| } | |
| TSTxnType | |
| TSHttpTxnTypeGet(TSHttpTxn txnp) | |
| { | |
| sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); | |
| HttpSM *sm = reinterpret_cast<HttpSM *>(txnp); | |
| TSTxnType retval = TS_TXN_TYPE_UNKNOWN; | |
| if (sm != nullptr) { | |
| if (sm->t_state.transparent_passthrough) { | |
| retval = TS_TXN_TYPE_TR_PASS_TUNNEL; | |
| } else if (sm->t_state.client_info.port_attribute == HttpProxyPort::TRANSPORT_BLIND_TUNNEL) { | |
| retval = TS_TXN_TYPE_EXPLICIT_TUNNEL; | |
| } else { | |
| retval = TS_TXN_TYPE_HTTP; | |
| } | |
| } | |
| return retval; | |
| } | |
| TSReturnCode | |
| TSConnectionLimitExemptListAdd(std::string_view ip_ranges) | |
| { | |
| swoc::TextView ip_ranges_tv{ip_ranges}; | |
| while (auto ip_range_tv = ip_ranges_tv.take_prefix_at(',')) { | |
| swoc::IPRange ip_range; | |
| if (!ip_range.load(ip_range_tv)) { | |
| return TS_ERROR; | |
| } | |
| bool success = ConnectionTracker::add_client_exempt_range(ip_range); | |
| if (!success) { | |
| return TS_ERROR; | |
| } | |
| } | |
| return TS_SUCCESS; | |
| } | |
| TSReturnCode | |
| TSConnectionLimitExemptListRemove(std::string_view ip_ranges) | |
| { | |
| swoc::TextView ip_ranges_tv{ip_ranges}; | |
| while (auto ip_range_tv = ip_ranges_tv.take_prefix_at(',')) { | |
| swoc::IPRange ip_range; | |
| if (!ip_range.load(ip_range_tv)) { | |
| return TS_ERROR; | |
| } | |
| bool success = ConnectionTracker::remove_client_exempt_range(ip_range); | |
| if (!success) { | |
| return TS_ERROR; | |
| } | |
| } | |
| return TS_SUCCESS; | |
| } | |
| void | |
| TSConnectionLimitExemptListClear() | |
| { | |
| ConnectionTracker::clear_client_exempt_list(); | |
| } | |
| TSReturnCode | |
| TSLogFieldRegister(std::string_view name, std::string_view symbol, TSLogType type, TSLogMarshalCallback marshal_cb, | |
| TSLogUnmarshalCallback unmarshal_cb, bool replace) | |
| { | |
| if (auto ite = Log::field_symbol_hash.find(symbol.data()); ite != Log::field_symbol_hash.end()) { | |
| if (replace) { | |
| // Symbol is registered and the plugin wants to replace it. | |
| // Need to unregister the existing entry first. | |
| Log::global_field_list.remove(ite->second); | |
| Log::field_symbol_hash.erase(ite); | |
| } else { | |
| // Symbol conflict. | |
| return TS_ERROR; | |
| } | |
| } | |
| // TSLogType mirrors LogField::Type's values and is static_cast to it below. | |
| // apidefs.h.in can't reference LogField::Type, so pin the alignment here (the | |
| // only TU that sees both) -- a reorder then fails to compile. | |
| static_assert(static_cast<int>(TS_LOG_TYPE_INT) == static_cast<int>(LogField::Type::sINT)); | |
| static_assert(static_cast<int>(TS_LOG_TYPE_STRING) == static_cast<int>(LogField::Type::STRING)); | |
| static_assert(static_cast<int>(TS_LOG_TYPE_ADDR) == static_cast<int>(LogField::Type::IP)); | |
| // Reject anything outside the public set before the cast, so a bad plugin | |
| // value can't become an INVALID/out-of-range LogField::Type and trip the ctor. | |
| if (type != TS_LOG_TYPE_INT && type != TS_LOG_TYPE_STRING && type != TS_LOG_TYPE_ADDR) { | |
| return TS_ERROR; | |
| } | |
| LogField *field = new LogField( | |
| name.data(), symbol.data(), static_cast<LogField::Type>(type), | |
| [marshal_cb = std::move(marshal_cb)](void *sm, char *buf) -> int { return marshal_cb(reinterpret_cast<TSHttpTxn>(sm), buf); }, | |
| unmarshal_cb); | |
| Log::register_field(field); | |
| return TS_SUCCESS; | |
| } | |
| int | |
| TSLogStringMarshal(char *buf, std::string_view str) | |
| { | |
| if (buf) { | |
| ink_strlcpy(buf, str.data(), str.length() + 1); | |
| } | |
| return str.length() + 1; | |
| } | |
| std::tuple<int, int> | |
| TSLogStringUnmarshal(char **buf, char *dest, int len) | |
| { | |
| // We cannot use LogAccess::unmarshal_str, etc. here because those internal | |
| // functions take care of log buffer alignment. This function needs to be | |
| // implemented as if it's a piece of code in plugin code, which is unaware | |
| // of the alignment. | |
| if (int l = strlen(*buf); l < len) { | |
| memcpy(dest, *buf, l); | |
| return {l, l}; | |
| } else { | |
| return {-1, -1}; | |
| } | |
| } | |
| int | |
| TSLogIntMarshal(char *buf, int64_t value) | |
| { | |
| if (buf) { | |
| *(reinterpret_cast<int64_t *>(buf)) = value; | |
| } | |
| return sizeof(int64_t); | |
| } | |
| std::tuple<int, int> | |
| TSLogIntUnmarshal(char **buf, char *dest, int len) | |
| { | |
| int64_t val = *(reinterpret_cast<int64_t *>(*buf)); | |
| auto [end, err] = std::to_chars(dest, dest + len, val); | |
| if (err == std::errc()) { | |
| *end = '\0'; | |
| return {sizeof(uint64_t), end - dest}; | |
| } | |
| return {-1, -1}; | |
| } | |
| int | |
| TSLogAddrMarshal(char *buf, sockaddr *addr) | |
| { | |
| LogFieldIpStorage data; | |
| int len = sizeof(data._ip); | |
| if (nullptr == addr) { | |
| data._ip._family = AF_UNSPEC; | |
| } else if (ats_is_ip4(addr)) { | |
| if (buf) { | |
| data._ip4._family = AF_INET; | |
| data._ip4._addr = ats_ip4_addr_cast(addr); | |
| } | |
| len = sizeof(data._ip4); | |
| } else if (ats_is_ip6(addr)) { | |
| if (buf) { | |
| data._ip6._family = AF_INET6; | |
| data._ip6._addr = ats_ip6_addr_cast(addr); | |
| } | |
| len = sizeof(data._ip6); | |
| } else if (ats_is_unix(addr)) { | |
| if (buf) { | |
| data._un._family = AF_UNIX; | |
| strncpy(data._un._path, ats_unix_cast(addr)->sun_path, TS_UNIX_SIZE); | |
| } | |
| len = sizeof(data._un); | |
| } else { | |
| data._ip._family = AF_UNSPEC; | |
| } | |
| if (buf) { | |
| memcpy(buf, &data, len); | |
| } | |
| return len; | |
| } | |
| std::tuple<int, int> | |
| TSLogAddrUnmarshal(char **buf, char *dest, int len) | |
| { | |
| IpEndpoint endpoint; | |
| int read_len = sizeof(LogFieldIp); | |
| LogFieldIp *raw = reinterpret_cast<LogFieldIp *>(*buf); | |
| if (AF_INET == raw->_family) { | |
| LogFieldIp4 *ip4 = static_cast<LogFieldIp4 *>(raw); | |
| ats_ip4_set(&endpoint, ip4->_addr); | |
| read_len = sizeof(*ip4); | |
| } else if (AF_INET6 == raw->_family) { | |
| LogFieldIp6 *ip6 = static_cast<LogFieldIp6 *>(raw); | |
| ats_ip6_set(&endpoint, ip6->_addr); | |
| read_len = sizeof(*ip6); | |
| } else if (AF_UNIX == raw->_family) { | |
| LogFieldUn *un = static_cast<LogFieldUn *>(raw); | |
| ats_unix_set(&endpoint, un->_path, TS_UNIX_SIZE); | |
| read_len = sizeof(*un); | |
| } else { | |
| ats_ip_invalidate(&endpoint); | |
| } | |
| if (!ats_is_ip(&endpoint) && !ats_is_unix(&endpoint)) { | |
| dest[0] = '0'; | |
| dest[1] = '\0'; | |
| return {-1, 1}; | |
| } else if (ats_ip_ntop(&endpoint, dest, len)) { | |
| return {read_len, static_cast<int>(::strlen(dest))}; | |
| } | |
| return {-1, -1}; | |
| } | |
| bool | |
| TSClientHello::is_available() const | |
| { | |
| return static_cast<bool>(*this); | |
| } | |
| uint16_t | |
| TSClientHello::get_version() const | |
| { | |
| return static_cast<TLSSNISupport::ClientHello *>(_client_hello)->getVersion(); | |
| } | |
| const uint8_t * | |
| TSClientHello::get_cipher_suites() const | |
| { | |
| return reinterpret_cast<const uint8_t *>(static_cast<TLSSNISupport::ClientHello *>(_client_hello)->getCipherSuites().data()); | |
| } | |
| size_t | |
| TSClientHello::get_cipher_suites_len() const | |
| { | |
| return static_cast<TLSSNISupport::ClientHello *>(_client_hello)->getCipherSuites().length(); | |
| } | |
| TSClientHello::TSExtensionTypeList::Iterator::Iterator(const void *ite) | |
| { | |
| static_assert(sizeof(_real_iterator) >= sizeof(TLSSNISupport::ClientHello::ExtensionIdIterator)); | |
| ink_assert(_real_iterator); | |
| ink_assert(ite); | |
| memcpy(_real_iterator, ite, sizeof(TLSSNISupport::ClientHello::ExtensionIdIterator)); | |
| } | |
| TSClientHello::TSExtensionTypeList::Iterator | |
| TSClientHello::TSExtensionTypeList::begin() | |
| { | |
| ink_assert(_ch); | |
| auto ch = static_cast<TLSSNISupport::ClientHello *>(_ch); | |
| auto ite = ch->begin(); | |
| // The temporal pointer is for the memcpy in the constructor. It's only used in the constructor. | |
| return TSClientHello::TSExtensionTypeList::Iterator(&ite); | |
| } | |
| TSClientHello::TSExtensionTypeList::Iterator | |
| TSClientHello::TSExtensionTypeList::end() | |
| { | |
| auto ite = static_cast<TLSSNISupport::ClientHello *>(_ch)->end(); | |
| // The temporal pointer is for the memcpy in the constructor. It's only used in the constructor. | |
| return TSClientHello::TSExtensionTypeList::Iterator(&ite); | |
| } | |
| TSClientHello::TSExtensionTypeList::Iterator & | |
| TSClientHello::TSExtensionTypeList::Iterator::operator++() | |
| { | |
| ++(*reinterpret_cast<TLSSNISupport::ClientHello::ExtensionIdIterator *>(_real_iterator)); | |
| return *this; | |
| } | |
| bool | |
| TSClientHello::TSExtensionTypeList::Iterator::operator==(const TSClientHello::TSExtensionTypeList::Iterator &b) const | |
| { | |
| return memcmp(_real_iterator, b._real_iterator, sizeof(_real_iterator)) == 0; | |
| } | |
| int | |
| TSClientHello::TSExtensionTypeList::Iterator::operator*() const | |
| { | |
| return *(*reinterpret_cast<const TLSSNISupport::ClientHello::ExtensionIdIterator *>(_real_iterator)); | |
| } |