-
Notifications
You must be signed in to change notification settings - Fork 2
Implement ESI interface #71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
09f5511
Initial work
TartanLlama 0ee3f2c
More testing
TartanLlama 7de06dd
Slowly getting there
TartanLlama 433f4e0
Getting there
TartanLlama 8db8cb1
Working ESI
TartanLlama 4d0486e
Comments
TartanLlama a6ca1f3
Remove empty file
TartanLlama 5233bd7
Formatting
TartanLlama 8e9972e
Formatting
TartanLlama 95b03cd
Make it harder to accidentally construct a tag type
TartanLlama c0e3435
Testing and docs
TartanLlama 293a600
Working tests
TartanLlama 594b279
Clippy
TartanLlama 9ecaf36
Fix tests
TartanLlama 74f7eba
Bump version
TartanLlama 099b3ee
Pin arguments to manual C callback bindings
TartanLlama d63810c
Use enum for dispatch fragment callback
TartanLlama File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| BreakBeforeBraces: Attach |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| #include "fastly/sdk.h" | ||
|
|
||
| int main() { | ||
| fastly::log::init_simple("logs", fastly::log::LogLevelFilter::Debug); | ||
| auto req{fastly::http::Request::from_client()}; | ||
|
|
||
| auto bereq = fastly::http::Request(fastly::http::Method::GET, | ||
| "https://esi-cpp-demo.edgecompute.app/") | ||
| .with_auto_decompress_gzip(true); | ||
| auto beresp = bereq.clone_without_body().send("esi-cpp-demo").value(); | ||
|
|
||
| // Pass in the request made to the backend as a template for fragment | ||
| // requests: this ensures that the fragment requests also ask for gzipped | ||
| // content and automatically gunzip the response content. | ||
| fastly::esi::Processor processor(std::move(bereq)); | ||
| auto dispatch_fragment_request = [](fastly::http::Request req) | ||
|
zkat marked this conversation as resolved.
|
||
| -> std::optional<fastly::esi::PendingFragmentContent> { | ||
| auto pending = req.send_async("esi-cpp-demo"); | ||
| if (pending) { | ||
| return fastly::esi::PendingFragmentContent{std::move(*pending)}; | ||
| } else { | ||
| return std::nullopt; | ||
| } | ||
| }; | ||
|
|
||
| auto result = processor.process_response( | ||
| beresp, std::nullopt, dispatch_fragment_request, std::nullopt); | ||
| if (!result) { | ||
| fastly::log::error("Failed to process response"); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| #ifndef FASTLY_ACCESS_BRIDGE_INTERNALS_H | ||
| #define FASTLY_ACCESS_BRIDGE_INTERNALS_H | ||
|
|
||
| #include <fastly/sdk-sys.h> | ||
|
|
||
| namespace fastly::detail { | ||
| // This type can be used to access the inner `rust::Box` of | ||
| // various wrapper types in the C++ SDK. | ||
| // It can also be used to construct wrapper types from raw pointers. | ||
| // This is intended for internal use only. | ||
| struct AccessBridgeInternals { | ||
| template <class T> static auto &get(T &obj) { return obj.inner(); } | ||
| template <class T> static auto &get(const T &obj) { return obj.inner(); } | ||
| template <class T, class U> static auto from_raw(U *ptr) { | ||
| return T(rust::Box<U>::from_raw(ptr)); | ||
| } | ||
| }; | ||
| } // namespace fastly::detail | ||
|
|
||
| #endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| // Some types (primarily callback types) must be defined in C++, but depend upon | ||
| // types defined in Rust. To break this circular dependency, we define empty | ||
| // "tag" structs here in C++ that can be referenced both from C++ and Rust, | ||
| // allowing Rust to pass pointers to these types back to C++ without needing to | ||
| // know their full definition. | ||
| // | ||
| // C++ types that implement the tags should inherit from the tag structs and the | ||
| // bindings should cast to/from the tag types as necessary. | ||
|
|
||
| #ifndef FASTLY_DETAIL_RUST_BRIDGE_TAGS_H | ||
| #define FASTLY_DETAIL_RUST_BRIDGE_TAGS_H | ||
|
|
||
| namespace fastly::detail::rust_bridge_tags { | ||
| namespace esi { | ||
| // esi.h:DispatchFragmentRequestFn | ||
| struct DispatchFragmentRequestFnTag { | ||
| // Ensure that only classes that inherit from this tag can be used as | ||
| // DispatchFragmentRequestFn. | ||
| protected: | ||
| DispatchFragmentRequestFnTag() = default; | ||
| }; | ||
| // esi.h:ProcessFragmentResponseFn | ||
| struct ProcessFragmentResponseFnTag { | ||
| protected: | ||
| ProcessFragmentResponseFnTag() = default; | ||
| }; | ||
| } // namespace esi | ||
| } // namespace fastly::detail::rust_bridge_tags | ||
|
|
||
| #endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| #ifndef FASTLY_ESI_H | ||
| #define FASTLY_ESI_H | ||
|
|
||
| #include <concepts> | ||
| #include <fastly/detail/rust_bridge_tags.h> | ||
| #include <fastly/error.h> | ||
| #include <fastly/expected.h> | ||
| #include <fastly/http/request.h> | ||
| #include <fastly/http/response.h> | ||
| #include <fastly/sdk-sys.h> | ||
| #include <functional> | ||
| #include <optional> | ||
| #include <string> | ||
| #include <utility> | ||
| #include <variant> | ||
|
|
||
| namespace fastly::esi { | ||
| /// Used to configure optional behaviour within the ESI processor. | ||
| struct Configuration { | ||
| public: | ||
| /// Create a new configuration object. | ||
| /// \param namespc The namespace to use for ESI tags. | ||
|
zkat marked this conversation as resolved.
|
||
| /// \param is_escaped_content Whether to escape content by default. | ||
| Configuration(std::string namespc = "esi", bool is_escaped_content = true) | ||
| : namespace_(std::move(namespc)), | ||
| is_escaped_content_(is_escaped_content) {} | ||
|
|
||
| std::string_view get_namespace() const { return namespace_; } | ||
| bool is_escaped_content() const { return is_escaped_content_; } | ||
|
|
||
| private: | ||
| std::string namespace_; | ||
| bool is_escaped_content_; | ||
| }; | ||
|
|
||
| /// Content that can be returned from a fragment request dispatcher. This can | ||
| /// either be a pending request, a response, or an empty value to indicate that | ||
| /// no content is available. | ||
| using PendingFragmentContent = | ||
| std::variant<http::request::PendingRequest, http::Response, std::monostate>; | ||
|
|
||
| /// A callback type used to dispatch requests for ESI fragments. | ||
| class DispatchFragmentRequestFn | ||
| : public detail::rust_bridge_tags::esi::DispatchFragmentRequestFnTag { | ||
| public: | ||
| /// The type of the dispatch function. | ||
| using function_type = | ||
| std::function<std::optional<PendingFragmentContent>(Request)>; | ||
|
|
||
| template <std::convertible_to<function_type> F> | ||
| DispatchFragmentRequestFn(F &&fn) : fn_(std::forward<F>(fn)) {} | ||
|
|
||
| private: | ||
| friend detail::AccessBridgeInternals; | ||
| auto &inner() const { return fn_; } | ||
| function_type fn_; | ||
| }; | ||
|
|
||
| /// A callback type used to process responses from ESI fragment requests. | ||
| class ProcessFragmentResponseFn | ||
| : public detail::rust_bridge_tags::esi::ProcessFragmentResponseFnTag { | ||
| public: | ||
| /// The type of the processing function. | ||
| using function_type = | ||
| std::function<std::optional<Response>(Request &, Response)>; | ||
|
|
||
| template <std::convertible_to<function_type> F> | ||
| ProcessFragmentResponseFn(F &&fn) : fn_(std::forward<F>(fn)) {} | ||
|
|
||
| private: | ||
| friend detail::AccessBridgeInternals; | ||
| auto &inner() const { return fn_; } | ||
| function_type fn_; | ||
| }; | ||
|
|
||
| /// An ESI processor that can process a response containing ESI tags, dispatch | ||
| /// requests for fragments, and process the fragment responses. | ||
| class Processor { | ||
| public: | ||
| /// Create a new ESI processor with the given configuration. | ||
| Processor(std::optional<Request> original_request_metadata = std::nullopt, | ||
| Configuration config = Configuration()); | ||
|
|
||
| /// Process a response containing ESI tags, optionally using the given | ||
| /// callbacks to dispatch requests for fragments and process the fragment | ||
| /// responses. | ||
| /// | ||
| /// \param src_document The response containing ESI tags to process. | ||
| /// \param client_response_metadata Optional original client request data used | ||
| /// for fragment requests. | ||
| /// \param dispatch_fragment_request Optional callback to dispatch requests | ||
| /// for fragments. | ||
| /// \param process_fragment_response Optional callback to process fragment | ||
| /// responses. | ||
| fastly::expected<void> process_response( | ||
| Response &src_document, | ||
| std::optional<Response> client_response_metadata = std::nullopt, | ||
| std::optional<DispatchFragmentRequestFn> dispatch_fragment_request = | ||
| std::nullopt, | ||
| std::optional<ProcessFragmentResponseFn> process_fragment_response = | ||
| std::nullopt); | ||
|
|
||
| /// Process a string containing ESI tags, optionally using the given | ||
| /// callbacks to dispatch requests for fragments and process the fragment | ||
| /// responses. | ||
| /// \param src_document The string containing ESI tags to process. | ||
| /// \param dispatch_fragment_request Optional callback to dispatch requests | ||
| /// for fragments. | ||
| /// \param process_fragment_response Optional callback to process fragment | ||
| /// responses. | ||
| fastly::expected<std::string> process_document( | ||
| const std::string &src_document, | ||
| std::optional<DispatchFragmentRequestFn> dispatch_fragment_request = | ||
| std::nullopt, | ||
| std::optional<ProcessFragmentResponseFn> process_fragment_response = | ||
| std::nullopt); | ||
|
|
||
| private: | ||
| rust::Box<fastly::sys::esi::Processor> processor_; | ||
| }; | ||
|
|
||
| } // namespace fastly::esi | ||
| #endif | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is fine for this PR, but I think we should generally leave version bumps to releases.