Skip to content
Draft
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
2 changes: 2 additions & 0 deletions runtime/fastly/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
cmake_minimum_required(VERSION 3.27)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

include("../StarlingMonkey/cmake/add_as_subproject.cmake")

add_builtin(
Expand Down
126 changes: 126 additions & 0 deletions runtime/fastly/builtins/fastly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,116 @@ bool Fastly::getGeolocationForIpAddress(JSContext *cx, unsigned argc, JS::Value
return JS_ParseJSON(cx, geo_info_str, args.rval());
}

bool Fastly::inspect(JSContext *cx, unsigned argc, JS::Value *vp) {
JS::CallArgs args = CallArgsFromVp(argc, vp);
REQUEST_HANDLER_ONLY("inspect");
if (!args.requireAtLeast(cx, "inspect", 1)) {
return false;
}

auto request_value = args.get(0);
if (!Request::is_instance(request_value)) {
JS_ReportErrorUTF8(cx, "inspect: request parameter must be an instance of Request");
return false;
}
auto inspect_response_obj = &request_value.toObject();

auto options_value = args.get(1);
JS::RootedObject options_obj(cx, options_value.isObject() ? &options_value.toObject() : nullptr);

host_api::InspectOptions inspect_options(Request::request_handle(inspect_response_obj),
RequestOrResponse::body_handle(inspect_response_obj));

if (options_value != nullptr) {

host_api::HostString corp_str;
JS::RootedValue corp_val(cx);
if (!JS_GetProperty(cx, options, "corp", &corp_val)) {
return false;
}
if (!corp_val.isNullOrUndefined()) {
if (!corp_val.isString()) {
api::throw_error(cx, api::Errors::TypeError, "inspect", "corp", "be a string");
return false;
}
corp_str = core::encode(cx, corp_val);
if (!corp_str) {
return false;
}
std::optional<std::string_view> corp = corp_str;
if (corp) {
inspect_options.corp_len = corp->length();
inspect_options.corp = std::move(corp->data());
}
}

host_api::HostString workspace_str;
JS::RootedValue workspace_val(cx);
if (!JS_GetProperty(cx, options, "workspace", &workspace_val)) {
return false;
}
if (!workspace_val.isNullOrUndefined()) {
if (!workspace_val.isString()) {
api::throw_error(cx, api::Errors::TypeError, "inspect", "workspace", "be a string");
return false;
}
workspace_str = core::encode(cx, workspace_val);
if (!workspace_str) {
return false;
}
std::optional<std::string_view> workspace = workspace_str;
if (workspace) {
inspect_options.workspace_len = workspace->length();
inspect_options.workspace = std::move(workspace->data());
}
}

host_api::HostString override_client_ip_str;
JS::RootedValue override_client_ip_val(cx);
if (!JS_GetProperty(cx, options, "overrideClientIp", &override_client_ip_val)) {
return false;
}
if (!override_client_ip_val.isNullOrUndefined()) {
if (!override_client_ip_val.isString()) {
api::throw_error(cx, api::Errors::TypeError, "fastly.inspect", "overrideClientIp",
"be a string");
return false;
}
override_client_ip_str = core::encode(cx, override_client_ip_val);
if (!override_client_ip_str) {
return false;
}

// TODO: Remove all of this and rely on the host for validation as the hostcall only takes one
// user-supplied parameter
int format = AF_INET;
size_t octets_len = 4;
if (std::find(override_client_ip_str.begin(), override_client_ip_str.end(), ':') !=
override_client_ip_str.end()) {
format = AF_INET6;
octets_len = 16;
}

uint8_t octets[sizeof(struct in6_addr)];
if (inet_pton(format, override_client_ip_str.begin(), octets) != 1) {
api::throw_error(cx, api::Errors::TypeError, "fastly.inspect", "overrideClientIp",
"be a valid IP address");
return false;
}
inspect_options.override_client_ip_len = octets_len;
inspect_options.override_client_ip = std::move(octets->data());
}
}

auto res = request_value->inspect(&inspect_options);
if (auto *err = res.to_err()) {
HANDLE_ERROR(cx, *err);
return false;
}

return JS_ParseJSON(cx, inspect_info_str, args.rval());
}

// TODO(performance): consider allowing logger creation during initialization, but then throw
// when trying to log.
// https://github.com/fastly/js-compute-runtime/issues/225
Expand Down Expand Up @@ -610,6 +720,7 @@ bool install(api::Engine *engine) {
JS_FN("enableDebugLogging", Fastly::enableDebugLogging, 1, JSPROP_ENUMERATE),
JS_FN("debugLog", debugLog, 1, JSPROP_ENUMERATE),
JS_FN("getGeolocationForIpAddress", Fastly::getGeolocationForIpAddress, 1, JSPROP_ENUMERATE),
JS_FN("inspect", Fastly::inspect, 1, JSPROP_ENUMERATE),
JS_FN("getLogger", Fastly::getLogger, 1, JSPROP_ENUMERATE),
JS_FN("includeBytes", Fastly::includeBytes, 1, JSPROP_ENUMERATE),
JS_FN("createFanoutHandoff", Fastly::createFanoutHandoff, 2, JSPROP_ENUMERATE),
Expand Down Expand Up @@ -751,6 +862,21 @@ bool install(api::Engine *engine) {
if (!engine->define_builtin_module("fastly:fanout", fanout_val)) {
return false;
}

// fastly:security
RootedValue inspect_val(engine->cx());
if (!JS_GetProperty(engine->cx(), fastly, "inspect", &inspect_val)) {
return false;
}
RootedObject security_builtin(engine->cx(), JS_NewObject(engine->cx(), nullptr));
RootedValue security_builtin_val(engine->cx(), JS::ObjectValue(*security_builtin));
if (!JS_SetProperty(engine->cx(), security_builtin, "inspect", inspect_val)) {
return false;
}
if (!engine->define_builtin_module("fastly:security", security_builtin_val)) {
return false;
}

// fastly:websocket
RootedObject websocket(engine->cx(), JS_NewObject(engine->cx(), nullptr));
RootedValue websocket_val(engine->cx(), JS::ObjectValue(*websocket));
Expand Down
1 change: 1 addition & 0 deletions runtime/fastly/builtins/fastly.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class Fastly : public builtins::BuiltinNoConstructor<Fastly> {
static bool defaultBackend_set(JSContext *cx, unsigned argc, JS::Value *vp);
static bool allowDynamicBackends_get(JSContext *cx, unsigned argc, JS::Value *vp);
static bool allowDynamicBackends_set(JSContext *cx, unsigned argc, JS::Value *vp);
static bool inspect(JSContext *cx, unsigned argc, JS::Value *vp);
};

JS::Result<std::tuple<JS::UniqueChars, size_t>> convertBodyInit(JSContext *cx,
Expand Down
2 changes: 1 addition & 1 deletion runtime/fastly/builtins/fetch/request-response.h
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ class Response final : public builtins::FinalizableBuiltinImpl<Response> {
/**
* Base-level response creation handler, for both upstream and downstream requests.
*/
static JSObject *create(JSContext *cx, JS::HandleObject response,
staticeJSObject *create(JSContext *cx, JS::HandleObject response,
host_api::HttpResp response_handle, host_api::HttpBody body_handle,
bool is_upstream, JSObject *grip_upgrade_request,
JSObject *websocket_upgrade_request, JS::HandleString backend);
Expand Down
22 changes: 22 additions & 0 deletions runtime/fastly/host-api/fastly.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ typedef struct fastly_host_http_response {
uint32_t f1;
} fastly_host_http_response;

typedef struct fastly_host_http_inspect_options {
uint8_t *corp;
uint32_t corp_len;
uint8_t *workspace;
uint32_t workspace_len;
uint8_t *override_client_ip_ptr;
uint32_t override_client_ip_len;
} fastly_host_http_inspect_options;

typedef fastly_host_http_response fastly_world_tuple2_handle_handle;

#define WASM_IMPORT(module, name) __attribute__((import_module(module), import_name(name)))
Expand Down Expand Up @@ -265,6 +274,13 @@ typedef enum BodyWriteEnd {
#define CACHE_OVERRIDE_STALE_WHILE_REVALIDATE (1u << 2)
#define CACHE_OVERRIDE_PCI (1u << 3)

typedef uint32_t req_inspect_config_options_mask;

#define FASTLY_HOST_HTTP_REQ_INSPECT_CONFIG_OPTIONS_MASK_RESERVED = 1 << 0;
#define FASTLY_HOST_HTTP_REQ_INSPECT_CONFIG_OPTIONS_MASK_CORP = 1 << 1;
#define FASTLY_HOST_HTTP_REQ_INSPECT_CONFIG_OPTIONS_MASK_WORKSPACE = 1 << 2;
#define FASTLY_HOST_HTTP_REQ_INSPECT_CONFIG_OPTIONS_MASK_OVERRIDE_CLIENT_IP = 1 << 3;

WASM_IMPORT("fastly_abi", "init")
int init(uint64_t abi_version);

Expand Down Expand Up @@ -620,6 +636,12 @@ int req_pending_req_wait_v2(uint32_t req_handle,
fastly_host_http_send_error_detail *send_error_detail,
uint32_t *resp_handle_out, uint32_t *resp_body_handle_out);

WASM_IMPORT("fastly_http_req", "inspect")
int req_inspect(uint32_t req_handle, uint32_t body_handle,
req_inspect_config_options_mask config_options_mask,
fastly_host_http_inspect_options *config, uint8_t *inspect_res_buf,
uint32_t inspect_res_buf_len, uint32_t *nwritten_out);

// Module fastly_http_resp
WASM_IMPORT("fastly_http_resp", "new")
int resp_new(uint32_t *resp_handle_out);
Expand Down
29 changes: 29 additions & 0 deletions runtime/fastly/host-api/host_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2117,6 +2117,35 @@ Result<HostString> HttpReq::get_suggested_cache_key() const {
return Result<HostString>::ok(make_host_string(str));
}

Result<HostString> Request::inspect(const InspectConfig *config) {
TRACE_CALL()
uint32_t inspect_opts_mask{0};

if (config.corp != nullptr) {
inspect_opts_mask |= FASTLY_HOST_HTTP_REQ_INSPECT_CONFIG_OPTIONS_MASK_CORP;
}

if (config.workspace != nullptr) {
inspect_opts_mask |= FASTLY_HOST_HTTP_REQ_INSPECT_CONFIG_OPTIONS_MASK_WORKSPACE;
}

if (config.override_client_ip != nullptr) {
inspect_opts_mask |= FASTLY_HOST_HTTP_REQ_INSPECT_CONFIG_OPTIONS_MASK_OVERRIDE_CLIENT_IP;
}

fastly::fastly_host_error err;
fastly::fastly_world_string ret;
ret.ptr = static_cast<uint8_t *>(cabi_malloc(HOSTCALL_BUFFER_LEN, 4));
if (!convert_result(fastly::req_inspect(this->req.handle, this->body.handle, inspect_opts_mask,
config, ret.ptr, &ret.len, ),
&err)) {
res.emplace_err(err);
} else {
res.emplace(make_host_string(ret));
}
return res;
}

// HttpCacheEntry method implementations
Result<HttpCacheEntry> HttpCacheEntry::lookup(const HttpReq &req, std::span<uint8_t> override_key) {
TRACE_CALL()
Expand Down
19 changes: 18 additions & 1 deletion runtime/fastly/host-api/host_api_fastly.h
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ struct TlsVersion {
uint8_t value = 0;

explicit TlsVersion(uint8_t raw);
explicit TlsVersion(){};
explicit TlsVersion() {};

uint8_t get_version() const;
double get_version_number() const;
Expand Down Expand Up @@ -515,6 +515,21 @@ enum class FramingHeadersMode : uint8_t {
ManuallyFromHeaders,
};

class InspectOptions final {
public:
uint8_t *corp = nullptr;
uint32_t corp_len = 0;
uint8_t *workspace = nullptr;
uint32_t workspace_len = 0;
uint8_t *override_client_ip_ptr = nullptr;
uint32_t override_client_ip_len = 0;
uint32_t req_handle;
uint32_t body_handle;

InspectOptions() = default;
explicit InspectOptions(uint32_t req, uint32_t body) : req_handle{req}, body_handle{body} {}
};

class HttpReq final : public HttpBase {
public:
using Handle = uint32_t;
Expand Down Expand Up @@ -658,6 +673,8 @@ struct Request {

Request() = default;
Request(HttpReq req, HttpBody body) : req{req}, body{body} {}

Result<HostString> inspect(const InspectConfig *config);
};

class GeoIp final {
Expand Down
Loading
Loading