src/api/InkAPI.cc (6,789 lines of code) (raw):

/** @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 <tuple> #include <unordered_map> #include <string_view> #include <string> #include "iocore/net/NetVConnection.h" #include "iocore/net/NetHandler.h" #include "iocore/net/UDPNet.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/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/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/rpc/jsonrpc/JsonRPC.h" #include <swoc/bwf_base.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> FetchSMAllocator; /* From proxy/http/HttpProxyServerMain.c: */ extern bool ssl_register_protocol(const char *, Continuation *); extern SSLSessionCache *session_cache; // declared extern in P_SSLConfig.h // External converters. extern MgmtConverter const &HttpDownServerCacheTimeConv; extern HttpSessionAccept *plugin_http_accept; extern HttpSessionAccept *plugin_http_transparent_accept; extern thread_local PluginThreadContext *pluginThreadContext; static ClassAllocator<APIHook> apiHookAllocator("apiHookAllocator"); extern ClassAllocator<INKContInternal> INKContAllocator; extern ClassAllocator<INKVConnInternal> INKVConnAllocator; static ClassAllocator<MIMEFieldSDKHandle> 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, HDR_HEAP_OBJ_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, ...) { va_list args; va_start(args, fmt); ErrorV(fmt, args); va_end(args); } 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 (obj->m_type == HDR_HEAP_OBJ_HTTP_HEADER) { impl = static_cast<HTTPHdrImpl *>(obj)->m_fields_impl; } else if (obj->m_type == HDR_HEAP_OBJ_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 (field_handle->m_type != HDR_HEAP_OBJ_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 != HDR_BUF_MAGIC_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 (field_handle->m_type != HDR_HEAP_OBJ_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 (field_handle->m_type != HDR_HEAP_OBJ_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 (field_handle->m_type != HDR_HEAP_OBJ_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 == HTTP_SM_MAGIC_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) { 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 (obj->m_type) { case HDR_HEAP_OBJ_URL: case HDR_HEAP_OBJ_HTTP_HEADER: case HDR_HEAP_OBJ_MIME_HEADER: return TS_SUCCESS; case HDR_HEAP_OBJ_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; } } //////////////////////////////////////////////////////////////////// // // 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) { // 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; } //////////////////////////////////////////////////////////////////// // // 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 = const char *(URL::*)(int *); using URLPartSetF = void (URL::*)(const char *, int); static const char * URLPartGet(TSMBuffer bufp, TSMLoc obj, int *length, URLPartGetF url_f) { 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); URL u; u.m_heap = (reinterpret_cast<HdrHeapSDKHandle *>(bufp))->m_heap; u.m_url_impl = reinterpret_cast<URLImpl *>(obj); return (u.*url_f)(length); } 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)(value, length); return TS_SUCCESS; } const char * TSUrlRawSchemeGet(TSMBuffer bufp, TSMLoc obj, int *length) { return URLPartGet(bufp, obj, length, &URL::scheme_get); } 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 URL_TYPE_HTTP: data = URL_SCHEME_HTTP; *length = URL_LEN_HTTP; break; case URL_TYPE_HTTPS: data = URL_SCHEME_HTTPS; *length = URL_LEN_HTTPS; 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) { return URLPartGet(bufp, obj, length, &URL::user_get); } 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) { return URLPartGet(bufp, obj, length, &URL::password_get); } 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) { return URLPartGet(bufp, obj, length, &URL::host_get); } 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) { return URLPartGet(bufp, obj, length, &URL::path_get); } 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) { return URLPartGet(bufp, obj, length, &URL::query_get); } 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) { return URLPartGet(bufp, obj, length, &URL::fragment_get); } 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) { // 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; } 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(); } } static void 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, value, length); } else { mime_field_value_set(heap, handle->mh, handle->field_ptr, value, 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, value, 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, name, 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(heap, mh); // 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, name, name_len); *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, s_handle->field_ptr->m_ptr_name, s_handle->field_ptr->m_len_name, s_handle->field_ptr->m_ptr_value, 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, s_field->m_ptr_value, 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); } handle->field_ptr->name_set(heap, handle->mh, name, length); if (attached) { mime_hdr_field_attach(handle->mh, handle->field_ptr, 1, nullptr); } return TS_SUCCESS; } 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, 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); } TSMimeFieldValueSet(bufp, field, idx, value, length); return TS_SUCCESS; } 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, value, 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) { sdk_assert(sdk_sanity_check_http_parser(parser) == TS_SUCCESS); http_parser_clear(reinterpret_cast<HTTPParser *>(parser)); ats_free(parser); } /***********/ /* 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(HTTP_TYPE_UNKNOWN); return reinterpret_cast<TSMLoc>(h.m_http); } void TSHttpHdrDestroy(TSMBuffer bufp, TSMLoc obj) { 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 } 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 (s_hh->m_type != HDR_HEAP_OBJ_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 ((s_hh->m_type != HDR_HEAP_OBJ_HTTP_HEADER) || (d_hh->m_type != HDR_HEAP_OBJ_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(h.m_http->m_type == HDR_HEAP_OBJ_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(h.m_http->m_type == HDR_HEAP_OBJ_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(h.m_http->m_type == HDR_HEAP_OBJ_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(h.m_http->m_type == HDR_HEAP_OBJ_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 == HDR_HEAP_OBJ_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(h.m_http->m_type == HDR_HEAP_OBJ_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 == HTTP_TYPE_UNKNOWN) { if (type == static_cast<TSHttpType>(HTTP_TYPE_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>(HTTP_TYPE_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(h.m_http->m_type == HDR_HEAP_OBJ_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(value, 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 != HTTP_TYPE_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 (hh->m_type != HDR_HEAP_OBJ_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) { // 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(h.m_http->m_type == HDR_HEAP_OBJ_HTTP_HEADER); h.status_set(static_cast<HTTPStatus>(status)); return TS_SUCCESS; } 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 == HDR_HEAP_OBJ_HTTP_HEADER); */ if (length < 0) { length = strlen(value); } h.reason_set(value, 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) { 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; } 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); } TSReturnCode TSMgmtIntGet(const char *var_name, TSMgmtInt *result) { auto res = RecGetRecordInt(const_cast<char *>(var_name), static_cast<RecInt *>(result)); // Try the old librecords first if (res == REC_ERR_FAIL) { int id = global_api_metrics.lookup(var_name); if (id == ts::Metrics::NOT_FOUND) { return TS_ERROR; } else { *result = global_api_metrics[id].load(); } } return TS_SUCCESS; } TSReturnCode TSMgmtCounterGet(const char *var_name, TSMgmtCounter *result) { auto res = RecGetRecordCounter(const_cast<char *>(var_name), static_cast<RecCounter *>(result)); // Try the old librecords first if (res == REC_ERR_FAIL) { int id = global_api_metrics.lookup(var_name); if (id == ts::Metrics::NOT_FOUND) { return TS_ERROR; } else { *result = global_api_metrics[id].load(); } } return TS_SUCCESS; } // ToDo: These don't have the new metrics, only librecords. TSReturnCode TSMgmtFloatGet(const char *var_name, TSMgmtFloat *result) { return RecGetRecordFloat(const_cast<char *>(var_name), static_cast<RecFloat *>(result)) == REC_ERR_OKAY ? TS_SUCCESS : TS_ERROR; } TSReturnCode TSMgmtStringGet(const char *var_name, TSMgmtString *result) { RecString tmp = nullptr; (void)RecGetRecordString_Xmalloc(const_cast<char *>(var_name), &tmp); if (tmp) { *result = 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) { 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(); } void TSContDataSet(TSCont contp, void *data) { sdk_assert(sdk_sanity_check_iocore_structure(contp) == TS_SUCCESS); INKContInternal *i = reinterpret_cast<INKContInternal *>(contp); i->mdata = data; } void * TSContDataGet(TSCont contp) { sdk_assert(sdk_sanity_check_iocore_structure(contp) == TS_SUCCESS); INKContInternal *i = reinterpret_cast<INKContInternal *>(contp); return i->mdata; } 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 != HTTP_TYPE_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::CACHE_LOOKUP_MISS: case HttpTransact::CACHE_LOOKUP_DOC_BUSY: *lookup_status = TS_CACHE_LOOKUP_MISS; break; case HttpTransact::CACHE_LOOKUP_HIT_STALE: *lookup_status = TS_CACHE_LOOKUP_HIT_STALE; break; case HttpTransact::CACHE_LOOKUP_HIT_WARNING: case HttpTransact::CACHE_LOOKUP_HIT_FRESH: *lookup_status = TS_CACHE_LOOKUP_HIT_FRESH; break; case HttpTransact::CACHE_LOOKUP_SKIPPED: *lookup_status = TS_CACHE_LOOKUP_SKIPPED; break; case HttpTransact::CACHE_LOOKUP_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::CACHE_LOOKUP_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::CACHE_LOOKUP_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::CACHE_LOOKUP_MISS; break; case TS_CACHE_LOOKUP_HIT_STALE: *sm_status = HttpTransact::CACHE_LOOKUP_HIT_STALE; break; case TS_CACHE_LOOKUP_HIT_FRESH: *sm_status = HttpTransact::CACHE_LOOKUP_HIT_FRESH; break; default: 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; } /** * 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 SQUID_LOG_ERR_CLIENT_ABORT: case SQUID_LOG_ERR_CLIENT_READ_ERROR: case SQUID_LOG_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::CACHE_WL_READ_RETRY) { return TS_ERROR; } s->api_update_cached_object = HttpTransact::UPDATE_CACHED_OBJECT_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_remote_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; } 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) { 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); } 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->getSSLSessionCacheHit() : 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) { 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(); } // 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) { sdk_assert(sdk_sanity_check_iocore_structure(connp) == TS_SUCCESS); VConnection *vc = reinterpret_cast<VConnection *>(connp); vc->do_io_close(); } void TSVConnAbort(TSVConn connp, int error) { sdk_assert(sdk_sanity_check_iocore_structure(connp) == TS_SUCCESS); VConnection *vc = reinterpret_cast<VConnection *>(connp); vc->do_io_close(error); } void TSVConnShutdown(TSVConn connp, int read, int write) { 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); } } 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 = HTTP_PLUGIN_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 = HTTP_PLUGIN_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(); } 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; } } /************************** 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) { sdk_assert(sdk_sanity_check_iocore_structure(the_object) == TS_SUCCESS); (reinterpret_cast<TextLogObject *>(the_object))->force_new_buffer(); } TSReturnCode TSTextLogObjectDestroy(TSTextLogObject the_object) { 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; } 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; sm->redirection_tries = 0; // 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) { sdk_assert(sdk_sanity_check_fetch_sm(fetch_sm) == TS_SUCCESS); (reinterpret_cast<FetchSM *>(fetch_sm))->ext_destroy(); } 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 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) == PARSE_RESULT_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, MIME_FIELD_ACCEPT_ENCODING, MIME_LEN_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; if (ink_aio_read(pAIO, 1) == 1) { return TS_SUCCESS; } 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; if (ink_aio_write(pAIO, 1) == 1) { return TS_SUCCESS; } 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; } // Little helper function to find the struct member static void * _conf_to_memberp(TSOverridableConfigKey conf, OverridableHttpConfigParams *overridableHttpConfig, MgmtConverter const *&conv) { void *ret = nullptr; conv = nullptr; switch (conf) { case TS_CONFIG_URL_REMAP_PRISTINE_HOST_HDR: ret = _memberp_to_generic(&overridableHttpConfig->maintain_pristine_host_hdr, conv); break; case TS_CONFIG_HTTP_CHUNKING_ENABLED: ret = _memberp_to_generic(&overridableHttpConfig->chunking_enabled, conv); break; case TS_CONFIG_HTTP_NEGATIVE_CACHING_ENABLED: ret = _memberp_to_generic(&overridableHttpConfig->negative_caching_enabled, conv); break; case TS_CONFIG_HTTP_NEGATIVE_CACHING_LIFETIME: ret = _memberp_to_generic(&overridableHttpConfig->negative_caching_lifetime, conv); break; case TS_CONFIG_HTTP_CACHE_WHEN_TO_REVALIDATE: ret = _memberp_to_generic(&overridableHttpConfig->cache_when_to_revalidate, conv); break; case TS_CONFIG_HTTP_KEEP_ALIVE_ENABLED_IN: ret = _memberp_to_generic(&overridableHttpConfig->keep_alive_enabled_in, conv); break; case TS_CONFIG_HTTP_KEEP_ALIVE_ENABLED_OUT: ret = _memberp_to_generic(&overridableHttpConfig->keep_alive_enabled_out, conv); break; case TS_CONFIG_HTTP_KEEP_ALIVE_POST_OUT: ret = _memberp_to_generic(&overridableHttpConfig->keep_alive_post_out, conv); break; case TS_CONFIG_HTTP_SERVER_SESSION_SHARING_MATCH: ret = _memberp_to_generic(&overridableHttpConfig->server_session_sharing_match, conv); break; case TS_CONFIG_NET_SOCK_RECV_BUFFER_SIZE_OUT: ret = _memberp_to_generic(&overridableHttpConfig->sock_recv_buffer_size_out, conv); break; case TS_CONFIG_NET_SOCK_SEND_BUFFER_SIZE_OUT: ret = _memberp_to_generic(&overridableHttpConfig->sock_send_buffer_size_out, conv); break; case TS_CONFIG_NET_SOCK_OPTION_FLAG_OUT: ret = _memberp_to_generic(&overridableHttpConfig->sock_option_flag_out, conv); break; case TS_CONFIG_HTTP_FORWARD_PROXY_AUTH_TO_PARENT: ret = _memberp_to_generic(&overridableHttpConfig->fwd_proxy_auth_to_parent, conv); break; case TS_CONFIG_HTTP_ANONYMIZE_REMOVE_FROM: ret = _memberp_to_generic(&overridableHttpConfig->anonymize_remove_from, conv); break; case TS_CONFIG_HTTP_ANONYMIZE_REMOVE_REFERER: ret = _memberp_to_generic(&overridableHttpConfig->anonymize_remove_referer, conv); break; case TS_CONFIG_HTTP_ANONYMIZE_REMOVE_USER_AGENT: ret = _memberp_to_generic(&overridableHttpConfig->anonymize_remove_user_agent, conv); break; case TS_CONFIG_HTTP_ANONYMIZE_REMOVE_COOKIE: ret = _memberp_to_generic(&overridableHttpConfig->anonymize_remove_cookie, conv); break; case TS_CONFIG_HTTP_ANONYMIZE_REMOVE_CLIENT_IP: ret = _memberp_to_generic(&overridableHttpConfig->anonymize_remove_client_ip, conv); break; case TS_CONFIG_HTTP_ANONYMIZE_INSERT_CLIENT_IP: ret = _memberp_to_generic(&overridableHttpConfig->anonymize_insert_client_ip, conv); break; case TS_CONFIG_HTTP_RESPONSE_SERVER_ENABLED: ret = _memberp_to_generic(&overridableHttpConfig->proxy_response_server_enabled, conv); break; case TS_CONFIG_HTTP_INSERT_SQUID_X_FORWARDED_FOR: ret = _memberp_to_generic(&overridableHttpConfig->insert_squid_x_forwarded_for, conv); break; case TS_CONFIG_HTTP_INSERT_FORWARDED: ret = _memberp_to_generic(&overridableHttpConfig->insert_forwarded, conv); break; case TS_CONFIG_HTTP_PROXY_PROTOCOL_OUT: ret = _memberp_to_generic(&overridableHttpConfig->proxy_protocol_out, conv); break; case TS_CONFIG_HTTP_SEND_HTTP11_REQUESTS: ret = _memberp_to_generic(&overridableHttpConfig->send_http11_requests, conv); break; case TS_CONFIG_HTTP_CACHE_HTTP: ret = _memberp_to_generic(&overridableHttpConfig->cache_http, conv); break; case TS_CONFIG_HTTP_CACHE_IGNORE_CLIENT_NO_CACHE: ret = _memberp_to_generic(&overridableHttpConfig->cache_ignore_client_no_cache, conv); break; case TS_CONFIG_HTTP_CACHE_IGNORE_CLIENT_CC_MAX_AGE: ret = _memberp_to_generic(&overridableHttpConfig->cache_ignore_client_cc_max_age, conv); break; case TS_CONFIG_HTTP_CACHE_IMS_ON_CLIENT_NO_CACHE: ret = _memberp_to_generic(&overridableHttpConfig->cache_ims_on_client_no_cache, conv); break; case TS_CONFIG_HTTP_CACHE_IGNORE_SERVER_NO_CACHE: ret = _memberp_to_generic(&overridableHttpConfig->cache_ignore_server_no_cache, conv); break; case TS_CONFIG_HTTP_CACHE_CACHE_RESPONSES_TO_COOKIES: ret = _memberp_to_generic(&overridableHttpConfig->cache_responses_to_cookies, conv); break; case TS_CONFIG_HTTP_CACHE_IGNORE_AUTHENTICATION: ret = _memberp_to_generic(&overridableHttpConfig->cache_ignore_auth, conv); break; case TS_CONFIG_HTTP_CACHE_IGNORE_QUERY: ret = _memberp_to_generic(&overridableHttpConfig->cache_ignore_query, conv); break; case TS_CONFIG_HTTP_CACHE_REQUIRED_HEADERS: ret = _memberp_to_generic(&overridableHttpConfig->cache_required_headers, conv); break; case TS_CONFIG_HTTP_INSERT_REQUEST_VIA_STR: ret = _memberp_to_generic(&overridableHttpConfig->insert_request_via_string, conv); break; case TS_CONFIG_HTTP_INSERT_RESPONSE_VIA_STR: ret = _memberp_to_generic(&overridableHttpConfig->insert_response_via_string, conv); break; case TS_CONFIG_HTTP_CACHE_HEURISTIC_MIN_LIFETIME: ret = _memberp_to_generic(&overridableHttpConfig->cache_heuristic_min_lifetime, conv); break; case TS_CONFIG_HTTP_CACHE_HEURISTIC_MAX_LIFETIME: ret = _memberp_to_generic(&overridableHttpConfig->cache_heuristic_max_lifetime, conv); break; case TS_CONFIG_HTTP_CACHE_GUARANTEED_MIN_LIFETIME: ret = _memberp_to_generic(&overridableHttpConfig->cache_guaranteed_min_lifetime, conv); break; case TS_CONFIG_HTTP_CACHE_GUARANTEED_MAX_LIFETIME: ret = _memberp_to_generic(&overridableHttpConfig->cache_guaranteed_max_lifetime, conv); break; case TS_CONFIG_HTTP_CACHE_MAX_STALE_AGE: ret = _memberp_to_generic(&overridableHttpConfig->cache_max_stale_age, conv); break; case TS_CONFIG_HTTP_KEEP_ALIVE_NO_ACTIVITY_TIMEOUT_IN: ret = _memberp_to_generic(&overridableHttpConfig->keep_alive_no_activity_timeout_in, conv); break; case TS_CONFIG_HTTP_KEEP_ALIVE_NO_ACTIVITY_TIMEOUT_OUT: ret = _memberp_to_generic(&overridableHttpConfig->keep_alive_no_activity_timeout_out, conv); break; case TS_CONFIG_HTTP_TRANSACTION_NO_ACTIVITY_TIMEOUT_IN: ret = _memberp_to_generic(&overridableHttpConfig->transaction_no_activity_timeout_in, conv); break; case TS_CONFIG_HTTP_TRANSACTION_NO_ACTIVITY_TIMEOUT_OUT: ret = _memberp_to_generic(&overridableHttpConfig->transaction_no_activity_timeout_out, conv); break; case TS_CONFIG_HTTP_TRANSACTION_ACTIVE_TIMEOUT_OUT: ret = _memberp_to_generic(&overridableHttpConfig->transaction_active_timeout_out, conv); break; case TS_CONFIG_HTTP_CONNECT_ATTEMPTS_MAX_RETRIES: ret = _memberp_to_generic(&overridableHttpConfig->connect_attempts_max_retries, conv); break; case TS_CONFIG_HTTP_CONNECT_ATTEMPTS_MAX_RETRIES_DOWN_SERVER: ret = _memberp_to_generic(&overridableHttpConfig->connect_attempts_max_retries_down_server, conv); break; case TS_CONFIG_HTTP_CONNECT_DOWN_POLICY: ret = _memberp_to_generic(&overridableHttpConfig->connect_down_policy, conv); break; case TS_CONFIG_HTTP_CONNECT_ATTEMPTS_RR_RETRIES: ret = _memberp_to_generic(&overridableHttpConfig->connect_attempts_rr_retries, conv); break; case TS_CONFIG_HTTP_CONNECT_ATTEMPTS_TIMEOUT: ret = _memberp_to_generic(&overridableHttpConfig->connect_attempts_timeout, conv); break; case TS_CONFIG_HTTP_DOWN_SERVER_CACHE_TIME: conv = &HttpDownServerCacheTimeConv; ret = &overridableHttpConfig->down_server_timeout; break; case TS_CONFIG_HTTP_DOC_IN_CACHE_SKIP_DNS: ret = _memberp_to_generic(&overridableHttpConfig->doc_in_cache_skip_dns, conv); break; case TS_CONFIG_HTTP_BACKGROUND_FILL_ACTIVE_TIMEOUT: ret = _memberp_to_generic(&overridableHttpConfig->background_fill_active_timeout, conv); break; case TS_CONFIG_HTTP_RESPONSE_SERVER_STR: ret = _memberp_to_generic(&overridableHttpConfig->proxy_response_server_string, conv); break; case TS_CONFIG_HTTP_CACHE_HEURISTIC_LM_FACTOR: ret = _memberp_to_generic(&overridableHttpConfig->cache_heuristic_lm_factor, conv); break; case TS_CONFIG_HTTP_BACKGROUND_FILL_COMPLETED_THRESHOLD: ret = _memberp_to_generic(&overridableHttpConfig->background_fill_threshold, conv); break; case TS_CONFIG_NET_SOCK_PACKET_MARK_OUT: ret = _memberp_to_generic(&overridableHttpConfig->sock_packet_mark_out, conv); break; case TS_CONFIG_NET_SOCK_PACKET_TOS_OUT: ret = _memberp_to_generic(&overridableHttpConfig->sock_packet_tos_out, conv); break; case TS_CONFIG_HTTP_INSERT_AGE_IN_RESPONSE: ret = _memberp_to_generic(&overridableHttpConfig->insert_age_in_response, conv); break; case TS_CONFIG_HTTP_CHUNKING_SIZE: ret = _memberp_to_generic(&overridableHttpConfig->http_chunking_size, conv); break; case TS_CONFIG_HTTP_DROP_CHUNKED_TRAILERS: ret = _memberp_to_generic(&overridableHttpConfig->http_drop_chunked_trailers, conv); break; case TS_CONFIG_HTTP_STRICT_CHUNK_PARSING: ret = _memberp_to_generic(&overridableHttpConfig->http_strict_chunk_parsing, conv); break; case TS_CONFIG_HTTP_FLOW_CONTROL_ENABLED: ret = _memberp_to_generic(&overridableHttpConfig->flow_control_enabled, conv); break; case TS_CONFIG_HTTP_FLOW_CONTROL_LOW_WATER_MARK: ret = _memberp_to_generic(&overridableHttpConfig->flow_low_water_mark, conv); break; case TS_CONFIG_HTTP_FLOW_CONTROL_HIGH_WATER_MARK: ret = _memberp_to_generic(&overridableHttpConfig->flow_high_water_mark, conv); break; case TS_CONFIG_HTTP_CACHE_RANGE_LOOKUP: ret = _memberp_to_generic(&overridableHttpConfig->cache_range_lookup, conv); break; case TS_CONFIG_HTTP_NORMALIZE_AE: ret = _memberp_to_generic(&overridableHttpConfig->normalize_ae, conv); break; case TS_CONFIG_HTTP_DEFAULT_BUFFER_SIZE: ret = _memberp_to_generic(&overridableHttpConfig->default_buffer_size_index, conv); break; case TS_CONFIG_HTTP_DEFAULT_BUFFER_WATER_MARK: ret = _memberp_to_generic(&overridableHttpConfig->default_buffer_water_mark, conv); break; case TS_CONFIG_HTTP_REQUEST_HEADER_MAX_SIZE: ret = _memberp_to_generic(&overridableHttpConfig->request_hdr_max_size, conv); break; case TS_CONFIG_HTTP_RESPONSE_HEADER_MAX_SIZE: ret = _memberp_to_generic(&overridableHttpConfig->response_hdr_max_size, conv); break; case TS_CONFIG_HTTP_NEGATIVE_REVALIDATING_ENABLED: ret = _memberp_to_generic(&overridableHttpConfig->negative_revalidating_enabled, conv); break; case TS_CONFIG_HTTP_NEGATIVE_REVALIDATING_LIFETIME: ret = _memberp_to_generic(&overridableHttpConfig->negative_revalidating_lifetime, conv); break; case TS_CONFIG_SSL_HSTS_MAX_AGE: ret = _memberp_to_generic(&overridableHttpConfig->proxy_response_hsts_max_age, conv); break; case TS_CONFIG_SSL_HSTS_INCLUDE_SUBDOMAINS: ret = _memberp_to_generic(&overridableHttpConfig->proxy_response_hsts_include_subdomains, conv); break; case TS_CONFIG_HTTP_CACHE_OPEN_READ_RETRY_TIME: ret = _memberp_to_generic(&overridableHttpConfig->cache_open_read_retry_time, conv); break; case TS_CONFIG_HTTP_CACHE_MAX_OPEN_READ_RETRIES: ret = _memberp_to_generic(&overridableHttpConfig->max_cache_open_read_retries, conv); break; case TS_CONFIG_HTTP_CACHE_RANGE_WRITE: ret = _memberp_to_generic(&overridableHttpConfig->cache_range_write, conv); break; case TS_CONFIG_HTTP_POST_CHECK_CONTENT_LENGTH_ENABLED: ret = _memberp_to_generic(&overridableHttpConfig->post_check_content_length_enabled, conv); break; case TS_CONFIG_HTTP_CACHE_POST_METHOD: ret = _memberp_to_generic(&overridableHttpConfig->cache_post_method, conv); break; case TS_CONFIG_HTTP_REQUEST_BUFFER_ENABLED: ret = _memberp_to_generic(&overridableHttpConfig->request_buffer_enabled, conv); break; case TS_CONFIG_HTTP_GLOBAL_USER_AGENT_HEADER: ret = _memberp_to_generic(&overridableHttpConfig->global_user_agent_header, conv); break; case TS_CONFIG_HTTP_AUTH_SERVER_SESSION_PRIVATE: ret = _memberp_to_generic(&overridableHttpConfig->auth_server_session_private, conv); break; case TS_CONFIG_HTTP_SLOW_LOG_THRESHOLD: ret = _memberp_to_generic(&overridableHttpConfig->slow_log_threshold, conv); break; case TS_CONFIG_HTTP_CACHE_GENERATION: ret = _memberp_to_generic(&overridableHttpConfig->cache_generation_number, conv); break; case TS_CONFIG_BODY_FACTORY_TEMPLATE_BASE: ret = _memberp_to_generic(&overridableHttpConfig->body_factory_template_base, conv); break; case TS_CONFIG_HTTP_CACHE_OPEN_WRITE_FAIL_ACTION: ret = _memberp_to_generic(&overridableHttpConfig->cache_open_write_fail_action, conv); break; case TS_CONFIG_HTTP_NUMBER_OF_REDIRECTIONS: ret = _memberp_to_generic(&overridableHttpConfig->number_of_redirections, conv); break; case TS_CONFIG_HTTP_CACHE_MAX_OPEN_WRITE_RETRIES: ret = _memberp_to_generic(&overridableHttpConfig->max_cache_open_write_retries, conv); break; case TS_CONFIG_HTTP_CACHE_MAX_OPEN_WRITE_RETRY_TIMEOUT: ret = _memberp_to_generic(&overridableHttpConfig->max_cache_open_write_retry_timeout, conv); break; case TS_CONFIG_HTTP_REDIRECT_USE_ORIG_CACHE_KEY: ret = _memberp_to_generic(&overridableHttpConfig->redirect_use_orig_cache_key, conv); break; case TS_CONFIG_HTTP_ATTACH_SERVER_SESSION_TO_CLIENT: ret = _memberp_to_generic(&overridableHttpConfig->attach_server_session_to_client, conv); break; case TS_CONFIG_HTTP_MAX_PROXY_CYCLES: ret = _memberp_to_generic(&overridableHttpConfig->max_proxy_cycles, conv); break; case TS_CONFIG_WEBSOCKET_NO_ACTIVITY_TIMEOUT: ret = _memberp_to_generic(&overridableHttpConfig->websocket_inactive_timeout, conv); break; case TS_CONFIG_WEBSOCKET_ACTIVE_TIMEOUT: ret = _memberp_to_generic(&overridableHttpConfig->websocket_active_timeout, conv); break; case TS_CONFIG_HTTP_UNCACHEABLE_REQUESTS_BYPASS_PARENT: ret = _memberp_to_generic(&overridableHttpConfig->uncacheable_requests_bypass_parent, conv); break; case TS_CONFIG_HTTP_PARENT_PROXY_TOTAL_CONNECT_ATTEMPTS: ret = _memberp_to_generic(&overridableHttpConfig->parent_connect_attempts, conv); break; case TS_CONFIG_HTTP_TRANSACTION_ACTIVE_TIMEOUT_IN: ret = _memberp_to_generic(&overridableHttpConfig->transaction_active_timeout_in, conv); break; case TS_CONFIG_SRV_ENABLED: ret = _memberp_to_generic(&overridableHttpConfig->srv_enabled, conv); break; case TS_CONFIG_HTTP_FORWARD_CONNECT_METHOD: ret = _memberp_to_generic(&overridableHttpConfig->forward_connect_method, conv); break; case TS_CONFIG_SSL_CLIENT_VERIFY_SERVER_POLICY: case TS_CONFIG_SSL_CLIENT_VERIFY_SERVER_PROPERTIES: case TS_CONFIG_SSL_CLIENT_SNI_POLICY: case TS_CONFIG_SSL_CLIENT_CERT_FILENAME: case TS_CONFIG_SSL_CERT_FILEPATH: case TS_CONFIG_SSL_CLIENT_PRIVATE_KEY_FILENAME: case TS_CONFIG_SSL_CLIENT_CA_CERT_FILENAME: case TS_CONFIG_SSL_CLIENT_ALPN_PROTOCOLS: // String, must be handled elsewhere break; case TS_CONFIG_PARENT_FAILURES_UPDATE_HOSTDB: ret = _memberp_to_generic(&overridableHttpConfig->parent_failures_update_hostdb, conv); break; case TS_CONFIG_HTTP_CACHE_IGNORE_ACCEPT_MISMATCH: ret = _memberp_to_generic(&overridableHttpConfig->ignore_accept_mismatch, conv); break; case TS_CONFIG_HTTP_CACHE_IGNORE_ACCEPT_LANGUAGE_MISMATCH: ret = _memberp_to_generic(&overridableHttpConfig->ignore_accept_language_mismatch, conv); break; case TS_CONFIG_HTTP_CACHE_IGNORE_ACCEPT_ENCODING_MISMATCH: ret = _memberp_to_generic(&overridableHttpConfig->ignore_accept_encoding_mismatch, conv); break; case TS_CONFIG_HTTP_CACHE_IGNORE_ACCEPT_CHARSET_MISMATCH: ret = _memberp_to_generic(&overridableHttpConfig->ignore_accept_charset_mismatch, conv); break; case TS_CONFIG_HTTP_PARENT_PROXY_FAIL_THRESHOLD: ret = _memberp_to_generic(&overridableHttpConfig->parent_fail_threshold, conv); break; case TS_CONFIG_HTTP_PARENT_PROXY_RETRY_TIME: ret = _memberp_to_generic(&overridableHttpConfig->parent_retry_time, conv); break; case TS_CONFIG_HTTP_PER_PARENT_CONNECT_ATTEMPTS: ret = _memberp_to_generic(&overridableHttpConfig->per_parent_connect_attempts, conv); break; case TS_CONFIG_HTTP_ALLOW_MULTI_RANGE: ret = _memberp_to_generic(&overridableHttpConfig->allow_multi_range, conv); break; case TS_CONFIG_HTTP_ALLOW_HALF_OPEN: ret = _memberp_to_generic(&overridableHttpConfig->allow_half_open, conv); break; case TS_CONFIG_HTTP_PER_SERVER_CONNECTION_MAX: ret = &overridableHttpConfig->connection_tracker_config.server_max; conv = &ConnectionTracker::MAX_SERVER_CONV; break; case TS_CONFIG_HTTP_SERVER_MIN_KEEP_ALIVE_CONNS: ret = &overridableHttpConfig->connection_tracker_config.server_min; conv = &ConnectionTracker::MIN_SERVER_CONV; break; case TS_CONFIG_HTTP_PER_SERVER_CONNECTION_MATCH: ret = &overridableHttpConfig->connection_tracker_config.server_match; conv = &ConnectionTracker::SERVER_MATCH_CONV; break; case TS_CONFIG_HTTP_HOST_RESOLUTION_PREFERENCE: ret = &overridableHttpConfig->host_res_data; conv = &HttpTransact::HOST_RES_CONV; break; case TS_CONFIG_HTTP_NO_DNS_JUST_FORWARD_TO_PARENT: ret = _memberp_to_generic(&overridableHttpConfig->no_dns_forward_to_parent, conv); break; case TS_CONFIG_PLUGIN_VC_DEFAULT_BUFFER_INDEX: ret = _memberp_to_generic(&overridableHttpConfig->plugin_vc_default_buffer_index, conv); break; case TS_CONFIG_PLUGIN_VC_DEFAULT_BUFFER_WATER_MARK: ret = _memberp_to_generic(&overridableHttpConfig->plugin_vc_default_buffer_water_mark, conv); break; case TS_CONFIG_NET_SOCK_NOTSENT_LOWAT: ret = _memberp_to_generic(&overridableHttpConfig->sock_packet_notsent_lowat, conv); break; case TS_CONFIG_BODY_FACTORY_RESPONSE_SUPPRESSION_MODE: ret = _memberp_to_generic(&overridableHttpConfig->response_suppression_mode, conv); break; case TS_CONFIG_HTTP_ENABLE_PARENT_TIMEOUT_MARKDOWNS: ret = _memberp_to_generic(&overridableHttpConfig->enable_parent_timeout_markdowns, conv); break; case TS_CONFIG_HTTP_DISABLE_PARENT_MARKDOWNS: ret = _memberp_to_generic(&overridableHttpConfig->disable_parent_markdowns, conv); break; case TS_CONFIG_NET_DEFAULT_INACTIVITY_TIMEOUT: ret = _memberp_to_generic(&overridableHttpConfig->default_inactivity_timeout, conv); break; case TS_CONFIG_HTTP_CACHE_CACHE_URLS_THAT_LOOK_DYNAMIC: ret = _memberp_to_generic(&overridableHttpConfig->cache_urls_that_look_dynamic, conv); break; // This helps avoiding compiler warnings, yet detect unhandled enum members. case TS_CONFIG_NULL: case TS_CONFIG_LAST_ENTRY: break; } return ret; } // 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); } /* 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(); 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) { swoc::LocalBufferWriter<1024> error; HttpForwarded::OptionBitSet bs = HttpForwarded::optStrToBitset(std::string_view(value, length), error); if (!error.size()) { s->t_state.my_txn_conf().insert_forwarded = bs; } else { Error("HTTP %.*s", static_cast<int>(error.size()), error.data()); } } break; case TS_CONFIG_HTTP_SERVER_SESSION_SHARING_MATCH: if (value && length > 0) { HttpConfig::load_server_session_sharing_match(value, s->t_state.my_txn_conf().server_session_sharing_match); s->t_state.my_txn_conf().server_session_sharing_match_str = const_cast<char *>(value); } break; case TS_CONFIG_SSL_CLIENT_VERIFY_SERVER_POLICY: if (value && length > 0) { s->t_state.my_txn_conf().ssl_client_verify_server_policy = const_cast<char *>(value); } break; case TS_CONFIG_SSL_CLIENT_VERIFY_SERVER_PROPERTIES: if (value && length > 0) { s->t_state.my_txn_conf().ssl_client_verify_server_properties = const_cast<char *>(value); } break; case TS_CONFIG_SSL_CLIENT_SNI_POLICY: if (value && length > 0) { s->t_state.my_txn_conf().ssl_client_sni_policy = const_cast<char *>(value); } break; case TS_CONFIG_SSL_CLIENT_CERT_FILENAME: if (value && length > 0) { s->t_state.my_txn_conf().ssl_client_cert_filename = const_cast<char *>(value); } break; case TS_CONFIG_SSL_CLIENT_PRIVATE_KEY_FILENAME: if (value && length > 0) { s->t_state.my_txn_conf().ssl_client_private_key_filename = const_cast<char *>(value); } break; case TS_CONFIG_SSL_CLIENT_CA_CERT_FILENAME: if (value && length > 0) { s->t_state.my_txn_conf().ssl_client_ca_cert_filename = const_cast<char *>(value); } break; case TS_CONFIG_SSL_CLIENT_ALPN_PROTOCOLS: if (value && length > 0) { s->t_state.my_txn_conf().ssl_client_alpn_protocols = const_cast<char *>(value); } break; case TS_CONFIG_SSL_CERT_FILEPATH: /* noop */ break; case TS_CONFIG_HTTP_HOST_RESOLUTION_PREFERENCE: if (value && length > 0) { s->t_state.my_txn_conf().host_res_data.conf_value = const_cast<char *>(value); } [[fallthrough]]; default: { if (value && length > 0) { MgmtConverter const *conv; void *dest = _conf_to_memberp(conf, &(s->t_state.my_txn_conf()), conv); if (dest != nullptr && conv != nullptr && conv->store_string) { conv->store_string(dest, std::string_view(value, length)); } else { return TS_ERROR; } } 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; 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 = HTTP_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 == HTTP_NO_KEEPALIVE) { sm->t_state.client_info.keep_alive = HTTP_KEEPALIVE; } return TS_SUCCESS; } // Parse a port descriptor for the proxy.config.http.server_ports descriptor format. TSPortDescriptor TSPortDescriptorParse(const char *descriptor) { HttpProxyPort *port = new HttpProxyPort(); if (descriptor && port->processOptions(descriptor)) { return reinterpret_cast<TSPortDescriptor>(port); } delete port; return nullptr; } TSReturnCode TSPortDescriptorAccept(TSPortDescriptor descp, TSCont contp) { Action *action = nullptr; HttpProxyPort *port = reinterpret_cast<HttpProxyPort *>(descp); 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; } 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 == BACKGROUND_FILL_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; } 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(&params->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(&params->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) { SSLReleaseContext(reinterpret_cast<SSL_CTX *>(ctx)); } TSReturnCode TSSslClientCertUpdate(const char *cert_path, const char *key_path) { if (nullptr == cert_path) { return TS_ERROR; } std::string key; shared_SSL_CTX client_ctx = nullptr; SSLConfigParams *params = SSLConfig::acquire(); // Generate second level key for client context lookup swoc::bwprint(key, "{}:{}", cert_path, key_path); Dbg(dbg_ctl_ssl_cert_update, "TSSslClientCertUpdate(): Use %.*s as key for lookup", static_cast<int>(key.size()), key.data()); if (nullptr != params) { // Try to update client contexts maps auto &ca_paths_map = params->top_level_ctx_map; auto &map_lock = params->ctxMapLock; std::string ca_paths_key; // First try to locate the client context and its CA path (by top level) ink_mutex_acquire(&map_lock); for (auto &ca_paths_pair : ca_paths_map) { auto &ctx_map = ca_paths_pair.second; auto iter = ctx_map.find(key); if (iter != ctx_map.end() && iter->second != nullptr) { ca_paths_key = ca_paths_pair.first; break; } } ink_mutex_release(&map_lock); // Only update on existing if (ca_paths_key.empty()) { return TS_ERROR; } // Extract CA related paths 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); // Build new client context client_ctx = shared_SSL_CTX(SSLCreateClientContext(params, ca_bundle_path.empty() ? nullptr : ca_bundle_path.c_str(), ca_bundle_file.empty() ? nullptr : ca_bundle_file.c_str(), cert_path, key_path), SSL_CTX_free); // Successfully generates a client context, update in the map ink_mutex_acquire(&map_lock); auto iter = ca_paths_map.find(ca_paths_key); if (iter != ca_paths_map.end() && iter->second.count(key)) { iter->second[key] = client_ctx; } else { client_ctx = nullptr; } ink_mutex_release(&map_lock); } return client_ctx ? 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 int pos = X509_NAME_get_index_by_NID(X509_get_subject_name(cert.get()), NID_commonName, -1); X509_NAME_ENTRY *common_name = X509_NAME_get_entry(X509_get_subject_name(cert.get()), pos); 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(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)); } } } TSSslSession TSSslSessionGet(const TSSslSessionID *session_id) { SSL_SESSION *session = nullptr; if (session_id && session_cache) { session_cache->getSession(reinterpret_cast<const SSLSessionID &>(*session_id), &session, nullptr); } return reinterpret_cast<TSSslSession>(session); } int TSSslSessionGetBuffer(const TSSslSessionID *session_id, char *buffer, int *len_ptr) { int true_len = 0; // Don't get if there is no session id or the cache is not yet set up if (session_id && session_cache && len_ptr) { true_len = session_cache->getSessionBuffer(reinterpret_cast<const SSLSessionID &>(*session_id), buffer, *len_ptr); } return true_len; } TSReturnCode TSSslSessionInsert(const TSSslSessionID *session_id, TSSslSession add_session, TSSslConnection ssl_conn) { // Don't insert if there is no session id or the cache is not yet set up if (session_id && session_cache) { if (dbg_ctl_ssl_session_cache_insert.on()) { const SSLSessionID *sid = reinterpret_cast<const SSLSessionID *>(session_id); char buf[sid->len * 2 + 1]; sid->toString(buf, sizeof(buf)); DbgPrint(dbg_ctl_ssl_session_cache_insert, "TSSslSessionInsert: Inserting session '%s' ", buf); } SSL_SESSION *session = reinterpret_cast<SSL_SESSION *>(add_session); SSL *ssl = reinterpret_cast<SSL *>(ssl_conn); session_cache->insertSession(reinterpret_cast<const SSLSessionID &>(*session_id), session, ssl); // insertSession returns void, assume all went well return TS_SUCCESS; } else { return TS_ERROR; } } TSReturnCode TSSslSessionRemove(const TSSslSessionID *session_id) { // Don't remove if there is no session id or the cache is not yet set up if (session_id && session_cache) { session_cache->removeSession(reinterpret_cast<const SSLSessionID &>(*session_id)); // removeSession returns void, assume all went well return TS_SUCCESS; } else { return TS_ERROR; } } // 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->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()->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::SM_ACTION_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 (PARSE_RESULT_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 &params) -> void { std::string msgId{id.data(), id.size()}; callback(msgId.c_str(), reinterpret_cast<TSYaml>(const_cast<YAML::Node *>(&params))); }, 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 &params) -> void { callback(reinterpret_cast<TSYaml>(const_cast<YAML::Node *>(&params))); }, 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 data = *reinterpret_cast<YAML::Node *>(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; }