src/cripts/Headers.cc (317 lines of code) (raw):

/* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this f 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 "cripts/Lulu.hpp" #include "cripts/Preamble.hpp" // Constants const cripts::Header::Iterator cripts::Header::Iterator::_end = cripts::Header::Iterator("__END__", cripts::Header::Iterator::END_TAG); namespace cripts { namespace Method { #undef DELETE // ToDo: macOS shenanigans here, defining DELETE as a macro const cripts::Header::Method GET(TS_HTTP_METHOD_GET, TS_HTTP_LEN_GET); const cripts::Header::Method HEAD(TS_HTTP_METHOD_HEAD, TS_HTTP_LEN_HEAD); const cripts::Header::Method POST(TS_HTTP_METHOD_POST, TS_HTTP_LEN_POST); const cripts::Header::Method PUT(TS_HTTP_METHOD_PUT, TS_HTTP_LEN_PUT); const cripts::Header::Method PUSH(TS_HTTP_METHOD_PUSH, TS_HTTP_LEN_PUSH); const cripts::Header::Method DELETE(TS_HTTP_METHOD_DELETE, TS_HTTP_LEN_DELETE); const cripts::Header::Method OPTIONS(TS_HTTP_METHOD_OPTIONS, TS_HTTP_LEN_OPTIONS); const cripts::Header::Method CONNECT(TS_HTTP_METHOD_CONNECT, TS_HTTP_LEN_CONNECT); const cripts::Header::Method TRACE(TS_HTTP_METHOD_TRACE, TS_HTTP_LEN_TRACE); // This is a special feature of ATS const cripts::Header::Method PURGE(TS_HTTP_METHOD_PURGE, TS_HTTP_LEN_PURGE); } // namespace Method Header::Status & Header::Status::operator=(int status) { _ensure_initialized(_owner); _status = static_cast<TSHttpStatus>(status); switch (_owner->_state->hook) { case TS_HTTP_SEND_RESPONSE_HDR_HOOK: case TS_HTTP_READ_RESPONSE_HDR_HOOK: case TS_HTTP_TXN_CLOSE_HOOK: TSHttpHdrStatusSet(_owner->_bufp, _owner->_hdr_loc, _status); break; default: TSHttpTxnStatusSet(_owner->_state->txnp, _status); break; } _owner->_state->context->p_instance.debug("Setting status = {}", status); return *this; } Header::Status::operator integer() { if (_status == TS_HTTP_STATUS_NONE) { _ensure_initialized(_owner); _status = TSHttpHdrStatusGet(_owner->_bufp, _owner->_hdr_loc); } return _status; } Header::Reason & Header::Reason::operator=(cripts::string_view reason) { _ensure_initialized(_owner); TSHttpHdrReasonSet(_owner->_bufp, _owner->_hdr_loc, reason.data(), reason.size()); _owner->_state->context->p_instance.debug("Setting reason = {}", reason); return *this; } Header::Body & Header::Body::operator=(cripts::string_view body) { auto b = static_cast<char *>(TSmalloc(body.size() + 1)); _ensure_initialized(_owner); memcpy(b, body.data(), body.size()); b[body.size()] = '\0'; TSHttpTxnErrorBodySet(_owner->_state->txnp, b, body.size(), nullptr); return *this; } cripts::string_view Header::Method::GetSV() { if (_method.size() == 0) { _ensure_initialized(_owner); int len; const char *value = TSHttpHdrMethodGet(_owner->_bufp, _owner->_hdr_loc, &len); _method = cripts::string_view(value, static_cast<cripts::string_view::size_type>(len)); } return _method; } cripts::string_view Header::CacheStatus::GetSV() { static std::array<cripts::string_view, 4> names{ "miss", // TS_CACHE_LOOKUP_MISS, "hit-stale", // TS_CACHE_LOOKUP_HIT_STALE, "hit-fresh", // TS_CACHE_LOOKUP_HIT_FRESH, "skipped" // TS_CACHE_LOOKUP_SKIPPED }; int status; _ensure_initialized(_owner); if (_cache.size() == 0) { TSAssert(_owner->_state->txnp); if (TSHttpTxnCacheLookupStatusGet(_owner->_state->txnp, &status) == TS_ERROR || status < 0 || status >= 4) { _cache = "none"; } else { _cache = names[status]; } } return _cache; } Header::String & Header::String::operator=(const cripts::string_view str) { _ensure_initialized(_owner); if (_field_loc) { if (str.empty()) { TSMLoc tmp; // Nuke the existing header, this will nuke all of them _owner->_state->context->p_instance.debug("Deleting header = {}", _name); while (_field_loc) { tmp = TSMimeHdrFieldNextDup(_owner->_bufp, _owner->_hdr_loc, _field_loc); TSMimeHdrFieldDestroy(_owner->_bufp, _owner->_hdr_loc, _field_loc); TSHandleMLocRelease(_owner->_bufp, _owner->_hdr_loc, _field_loc); _field_loc = tmp; } } else { TSMLoc tmp = nullptr; bool first = true; // Replace the existing header while (_field_loc) { tmp = TSMimeHdrFieldNextDup(_owner->_bufp, _owner->_hdr_loc, _field_loc); if (first) { first = false; if (TS_SUCCESS == TSMimeHdrFieldValueStringSet(_owner->_bufp, _owner->_hdr_loc, _field_loc, -1, str.data(), str.size())) { _owner->_state->context->p_instance.debug("Replacing header {} = {}", _name, str); } } else { TSMimeHdrFieldDestroy(_owner->_bufp, _owner->_hdr_loc, _field_loc); } TSHandleMLocRelease(_owner->_bufp, _owner->_hdr_loc, _field_loc); _field_loc = tmp; } } } else { if (str.size() > 0) { // Create a new header _owner->_state->context->p_instance.debug("Adding header {} = {} ", _name, str); if (TS_SUCCESS == TSMimeHdrFieldCreateNamed(_owner->_bufp, _owner->_hdr_loc, _name.data(), _name.size(), &_field_loc)) { if (TS_SUCCESS == TSMimeHdrFieldValueStringSet(_owner->_bufp, _owner->_hdr_loc, _field_loc, -1, str.data(), str.size())) { TSMimeHdrFieldAppend(_owner->_bufp, _owner->_hdr_loc, _field_loc); } } } else { // We don't allow setting an empty header, no-op } } return *this; } Header::String & Header::String::operator=(integer val) { _ensure_initialized(_owner); if (_field_loc) { TSMLoc tmp = nullptr; bool first = true; // Replace the existing header while (_field_loc) { tmp = TSMimeHdrFieldNextDup(_owner->_bufp, _owner->_hdr_loc, _field_loc); if (first) { first = false; // tsapi TSReturnCode TSMimeHdrFieldValueInt64Set(TSMBuffer bufp, TSMLoc hdr, TSMLoc field, int idx, int64_t value); if (TS_SUCCESS == TSMimeHdrFieldValueInt64Set(_owner->_bufp, _owner->_hdr_loc, _field_loc, -1, val)) { _owner->_state->context->p_instance.debug("Replacing integer header {} = {} ", _name, val); } } else { TSMimeHdrFieldDestroy(_owner->_bufp, _owner->_hdr_loc, _field_loc); } TSHandleMLocRelease(_owner->_bufp, _owner->_hdr_loc, _field_loc); _field_loc = tmp; } } else { // Create a new header _owner->_state->context->p_instance.debug("Adding integer header {} = {} ", _name, val); if (TS_SUCCESS == TSMimeHdrFieldCreateNamed(_owner->_bufp, _owner->_hdr_loc, _name.data(), _name.size(), &_field_loc)) { if (TS_SUCCESS == TSMimeHdrFieldValueInt64Set(_owner->_bufp, _owner->_hdr_loc, _field_loc, -1, val)) { TSMimeHdrFieldAppend(_owner->_bufp, _owner->_hdr_loc, _field_loc); } } } return *this; } Header::String & Header::String::operator+=(const cripts::string_view str) { _ensure_initialized(_owner); if (_field_loc) { if (!str.empty()) { // Drop the old field loc for now ... ToDo: Oh well, we need to figure out how to handle multi-value headers better TSHandleMLocRelease(_owner->_bufp, _owner->_hdr_loc, _field_loc); if (TS_SUCCESS == TSMimeHdrFieldCreateNamed(_owner->_bufp, _owner->_hdr_loc, _name.data(), _name.size(), &_field_loc)) { if (TS_SUCCESS == TSMimeHdrFieldValueStringSet(_owner->_bufp, _owner->_hdr_loc, _field_loc, -1, str.data(), str.size())) { _owner->_state->context->p_instance.debug("Appending header {} = {} ", _name, str); TSMimeHdrFieldAppend(_owner->_bufp, _owner->_hdr_loc, _field_loc); } } // Nothing to append with an empty header, we don't allow that } } else { // No previous field, so just make a new one operator=(str); } return *this; } Header::String Header::operator[](const cripts::string_view str) { _ensure_initialized(this); TSAssert(_bufp && _hdr_loc); TSMLoc field_loc = TSMimeHdrFieldFind(_bufp, _hdr_loc, str.data(), str.size()); Header::String ret; if (field_loc) { int len = 0; const char *value = TSMimeHdrFieldValueStringGet(_bufp, _hdr_loc, field_loc, -1, &len); ret._initialize(str, cripts::string_view(value, len), this, field_loc); } else { ret._initialize(str, {}, this, nullptr); } return ret; } Client::Request & Client::Request::_get(cripts::Context *context) { _ensure_initialized(&context->_client.request); return context->_client.request; } void Client::Request::_initialize() { TSAssert(_state->txnp); if (TSHttpTxnClientReqGet(_state->txnp, &_bufp, &_hdr_loc) != TS_SUCCESS) { _state->error.Fail(); } else { super_type::_initialize(); // Don't initialize unless properly setup } } Header::Iterator Header::begin() { _ensure_initialized(this); // Cleanup any lingering iterator state if (_iterator_loc) { TSHandleMLocRelease(_bufp, _hdr_loc, _iterator_loc); _iterator_loc = nullptr; } // Start the new iterator ++_iterator_tag; _iterator_loc = TSMimeHdrFieldGet(_bufp, _hdr_loc, 0); if (_iterator_loc) { int name_len; const char *name = TSMimeHdrFieldNameGet(_bufp, _hdr_loc, _iterator_loc, &name_len); cripts::string_view name_view(name, name_len); return {name_view, _iterator_tag, this}; } else { return Iterator::end(); // Seems unlikely that we'd not have any headers... } } cripts::string_view Header::iterate() { _ensure_initialized(this); TSMLoc next_loc = TSMimeHdrFieldNext(_bufp, _hdr_loc, _iterator_loc); TSHandleMLocRelease(_bufp, _hdr_loc, _iterator_loc); _iterator_loc = next_loc; if (_iterator_loc) { int name_len; const char *name = TSMimeHdrFieldNameGet(_bufp, _hdr_loc, _iterator_loc, &name_len); return {name, static_cast<cripts::string_view::size_type>(name_len)}; } else { return ""; } } Client::Response & Client::Response::_get(cripts::Context *context) { _ensure_initialized(&context->_client.response); return context->_client.response; } void Client::Response::_initialize() { CAssert(_state->hook != TS_HTTP_READ_REQUEST_HDR_HOOK); CAssert(_state->hook != TS_HTTP_POST_REMAP_HOOK); CAssert(_state->hook != TS_HTTP_SEND_REQUEST_HDR_HOOK); TSAssert(_state->txnp); if (TSHttpTxnClientRespGet(_state->txnp, &_bufp, &_hdr_loc) != TS_SUCCESS) { _state->error.Fail(); } else { super_type::_initialize(); // Don't initialize unless properly setup } } Server::Request & Server::Request::_get(cripts::Context *context) { _ensure_initialized(&context->_server.request); return context->_server.request; } void Server::Request::_initialize() { CAssert(_state->hook != TS_HTTP_READ_REQUEST_HDR_HOOK); CAssert(_state->hook != TS_HTTP_POST_REMAP_HOOK); CAssert(_state->hook != TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK); CAssert(_state->hook != TS_HTTP_READ_RESPONSE_HDR_HOOK); if (!Initialized()) { TSAssert(_state->txnp); if (TSHttpTxnServerReqGet(_state->txnp, &_bufp, &_hdr_loc) != TS_SUCCESS) { _state->error.Fail(); } else { super_type::_initialize(); // Don't initialize unless properly setup } } } Server::Response & Server::Response::_get(cripts::Context *context) { _ensure_initialized(&context->_server.response); return context->_server.response; } void Server::Response::_initialize() { CAssert(_state->hook != TS_HTTP_READ_REQUEST_HDR_HOOK); CAssert(_state->hook != TS_HTTP_POST_REMAP_HOOK); CAssert(_state->hook != TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK); CAssert(_state->hook != TS_HTTP_SEND_REQUEST_HDR_HOOK); TSAssert(_state->txnp); if (TSHttpTxnServerRespGet(_state->txnp, &_bufp, &_hdr_loc) != TS_SUCCESS) { _state->error.Fail(); } else { super_type::_initialize(); // Don't initialize unless properly setup } } } // namespace cripts