mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 18:38:55 +00:00
### What does this PR do? Fixes https://github.com/oven-sh/bun/issues/22755 Fixes https://github.com/oven-sh/bun/issues/19790 Fixes https://github.com/oven-sh/bun/issues/16372 ### How did you verify your code works? --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1766 lines
72 KiB
C++
1766 lines
72 KiB
C++
#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" {
|
||
struct us_socket_stream_buffer_t {
|
||
char* list_ptr = nullptr;
|
||
size_t list_cap = 0;
|
||
size_t listLen = 0;
|
||
size_t total_bytes_written = 0;
|
||
size_t cursor = 0;
|
||
|
||
size_t bufferedSize() const
|
||
{
|
||
return listLen - cursor;
|
||
}
|
||
size_t totalBytesWritten() const
|
||
{
|
||
return total_bytes_written;
|
||
}
|
||
};
|
||
}
|
||
|
||
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);
|
||
|
||
extern "C" void Bun__NodeHTTPResponse_setClosed(void* zigResponse);
|
||
extern "C" void Bun__NodeHTTPResponse_onClose(void* zigResponse, JSC::EncodedJSValue jsValue);
|
||
extern "C" EncodedJSValue us_socket_buffered_js_write(void* socket, bool is_ssl, bool ended, us_socket_stream_buffer_t* streamBuffer, JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue data, JSC::EncodedJSValue encoding);
|
||
extern "C" void us_socket_free_stream_buffer(us_socket_stream_buffer_t* streamBuffer);
|
||
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(jsNodeHttpServerSocketGetterOnDrain);
|
||
JSC_DECLARE_CUSTOM_GETTER(jsNodeHttpServerSocketGetterClosed);
|
||
JSC_DECLARE_CUSTOM_SETTER(jsNodeHttpServerSocketSetterOnClose);
|
||
JSC_DECLARE_CUSTOM_SETTER(jsNodeHttpServerSocketSetterOnDrain);
|
||
JSC_DECLARE_CUSTOM_SETTER(jsNodeHttpServerSocketSetterOnData);
|
||
JSC_DECLARE_CUSTOM_GETTER(jsNodeHttpServerSocketGetterOnData);
|
||
JSC_DECLARE_CUSTOM_GETTER(jsNodeHttpServerSocketGetterBytesWritten);
|
||
JSC_DECLARE_HOST_FUNCTION(jsFunctionNodeHTTPServerSocketClose);
|
||
JSC_DECLARE_HOST_FUNCTION(jsFunctionNodeHTTPServerSocketWrite);
|
||
JSC_DECLARE_HOST_FUNCTION(jsFunctionNodeHTTPServerSocketEnd);
|
||
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);
|
||
JSC_DECLARE_CUSTOM_GETTER(jsNodeHttpServerSocketGetterIsSecureEstablished);
|
||
// 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 } },
|
||
{ "ondrain"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsNodeHttpServerSocketGetterOnDrain, jsNodeHttpServerSocketSetterOnDrain } },
|
||
{ "ondata"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsNodeHttpServerSocketGetterOnData, jsNodeHttpServerSocketSetterOnData } },
|
||
{ "bytesWritten"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsNodeHttpServerSocketGetterBytesWritten, noOpSetter } },
|
||
{ "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 } },
|
||
{ "write"_s, static_cast<unsigned>(PropertyAttribute::Function | PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::NativeFunctionType, jsFunctionNodeHTTPServerSocketWrite, 2 } },
|
||
{ "end"_s, static_cast<unsigned>(PropertyAttribute::Function | PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::NativeFunctionType, jsFunctionNodeHTTPServerSocketEnd, 0 } },
|
||
{ "secureEstablished"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::GetterSetterType, jsNodeHttpServerSocketGetterIsSecureEstablished, noOpSetter } },
|
||
};
|
||
|
||
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;
|
||
us_socket_stream_buffer_t streamBuffer = {};
|
||
us_socket_t* socket = nullptr;
|
||
unsigned is_ssl : 1 = 0;
|
||
unsigned ended : 1 = 0;
|
||
JSC::Strong<JSNodeHTTPServerSocket> strongThis = {};
|
||
|
||
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);
|
||
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);
|
||
}
|
||
// This means:
|
||
// - [x] TLS
|
||
// - [x] Handshake has completed
|
||
// - [x] Handshake marked the connection as authorized
|
||
bool isAuthorized() const
|
||
{
|
||
// is secure means that tls was established successfully
|
||
if (!is_ssl || !socket) return false;
|
||
auto* context = us_socket_context(is_ssl, socket);
|
||
if (!context) return false;
|
||
auto* data = (uWS::HttpContextData<true>*)us_socket_context_ext(is_ssl, context);
|
||
if (!data) return false;
|
||
return data->flags.isAuthorized;
|
||
}
|
||
~JSNodeHTTPServerSocket()
|
||
{
|
||
if (socket) {
|
||
if (is_ssl) {
|
||
clearSocketData<true>(socket);
|
||
} else {
|
||
clearSocketData<false>(socket);
|
||
}
|
||
}
|
||
us_socket_free_stream_buffer(&streamBuffer);
|
||
}
|
||
|
||
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<JSObject> functionToCallOnDrain;
|
||
mutable WriteBarrier<JSObject> functionToCallOnData;
|
||
mutable WriteBarrier<WebCore::JSNodeHTTPResponse> currentResponseObject;
|
||
mutable WriteBarrier<JSObject> m_remoteAddress;
|
||
mutable WriteBarrier<JSObject> m_localAddress;
|
||
mutable WriteBarrier<JSObject> m_duplex;
|
||
|
||
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 detach()
|
||
{
|
||
this->m_duplex.clear();
|
||
this->currentResponseObject.clear();
|
||
this->strongThis.clear();
|
||
}
|
||
|
||
void onClose()
|
||
{
|
||
|
||
this->socket = nullptr;
|
||
if (auto* res = this->currentResponseObject.get(); res != nullptr && res->m_ctx != nullptr) {
|
||
Bun__NodeHTTPResponse_setClosed(res->m_ctx);
|
||
}
|
||
|
||
// This function can be called during GC!
|
||
Zig::GlobalObject* globalObject = static_cast<Zig::GlobalObject*>(this->globalObject());
|
||
if (!functionToCallOnClose) {
|
||
if (auto* res = this->currentResponseObject.get(); res != nullptr && res->m_ctx != nullptr) {
|
||
Bun__NodeHTTPResponse_onClose(res->m_ctx, JSValue::encode(res));
|
||
}
|
||
this->detach();
|
||
return;
|
||
}
|
||
|
||
WebCore::ScriptExecutionContext* scriptExecutionContext = globalObject->scriptExecutionContext();
|
||
|
||
if (scriptExecutionContext) {
|
||
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) {
|
||
if (auto* res = thisObject->currentResponseObject.get(); res != nullptr && res->m_ctx != nullptr) {
|
||
Bun__NodeHTTPResponse_onClose(res->m_ctx, JSValue::encode(res));
|
||
}
|
||
thisObject->detach();
|
||
return;
|
||
}
|
||
auto callData = JSC::getCallData(callbackObject);
|
||
MarkedArgumentBuffer args;
|
||
EnsureStillAliveScope ensureStillAlive(self);
|
||
|
||
if (globalObject->scriptExecutionStatus(globalObject, thisObject) == ScriptExecutionStatus::Running) {
|
||
if (auto* res = thisObject->currentResponseObject.get(); res != nullptr && res->m_ctx != nullptr) {
|
||
Bun__NodeHTTPResponse_onClose(res->m_ctx, JSValue::encode(res));
|
||
}
|
||
|
||
profiledCall(globalObject, JSC::ProfilingReason::API, callbackObject, callData, thisObject, args, exception);
|
||
|
||
if (auto* ptr = exception.get()) {
|
||
exception.clear();
|
||
globalObject->reportUncaughtExceptionAtEventLoop(globalObject, ptr);
|
||
}
|
||
}
|
||
thisObject->detach();
|
||
});
|
||
}
|
||
}
|
||
|
||
void onDrain()
|
||
{
|
||
// This function can be called during GC!
|
||
Zig::GlobalObject* globalObject = static_cast<Zig::GlobalObject*>(this->globalObject());
|
||
if (!functionToCallOnDrain) {
|
||
return;
|
||
}
|
||
|
||
auto bufferedSize = this->streamBuffer.bufferedSize();
|
||
if (bufferedSize > 0) {
|
||
|
||
auto* globalObject = defaultGlobalObject(this->globalObject());
|
||
auto scope = DECLARE_CATCH_SCOPE(globalObject->vm());
|
||
us_socket_buffered_js_write(this->socket, this->is_ssl, this->ended, &this->streamBuffer, globalObject, JSValue::encode(JSC::jsUndefined()), JSValue::encode(JSC::jsUndefined()));
|
||
if (scope.exception()) {
|
||
globalObject->reportUncaughtExceptionAtEventLoop(globalObject, scope.exception());
|
||
return;
|
||
}
|
||
bufferedSize = this->streamBuffer.bufferedSize();
|
||
|
||
if (bufferedSize > 0) {
|
||
// need to drain more
|
||
return;
|
||
}
|
||
}
|
||
WebCore::ScriptExecutionContext* scriptExecutionContext = globalObject->scriptExecutionContext();
|
||
|
||
if (scriptExecutionContext) {
|
||
scriptExecutionContext->postTask([self = this](ScriptExecutionContext& context) {
|
||
WTF::NakedPtr<JSC::Exception> exception;
|
||
auto* globalObject = defaultGlobalObject(context.globalObject());
|
||
auto* thisObject = self;
|
||
auto* callbackObject = thisObject->functionToCallOnDrain.get();
|
||
if (!callbackObject) {
|
||
return;
|
||
}
|
||
auto callData = JSC::getCallData(callbackObject);
|
||
MarkedArgumentBuffer args;
|
||
EnsureStillAliveScope ensureStillAlive(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);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
void
|
||
onData(const char* data, int length, bool last)
|
||
{
|
||
// This function can be called during GC!
|
||
Zig::GlobalObject* globalObject = static_cast<Zig::GlobalObject*>(this->globalObject());
|
||
if (!functionToCallOnData) {
|
||
return;
|
||
}
|
||
|
||
WebCore::ScriptExecutionContext* scriptExecutionContext = globalObject->scriptExecutionContext();
|
||
|
||
if (scriptExecutionContext) {
|
||
auto scope = DECLARE_CATCH_SCOPE(globalObject->vm());
|
||
JSC::JSUint8Array* buffer = WebCore::createBuffer(globalObject, std::span<const uint8_t>(reinterpret_cast<const uint8_t*>(data), length));
|
||
auto chunk = JSC::JSValue(buffer);
|
||
if (scope.exception()) {
|
||
globalObject->reportUncaughtExceptionAtEventLoop(globalObject, scope.exception());
|
||
return;
|
||
}
|
||
gcProtect(chunk);
|
||
scriptExecutionContext->postTask([self = this, chunk = chunk, last = last](ScriptExecutionContext& context) {
|
||
WTF::NakedPtr<JSC::Exception> exception;
|
||
auto* globalObject = defaultGlobalObject(context.globalObject());
|
||
auto* thisObject = self;
|
||
auto* callbackObject = thisObject->functionToCallOnData.get();
|
||
EnsureStillAliveScope ensureChunkStillAlive(chunk);
|
||
gcUnprotect(chunk);
|
||
if (!callbackObject) {
|
||
return;
|
||
}
|
||
|
||
auto callData = JSC::getCallData(callbackObject);
|
||
MarkedArgumentBuffer args;
|
||
args.append(chunk);
|
||
args.append(JSC::jsBoolean(last));
|
||
EnsureStillAliveScope ensureStillAlive(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);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
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 (!thisObject) [[unlikely]] {
|
||
return JSValue::encode(JSC::jsUndefined());
|
||
}
|
||
if (thisObject->isClosed()) {
|
||
return JSValue::encode(JSC::jsUndefined());
|
||
}
|
||
thisObject->close();
|
||
|
||
return JSValue::encode(JSC::jsUndefined());
|
||
}
|
||
|
||
JSC_DEFINE_HOST_FUNCTION(jsFunctionNodeHTTPServerSocketWrite, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||
{
|
||
auto* thisObject = jsDynamicCast<JSNodeHTTPServerSocket*>(callFrame->thisValue());
|
||
if (!thisObject) [[unlikely]] {
|
||
return JSValue::encode(JSC::jsNumber(0));
|
||
}
|
||
if (thisObject->isClosed() || thisObject->ended) {
|
||
return JSValue::encode(JSC::jsNumber(0));
|
||
}
|
||
|
||
return us_socket_buffered_js_write(thisObject->socket, thisObject->is_ssl, thisObject->ended, &thisObject->streamBuffer, globalObject, JSValue::encode(callFrame->argument(0)), JSValue::encode(callFrame->argument(1)));
|
||
}
|
||
|
||
JSC_DEFINE_HOST_FUNCTION(jsFunctionNodeHTTPServerSocketEnd, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||
{
|
||
auto* thisObject = jsDynamicCast<JSNodeHTTPServerSocket*>(callFrame->thisValue());
|
||
if (!thisObject) [[unlikely]] {
|
||
return JSValue::encode(JSC::jsUndefined());
|
||
}
|
||
if (thisObject->isClosed()) {
|
||
return JSValue::encode(JSC::jsUndefined());
|
||
}
|
||
|
||
thisObject->ended = true;
|
||
auto bufferedSize = thisObject->streamBuffer.bufferedSize();
|
||
if (bufferedSize == 0) {
|
||
return us_socket_buffered_js_write(thisObject->socket, thisObject->is_ssl, thisObject->ended, &thisObject->streamBuffer, globalObject, JSValue::encode(JSC::jsUndefined()), JSValue::encode(JSC::jsUndefined()));
|
||
}
|
||
return JSValue::encode(JSC::jsUndefined());
|
||
}
|
||
|
||
JSC_DEFINE_CUSTOM_GETTER(jsNodeHttpServerSocketGetterIsSecureEstablished, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
|
||
{
|
||
auto* thisObject = jsCast<JSNodeHTTPServerSocket*>(JSC::JSValue::decode(thisValue));
|
||
return JSValue::encode(JSC::jsBoolean(thisObject->isAuthorized()));
|
||
}
|
||
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_GETTER(jsNodeHttpServerSocketGetterOnDrain, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
|
||
{
|
||
auto* thisObject = jsCast<JSNodeHTTPServerSocket*>(JSC::JSValue::decode(thisValue));
|
||
|
||
if (thisObject->functionToCallOnDrain) {
|
||
return JSValue::encode(thisObject->functionToCallOnDrain.get());
|
||
}
|
||
|
||
return JSValue::encode(JSC::jsUndefined());
|
||
}
|
||
JSC_DEFINE_CUSTOM_SETTER(jsNodeHttpServerSocketSetterOnDrain, (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->functionToCallOnDrain.clear();
|
||
return true;
|
||
}
|
||
|
||
if (!value.isCallable()) {
|
||
return false;
|
||
}
|
||
|
||
thisObject->functionToCallOnDrain.set(vm, thisObject, value.getObject());
|
||
return true;
|
||
}
|
||
JSC_DEFINE_CUSTOM_GETTER(jsNodeHttpServerSocketGetterOnData, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
|
||
{
|
||
auto* thisObject = jsCast<JSNodeHTTPServerSocket*>(JSC::JSValue::decode(thisValue));
|
||
|
||
if (thisObject->functionToCallOnData) {
|
||
return JSValue::encode(thisObject->functionToCallOnData.get());
|
||
}
|
||
|
||
return JSValue::encode(JSC::jsUndefined());
|
||
}
|
||
JSC_DEFINE_CUSTOM_SETTER(jsNodeHttpServerSocketSetterOnData, (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->functionToCallOnData.clear();
|
||
return true;
|
||
}
|
||
|
||
if (!value.isCallable()) {
|
||
return false;
|
||
}
|
||
|
||
thisObject->functionToCallOnData.set(vm, thisObject, value.getObject());
|
||
return true;
|
||
}
|
||
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(jsNodeHttpServerSocketGetterBytesWritten, (JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, PropertyName propertyName))
|
||
{
|
||
auto* thisObject = jsCast<JSNodeHTTPServerSocket*>(JSC::JSValue::decode(thisValue));
|
||
return JSValue::encode(JSC::jsNumber(thisObject->streamBuffer.totalBytesWritten()));
|
||
}
|
||
|
||
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->functionToCallOnDrain);
|
||
visitor.append(fn->functionToCallOnData);
|
||
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" JSC::EncodedJSValue Bun__createNodeHTTPServerSocketForClientError(bool isSSL, us_socket_t* us_socket, Zig::GlobalObject* globalObject)
|
||
{
|
||
auto& vm = globalObject->vm();
|
||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||
|
||
RETURN_IF_EXCEPTION(scope, {});
|
||
|
||
if (isSSL) {
|
||
uWS::HttpResponse<true>* response = reinterpret_cast<uWS::HttpResponse<true>*>(us_socket);
|
||
auto* currentSocketDataPtr = reinterpret_cast<JSC::JSCell*>(response->getHttpResponseData()->socketData);
|
||
if (currentSocketDataPtr) {
|
||
return JSValue::encode(currentSocketDataPtr);
|
||
}
|
||
} else {
|
||
uWS::HttpResponse<false>* response = reinterpret_cast<uWS::HttpResponse<false>*>(us_socket);
|
||
auto* currentSocketDataPtr = reinterpret_cast<JSC::JSCell*>(response->getHttpResponseData()->socketData);
|
||
if (currentSocketDataPtr) {
|
||
return JSValue::encode(currentSocketDataPtr);
|
||
}
|
||
}
|
||
// socket without response because is not valid http
|
||
JSNodeHTTPServerSocket* socket = JSNodeHTTPServerSocket::create(
|
||
vm,
|
||
globalObject->m_JSNodeHTTPServerSocketStructure.getInitializedOnMainThread(globalObject),
|
||
us_socket,
|
||
isSSL, nullptr);
|
||
if (isSSL) {
|
||
uWS::HttpResponse<true>* response = reinterpret_cast<uWS::HttpResponse<true>*>(us_socket);
|
||
response->getHttpResponseData()->socketData = socket;
|
||
} else {
|
||
uWS::HttpResponse<false>* response = reinterpret_cast<uWS::HttpResponse<false>*>(us_socket);
|
||
response->getHttpResponseData()->socketData = socket;
|
||
}
|
||
RETURN_IF_EXCEPTION(scope, {});
|
||
if (socket) {
|
||
socket->strongThis.set(vm, socket);
|
||
return JSValue::encode(socket);
|
||
}
|
||
|
||
return JSValue::encode(JSC::jsNull());
|
||
}
|
||
|
||
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*);
|
||
extern "C" EncodedJSValue Server__setAppFlags(JSC::JSGlobalObject*, EncodedJSValue, bool require_host_header, bool use_strict_method_validation);
|
||
extern "C" EncodedJSValue Server__setOnClientError(JSC::JSGlobalObject*, EncodedJSValue, EncodedJSValue);
|
||
extern "C" EncodedJSValue Server__setMaxHTTPHeaderSize(JSC::JSGlobalObject*, EncodedJSValue, uint64_t);
|
||
|
||
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, JSValue methodString, 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() });
|
||
args.append(jsString(vm, WTFMove(fullURL)));
|
||
}
|
||
|
||
// Get the method.
|
||
if (methodString.isUndefinedOrNull()) [[unlikely]] {
|
||
std::string_view methodView = request->getMethod();
|
||
WTF::String methodString = String::fromUTF8ReplacingInvalidSequences({ reinterpret_cast<const LChar*>(methodView.data()), methodView.length() });
|
||
args.append(jsString(vm, WTFMove(methodString)));
|
||
} else {
|
||
args.append(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);
|
||
RETURN_IF_EXCEPTION(scope, );
|
||
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->putDirectMayBeIndex(globalObject, nameIdentifier, jsValue);
|
||
RETURN_IF_EXCEPTION(scope, void());
|
||
arrayValues.append(nameString);
|
||
arrayValues.append(jsValue);
|
||
RETURN_IF_EXCEPTION(scope, void());
|
||
}
|
||
}
|
||
|
||
JSC::JSArray* array;
|
||
{
|
||
|
||
ObjectInitializationScope initializationScope(vm);
|
||
if ((array = JSArray::tryCreateUninitializedRestricted(initializationScope, nullptr, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous), arrayValues.size()))) [[likely]] {
|
||
EncodedJSValue* data = arrayValues.data();
|
||
for (size_t i = 0, size = arrayValues.size(); i < size; ++i) {
|
||
array->initializeIndex(initializationScope, i, JSValue::decode(data[i]));
|
||
}
|
||
} else {
|
||
RETURN_IF_EXCEPTION(scope, );
|
||
array = constructArray(globalObject, static_cast<ArrayAllocationProfile*>(nullptr), arrayValues);
|
||
RETURN_IF_EXCEPTION(scope, );
|
||
}
|
||
}
|
||
|
||
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, WTFMove(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);
|
||
RETURN_IF_EXCEPTION(scope, {});
|
||
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 (value.isNull()) [[unlikely]] {
|
||
throwOutOfMemoryError(globalObject, scope);
|
||
return {};
|
||
}
|
||
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);
|
||
RETURN_IF_EXCEPTION(scope, {});
|
||
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 assignOnNodeJSCompat(uWS::TemplatedApp<isSSL>* app)
|
||
{
|
||
app->setOnSocketClosed([](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();
|
||
});
|
||
app->setOnSocketDrain([](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->onDrain();
|
||
});
|
||
app->setOnSocketData([](void* socketData, int is_ssl, struct us_socket_t* rawSocket, const char* data, int length, bool last) -> void {
|
||
auto* socket = reinterpret_cast<JSNodeHTTPServerSocket*>(socketData);
|
||
ASSERT(rawSocket == socket->socket || socket->socket == nullptr);
|
||
socket->onData(data, length, last);
|
||
});
|
||
}
|
||
|
||
extern "C" void NodeHTTP_assignOnNodeJSCompat(bool is_ssl, void* uws_app)
|
||
{
|
||
if (is_ssl) {
|
||
assignOnNodeJSCompat<true>(reinterpret_cast<uWS::TemplatedApp<true>*>(uws_app));
|
||
} else {
|
||
assignOnNodeJSCompat<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,
|
||
JSValue methodString,
|
||
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, methodString, args, globalObject, vm);
|
||
RETURN_IF_EXCEPTION(scope, {});
|
||
|
||
bool hasBody = false;
|
||
WebCore::JSNodeHTTPResponse* nodeHTTPResponseObject = jsCast<WebCore::JSNodeHTTPResponse*>(JSValue::decode(NodeHTTPResponse__createForJS(any_server, globalObject, &hasBody, request, isSSL, response, upgrade_ctx, nodeHttpResponsePtr)));
|
||
|
||
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());
|
||
}
|
||
args.append(jsBoolean(request->isAncient()));
|
||
|
||
JSValue returnValue = AsyncContextFrame::profiledCall(globalObject, callbackObject, jsUndefined(), args);
|
||
RETURN_IF_EXCEPTION(scope, {});
|
||
|
||
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();
|
||
}
|
||
}
|
||
|
||
// Prevent automatic Date header insertion when user provides one
|
||
if (header.key == WebCore::HTTPHeaderName::Date) {
|
||
data->state |= uWS::HttpResponseData<isSSL>::HTTP_WROTE_DATE_HEADER;
|
||
}
|
||
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 (headersObject->hasNonReifiedStaticProperties()) [[unlikely]] {
|
||
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);
|
||
RETURN_IF_EXCEPTION(scope, 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]);
|
||
RETURN_IF_EXCEPTION(scope, );
|
||
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,
|
||
EncodedJSValue methodString,
|
||
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),
|
||
JSValue::decode(methodString),
|
||
request,
|
||
response,
|
||
upgrade_ctx,
|
||
nodeHttpResponsePtr);
|
||
}
|
||
|
||
extern "C" EncodedJSValue NodeHTTPServer__onRequest_https(
|
||
size_t any_server,
|
||
Zig::GlobalObject* globalObject,
|
||
EncodedJSValue thisValue,
|
||
EncodedJSValue callback,
|
||
EncodedJSValue methodString,
|
||
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),
|
||
JSValue::decode(methodString),
|
||
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, {});
|
||
}
|
||
|
||
RELEASE_AND_RETURN(scope, 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(jsHTTPSetCustomOptions, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||
{
|
||
auto& vm = JSC::getVM(globalObject);
|
||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||
ASSERT(callFrame->argumentCount() == 5);
|
||
// This is an internal binding.
|
||
JSValue serverValue = callFrame->uncheckedArgument(0);
|
||
JSValue requireHostHeader = callFrame->uncheckedArgument(1);
|
||
JSValue useStrictMethodValidation = callFrame->uncheckedArgument(2);
|
||
JSValue maxHeaderSize = callFrame->uncheckedArgument(3);
|
||
JSValue callback = callFrame->uncheckedArgument(4);
|
||
|
||
double maxHeaderSizeNumber = maxHeaderSize.toNumber(globalObject);
|
||
RETURN_IF_EXCEPTION(scope, {});
|
||
|
||
Server__setAppFlags(globalObject, JSValue::encode(serverValue), requireHostHeader.toBoolean(globalObject), useStrictMethodValidation.toBoolean(globalObject));
|
||
RETURN_IF_EXCEPTION(scope, {});
|
||
|
||
Server__setMaxHTTPHeaderSize(globalObject, JSValue::encode(serverValue), maxHeaderSizeNumber);
|
||
RETURN_IF_EXCEPTION(scope, {});
|
||
|
||
Server__setOnClientError(globalObject, JSValue::encode(serverValue), JSValue::encode(callback));
|
||
RETURN_IF_EXCEPTION(scope, {});
|
||
|
||
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)) {
|
||
RELEASE_AND_RETURN(scope, fetchHeadersGetSetCookie(globalObject, vm, impl));
|
||
}
|
||
|
||
WebCore::ExceptionOr<String> res = impl->get(name);
|
||
if (res.hasException()) {
|
||
WebCore::propagateException(globalObject, scope, res.releaseException());
|
||
RELEASE_AND_RETURN(scope, {});
|
||
}
|
||
|
||
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);
|
||
RETURN_IF_EXCEPTION(scope, {});
|
||
|
||
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);
|
||
RETURN_IF_EXCEPTION(scope, {});
|
||
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);
|
||
RETURN_IF_EXCEPTION(scope, {});
|
||
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, "setServerCustomOptions"_s)),
|
||
JSC::JSFunction::create(vm, globalObject, 2, "setServerCustomOptions"_s, jsHTTPSetCustomOptions, 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
|