Files
bun.sh/src/bun.js/bindings/NodeHTTP.cpp
2025-03-24 23:49:02 -07:00

1476 lines
56 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "root.h"
#include "JSDOMGlobalObjectInlines.h"
#include "ZigGlobalObject.h"
#include <JavaScriptCore/GlobalObjectMethodTable.h>
#include "helpers.h"
#include "BunClientData.h"
#include <JavaScriptCore/AggregateError.h>
#include <JavaScriptCore/InternalFieldTuple.h>
#include <JavaScriptCore/ObjectConstructor.h>
#include <JavaScriptCore/ObjectConstructor.h>
#include <JavaScriptCore/JSFunction.h>
#include "wtf/URL.h"
#include "JSFetchHeaders.h"
#include "JSDOMExceptionHandling.h"
#include <bun-uws/src/App.h>
#include "ZigGeneratedClasses.h"
#include "ScriptExecutionContext.h"
#include "AsyncContextFrame.h"
#include "ZigGeneratedClasses.h"
#include <JavaScriptCore/LazyPropertyInlines.h>
#include <JavaScriptCore/VMTrapsInlines.h>
#include "JSSocketAddressDTO.h"
extern "C" uint64_t uws_res_get_remote_address_info(void* res, const char** dest, int* port, bool* is_ipv6);
extern "C" uint64_t uws_res_get_local_address_info(void* res, const char** dest, int* port, bool* is_ipv6);
namespace Bun {
using namespace JSC;
using namespace WebCore;
JSC_DEFINE_CUSTOM_SETTER(noOpSetter, (JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue value, PropertyName propertyName))
{
return false;
}
JSC_DECLARE_CUSTOM_GETTER(jsNodeHttpServerSocketGetterOnClose);
JSC_DECLARE_CUSTOM_GETTER(jsNodeHttpServerSocketGetterClosed);
JSC_DECLARE_CUSTOM_SETTER(jsNodeHttpServerSocketSetterOnClose);
JSC_DECLARE_HOST_FUNCTION(jsFunctionNodeHTTPServerSocketClose);
JSC_DECLARE_CUSTOM_GETTER(jsNodeHttpServerSocketGetterResponse);
JSC_DECLARE_CUSTOM_GETTER(jsNodeHttpServerSocketGetterRemoteAddress);
JSC_DECLARE_CUSTOM_GETTER(jsNodeHttpServerSocketGetterLocalAddress);
BUN_DECLARE_HOST_FUNCTION(Bun__drainMicrotasksFromJS);
JSC_DECLARE_CUSTOM_GETTER(jsNodeHttpServerSocketGetterDuplex);
JSC_DECLARE_CUSTOM_SETTER(jsNodeHttpServerSocketSetterDuplex);
// Create a static hash table of values containing an onclose DOMAttributeGetterSetter and a close function
static const HashTableValue JSNodeHTTPServerSocketPrototypeTableValues[] = {
{ "onclose"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsNodeHttpServerSocketGetterOnClose, jsNodeHttpServerSocketSetterOnClose } },
{ "closed"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::GetterSetterType, jsNodeHttpServerSocketGetterClosed, noOpSetter } },
{ "response"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::GetterSetterType, jsNodeHttpServerSocketGetterResponse, noOpSetter } },
{ "duplex"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsNodeHttpServerSocketGetterDuplex, jsNodeHttpServerSocketSetterDuplex } },
{ "remoteAddress"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::GetterSetterType, jsNodeHttpServerSocketGetterRemoteAddress, noOpSetter } },
{ "localAddress"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::GetterSetterType, jsNodeHttpServerSocketGetterLocalAddress, noOpSetter } },
{ "close"_s, static_cast<unsigned>(PropertyAttribute::Function | PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::NativeFunctionType, jsFunctionNodeHTTPServerSocketClose, 0 } },
};
class JSNodeHTTPServerSocketPrototype final : public JSC::JSNonFinalObject {
public:
using Base = JSC::JSNonFinalObject;
static JSNodeHTTPServerSocketPrototype* create(VM& vm, Structure* structure)
{
JSNodeHTTPServerSocketPrototype* prototype = new (NotNull, allocateCell<JSNodeHTTPServerSocketPrototype>(vm)) JSNodeHTTPServerSocketPrototype(vm, structure);
prototype->finishCreation(vm);
return prototype;
}
DECLARE_INFO;
static constexpr bool needsDestruction = false;
static constexpr unsigned StructureFlags = Base::StructureFlags | HasStaticPropertyTable;
template<typename CellType, JSC::SubspaceAccess>
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
{
STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSNodeHTTPServerSocketPrototype, Base);
return &vm.plainObjectSpace();
}
private:
JSNodeHTTPServerSocketPrototype(VM& vm, Structure* structure)
: Base(vm, structure)
{
}
void finishCreation(VM& vm)
{
Base::finishCreation(vm);
ASSERT(inherits(info()));
reifyStaticProperties(vm, info(), JSNodeHTTPServerSocketPrototypeTableValues, *this);
this->structure()->setMayBePrototype(true);
}
};
class JSNodeHTTPServerSocket : public JSC::JSDestructibleObject {
public:
using Base = JSC::JSDestructibleObject;
static JSNodeHTTPServerSocket* create(JSC::VM& vm, JSC::Structure* structure, us_socket_t* socket, bool is_ssl, WebCore::JSNodeHTTPResponse* response)
{
auto* object = new (JSC::allocateCell<JSNodeHTTPServerSocket>(vm)) JSNodeHTTPServerSocket(vm, structure, socket, is_ssl, response);
object->finishCreation(vm);
return object;
}
static JSNodeHTTPServerSocket* create(JSC::VM& vm, Zig::GlobalObject* globalObject, us_socket_t* socket, bool is_ssl, WebCore::JSNodeHTTPResponse* response)
{
auto* structure = globalObject->m_JSNodeHTTPServerSocketStructure.getInitializedOnMainThread(globalObject);
return create(vm, structure, socket, is_ssl, response);
}
static void destroy(JSC::JSCell* cell)
{
static_cast<JSNodeHTTPServerSocket*>(cell)->JSNodeHTTPServerSocket::~JSNodeHTTPServerSocket();
}
template<bool SSL>
static void clearSocketData(us_socket_t* socket)
{
auto* httpResponseData = (uWS::HttpResponseData<SSL>*)us_socket_ext(SSL, socket);
if (httpResponseData->socketData) {
httpResponseData->socketData = nullptr;
}
}
void close()
{
if (socket) {
us_socket_close(is_ssl, socket, 0, nullptr);
}
}
bool isClosed() const
{
return !socket || us_socket_is_closed(is_ssl, socket);
}
~JSNodeHTTPServerSocket()
{
if (socket) {
if (is_ssl) {
clearSocketData<true>(socket);
} else {
clearSocketData<false>(socket);
}
}
}
JSNodeHTTPServerSocket(JSC::VM& vm, JSC::Structure* structure, us_socket_t* socket, bool is_ssl, WebCore::JSNodeHTTPResponse* response)
: JSC::JSDestructibleObject(vm, structure)
, socket(socket)
, is_ssl(is_ssl)
{
currentResponseObject.setEarlyValue(vm, this, response);
}
mutable WriteBarrier<JSObject> functionToCallOnClose;
mutable WriteBarrier<WebCore::JSNodeHTTPResponse> currentResponseObject;
mutable WriteBarrier<JSObject> m_remoteAddress;
mutable WriteBarrier<JSObject> m_localAddress;
mutable WriteBarrier<JSObject> m_duplex;
unsigned is_ssl : 1;
us_socket_t* socket;
JSC::Strong<JSNodeHTTPServerSocket> strongThis = {};
DECLARE_INFO;
DECLARE_VISIT_CHILDREN;
template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
{
if constexpr (mode == JSC::SubspaceAccess::Concurrently)
return nullptr;
return WebCore::subspaceForImpl<JSNodeHTTPServerSocket, UseCustomHeapCellType::No>(
vm,
[](auto& spaces) { return spaces.m_clientSubspaceForJSNodeHTTPServerSocket.get(); },
[](auto& spaces, auto&& space) { spaces.m_clientSubspaceForJSNodeHTTPServerSocket = std::forward<decltype(space)>(space); },
[](auto& spaces) { return spaces.m_subspaceForJSNodeHTTPServerSocket.get(); },
[](auto& spaces, auto&& space) { spaces.m_subspaceForJSNodeHTTPServerSocket = std::forward<decltype(space)>(space); });
}
void onClose()
{
this->socket = nullptr;
this->m_duplex.clear();
this->currentResponseObject.clear();
// This function can be called during GC!
Zig::GlobalObject* globalObject = static_cast<Zig::GlobalObject*>(this->globalObject());
if (!functionToCallOnClose) {
this->strongThis.clear();
return;
}
WebCore::ScriptExecutionContext* scriptExecutionContext = globalObject->scriptExecutionContext();
if (scriptExecutionContext) {
JSC::gcProtect(this);
scriptExecutionContext->postTask([self = this](ScriptExecutionContext& context) {
WTF::NakedPtr<JSC::Exception> exception;
auto* globalObject = defaultGlobalObject(context.globalObject());
auto* thisObject = self;
auto* callbackObject = thisObject->functionToCallOnClose.get();
if (!callbackObject) {
JSC::gcUnprotect(self);
return;
}
auto callData = JSC::getCallData(callbackObject);
MarkedArgumentBuffer args;
EnsureStillAliveScope ensureStillAlive(self);
JSC::gcUnprotect(self);
if (globalObject->scriptExecutionStatus(globalObject, thisObject) == ScriptExecutionStatus::Running) {
profiledCall(globalObject, JSC::ProfilingReason::API, callbackObject, callData, thisObject, args, exception);
if (auto* ptr = exception.get()) {
exception.clear();
globalObject->reportUncaughtExceptionAtEventLoop(globalObject, ptr);
}
}
});
}
this->strongThis.clear();
}
static Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
{
auto* structure = JSC::Structure::create(vm, globalObject, globalObject->objectPrototype(), JSC::TypeInfo(JSC::ObjectType, StructureFlags), JSNodeHTTPServerSocketPrototype::info());
auto* prototype = JSNodeHTTPServerSocketPrototype::create(vm, structure);
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
}
void finishCreation(JSC::VM& vm)
{
Base::finishCreation(vm);
}
};
JSC_DEFINE_HOST_FUNCTION(jsFunctionNodeHTTPServerSocketClose, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
auto* thisObject = jsDynamicCast<JSNodeHTTPServerSocket*>(callFrame->thisValue());
if (UNLIKELY(!thisObject)) {
return JSValue::encode(JSC::jsUndefined());
}
if (thisObject->isClosed()) {
return JSValue::encode(JSC::jsUndefined());
}
thisObject->close();
return JSValue::encode(JSC::jsUndefined());
}
JSC_DEFINE_CUSTOM_GETTER(jsNodeHttpServerSocketGetterDuplex, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
{
auto* thisObject = jsCast<JSNodeHTTPServerSocket*>(JSC::JSValue::decode(thisValue));
if (thisObject->m_duplex) {
return JSValue::encode(thisObject->m_duplex.get());
}
return JSValue::encode(JSC::jsNull());
}
JSC_DEFINE_CUSTOM_SETTER(jsNodeHttpServerSocketSetterDuplex, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue encodedValue, JSC::PropertyName propertyName))
{
auto& vm = globalObject->vm();
auto* thisObject = jsCast<JSNodeHTTPServerSocket*>(JSC::JSValue::decode(thisValue));
JSValue value = JSC::JSValue::decode(encodedValue);
if (auto* object = value.getObject()) {
thisObject->m_duplex.set(vm, thisObject, object);
} else {
thisObject->m_duplex.clear();
}
return true;
}
JSC_DEFINE_CUSTOM_GETTER(jsNodeHttpServerSocketGetterRemoteAddress, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
{
auto& vm = globalObject->vm();
auto* thisObject = jsCast<JSNodeHTTPServerSocket*>(JSC::JSValue::decode(thisValue));
if (thisObject->m_remoteAddress) {
return JSValue::encode(thisObject->m_remoteAddress.get());
}
us_socket_t* socket = thisObject->socket;
if (!socket) {
return JSValue::encode(JSC::jsNull());
}
const char* address = nullptr;
int port = 0;
bool is_ipv6 = false;
uws_res_get_remote_address_info(socket, &address, &port, &is_ipv6);
if (address == nullptr) {
return JSValue::encode(JSC::jsNull());
}
auto addressString = WTF::String::fromUTF8(address);
if (addressString.isEmpty()) {
return JSValue::encode(JSC::jsNull());
}
auto* object = JSSocketAddressDTO::create(defaultGlobalObject(globalObject), jsString(vm, addressString), port, is_ipv6);
thisObject->m_remoteAddress.set(vm, thisObject, object);
return JSValue::encode(object);
}
JSC_DEFINE_CUSTOM_GETTER(jsNodeHttpServerSocketGetterLocalAddress, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
{
auto& vm = globalObject->vm();
auto* thisObject = jsCast<JSNodeHTTPServerSocket*>(JSC::JSValue::decode(thisValue));
if (thisObject->m_localAddress) {
return JSValue::encode(thisObject->m_localAddress.get());
}
us_socket_t* socket = thisObject->socket;
if (!socket) {
return JSValue::encode(JSC::jsNull());
}
const char* address = nullptr;
int port = 0;
bool is_ipv6 = false;
uws_res_get_local_address_info(socket, &address, &port, &is_ipv6);
if (address == nullptr) {
return JSValue::encode(JSC::jsNull());
}
auto addressString = WTF::String::fromUTF8(address);
if (addressString.isEmpty()) {
return JSValue::encode(JSC::jsNull());
}
auto* object = JSSocketAddressDTO::create(defaultGlobalObject(globalObject), jsString(vm, addressString), port, is_ipv6);
thisObject->m_localAddress.set(vm, thisObject, object);
return JSValue::encode(object);
}
JSC_DEFINE_CUSTOM_GETTER(jsNodeHttpServerSocketGetterOnClose, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
{
auto* thisObject = jsCast<JSNodeHTTPServerSocket*>(JSC::JSValue::decode(thisValue));
if (thisObject->functionToCallOnClose) {
return JSValue::encode(thisObject->functionToCallOnClose.get());
}
return JSValue::encode(JSC::jsUndefined());
}
JSC_DEFINE_CUSTOM_SETTER(jsNodeHttpServerSocketSetterOnClose, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue encodedValue, JSC::PropertyName propertyName))
{
auto& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
auto* thisObject = jsCast<JSNodeHTTPServerSocket*>(JSC::JSValue::decode(thisValue));
JSValue value = JSC::JSValue::decode(encodedValue);
if (value.isUndefined() || value.isNull()) {
thisObject->functionToCallOnClose.clear();
return true;
}
if (!value.isCallable()) {
return false;
}
thisObject->functionToCallOnClose.set(vm, thisObject, value.getObject());
return true;
}
JSC_DEFINE_CUSTOM_GETTER(jsNodeHttpServerSocketGetterClosed, (JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, PropertyName propertyName))
{
auto* thisObject = jsCast<JSNodeHTTPServerSocket*>(JSC::JSValue::decode(thisValue));
return JSValue::encode(JSC::jsBoolean(thisObject->isClosed()));
}
JSC_DEFINE_CUSTOM_GETTER(jsNodeHttpServerSocketGetterResponse, (JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, PropertyName propertyName))
{
auto* thisObject = jsCast<JSNodeHTTPServerSocket*>(JSC::JSValue::decode(thisValue));
if (!thisObject->currentResponseObject) {
return JSValue::encode(JSC::jsNull());
}
return JSValue::encode(thisObject->currentResponseObject.get());
}
template<typename Visitor>
void JSNodeHTTPServerSocket::visitChildrenImpl(JSCell* cell, Visitor& visitor)
{
JSNodeHTTPServerSocket* fn = jsCast<JSNodeHTTPServerSocket*>(cell);
ASSERT_GC_OBJECT_INHERITS(fn, info());
Base::visitChildren(fn, visitor);
visitor.append(fn->currentResponseObject);
visitor.append(fn->functionToCallOnClose);
visitor.append(fn->m_remoteAddress);
visitor.append(fn->m_localAddress);
visitor.append(fn->m_duplex);
}
DEFINE_VISIT_CHILDREN(JSNodeHTTPServerSocket);
template<bool SSL>
static JSNodeHTTPServerSocket* getNodeHTTPServerSocket(us_socket_t* socket)
{
auto* httpResponseData = (uWS::HttpResponseData<SSL>*)us_socket_ext(SSL, socket);
return reinterpret_cast<JSNodeHTTPServerSocket*>(httpResponseData->socketData);
}
template<bool SSL>
static WebCore::JSNodeHTTPResponse* getNodeHTTPResponse(us_socket_t* socket)
{
auto* serverSocket = getNodeHTTPServerSocket<SSL>(socket);
if (!serverSocket) {
return nullptr;
}
return serverSocket->currentResponseObject.get();
}
const JSC::ClassInfo JSNodeHTTPServerSocket::s_info = { "NodeHTTPServerSocket"_s, &Base::s_info, nullptr, nullptr,
CREATE_METHOD_TABLE(JSNodeHTTPServerSocket) };
const JSC::ClassInfo JSNodeHTTPServerSocketPrototype::s_info = { "NodeHTTPServerSocket"_s, &Base::s_info, nullptr, nullptr,
CREATE_METHOD_TABLE(JSNodeHTTPServerSocketPrototype) };
template<bool SSL>
static void* getNodeHTTPResponsePtr(us_socket_t* socket)
{
WebCore::JSNodeHTTPResponse* responseObject = getNodeHTTPResponse<SSL>(socket);
if (!responseObject) {
return nullptr;
}
return responseObject->wrapped();
}
extern "C" EncodedJSValue Bun__getNodeHTTPResponseThisValue(bool is_ssl, us_socket_t* socket)
{
if (is_ssl) {
return JSValue::encode(getNodeHTTPResponse<true>(socket));
}
return JSValue::encode(getNodeHTTPResponse<false>(socket));
}
extern "C" EncodedJSValue Bun__getNodeHTTPServerSocketThisValue(bool is_ssl, us_socket_t* socket)
{
if (is_ssl) {
return JSValue::encode(getNodeHTTPServerSocket<true>(socket));
}
return JSValue::encode(getNodeHTTPServerSocket<false>(socket));
}
extern "C" void Bun__setNodeHTTPServerSocketUsSocketValue(EncodedJSValue thisValue, us_socket_t* socket)
{
auto* response = jsCast<JSNodeHTTPServerSocket*>(JSValue::decode(thisValue));
response->socket = socket;
}
extern "C" void Bun__callNodeHTTPServerSocketOnClose(EncodedJSValue thisValue)
{
auto* response = jsCast<JSNodeHTTPServerSocket*>(JSValue::decode(thisValue));
response->onClose();
}
BUN_DECLARE_HOST_FUNCTION(jsFunctionRequestOrResponseHasBodyValue);
BUN_DECLARE_HOST_FUNCTION(jsFunctionGetCompleteRequestOrResponseBodyValueAsArrayBuffer);
extern "C" uWS::HttpRequest* Request__getUWSRequest(void*);
extern "C" void Request__setInternalEventCallback(void*, EncodedJSValue, JSC::JSGlobalObject*);
extern "C" void Request__setTimeout(void*, EncodedJSValue, JSC::JSGlobalObject*);
extern "C" bool NodeHTTPResponse__setTimeout(void*, EncodedJSValue, JSC::JSGlobalObject*);
extern "C" void Server__setIdleTimeout(EncodedJSValue, EncodedJSValue, JSC::JSGlobalObject*);
static EncodedJSValue assignHeadersFromFetchHeaders(FetchHeaders& impl, JSObject* prototype, JSObject* objectValue, JSC::InternalFieldTuple* tuple, JSC::JSGlobalObject* globalObject, JSC::VM& vm)
{
auto scope = DECLARE_THROW_SCOPE(vm);
uint32_t size = std::min(impl.sizeAfterJoiningSetCookieHeader(), static_cast<uint32_t>(JSFinalObject::maxInlineCapacity));
JSC::JSArray* array = constructEmptyArray(globalObject, nullptr, impl.size() * 2);
RETURN_IF_EXCEPTION(scope, {});
JSC::JSObject* obj = JSC::constructEmptyObject(globalObject, prototype, size);
RETURN_IF_EXCEPTION(scope, {});
unsigned arrayI = 0;
auto& internal = impl.internalHeaders();
{
auto& vec = internal.commonHeaders();
for (const auto& it : vec) {
const auto& name = it.key;
const auto& value = it.value;
const auto impl = WTF::httpHeaderNameStringImpl(name);
JSString* jsValue = jsString(vm, value);
obj->putDirect(vm, Identifier::fromString(vm, impl), jsValue, 0);
array->putDirectIndex(globalObject, arrayI++, jsString(vm, impl));
array->putDirectIndex(globalObject, arrayI++, jsValue);
RETURN_IF_EXCEPTION(scope, {});
}
}
{
const auto& values = internal.getSetCookieHeaders();
size_t count = values.size();
if (count > 0) {
JSC::JSArray* setCookies = constructEmptyArray(globalObject, nullptr, count);
RETURN_IF_EXCEPTION(scope, {});
const auto setCookieHeaderString = WTF::httpHeaderNameStringImpl(HTTPHeaderName::SetCookie);
JSString* setCookie = jsString(vm, setCookieHeaderString);
for (size_t i = 0; i < count; ++i) {
auto* out = jsString(vm, values[i]);
array->putDirectIndex(globalObject, arrayI++, setCookie);
array->putDirectIndex(globalObject, arrayI++, out);
setCookies->putDirectIndex(globalObject, i, out);
RETURN_IF_EXCEPTION(scope, {});
}
RETURN_IF_EXCEPTION(scope, {});
obj->putDirect(vm, JSC::Identifier::fromString(vm, setCookieHeaderString), setCookies, 0);
}
}
{
const auto& vec = internal.uncommonHeaders();
for (const auto& it : vec) {
const auto& name = it.key;
const auto& value = it.value;
auto* jsValue = jsString(vm, value);
obj->putDirect(vm, Identifier::fromString(vm, name.convertToASCIILowercase()), jsValue, 0);
array->putDirectIndex(globalObject, arrayI++, jsString(vm, name));
array->putDirectIndex(globalObject, arrayI++, jsValue);
}
}
tuple->putInternalField(vm, 0, obj);
tuple->putInternalField(vm, 1, array);
return JSValue::encode(tuple);
}
static void assignHeadersFromUWebSocketsForCall(uWS::HttpRequest* request, MarkedArgumentBuffer& args, JSC::JSGlobalObject* globalObject, JSC::VM& vm)
{
auto scope = DECLARE_THROW_SCOPE(vm);
std::string_view fullURLStdStr = request->getFullUrl();
String fullURL = String::fromUTF8ReplacingInvalidSequences({ reinterpret_cast<const LChar*>(fullURLStdStr.data()), fullURLStdStr.length() });
// Get the URL.
{
args.append(jsString(vm, fullURL));
}
// Get the method.
{
std::string_view methodView = request->getMethod();
WTF::String methodString;
switch (methodView.length()) {
case 3: {
if (methodView == std::string_view("get", 3)) {
methodString = "GET"_s;
break;
}
if (methodView == std::string_view("put", 3)) {
methodString = "PUT"_s;
break;
}
break;
}
case 4: {
if (methodView == std::string_view("post", 4)) {
methodString = "POST"_s;
break;
}
if (methodView == std::string_view("head", 4)) {
methodString = "HEAD"_s;
break;
}
if (methodView == std::string_view("copy", 4)) {
methodString = "COPY"_s;
break;
}
}
case 5: {
if (methodView == std::string_view("patch", 5)) {
methodString = "PATCH"_s;
break;
}
if (methodView == std::string_view("merge", 5)) {
methodString = "MERGE"_s;
break;
}
if (methodView == std::string_view("trace", 5)) {
methodString = "TRACE"_s;
break;
}
if (methodView == std::string_view("fetch", 5)) {
methodString = "FETCH"_s;
break;
}
if (methodView == std::string_view("purge", 5)) {
methodString = "PURGE"_s;
break;
}
break;
}
case 6: {
if (methodView == std::string_view("delete", 6)) {
methodString = "DELETE"_s;
break;
}
break;
}
case 7: {
if (methodView == std::string_view("connect", 7)) {
methodString = "CONNECT"_s;
break;
}
if (methodView == std::string_view("options", 7)) {
methodString = "OPTIONS"_s;
break;
}
break;
}
}
if (methodString.isNull()) {
methodString = String::fromUTF8ReplacingInvalidSequences({ reinterpret_cast<const LChar*>(methodView.data()), methodView.length() });
}
args.append(jsString(vm, methodString));
}
size_t size = 0;
for (auto it = request->begin(); it != request->end(); ++it) {
size++;
}
JSC::JSObject* headersObject = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), std::min(size, static_cast<size_t>(JSFinalObject::maxInlineCapacity)));
RETURN_IF_EXCEPTION(scope, void());
JSC::JSArray* setCookiesHeaderArray = nullptr;
JSC::JSString* setCookiesHeaderString = nullptr;
MarkedArgumentBuffer arrayValues;
args.append(headersObject);
for (auto it = request->begin(); it != request->end(); ++it) {
auto pair = *it;
StringView nameView = StringView(std::span { reinterpret_cast<const LChar*>(pair.first.data()), pair.first.length() });
std::span<LChar> data;
auto value = String::createUninitialized(pair.second.length(), data);
if (pair.second.length() > 0)
memcpy(data.data(), pair.second.data(), pair.second.length());
HTTPHeaderName name;
JSString* jsValue = jsString(vm, value);
HTTPHeaderIdentifiers& identifiers = WebCore::clientData(vm)->httpHeaderIdentifiers();
Identifier nameIdentifier;
JSString* nameString = nullptr;
if (WebCore::findHTTPHeaderName(nameView, name)) {
nameString = identifiers.stringFor(globalObject, name);
nameIdentifier = identifiers.identifierFor(vm, name);
} else {
WTF::String wtfString = nameView.toString();
nameString = jsString(vm, wtfString);
nameIdentifier = Identifier::fromString(vm, wtfString.convertToASCIILowercase());
}
if (name == WebCore::HTTPHeaderName::SetCookie) {
if (!setCookiesHeaderArray) {
setCookiesHeaderArray = constructEmptyArray(globalObject, nullptr);
setCookiesHeaderString = nameString;
headersObject->putDirect(vm, nameIdentifier, setCookiesHeaderArray, 0);
RETURN_IF_EXCEPTION(scope, void());
}
arrayValues.append(setCookiesHeaderString);
arrayValues.append(jsValue);
setCookiesHeaderArray->push(globalObject, jsValue);
RETURN_IF_EXCEPTION(scope, void());
} else {
headersObject->putDirect(vm, nameIdentifier, jsValue, 0);
arrayValues.append(nameString);
arrayValues.append(jsValue);
RETURN_IF_EXCEPTION(scope, void());
}
}
JSC::JSArray* array;
{
ObjectInitializationScope initializationScope(vm);
if (LIKELY(array = JSArray::tryCreateUninitializedRestricted(initializationScope, nullptr, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous), arrayValues.size()))) {
EncodedJSValue* data = arrayValues.data();
for (size_t i = 0, size = arrayValues.size(); i < size; ++i) {
array->initializeIndex(initializationScope, i, JSValue::decode(data[i]));
}
} else {
array = constructArray(globalObject, static_cast<ArrayAllocationProfile*>(nullptr), arrayValues);
}
}
args.append(array);
}
// This is an 8% speedup.
static EncodedJSValue assignHeadersFromUWebSockets(uWS::HttpRequest* request, JSObject* prototype, JSObject* objectValue, JSC::InternalFieldTuple* tuple, JSC::JSGlobalObject* globalObject, JSC::VM& vm)
{
auto scope = DECLARE_THROW_SCOPE(vm);
auto& builtinNames = WebCore::builtinNames(vm);
std::string_view fullURLStdStr = request->getFullUrl();
String fullURL = String::fromUTF8ReplacingInvalidSequences({ reinterpret_cast<const LChar*>(fullURLStdStr.data()), fullURLStdStr.length() });
{
PutPropertySlot slot(objectValue, false);
objectValue->put(objectValue, globalObject, builtinNames.urlPublicName(), jsString(vm, fullURL), slot);
RETURN_IF_EXCEPTION(scope, {});
}
{
PutPropertySlot slot(objectValue, false);
std::string_view methodView = request->getMethod();
WTF::String methodString;
switch (methodView.length()) {
case 3: {
if (methodView == std::string_view("get", 3)) {
methodString = "GET"_s;
break;
}
if (methodView == std::string_view("put", 3)) {
methodString = "PUT"_s;
break;
}
break;
}
case 4: {
if (methodView == std::string_view("post", 4)) {
methodString = "POST"_s;
break;
}
if (methodView == std::string_view("head", 4)) {
methodString = "HEAD"_s;
break;
}
if (methodView == std::string_view("copy", 4)) {
methodString = "COPY"_s;
break;
}
}
case 5: {
if (methodView == std::string_view("patch", 5)) {
methodString = "PATCH"_s;
break;
}
if (methodView == std::string_view("merge", 5)) {
methodString = "MERGE"_s;
break;
}
if (methodView == std::string_view("trace", 5)) {
methodString = "TRACE"_s;
break;
}
if (methodView == std::string_view("fetch", 5)) {
methodString = "FETCH"_s;
break;
}
if (methodView == std::string_view("purge", 5)) {
methodString = "PURGE"_s;
break;
}
break;
}
case 6: {
if (methodView == std::string_view("delete", 6)) {
methodString = "DELETE"_s;
break;
}
break;
}
case 7: {
if (methodView == std::string_view("connect", 7)) {
methodString = "CONNECT"_s;
break;
}
if (methodView == std::string_view("options", 7)) {
methodString = "OPTIONS"_s;
break;
}
break;
}
}
if (methodString.isNull()) {
methodString = String::fromUTF8ReplacingInvalidSequences({ reinterpret_cast<const LChar*>(methodView.data()), methodView.length() });
}
objectValue->put(objectValue, globalObject, builtinNames.methodPublicName(), jsString(vm, methodString), slot);
RETURN_IF_EXCEPTION(scope, {});
}
size_t size = 0;
for (auto it = request->begin(); it != request->end(); ++it) {
size++;
}
JSC::JSObject* headersObject = JSC::constructEmptyObject(globalObject, prototype, std::min(size, static_cast<size_t>(JSFinalObject::maxInlineCapacity)));
RETURN_IF_EXCEPTION(scope, {});
JSC::JSArray* array = constructEmptyArray(globalObject, nullptr, size * 2);
JSC::JSArray* setCookiesHeaderArray = nullptr;
JSC::JSString* setCookiesHeaderString = nullptr;
unsigned i = 0;
for (auto it = request->begin(); it != request->end(); ++it) {
auto pair = *it;
StringView nameView = StringView(std::span { reinterpret_cast<const LChar*>(pair.first.data()), pair.first.length() });
std::span<LChar> data;
auto value = String::tryCreateUninitialized(pair.second.length(), data);
if (UNLIKELY(value.isNull())) {
throwOutOfMemoryError(globalObject, scope);
return JSValue::encode({});
}
if (pair.second.length() > 0)
memcpy(data.data(), pair.second.data(), pair.second.length());
HTTPHeaderName name;
WTF::String nameString;
WTF::String lowercasedNameString;
if (WebCore::findHTTPHeaderName(nameView, name)) {
nameString = WTF::httpHeaderNameStringImpl(name);
lowercasedNameString = nameString;
} else {
nameString = nameView.toString();
lowercasedNameString = nameString.convertToASCIILowercase();
}
JSString* jsValue = jsString(vm, value);
if (name == WebCore::HTTPHeaderName::SetCookie) {
if (!setCookiesHeaderArray) {
setCookiesHeaderArray = constructEmptyArray(globalObject, nullptr);
setCookiesHeaderString = jsString(vm, nameString);
headersObject->putDirect(vm, Identifier::fromString(vm, lowercasedNameString), setCookiesHeaderArray, 0);
RETURN_IF_EXCEPTION(scope, {});
}
array->putDirectIndex(globalObject, i++, setCookiesHeaderString);
array->putDirectIndex(globalObject, i++, jsValue);
setCookiesHeaderArray->push(globalObject, jsValue);
RETURN_IF_EXCEPTION(scope, {});
} else {
headersObject->putDirect(vm, Identifier::fromString(vm, lowercasedNameString), jsValue, 0);
array->putDirectIndex(globalObject, i++, jsString(vm, nameString));
array->putDirectIndex(globalObject, i++, jsValue);
RETURN_IF_EXCEPTION(scope, {});
}
}
tuple->putInternalField(vm, 0, headersObject);
tuple->putInternalField(vm, 1, array);
return JSValue::encode(tuple);
}
template<bool isSSL>
static void assignOnCloseFunction(uWS::TemplatedApp<isSSL>* app)
{
app->setOnClose([](void* socketData, int is_ssl, struct us_socket_t* rawSocket) -> void {
auto* socket = reinterpret_cast<JSNodeHTTPServerSocket*>(socketData);
ASSERT(rawSocket == socket->socket || socket->socket == nullptr);
socket->onClose();
});
}
extern "C" void NodeHTTP_assignOnCloseFunction(bool is_ssl, void* uws_app)
{
if (is_ssl) {
assignOnCloseFunction<true>(reinterpret_cast<uWS::TemplatedApp<true>*>(uws_app));
} else {
assignOnCloseFunction<false>(reinterpret_cast<uWS::TemplatedApp<false>*>(uws_app));
}
}
extern "C" void NodeHTTP_setUsingCustomExpectHandler(bool is_ssl, void* uws_app, bool value)
{
if (is_ssl) {
reinterpret_cast<uWS::TemplatedApp<true>*>(uws_app)->setUsingCustomExpectHandler(value);
} else {
reinterpret_cast<uWS::TemplatedApp<false>*>(uws_app)->setUsingCustomExpectHandler(value);
}
}
extern "C" EncodedJSValue NodeHTTPResponse__createForJS(size_t any_server, JSC::JSGlobalObject* globalObject, bool* hasBody, uWS::HttpRequest* request, int isSSL, void* response_ptr, void* upgrade_ctx, void** nodeHttpResponsePtr);
template<bool isSSL>
static EncodedJSValue NodeHTTPServer__onRequest(
size_t any_server,
Zig::GlobalObject* globalObject,
JSValue thisValue,
JSValue callback,
uWS::HttpRequest* request,
uWS::HttpResponse<isSSL>* response,
void* upgrade_ctx,
void** nodeHttpResponsePtr)
{
auto& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
JSObject* callbackObject = jsCast<JSObject*>(callback);
MarkedArgumentBuffer args;
args.append(thisValue);
assignHeadersFromUWebSocketsForCall(request, args, globalObject, vm);
if (scope.exception()) {
auto* exception = scope.exception();
response->endWithoutBody();
scope.clearException();
return JSValue::encode(exception);
}
bool hasBody = false;
WebCore::JSNodeHTTPResponse* nodeHTTPResponseObject = jsCast<WebCore::JSNodeHTTPResponse*>(JSValue::decode(NodeHTTPResponse__createForJS(any_server, globalObject, &hasBody, request, isSSL, response, upgrade_ctx, nodeHttpResponsePtr)));
JSC::CallData callData = getCallData(callbackObject);
args.append(nodeHTTPResponseObject);
args.append(jsBoolean(hasBody));
auto* currentSocketDataPtr = reinterpret_cast<JSC::JSCell*>(response->getHttpResponseData()->socketData);
if (currentSocketDataPtr) {
auto* thisSocket = jsCast<JSNodeHTTPServerSocket*>(currentSocketDataPtr);
thisSocket->currentResponseObject.set(vm, thisSocket, nodeHTTPResponseObject);
args.append(thisSocket);
args.append(jsBoolean(false));
if (thisSocket->m_duplex) {
args.append(thisSocket->m_duplex.get());
} else {
args.append(jsUndefined());
}
} else {
JSNodeHTTPServerSocket* socket = JSNodeHTTPServerSocket::create(
vm,
globalObject->m_JSNodeHTTPServerSocketStructure.getInitializedOnMainThread(globalObject),
(us_socket_t*)response,
isSSL, nodeHTTPResponseObject);
socket->strongThis.set(vm, socket);
response->getHttpResponseData()->socketData = socket;
args.append(socket);
args.append(jsBoolean(true));
args.append(jsUndefined());
}
WTF::NakedPtr<JSC::Exception> exception;
JSValue returnValue = JSC::profiledCall(globalObject, JSC::ProfilingReason::API, callbackObject, callData, jsUndefined(), args, exception);
if (exception) {
auto* ptr = exception.get();
exception.clear();
return JSValue::encode(ptr);
}
return JSValue::encode(returnValue);
}
template<bool isSSL>
static void writeResponseHeader(uWS::HttpResponse<isSSL>* res, const WTF::StringView& name, const WTF::StringView& value)
{
WTF::CString nameStr;
WTF::CString valueStr;
std::string_view nameView;
std::string_view valueView;
if (name.is8Bit()) {
const auto nameSpan = name.span8();
ASSERT(name.containsOnlyASCII());
nameView = std::string_view(reinterpret_cast<const char*>(nameSpan.data()), nameSpan.size());
} else {
nameStr = name.utf8();
nameView = std::string_view(nameStr.data(), nameStr.length());
}
if (value.is8Bit()) {
const auto valueSpan = value.span8();
valueView = std::string_view(reinterpret_cast<const char*>(valueSpan.data()), valueSpan.size());
} else {
valueStr = value.utf8();
valueView = std::string_view(valueStr.data(), valueStr.length());
}
res->writeHeader(nameView, valueView);
}
template<bool isSSL>
static void writeFetchHeadersToUWSResponse(WebCore::FetchHeaders& headers, uWS::HttpResponse<isSSL>* res)
{
auto& internalHeaders = headers.internalHeaders();
for (auto& value : internalHeaders.getSetCookieHeaders()) {
if (value.is8Bit()) {
const auto valueSpan = value.span8();
res->writeHeader(std::string_view("set-cookie", 10), std::string_view(reinterpret_cast<const char*>(valueSpan.data()), valueSpan.size()));
} else {
WTF::CString valueStr = value.utf8();
res->writeHeader(std::string_view("set-cookie", 10), std::string_view(valueStr.data(), valueStr.length()));
}
}
auto* data = res->getHttpResponseData();
for (const auto& header : internalHeaders.commonHeaders()) {
const auto& name = WebCore::httpHeaderNameString(header.key);
const auto& value = header.value;
// We have to tell uWS not to automatically insert a TransferEncoding or Date header.
// Otherwise, you get this when using Fastify;
//
// curl http://localhost:3000 --verbose
// * Trying [::1]:3000...
// * Connected to localhost (::1) port 3000
// > GET / HTTP/1.1
// > Host: localhost:3000
// > User-Agent: curl/8.4.0
// > Accept: */*
// >
// < HTTP/1.1 200 OK
// < Content-Type: application/json; charset=utf-8
// < Content-Length: 17
// < Date: Sun, 06 Oct 2024 13:37:01 GMT
// < Transfer-Encoding: chunked
// <
//
if (header.key == WebCore::HTTPHeaderName::ContentLength) {
if (!(data->state & uWS::HttpResponseData<isSSL>::HTTP_WROTE_CONTENT_LENGTH_HEADER)) {
data->state |= uWS::HttpResponseData<isSSL>::HTTP_WROTE_CONTENT_LENGTH_HEADER;
res->writeMark();
}
}
writeResponseHeader<isSSL>(res, name, value);
}
for (auto& header : internalHeaders.uncommonHeaders()) {
const auto& name = header.key;
const auto& value = header.value;
writeResponseHeader<isSSL>(res, name, value);
}
}
template<bool isSSL>
static void NodeHTTPServer__writeHead(
JSC::JSGlobalObject* globalObject,
const char* statusMessage,
size_t statusMessageLength,
JSValue headersObjectValue,
uWS::HttpResponse<isSSL>* response)
{
auto& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
JSObject* headersObject = headersObjectValue.getObject();
if (response->getLoopData()->canCork() && response->getBufferedAmount() == 0) {
response->getLoopData()->setCorkedSocket(response, isSSL);
}
response->writeStatus(std::string_view(statusMessage, statusMessageLength));
if (headersObject) {
if (auto* fetchHeaders = jsDynamicCast<WebCore::JSFetchHeaders*>(headersObject)) {
writeFetchHeadersToUWSResponse<isSSL>(fetchHeaders->wrapped(), response);
return;
}
if (UNLIKELY(headersObject->hasNonReifiedStaticProperties())) {
headersObject->reifyAllStaticProperties(globalObject);
RETURN_IF_EXCEPTION(scope, void());
}
auto* structure = headersObject->structure();
if (structure->canPerformFastPropertyEnumeration()) {
structure->forEachProperty(vm, [&](const auto& entry) {
JSValue headerValue = headersObject->getDirect(entry.offset());
if (!headerValue.isString()) {
return true;
}
String key = entry.key();
String value = headerValue.toWTFString(globalObject);
if (scope.exception()) {
return false;
}
writeResponseHeader<isSSL>(response, key, value);
return true;
});
} else {
PropertyNameArray propertyNames(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude);
headersObject->getOwnPropertyNames(headersObject, globalObject, propertyNames, DontEnumPropertiesMode::Exclude);
RETURN_IF_EXCEPTION(scope, void());
for (unsigned i = 0; i < propertyNames.size(); ++i) {
JSValue headerValue = headersObject->getIfPropertyExists(globalObject, propertyNames[i]);
if (!headerValue.isString()) {
continue;
}
String key = propertyNames[i].string();
String value = headerValue.toWTFString(globalObject);
RETURN_IF_EXCEPTION(scope, void());
writeResponseHeader<isSSL>(response, key, value);
}
}
}
RELEASE_AND_RETURN(scope, void());
}
extern "C" void NodeHTTPServer__writeHead_http(
JSC::JSGlobalObject* globalObject,
const char* statusMessage,
size_t statusMessageLength,
JSValue headersObjectValue,
uWS::HttpResponse<false>* response)
{
return NodeHTTPServer__writeHead<false>(globalObject, statusMessage, statusMessageLength, headersObjectValue, response);
}
extern "C" void NodeHTTPServer__writeHead_https(
JSC::JSGlobalObject* globalObject,
const char* statusMessage,
size_t statusMessageLength,
JSValue headersObjectValue,
uWS::HttpResponse<true>* response)
{
return NodeHTTPServer__writeHead<true>(globalObject, statusMessage, statusMessageLength, headersObjectValue, response);
}
extern "C" EncodedJSValue NodeHTTPServer__onRequest_http(
size_t any_server,
Zig::GlobalObject* globalObject,
EncodedJSValue thisValue,
EncodedJSValue callback,
uWS::HttpRequest* request,
uWS::HttpResponse<false>* response,
void* upgrade_ctx,
void** nodeHttpResponsePtr)
{
return NodeHTTPServer__onRequest<false>(any_server, globalObject, JSValue::decode(thisValue), JSValue::decode(callback), request, response, upgrade_ctx, nodeHttpResponsePtr);
}
extern "C" EncodedJSValue NodeHTTPServer__onRequest_https(
size_t any_server,
Zig::GlobalObject* globalObject,
EncodedJSValue thisValue,
EncodedJSValue callback,
uWS::HttpRequest* request,
uWS::HttpResponse<true>* response,
void* upgrade_ctx,
void** nodeHttpResponsePtr)
{
return NodeHTTPServer__onRequest<true>(any_server, globalObject, JSValue::decode(thisValue), JSValue::decode(callback), request, response, upgrade_ctx, nodeHttpResponsePtr);
}
JSC_DEFINE_HOST_FUNCTION(jsHTTPAssignHeaders, (JSGlobalObject * globalObject, CallFrame* callFrame))
{
auto& vm = JSC::getVM(globalObject);
auto scope = DECLARE_THROW_SCOPE(vm);
// This is an internal binding.
JSValue requestValue = callFrame->uncheckedArgument(0);
JSObject* objectValue = callFrame->uncheckedArgument(1).getObject();
JSC::InternalFieldTuple* tuple = jsCast<JSC::InternalFieldTuple*>(callFrame->uncheckedArgument(2));
ASSERT(callFrame->argumentCount() == 3);
JSValue headersValue = JSValue();
JSValue urlValue = JSValue();
if (auto* jsRequest = jsDynamicCast<WebCore::JSRequest*>(requestValue)) {
if (uWS::HttpRequest* request = Request__getUWSRequest(jsRequest->wrapped())) {
return assignHeadersFromUWebSockets(request, globalObject->objectPrototype(), objectValue, tuple, globalObject, vm);
}
if (jsRequest->m_headers) {
headersValue = jsRequest->m_headers.get();
}
if (jsRequest->m_url) {
urlValue = jsRequest->m_url.get();
}
}
if (requestValue.isObject()) {
if (!headersValue) {
headersValue = requestValue.getObject()->getIfPropertyExists(globalObject, WebCore::builtinNames(vm).headersPublicName());
RETURN_IF_EXCEPTION(scope, {});
}
if (!urlValue) {
urlValue = requestValue.getObject()->getIfPropertyExists(globalObject, WebCore::builtinNames(vm).urlPublicName());
RETURN_IF_EXCEPTION(scope, {});
}
if (headersValue) {
if (auto* headers = jsDynamicCast<WebCore::JSFetchHeaders*>(headersValue)) {
FetchHeaders& impl = headers->wrapped();
if (urlValue) {
if (urlValue.isString()) {
String url = urlValue.toWTFString(globalObject);
RETURN_IF_EXCEPTION(scope, {});
if (url.startsWith("https://"_s) || url.startsWith("http://"_s) || url.startsWith("file://"_s)) {
WTF::URL urlObj = WTF::URL({}, url);
if (urlObj.isValid()) {
urlValue = jsString(vm, makeString(urlObj.path(), urlObj.query().isEmpty() ? emptyString() : urlObj.queryWithLeadingQuestionMark()));
}
}
} else {
urlValue = jsEmptyString(vm);
}
PutPropertySlot slot(objectValue, false);
objectValue->put(objectValue, globalObject, WebCore::builtinNames(vm).urlPublicName(), urlValue, slot);
RETURN_IF_EXCEPTION(scope, {});
}
return assignHeadersFromFetchHeaders(impl, globalObject->objectPrototype(), objectValue, tuple, globalObject, vm);
}
}
}
return JSValue::encode(jsNull());
}
JSC_DEFINE_HOST_FUNCTION(jsHTTPAssignEventCallback, (JSGlobalObject * globalObject, CallFrame* callFrame))
{
auto& vm = JSC::getVM(globalObject);
auto scope = DECLARE_THROW_SCOPE(vm);
// This is an internal binding.
JSValue requestValue = callFrame->uncheckedArgument(0);
JSValue callback = callFrame->uncheckedArgument(1);
ASSERT(callFrame->argumentCount() == 2);
if (auto* jsRequest = jsDynamicCast<WebCore::JSRequest*>(requestValue)) {
Request__setInternalEventCallback(jsRequest->wrapped(), JSValue::encode(callback), globalObject);
}
return JSValue::encode(jsNull());
}
JSC_DEFINE_HOST_FUNCTION(jsHTTPSetTimeout, (JSGlobalObject * globalObject, CallFrame* callFrame))
{
auto& vm = JSC::getVM(globalObject);
auto scope = DECLARE_THROW_SCOPE(vm);
// This is an internal binding.
JSValue requestValue = callFrame->uncheckedArgument(0);
JSValue seconds = callFrame->uncheckedArgument(1);
ASSERT(callFrame->argumentCount() == 2);
if (auto* jsRequest = jsDynamicCast<WebCore::JSRequest*>(requestValue)) {
Request__setTimeout(jsRequest->wrapped(), JSValue::encode(seconds), globalObject);
}
if (auto* nodeHttpResponse = jsDynamicCast<WebCore::JSNodeHTTPResponse*>(requestValue)) {
NodeHTTPResponse__setTimeout(nodeHttpResponse->wrapped(), JSValue::encode(seconds), globalObject);
}
return JSValue::encode(jsUndefined());
}
JSC_DEFINE_HOST_FUNCTION(jsHTTPSetServerIdleTimeout, (JSGlobalObject * globalObject, CallFrame* callFrame))
{
auto& vm = JSC::getVM(globalObject);
auto scope = DECLARE_THROW_SCOPE(vm);
// This is an internal binding.
JSValue serverValue = callFrame->uncheckedArgument(0);
JSValue seconds = callFrame->uncheckedArgument(1);
ASSERT(callFrame->argumentCount() == 2);
Server__setIdleTimeout(JSValue::encode(serverValue), JSValue::encode(seconds), globalObject);
return JSValue::encode(jsUndefined());
}
JSC_DEFINE_HOST_FUNCTION(jsHTTPGetHeader, (JSGlobalObject * globalObject, CallFrame* callFrame))
{
auto& vm = JSC::getVM(globalObject);
auto scope = DECLARE_THROW_SCOPE(vm);
JSValue headersValue = callFrame->argument(0);
if (auto* headers = jsDynamicCast<WebCore::JSFetchHeaders*>(headersValue)) {
JSValue nameValue = callFrame->argument(1);
if (nameValue.isString()) {
FetchHeaders* impl = &headers->wrapped();
JSString* nameString = nameValue.toString(globalObject);
RETURN_IF_EXCEPTION(scope, {});
const auto name = nameString->view(globalObject);
RETURN_IF_EXCEPTION(scope, {});
if (WTF::equalIgnoringASCIICase(name, "set-cookie"_s)) {
return fetchHeadersGetSetCookie(globalObject, vm, impl);
}
WebCore::ExceptionOr<String> res = impl->get(name);
if (res.hasException()) {
WebCore::propagateException(globalObject, scope, res.releaseException());
return JSValue::encode(jsUndefined());
}
String value = res.returnValue();
if (value.isEmpty()) {
return JSValue::encode(jsUndefined());
}
return JSC::JSValue::encode(jsString(vm, value));
}
}
return JSValue::encode(jsUndefined());
}
JSC_DEFINE_HOST_FUNCTION(jsHTTPSetHeader, (JSGlobalObject * globalObject, CallFrame* callFrame))
{
auto& vm = JSC::getVM(globalObject);
auto scope = DECLARE_THROW_SCOPE(vm);
JSValue headersValue = callFrame->argument(0);
JSValue nameValue = callFrame->argument(1);
JSValue valueValue = callFrame->argument(2);
if (auto* headers = jsDynamicCast<WebCore::JSFetchHeaders*>(headersValue)) {
if (nameValue.isString()) {
String name = nameValue.toWTFString(globalObject);
FetchHeaders* impl = &headers->wrapped();
if (valueValue.isUndefined())
return JSValue::encode(jsUndefined());
if (isArray(globalObject, valueValue)) {
auto* array = jsCast<JSArray*>(valueValue);
unsigned length = array->length();
if (length > 0) {
JSValue item = array->getIndex(globalObject, 0);
if (UNLIKELY(scope.exception()))
return JSValue::encode(jsUndefined());
auto value = item.toWTFString(globalObject);
RETURN_IF_EXCEPTION(scope, {});
impl->set(name, value);
RETURN_IF_EXCEPTION(scope, {});
}
for (unsigned i = 1; i < length; ++i) {
JSValue value = array->getIndex(globalObject, i);
if (UNLIKELY(scope.exception()))
return JSValue::encode(jsUndefined());
auto string = value.toWTFString(globalObject);
RETURN_IF_EXCEPTION(scope, {});
impl->append(name, string);
RETURN_IF_EXCEPTION(scope, {});
}
RELEASE_AND_RETURN(scope, JSValue::encode(jsUndefined()));
return JSValue::encode(jsUndefined());
}
auto value = valueValue.toWTFString(globalObject);
RETURN_IF_EXCEPTION(scope, {});
impl->set(name, value);
RETURN_IF_EXCEPTION(scope, {});
return JSValue::encode(jsUndefined());
}
}
return JSValue::encode(jsUndefined());
}
JSValue createNodeHTTPInternalBinding(Zig::GlobalObject* globalObject)
{
auto* obj = constructEmptyObject(globalObject);
VM& vm = globalObject->vm();
obj->putDirect(
vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "setHeader"_s)),
JSC::JSFunction::create(vm, globalObject, 3, "setHeader"_s, jsHTTPSetHeader, ImplementationVisibility::Public), 0);
obj->putDirect(
vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "getHeader"_s)),
JSC::JSFunction::create(vm, globalObject, 2, "getHeader"_s, jsHTTPGetHeader, ImplementationVisibility::Public), 0);
obj->putDirect(
vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "assignHeaders"_s)),
JSC::JSFunction::create(vm, globalObject, 2, "assignHeaders"_s, jsHTTPAssignHeaders, ImplementationVisibility::Public), 0);
obj->putDirect(
vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "assignEventCallback"_s)),
JSC::JSFunction::create(vm, globalObject, 2, "assignEventCallback"_s, jsHTTPAssignEventCallback, ImplementationVisibility::Public), 0);
obj->putDirect(
vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "setRequestTimeout"_s)),
JSC::JSFunction::create(vm, globalObject, 2, "setRequestTimeout"_s, jsHTTPSetTimeout, ImplementationVisibility::Public), 0);
obj->putDirect(
vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "setServerIdleTimeout"_s)),
JSC::JSFunction::create(vm, globalObject, 2, "setServerIdleTimeout"_s, jsHTTPSetServerIdleTimeout, ImplementationVisibility::Public), 0);
obj->putDirect(
vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "Response"_s)),
globalObject->JSResponseConstructor(), 0);
obj->putDirect(
vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "Request"_s)),
globalObject->JSRequestConstructor(), 0);
obj->putDirect(
vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "Blob"_s)),
globalObject->JSBlobConstructor(), 0);
obj->putDirect(
vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "Headers"_s)),
WebCore::JSFetchHeaders::getConstructor(vm, globalObject), 0);
obj->putDirect(
vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "headersTuple"_s)),
JSC::InternalFieldTuple::create(vm, globalObject->m_internalFieldTupleStructure.get()), 0);
obj->putDirectNativeFunction(
vm, globalObject, JSC::PropertyName(JSC::Identifier::fromString(vm, "webRequestOrResponseHasBodyValue"_s)),
1, jsFunctionRequestOrResponseHasBodyValue, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, 0);
obj->putDirectNativeFunction(
vm, globalObject, JSC::PropertyName(JSC::Identifier::fromString(vm, "getCompleteWebRequestOrResponseBodyValueAsArrayBuffer"_s)),
1, jsFunctionGetCompleteRequestOrResponseBodyValueAsArrayBuffer, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, 0);
obj->putDirectNativeFunction(
vm, globalObject, JSC::PropertyName(JSC::Identifier::fromString(vm, "drainMicrotasks"_s)),
0, Bun__drainMicrotasksFromJS, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, 0);
return obj;
}
extern "C" void WebCore__FetchHeaders__toUWSResponse(WebCore::FetchHeaders* arg0, bool is_ssl, void* arg2)
{
if (is_ssl) {
writeFetchHeadersToUWSResponse<true>(*arg0, reinterpret_cast<uWS::HttpResponse<true>*>(arg2));
} else {
writeFetchHeadersToUWSResponse<false>(*arg0, reinterpret_cast<uWS::HttpResponse<false>*>(arg2));
}
}
JSC::Structure* createNodeHTTPServerSocketStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
{
return JSNodeHTTPServerSocket::createStructure(vm, globalObject);
}
} // namespace Bun