From b6572711067f3f626c5bfe3283f3508ac8235410 Mon Sep 17 00:00:00 2001 From: Katsuyuki Omuro Date: Mon, 24 Aug 2026 14:53:08 +0900 Subject: [PATCH] Implement Response::see_other/redirect/temporary_redirect These three static constructors were declared in `include/fastly/http/response.h` (lines 142, 154, 166) and had working Rust wrappers and cxx bridge declarations (`src/http/response.rs`, `src/lib.rs`), but were never defined in the public C++ layer. Because a declaration without a definition is legal C++ and nothing in the repo called them, the whole build and test suite passed with the gap in place; any user calling one got an `undefined symbol` link error instead of a compile error: wasm-ld: error: undefined symbol: fastly::http::Response::redirect(std::string_view) The three definitions mirror the existing `Response::from_status`, passing the destination across the bridge with `static_cast` per the bridge's string convention. Adds test/response_redirect.cpp asserting the `Location` header for each, plus a non-null-terminated `string_view` case to cover the conversion. The status codes these set (303/308/307) are not asserted because `Response::get_status()` does not exist on this branch; those assertions belong with the get_status work. Co-Authored-By: Claude Opus 5 (1M context) --- src/cpp/http/response.cpp | 18 ++++++++++++++ test/response_redirect.cpp | 49 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 test/response_redirect.cpp diff --git a/src/cpp/http/response.cpp b/src/cpp/http/response.cpp index e6a8456..5ee7925 100644 --- a/src/cpp/http/response.cpp +++ b/src/cpp/http/response.cpp @@ -31,6 +31,24 @@ Response Response::from_status(StatusCode status) { return res; } +Response Response::see_other(std::string_view destination) { + Response res(fastly::sys::http::m_static_http_response_see_other( + static_cast(destination))); + return res; +} + +Response Response::redirect(std::string_view destination) { + Response res(fastly::sys::http::m_static_http_response_redirect( + static_cast(destination))); + return res; +} + +Response Response::temporary_redirect(std::string_view destination) { + Response res(fastly::sys::http::m_static_http_response_temporary_redirect( + static_cast(destination))); + return res; +} + Response Response::with_body(Body body) && { this->set_body(std::move(body)); return std::move(*this); diff --git a/test/response_redirect.cpp b/test/response_redirect.cpp new file mode 100644 index 0000000..2be7360 --- /dev/null +++ b/test/response_redirect.cpp @@ -0,0 +1,49 @@ +#include +#include + +using namespace fastly::http; + +namespace { + +// Returns the response's `Location` header, or std::nullopt if absent. +std::optional location_of(Response &resp) { + auto header{resp.get_header("Location")}; + if (!header.has_value() || !header->has_value()) { + return std::nullopt; + } + auto value{header->value().string()}; + if (!value.has_value()) { + return std::nullopt; + } + return std::string(*value); +} + +} // namespace + +TEST_CASE("Response redirect constructors set the Location header", + "[response]") { + SECTION("Response::see_other") { + auto resp{Response::see_other("https://www.fastly.com")}; + REQUIRE(location_of(resp) == std::optional("https://www.fastly.com")); + } + + SECTION("Response::redirect") { + auto resp{Response::redirect("https://www.fastly.com")}; + REQUIRE(location_of(resp) == std::optional("https://www.fastly.com")); + } + + SECTION("Response::temporary_redirect") { + auto resp{Response::temporary_redirect("https://www.fastly.com")}; + REQUIRE(location_of(resp) == std::optional("https://www.fastly.com")); + } + + SECTION("accepts a std::string_view over a non-null-terminated buffer") { + std::string buf{"https://www.fastly.com/path-and-then-some"}; + auto resp{Response::redirect(std::string_view(buf).substr(0, 22))}; + REQUIRE(location_of(resp) == std::optional("https://www.fastly.com")); + } +} + +// Required due to https://github.com/WebAssembly/wasi-libc/issues/485 +#include +int main(int argc, char *argv[]) { return Catch::Session().run(argc, argv); }