Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions include/fastly/http/request.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,12 @@ class Request {
/// empty body.
Request(Method method, std::string_view url);

/// Create a new request with an arbitrary method string (e.g. `PURGE`)
/// rather than one of the standard methods in `Method`, no headers, and an
/// empty body. Fails if `method` is not a valid HTTP method token.
static fastly::expected<Request> create(std::string_view method,
std::string_view url);

/// Create a new `GET` `Request` with the given URL, no headers, and an
/// empty body.
static Request get(std::string_view url);
Expand Down Expand Up @@ -505,12 +511,27 @@ class Request {
/// Builder-style equivalent of `Request::set_method()`.
Request with_method(Method method) &&;

/// Builder-style equivalent of `Request::set_method()`, accepting an
/// arbitrary method string (e.g. `PURGE`) rather than one of the standard
/// methods in `Method`. Fails if `method` is not a valid HTTP method token.
fastly::expected<Request> with_method(std::string_view method) &&;

/// Get the request method.
Method get_method();

/// Get the request method as a string. Unlike `Request::get_method()`,
/// this works for nonstandard methods (e.g. `PURGE`) that aren't
/// represented in `Method`.
std::string get_method_str();

/// Set the request method.
void set_method(Method method);

/// Set the request method to an arbitrary method string (e.g. `PURGE`)
/// rather than one of the standard methods in `Method`. Fails if `method`
/// is not a valid HTTP method token.
fastly::expected<void> set_method(std::string_view method);

/// Builder-style equivalent of `Request::set_url()`.
fastly::expected<Request> with_url(std::string_view url) &&;

Expand Down
34 changes: 34 additions & 0 deletions src/cpp/http/request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,20 @@ Request::Request(Method method, std::string_view url)
: req(fastly::sys::http::m_static_http_request_new(
method, static_cast<std::string>(url))) {}

fastly::expected<Request> Request::create(std::string_view method,
std::string_view url) {
fastly::sys::http::Request *out;
fastly::sys::error::FastlyError *err;
fastly::sys::http::m_static_http_request_new_str(
static_cast<std::string>(method), static_cast<std::string>(url), out,
err);
if (err != nullptr) {
return fastly::unexpected(err);
} else {
return FSLY_BOX(http, Request, out);
}
}

Request Request::from_client() {
Request req{fastly::sys::http::m_static_http_request_from_client()};
return req;
Expand Down Expand Up @@ -433,10 +447,30 @@ Request Request::with_method(Method method) && {
return std::move(*this);
}

fastly::expected<Request> Request::with_method(std::string_view method) && {
return this->set_method(method).map([this]() { return std::move(*this); });
}

Method Request::get_method() { return this->req->get_method(); }

std::string Request::get_method_str() {
std::string out;
this->req->get_method_str(out);
return out;
}

void Request::set_method(Method method) { this->req->set_method(method); }

fastly::expected<void> Request::set_method(std::string_view method) {
fastly::sys::error::FastlyError *err;
this->req->set_method_str(static_cast<std::string>(method), err);
if (err != nullptr) {
return fastly::unexpected(err);
} else {
return fastly::expected<void>();
}
}

fastly::expected<Request> Request::with_url(std::string_view url) && {
return this->set_url(url).map([this]() { return std::move(*this); });
}
Expand Down
3 changes: 3 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ pub enum FastlyError {
#[error(transparent)]
InvalidStatusCode(#[from] http::status::InvalidStatusCode),
#[error(transparent)]
InvalidMethod(#[from] http::method::InvalidMethod),
#[error(transparent)]
IoError(#[from] std::io::Error),
#[error(transparent)]
#[allow(clippy::enum_variant_names)]
Expand Down Expand Up @@ -101,6 +103,7 @@ impl FastlyError {
FastlyError::InvalidHeaderName(_) => FastlyErrorCode::InvalidHeaderName,
FastlyError::InvalidHeaderValue(_) => FastlyErrorCode::InvalidHeaderValue,
FastlyError::InvalidStatusCode(_) => FastlyErrorCode::InvalidStatusCode,
FastlyError::InvalidMethod(_) => FastlyErrorCode::InvalidMethod,
FastlyError::IoError(_) => FastlyErrorCode::IoError,
FastlyError::FastlyError(_) => FastlyErrorCode::FastlyError,
FastlyError::FastlySendError(_) => FastlyErrorCode::FastlySendError,
Expand Down
22 changes: 22 additions & 0 deletions src/http/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,19 @@ pub fn m_static_http_request_new(method: Method, url: &CxxString) -> Box<Request
)))
}

pub fn m_static_http_request_new_str(
method: &CxxString,
url: &CxxString,
mut out: Pin<&mut *mut Request>,
mut err: ErrPtr,
) {
let method = try_fe!(err, fastly::http::Method::try_from(method.as_bytes()));
out.set(Box::into_raw(Box::new(Request(fastly::Request::new(
method,
url.to_str().expect("Invalid UTF-8 in URL"),
)))));
}

pub fn m_static_http_request_get(url: &CxxString) -> Box<Request> {
Box::new(Request(fastly::Request::get(
url.to_str().expect("Invalid UTF-8 in URL"),
Expand Down Expand Up @@ -429,6 +442,15 @@ impl Request {
self.0.set_method(method);
}

pub fn get_method_str(&self, out: Pin<&mut CxxString>) {
out.push_str(self.0.get_method_str());
}

pub fn set_method_str(&mut self, method: &CxxString, mut err: ErrPtr) {
let method = try_fe!(err, fastly::http::Method::try_from(method.as_bytes()));
self.0.set_method(method);
}

pub fn get_url(&self, out: Pin<&mut CxxString>) {
out.push_str(self.0.get_url().as_str());
}
Expand Down
9 changes: 9 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ mod ffi {
InvalidHeaderName,
InvalidHeaderValue,
InvalidStatusCode,
InvalidMethod,
IoError,
FastlyError,
FastlySendError,
Expand Down Expand Up @@ -434,6 +435,12 @@ mod ffi {
fn m_static_http_request_trace(url: &CxxString) -> Box<Request>;
fn m_static_http_request_patch(url: &CxxString) -> Box<Request>;
fn m_static_http_request_new(method: Method, url: &CxxString) -> Box<Request>;
fn m_static_http_request_new_str(
method: &CxxString,
url: &CxxString,
mut out: Pin<&mut *mut Request>,
mut err: Pin<&mut *mut FastlyError>,
);
fn m_static_http_request_from_client() -> Box<Request>;

// Regular methods
Expand Down Expand Up @@ -506,6 +513,8 @@ mod ffi {
) -> bool;
fn get_method(&self) -> Method;
fn set_method(&mut self, method: Method);
fn get_method_str(&self, mut out: Pin<&mut CxxString>);
fn set_method_str(&mut self, method: &CxxString, mut err: Pin<&mut *mut FastlyError>);
fn get_url(&self, mut out: Pin<&mut CxxString>);
fn set_url(&mut self, url: &CxxString, mut err: Pin<&mut *mut FastlyError>);
fn get_path(&self, mut out: Pin<&mut CxxString>);
Expand Down
50 changes: 50 additions & 0 deletions test/request.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include <catch2/catch_test_macros.hpp>
#include <fastly/http/request.h>

using namespace fastly::http;

TEST_CASE("Request::get_method_str reflects a standard method", "[request]") {
auto req = Request::get("https://example.com");
REQUIRE(req.get_method_str() == "GET");
}

TEST_CASE("Request::create accepts a nonstandard method", "[request]") {
auto req = Request::create("PURGE", "https://example.com");
REQUIRE(req.has_value());
REQUIRE(req->get_method_str() == "PURGE");
}

TEST_CASE("Request::create rejects an invalid method token", "[request]") {
auto req = Request::create("BAD METHOD", "https://example.com");
REQUIRE(!req.has_value());
REQUIRE(req.error().error_code() == fastly::FastlyErrorCode::InvalidMethod);
}

TEST_CASE("Request::set_method(string) accepts a nonstandard method",
"[request]") {
auto req = Request::get("https://example.com");
auto res = req.set_method("PURGE");
REQUIRE(res.has_value());
REQUIRE(req.get_method_str() == "PURGE");
}

TEST_CASE("Request::set_method(string) rejects an invalid method token",
"[request]") {
auto req = Request::get("https://example.com");
auto res = req.set_method("BAD METHOD");
REQUIRE(!res.has_value());
REQUIRE(res.error().error_code() == fastly::FastlyErrorCode::InvalidMethod);
// The request's method is unchanged after a failed set_method.
REQUIRE(req.get_method_str() == "GET");
}

TEST_CASE("Request::with_method(string) accepts a nonstandard method",
"[request]") {
auto req = Request::get("https://example.com").with_method("PURGE");
REQUIRE(req.has_value());
REQUIRE(req->get_method_str() == "PURGE");
}

// Required due to https://github.com/WebAssembly/wasi-libc/issues/485
#include <catch2/catch_session.hpp>
int main(int argc, char *argv[]) { return Catch::Session().run(argc, argv); }
Loading