| /* |
| 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 |