void do_execute_request()

in src/apps/js_v8/js_v8_base.cpp [69:298]


    void do_execute_request(
      const ccf::endpoints::EndpointProperties& props,
      ccf::endpoints::EndpointContext& endpoint_ctx,
      ccf::historical::StatePtr historical_state)
    {
      // Isolates are re-used across requests
      thread_local V8Isolate isolate;

      // Each request is executed in a new context.
      // A context is used in a browser to separate different
      // origins of a page, for example iframes.
      // No state is shared between contexts, except when
      // explicitly allowed through security tokens, which
      // are not used here.
      V8Context ctx(isolate);

      // Make sure handles are cleaned up at request end
      v8::HandleScope handle_scope(isolate);

      // Run finalizers at the end of the request
      // no matter whether a context is re-used or not.
      V8Context::FinalizerScope finalizer_scope(ctx);

      // set a callback that loads modules from the KV
      ctx.set_module_load_callback(
        ccf::v8_kv_module_load_callback, &endpoint_ctx.tx);

      v8::Local<v8::Context> context = ctx.get_context();
      v8::TryCatch try_catch(isolate);

      // Populate globals
      v8::Local<v8::Value> console_global =
        v8_tmpl::ConsoleGlobal::wrap(context);
      ctx.install_global("console", console_global);

      v8_tmpl::TxContext txctx{&endpoint_ctx.tx, v8_tmpl::TxAccess::APP};
      v8::Local<v8::Value> ccf_global = v8_tmpl::CCFGlobal::wrap(
        context,
        &txctx,
        &historical_state,
        this,
        &node_context.get_historical_state(),
        endpoint_ctx.rpc_ctx.get());
      ctx.install_global("ccf", ccf_global);

      // Call exported function
      v8::Local<v8::Value> request =
        ccf::v8_tmpl::Request::wrap(context, &endpoint_ctx, this);
      std::vector<v8::Local<v8::Value>> args{request};
      v8::Local<v8::Value> val =
        ctx.run(props.js_module, props.js_function, args);

      if (val.IsEmpty())
      {
        v8_util::report_exception(isolate, &try_catch);
        auto exception_str =
          v8_util::get_exception_message(isolate, &try_catch);

        endpoint_ctx.rpc_ctx->set_error(
          HTTP_STATUS_INTERNAL_SERVER_ERROR,
          ccf::errors::InternalError,
          std::move(exception_str));
        return;
      }

      // Handle return value: {body, headers, statusCode}
      if (!val->IsObject())
      {
        endpoint_ctx.rpc_ctx->set_error(
          HTTP_STATUS_INTERNAL_SERVER_ERROR,
          ccf::errors::InternalError,
          "Invalid endpoint function return value (not an object).");
        return;
      }

      // Response body (also sets a default response content-type header)
      v8::Local<v8::Object> obj = val.As<v8::Object>();
      v8::Local<v8::Value> response_body_js;
      if (!obj->Get(context, v8_util::to_v8_str(isolate, "body"))
             .ToLocal(&response_body_js))
      {
        v8_util::report_exception(isolate, &try_catch);
        endpoint_ctx.rpc_ctx->set_error(
          HTTP_STATUS_INTERNAL_SERVER_ERROR,
          ccf::errors::InternalError,
          "Invalid endpoint function return value (cannot access body).");
        return;
      }
      if (!response_body_js->IsUndefined())
      {
        std::vector<uint8_t> response_body;
        size_t buf_size;
        size_t buf_offset;
        v8::Local<v8::ArrayBuffer> array_buffer;
        if (response_body_js->IsArrayBufferView())
        {
          auto view = response_body_js.As<v8::ArrayBufferView>();
          buf_offset = view->ByteOffset();
          buf_size = view->ByteLength();
          array_buffer = view->Buffer();
        }
        else if (response_body_js->IsArrayBuffer())
        {
          array_buffer = response_body_js.As<v8::ArrayBuffer>();
          buf_offset = 0;
          buf_size = array_buffer->ByteLength();
        }
        if (!array_buffer.IsEmpty())
        {
          uint8_t* buf = (uint8_t*)array_buffer->GetBackingStore()->Data();
          buf += buf_offset;
          endpoint_ctx.rpc_ctx->set_response_header(
            http::headers::CONTENT_TYPE,
            http::headervalues::contenttype::OCTET_STREAM);
          response_body = std::vector<uint8_t>(buf, buf + buf_size);
        }
        else
        {
          std::string str;
          if (response_body_js->IsString())
          {
            endpoint_ctx.rpc_ctx->set_response_header(
              http::headers::CONTENT_TYPE,
              http::headervalues::contenttype::TEXT);
            v8::Local<v8::String> str_val = response_body_js.As<v8::String>();
            str = v8_util::to_str(isolate, str_val);
          }
          else
          {
            endpoint_ctx.rpc_ctx->set_response_header(
              http::headers::CONTENT_TYPE,
              http::headervalues::contenttype::JSON);
            v8::Local<v8::String> json;
            if (!v8::JSON::Stringify(context, response_body_js).ToLocal(&json))
            {
              v8_util::report_exception(isolate, &try_catch);
              endpoint_ctx.rpc_ctx->set_error(
                HTTP_STATUS_INTERNAL_SERVER_ERROR,
                ccf::errors::InternalError,
                "Invalid endpoint function return value (error during JSON "
                "conversion of body).");
              return;
            }
            str = v8_util::to_str(isolate, json);
          }
          response_body = std::vector<uint8_t>(str.begin(), str.end());
        }
        endpoint_ctx.rpc_ctx->set_response_body(std::move(response_body));
      }

      // Response headers
      v8::Local<v8::Value> response_headers_js;
      if (!obj->Get(context, v8_util::to_v8_str(isolate, "headers"))
             .ToLocal(&response_headers_js))
      {
        v8_util::report_exception(isolate, &try_catch);
        endpoint_ctx.rpc_ctx->set_error(
          HTTP_STATUS_INTERNAL_SERVER_ERROR,
          ccf::errors::InternalError,
          "Invalid endpoint function return value (cannot access headers).");
        return;
      }
      if (!response_headers_js->IsNullOrUndefined())
      {
        if (!response_headers_js->IsObject())
        {
          endpoint_ctx.rpc_ctx->set_error(
            HTTP_STATUS_INTERNAL_SERVER_ERROR,
            ccf::errors::InternalError,
            "Invalid endpoint function return value (headers is not an "
            "object).");
          return;
        }
        v8::Local<v8::Object> headers_obj =
          response_headers_js.As<v8::Object>();
        v8::Local<v8::Array> headers_arr =
          headers_obj->GetOwnPropertyNames(context).ToLocalChecked();
        for (uint32_t i = 0; i < headers_arr->Length(); i++)
        {
          v8::Local<v8::Value> key =
            headers_arr->Get(context, i).ToLocalChecked();
          v8::Local<v8::Value> val =
            headers_obj->Get(context, key).ToLocalChecked();
          if (!key->IsString() || !val->IsString())
          {
            endpoint_ctx.rpc_ctx->set_error(
              HTTP_STATUS_INTERNAL_SERVER_ERROR,
              ccf::errors::InternalError,
              "Invalid endpoint function return value (header key/value "
              "type).");
            return;
          }
          std::string key_str = v8_util::to_str(isolate, key.As<v8::String>());
          std::string val_str = v8_util::to_str(isolate, val.As<v8::String>());
          endpoint_ctx.rpc_ctx->set_response_header(key_str, val_str);
        }
      }

      // Response status code
      int response_status_code = HTTP_STATUS_OK;
      v8::Local<v8::Value> status_code_js;
      if (!obj->Get(context, v8_util::to_v8_str(isolate, "statusCode"))
             .ToLocal(&status_code_js))
      {
        v8_util::report_exception(isolate, &try_catch);
        endpoint_ctx.rpc_ctx->set_error(
          HTTP_STATUS_INTERNAL_SERVER_ERROR,
          ccf::errors::InternalError,
          "Invalid endpoint function return value (cannot access statusCode).");
        return;
      }
      if (!status_code_js->IsNullOrUndefined())
      {
        v8::Local<v8::Uint32> status_code;
        if (
          !status_code_js->IsNumber() ||
          !status_code_js->ToUint32(context).ToLocal(&status_code))
        {
          if (try_catch.HasCaught())
            v8_util::report_exception(isolate, &try_catch);
          endpoint_ctx.rpc_ctx->set_error(
            HTTP_STATUS_INTERNAL_SERVER_ERROR,
            ccf::errors::InternalError,
            "Invalid endpoint function return value (status code type).");
          return;
        }
        response_status_code = status_code->Value();
      }
      endpoint_ctx.rpc_ctx->set_response_status(response_status_code);
    }