| /* |
| 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. |
| */ |
| #pragma once |
| |
| #include <cstring> |
| #include <iterator> |
| #include <string> |
| |
| #include "ts/ts.h" |
| |
| #include "cripts/Transaction.hpp" |
| #include "cripts/Lulu.hpp" |
| |
| namespace cripts |
| { |
| |
| class Header |
| { |
| using self_type = Header; |
| |
| public: |
| class Status |
| { |
| using self_type = Status; |
| |
| public: |
| Status() = delete; |
| Status(Header *owner) : _owner(owner) {} |
| |
| operator integer(); // This should not be explicit, nor const |
| self_type &operator=(int status); |
| |
| private: |
| Header *_owner = nullptr; |
| TSHttpStatus _status = TS_HTTP_STATUS_NONE; |
| |
| }; // End class cripts::Header::Status |
| |
| class Reason |
| { |
| using self_type = Reason; |
| |
| public: |
| Reason() = delete; |
| Reason(Header *owner) : _owner(owner) {} |
| |
| self_type &operator=(cripts::string_view reason); |
| |
| private: |
| Header *_owner = nullptr; |
| }; // End class cripts::Header::Reason |
| |
| class Body |
| { |
| using self_type = Body; |
| |
| public: |
| Body() = delete; |
| Body(Header *owner) : _owner(owner) {} |
| |
| self_type &operator=(cripts::string_view body); |
| |
| private: |
| Header *_owner = nullptr; |
| }; // End class cripts::Header::Body |
| |
| class Method |
| { |
| using self_type = Method; |
| |
| public: |
| Method() = delete; |
| Method(cripts::string_view const &method) : _method(method) {} |
| Method(const char *const method, int len) |
| { |
| _method = cripts::string_view(method, static_cast<cripts::string_view::size_type>(len)); |
| } |
| |
| Method(Header *owner) : _owner(owner) {} |
| |
| cripts::string_view GetSV(); |
| |
| operator cripts::string_view() { return GetSV(); } |
| |
| // ToDo: This is a bit weird, but seems needed (for now) to allow for the |
| // cripts::Header::Method::* constants. |
| [[nodiscard]] cripts::string_view::const_pointer |
| Data() const |
| { |
| CAssert(_method.size() > 0); |
| return _method.data(); |
| } |
| |
| cripts::string_view::const_pointer |
| Data() |
| { |
| return GetSV().data(); |
| } |
| |
| cripts::string_view::size_type |
| Size() |
| { |
| return GetSV().size(); |
| } |
| |
| cripts::string_view::size_type |
| Length() |
| { |
| return GetSV().size(); |
| } |
| |
| bool |
| operator==(Method const &rhs) |
| { |
| return GetSV().data() == rhs.Data(); |
| } |
| |
| bool |
| operator!=(Method const &rhs) |
| { |
| return GetSV().data() != rhs.Data(); |
| } |
| |
| private: |
| Header *_owner = nullptr; |
| cripts::string_view _method; |
| |
| }; // End class cripts::Header::Method |
| |
| class String : public cripts::StringViewMixin<String> |
| { |
| using super_type = cripts::StringViewMixin<String>; |
| using self_type = String; |
| |
| public: |
| ~String() |
| { |
| if (_field_loc) { |
| TSHandleMLocRelease(_owner->_bufp, _owner->_hdr_loc, _field_loc); |
| _field_loc = nullptr; |
| } |
| } |
| |
| public: |
| // Implemented in Headers.cc, they are pretty large |
| self_type &operator=(const cripts::string_view str) override; |
| self_type &operator=(integer val); |
| self_type &operator+=(const cripts::string_view str); |
| |
| // ToDo: This was useful at some point, but was breaking other useful uses. Leaving |
| // it here for now for maybe future work. |
| // template <size_t N> |
| // self_type & |
| // operator=(const char (&str)[N]) |
| //{ |
| // return operator=(cripts::string_view(str, str[N - 1] ? N : N - 1)); |
| //} |
| |
| self_type &operator=(std::nullptr_t) = delete; |
| |
| self_type & |
| operator=(char const *str) |
| { |
| return operator=(cripts::string_view(str, strlen(str))); |
| } |
| |
| self_type & |
| operator=(const std::string &str) |
| { |
| return operator=(cripts::string_view(str)); |
| } |
| |
| private: |
| friend class Header; |
| |
| void |
| _initialize(cripts::string_view name, cripts::string_view value, Header *owner, TSMLoc field_loc) |
| { |
| _setSV(value); |
| _name = name; |
| _owner = owner; |
| _field_loc = field_loc; |
| } |
| |
| Header *_owner = nullptr; |
| TSMLoc _field_loc = nullptr; |
| cripts::string_view _name; |
| |
| }; // Class cripts::Header::String |
| |
| class Name : public cripts::StringViewMixin<Name> |
| { |
| using super_type = cripts::StringViewMixin<Name>; |
| using self_type = Name; |
| |
| public: |
| operator cripts::string_view() const { return GetSV(); } |
| |
| self_type & |
| operator=(const cripts::string_view str) override |
| { |
| _setSV(str); |
| |
| return *this; |
| } |
| |
| using super_type::StringViewMixin; |
| |
| }; // Class cripts::Header::Name |
| |
| public: |
| class Iterator |
| { |
| public: |
| using iterator_category = std::input_iterator_tag; |
| using value_type = Name; |
| using difference_type = std::ptrdiff_t; |
| using pointer = const Name *; |
| using reference = const Name &; |
| |
| const static uint32_t END_TAG = UINT32_MAX; |
| |
| Iterator(Name view, uint32_t tag) : _view(std::move(view)), _tag(tag) {} // Special for the end iterator |
| Iterator(Name view, uint32_t tag, Header *owner) : _view(std::move(view)), _tag(tag), _owner(owner) {} |
| |
| reference |
| operator*() const |
| { |
| return _view; |
| } |
| |
| pointer |
| operator->() |
| { |
| return &_view; |
| } |
| |
| // Prefix increment |
| Iterator & |
| operator++() |
| { |
| if (_tag != END_TAG) { |
| CAssert(_tag == _owner->_iterator_tag); |
| |
| _view = _owner->iterate(); |
| if (_view.empty()) { |
| _tag = END_TAG; |
| } |
| } |
| |
| return *this; |
| } |
| |
| friend bool |
| operator==(const Iterator &a, const Iterator &b) |
| { |
| return a._tag == b._tag; |
| }; |
| |
| friend bool |
| operator!=(const Iterator &a, const Iterator &b) |
| { |
| return a._tag != b._tag; |
| }; |
| |
| static const Iterator |
| end() |
| { |
| return _end; |
| } |
| |
| private: |
| Name _view = nullptr; |
| uint32_t _tag = 0; |
| Header *_owner = nullptr; |
| |
| static const Iterator _end; |
| }; // Class cripts::Header::iterator |
| |
| Header() : status(this), reason(this), body(this) {} |
| |
| ~Header() { Reset(); } |
| |
| // Clear anything "cached" in the Header, this is rather draconian, but it's |
| // safe... |
| void |
| Reset() |
| { |
| if (_bufp && _hdr_loc) { |
| TSHandleMLocRelease(_bufp, TS_NULL_MLOC, _hdr_loc); |
| _hdr_loc = nullptr; |
| _bufp = nullptr; |
| } |
| _initialized = false; |
| } |
| |
| [[nodiscard]] TSMBuffer |
| BufP() |
| { |
| _ensure_initialized(this); |
| return _bufp; |
| } |
| |
| [[nodiscard]] TSMLoc |
| MLoc() |
| { |
| _ensure_initialized(this); |
| return _hdr_loc; |
| } |
| |
| String operator[](const cripts::string_view str); |
| time_t AsDate(const cripts::string_view str); |
| |
| [[nodiscard]] bool |
| Initialized() const |
| { |
| return _initialized; |
| } |
| |
| void |
| Erase(const cripts::string_view header) |
| { |
| _ensure_initialized(this); |
| operator[](header) = ""; |
| } |
| |
| Iterator begin(); |
| cripts::string_view iterate(); // This is a little helper for the iterators |
| |
| [[nodiscard]] Iterator |
| end() const |
| { |
| return Iterator::end(); // Static end iterator. ToDo: Does this have any value over making a new one always? |
| } |
| |
| // This should only be called from the Context initializers! |
| void |
| set_state(cripts::Transaction *state) |
| { |
| _state = state; |
| } |
| |
| Status status; |
| Reason reason; |
| Body body; |
| |
| protected: |
| static void |
| _ensure_initialized(self_type *ptr) |
| { |
| if (!ptr->Initialized()) [[unlikely]] { |
| ptr->_initialize(); |
| } |
| } |
| |
| void virtual _initialize() { _initialized = true; } |
| |
| TSMBuffer _bufp = nullptr; |
| TSMLoc _hdr_loc = nullptr; |
| cripts::Transaction *_state = nullptr; // Pointer into the owning Context's State |
| TSMLoc _iterator_loc = nullptr; |
| uint32_t _iterator_tag = 0; // This is used to assure that we don't have more than one active iterator on a header |
| bool _initialized = false; |
| |
| }; // End class Header |
| |
| class RequestHeader : public Header |
| { |
| using super_type = Header; |
| using self_type = RequestHeader; |
| |
| public: |
| RequestHeader() : method(this) {} // Special case since only this header has a method |
| |
| Method method; |
| }; // End class RequestHeader |
| |
| class ResponseHeader : public Header |
| { |
| private: |
| using super_type = Header; |
| using self_type = ResponseHeader; |
| |
| public: |
| ResponseHeader() = default; |
| |
| }; // End class ResponseHeader |
| |
| namespace Client |
| { |
| class URL; |
| class Request : public RequestHeader |
| { |
| using super_type = RequestHeader; |
| using self_type = Request; |
| |
| public: |
| Request() = default; |
| Request(const self_type &) = delete; |
| void operator=(const self_type &) = delete; |
| |
| // Implemented later, because needs the context. |
| static self_type &_get(cripts::Context *context); |
| void _initialize() override; |
| |
| }; // End class Client::Request |
| |
| class Response : public ResponseHeader |
| { |
| using super_type = ResponseHeader; |
| using self_type = Response; |
| |
| public: |
| Response() = default; |
| Response(const self_type &) = delete; |
| void operator=(const self_type &) = delete; |
| |
| // Implemented later, because needs the context. |
| static self_type &_get(cripts::Context *context); |
| void _initialize() override; |
| |
| }; // End class Client::Response |
| |
| } // namespace Client |
| |
| namespace Server |
| { |
| class Request : public RequestHeader |
| { |
| using super_type = RequestHeader; |
| using self_type = Request; |
| |
| public: |
| Request() = default; |
| Request(const self_type &) = delete; |
| void operator=(const self_type &) = delete; |
| |
| // Implemented later, because needs the context. |
| static self_type &_get(cripts::Context *context); |
| void _initialize() override; |
| |
| }; // End class Server::Request |
| |
| class Response : public ResponseHeader |
| { |
| using super_type = ResponseHeader; |
| using self_type = Response; |
| |
| public: |
| Response() = default; |
| Response(const self_type &) = delete; |
| void operator=(const self_type &) = delete; |
| |
| // Implemented later, because needs the context. |
| static self_type &_get(cripts::Context *context); |
| void _initialize() override; |
| |
| }; // End class Server::Response |
| |
| } // namespace Server |
| |
| namespace Cache |
| { |
| class Response : public ResponseHeader |
| { |
| using super_type = ResponseHeader; |
| using self_type = Response; |
| |
| class LookupStatus |
| { |
| using self_type = LookupStatus; |
| |
| public: |
| LookupStatus() = delete; |
| LookupStatus(Response *owner) : _owner(owner) {} |
| |
| cripts::string_view GetSV(); |
| self_type &operator=(int status); |
| |
| operator integer(); |
| |
| private: |
| Response *_owner = nullptr; |
| int _lookup = -1; // Note set yet |
| |
| }; // Class cripts::Cache::LookupStatus |
| |
| public: |
| Response() : ResponseHeader(), lookupstatus(this){}; |
| |
| Response(const self_type &) = delete; |
| void operator=(const self_type &) = delete; |
| |
| // Implemented later, because needs the context. |
| static self_type &_get(cripts::Context *context); |
| void _initialize() override; |
| |
| LookupStatus lookupstatus; |
| |
| }; // End class Cache::Response |
| |
| } // namespace Cache |
| |
| // Some static methods for the Method class |
| namespace Method |
| { |
| #undef DELETE // ToDo: macOS shenanigans here, defining DELETE as a macro |
| extern const cripts::Header::Method GET; |
| extern const cripts::Header::Method HEAD; |
| extern const cripts::Header::Method POST; |
| extern const cripts::Header::Method PUT; |
| extern const cripts::Header::Method PUSH; |
| extern const cripts::Header::Method DELETE; |
| extern const cripts::Header::Method OPTIONS; |
| extern const cripts::Header::Method CONNECT; |
| extern const cripts::Header::Method TRACE; |
| // This is a special feature of ATS |
| extern const cripts::Header::Method PURGE; |
| } // namespace Method |
| |
| // Lookup status constants, these are used in the Cache::Response::lookupstatus |
| namespace LookupStatus |
| { |
| inline constexpr int NONE = -1; |
| inline constexpr int MISS = TS_CACHE_LOOKUP_MISS; |
| inline constexpr int HIT_STALE = TS_CACHE_LOOKUP_HIT_STALE; |
| inline constexpr int HIT_FRESH = TS_CACHE_LOOKUP_HIT_FRESH; |
| inline constexpr int SKIPPED = TS_CACHE_LOOKUP_SKIPPED; |
| } // namespace LookupStatus |
| |
| class Context; |
| } // namespace cripts |
| |
| // Formatters for {fmt} |
| namespace fmt |
| { |
| template <> struct formatter<cripts::Header::Method> { |
| constexpr auto |
| parse(format_parse_context &ctx) -> decltype(ctx.begin()) |
| { |
| return ctx.begin(); |
| } |
| |
| template <typename FormatContext> |
| auto |
| format(cripts::Header::Method &method, FormatContext &ctx) const -> decltype(ctx.out()) |
| { |
| return fmt::format_to(ctx.out(), "{}", method.GetSV()); |
| } |
| }; |
| |
| template <> struct formatter<cripts::Header::String> { |
| constexpr auto |
| parse(format_parse_context &ctx) -> decltype(ctx.begin()) |
| { |
| return ctx.begin(); |
| } |
| |
| template <typename FormatContext> |
| auto |
| format(const cripts::Header::String &str, FormatContext &ctx) const -> decltype(ctx.out()) |
| { |
| return fmt::format_to(ctx.out(), "{}", str.GetSV()); |
| } |
| }; |
| |
| template <> struct formatter<cripts::Header::Name> { |
| constexpr auto |
| parse(format_parse_context &ctx) -> decltype(ctx.begin()) |
| { |
| return ctx.begin(); |
| } |
| |
| template <typename FormatContext> |
| auto |
| format(const cripts::Header::Name &name, FormatContext &ctx) const -> decltype(ctx.out()) |
| { |
| return fmt::format_to(ctx.out(), "{}", name.GetSV()); |
| } |
| }; |
| |
| } // namespace fmt |