@@ -169,6 +169,112 @@ bool Fastly::getGeolocationForIpAddress(JSContext *cx, unsigned argc, JS::Value
169169 return JS_ParseJSON (cx, geo_info_str, args.rval ());
170170}
171171
172+ bool Fastly::inspect (JSContext *cx, unsigned argc, JS::Value *vp) {
173+ JS::CallArgs args = CallArgsFromVp (argc, vp);
174+ REQUEST_HANDLER_ONLY (" inspect" );
175+ if (!args.requireAtLeast (cx, " inspect" , 1 )) {
176+ return false ;
177+ }
178+
179+ auto request_value = args.get (0 );
180+ if (!Request::is_instance (request_value)) {
181+ JS_ReportErrorUTF8 (cx, " inspect: request parameter must be an instance of Request" );
182+ return false ;
183+ }
184+ auto grip_upgrade_request = &request_value.toObject ();
185+
186+ auto options_value = args.get (1 );
187+ JS::RootedObject options_obj (cx, options_value.isObject () ? &options_value.toObject () : nullptr );
188+
189+ host_api::InspectOptions inspect_options (request_value.req .handle , request_value.body .handle );
190+
191+ if (options_value != nullptr ) {
192+
193+ host_api::HostString corp_str;
194+ JS::RootedValue corp_val (cx);
195+ if (!JS_GetProperty (cx, options, " corp" , &corp_val)) {
196+ return false ;
197+ }
198+ if (!corp_val.isNullOrUndefined ()) {
199+ if (!corp_val.isString ()) {
200+ api::throw_error (cx, api::Errors::TypeError, " inspect" , " corp" , " be a string" );
201+ return false ;
202+ }
203+ corp_str = core::encode (cx, corp_val);
204+ if (!corp_str) {
205+ return false ;
206+ }
207+ std::optional<std::string_view> corp = corp_str;
208+ if (corp) {
209+ inspect_options.corp_len = corp->length ();
210+ inspect_options.corp = std::move (corp->data ());
211+ }
212+ }
213+
214+ host_api::HostString workspace_str;
215+ JS::RootedValue workspace_val (cx);
216+ if (!JS_GetProperty (cx, options, " workspace" , &workspace_val)) {
217+ return false ;
218+ }
219+ if (!workspace_val.isNullOrUndefined ()) {
220+ if (!workspace_val.isString ()) {
221+ api::throw_error (cx, api::Errors::TypeError, " inspect" , " workspace" , " be a string" );
222+ return false ;
223+ }
224+ workspace_str = core::encode (cx, workspace_val);
225+ if (!workspace_str) {
226+ return false ;
227+ }
228+ std::optional<std::string_view> workspace = workspace_str;
229+ if (workspace) {
230+ inspect_options.workspace_len = workspace->length ();
231+ inspect_options.workspace = std::move (workspace->data ());
232+ }
233+ }
234+
235+ host_api::HostString override_client_ip_str;
236+ JS::RootedValue override_client_ip_val (cx);
237+ if (!JS_GetProperty (cx, options, " overrideClientIp" , &override_client_ip_val)) {
238+ return false ;
239+ }
240+ if (!override_client_ip_val.isNullOrUndefined ()) {
241+ if (!override_client_ip_val.isString ()) {
242+ api::throw_error (cx, api::Errors::TypeError, " fastly.inspect" , " overrideClientIp" , " be a string" );
243+ return false ;
244+ }
245+ override_client_ip_str = core::encode (cx, override_client_ip_val);
246+ if (!override_client_ip_str) {
247+ return false ;
248+ }
249+
250+ // TODO: Remove all of this and rely on the host for validation as the hostcall only takes one
251+ // user-supplied parameter
252+ int format = AF_INET;
253+ size_t octets_len = 4 ;
254+ if (std::find (override_client_ip_str.begin (), override_client_ip_str.end (), ' :' ) != override_client_ip_str.end ()) {
255+ format = AF_INET6;
256+ octets_len = 16 ;
257+ }
258+
259+ uint8_t octets[sizeof (struct in6_addr )];
260+ if (inet_pton (format, override_client_ip_str.begin (), octets) != 1 ) {
261+ api::throw_error (cx, api::Errors::TypeError, " fastly.inspect" , " overrideClientIp" , " be a valid IP address" );
262+ return false ;
263+ }
264+ inspect_options.override_client_ip_len = octets_len;
265+ inspect_options.override_client_ip = std::move (octets->data ());
266+ }
267+ }
268+
269+ auto res = request_value->inspect (&inspect_options);
270+ if (auto *err = res.to_err ()) {
271+ HANDLE_ERROR (cx, *err);
272+ return false ;
273+ }
274+
275+ return JS_ParseJSON (cx, inspect_info_str, args.rval ());
276+ }
277+
172278// TODO(performance): consider allowing logger creation during initialization, but then throw
173279// when trying to log.
174280// https://github.com/fastly/js-compute-runtime/issues/225
@@ -610,6 +716,7 @@ bool install(api::Engine *engine) {
610716 JS_FN (" enableDebugLogging" , Fastly::enableDebugLogging, 1 , JSPROP_ENUMERATE),
611717 JS_FN (" debugLog" , debugLog, 1 , JSPROP_ENUMERATE),
612718 JS_FN (" getGeolocationForIpAddress" , Fastly::getGeolocationForIpAddress, 1 , JSPROP_ENUMERATE),
719+ JS_FN (" inspect" , Fastly::inspect, 1 , JSPROP_ENUMERATE),
613720 JS_FN (" getLogger" , Fastly::getLogger, 1 , JSPROP_ENUMERATE),
614721 JS_FN (" includeBytes" , Fastly::includeBytes, 1 , JSPROP_ENUMERATE),
615722 JS_FN (" createFanoutHandoff" , Fastly::createFanoutHandoff, 2 , JSPROP_ENUMERATE),
@@ -751,6 +858,23 @@ bool install(api::Engine *engine) {
751858 if (!engine->define_builtin_module (" fastly:fanout" , fanout_val)) {
752859 return false ;
753860 }
861+
862+ // fastly:security
863+ RootedValue inspect_val (engine->cx ());
864+ if (!JS_GetProperty (engine->cx (), fastly, " inspect" ,
865+ &inspect_val)) {
866+ return false ;
867+ }
868+ RootedObject security_builtin (engine->cx (), JS_NewObject (engine->cx (), nullptr ));
869+ RootedValue security_builtin_val (engine->cx (), JS::ObjectValue (*security_builtin));
870+ if (!JS_SetProperty (engine->cx (), security_builtin, " inspect" ,
871+ inspect_val)) {
872+ return false ;
873+ }
874+ if (!engine->define_builtin_module (" fastly:security" , security_builtin_val)) {
875+ return false ;
876+ }
877+
754878 // fastly:websocket
755879 RootedObject websocket (engine->cx (), JS_NewObject (engine->cx (), nullptr ));
756880 RootedValue websocket_val (engine->cx (), JS::ObjectValue (*websocket));
0 commit comments