| /* | |
| 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, _owner->_state->txnp, "cripts"); | |
| break; | |
| default: | |
| TSHttpTxnStatusSet(_owner->_state->txnp, _status, "cripts"); | |
| 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; | |
| } | |
| 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; | |
| } | |
| time_t | |
| Header::AsDate(const cripts::string_view str) | |
| { | |
| _ensure_initialized(this); | |
| TSAssert(_bufp && _hdr_loc); | |
| time_t ret = 0; | |
| TSMLoc field_loc = TSMimeHdrFieldFind(_bufp, _hdr_loc, str.data(), str.size()); | |
| if (field_loc) { | |
| ret = TSMimeHdrFieldValueDateGet(_bufp, _hdr_loc, field_loc); | |
| // Since this is not owned by a Header::String, we have to release it now | |
| TSHandleMLocRelease(_bufp, _hdr_loc, field_loc); | |
| } | |
| 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 | |
| } | |
| } | |
| Cache::Response & | |
| Cache::Response::_get(cripts::Context *context) | |
| { | |
| _ensure_initialized(&context->_cache.response); | |
| return context->_cache.response; | |
| } | |
| void | |
| Cache::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 (TSHttpTxnCachedRespGet(_state->txnp, &_bufp, &_hdr_loc) != TS_SUCCESS) { | |
| _state->error.Fail(); | |
| } else { | |
| super_type::_initialize(); // Don't initialize unless properly setup | |
| } | |
| } | |
| Cache::Response::LookupStatus::operator integer() | |
| { | |
| if (_lookup == -1) { | |
| TSAssert(_owner->_state->txnp); | |
| if (TSHttpTxnCacheLookupStatusGet(_owner->_state->txnp, &_lookup) == TS_ERROR || _lookup < 0 || _lookup >= 4) { | |
| _lookup = -1; | |
| } | |
| } | |
| return _lookup; | |
| } | |
| cripts::string_view | |
| Cache::Response::LookupStatus::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 = operator integer(); | |
| if (status == -1) { | |
| return "none"; | |
| } else { | |
| return names[status]; | |
| } | |
| } | |
| Cache::Response::LookupStatus & | |
| Cache::Response::LookupStatus::operator=(int lookup) | |
| { | |
| if (TSHttpTxnCacheLookupStatusSet(_owner->_state->txnp, lookup) == TS_SUCCESS) { | |
| _owner->_state->context->p_instance.debug("Setting LookupStatus = {}", lookup); | |
| } else { | |
| // ToDo: Should we fail here? | |
| // _owner->_state->error.Fail(); | |
| } | |
| return *this; | |
| } | |
| } // namespace cripts |