mirror of
https://github.com/oven-sh/bun
synced 2026-02-12 03:48:56 +00:00
Refactor: Split JSNodeHTTPServerSocket into separate files (#23203)
## Summary Split `JSNodeHTTPServerSocket` and `JSNodeHTTPServerSocketPrototype` from `NodeHTTP.cpp` into dedicated files, following the same pattern as `JSDiffieHellman` in the crypto module. ## Changes - **Created 4 new files:** - `JSNodeHTTPServerSocket.h` - Class declaration - `JSNodeHTTPServerSocket.cpp` - Class implementation and methods - `JSNodeHTTPServerSocketPrototype.h` - Prototype declaration - `JSNodeHTTPServerSocketPrototype.cpp` - Prototype methods and property table - **Moved from NodeHTTP.cpp:** - All custom getters/setters (onclose, ondrain, ondata, etc.) - All host functions (close, write, end) - Event handlers (onClose, onDrain, onData) - Helper functions and templates - **Preserved:** - All extern C bindings for Zig interop - All existing functionality - Proper namespace and include structure - **Merged changes from main:** - Added `upgraded` flag for websocket support (from #23150) - Updated `clearSocketData` to handle WebSocketData - Added `onSocketUpgraded` callback handler ## Impact - Reduced `NodeHTTP.cpp` from ~1766 lines to 1010 lines (43% reduction) - Better code organization and maintainability - No functional changes ## Test plan - [x] Build compiles successfully - [x] `test/js/node/http/node-http.test.ts` passes (72/74 tests pass, same as before) - [x] `test/js/node/http/node-http-with-ws.test.ts` passes (websocket upgrade test) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Bot <claude-bot@bun.sh> Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -21,770 +21,14 @@
|
||||
#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);
|
||||
#include "node/JSNodeHTTPServerSocket.h"
|
||||
#include "node/JSNodeHTTPServerSocketPrototype.h"
|
||||
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;
|
||||
unsigned upgraded : 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(bool upgraded, us_socket_t* socket)
|
||||
{
|
||||
if (upgraded) {
|
||||
auto* webSocket = (uWS::WebSocketData*)us_socket_ext(SSL, socket);
|
||||
webSocket->socketData = nullptr;
|
||||
} else {
|
||||
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>(this->upgraded, socket);
|
||||
} else {
|
||||
clearSocketData<false>(this->upgraded, 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*);
|
||||
@@ -1769,9 +1013,4 @@ extern "C" void WebCore__FetchHeaders__toUWSResponse(WebCore::FetchHeaders* arg0
|
||||
}
|
||||
}
|
||||
|
||||
JSC::Structure* createNodeHTTPServerSocketStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
|
||||
{
|
||||
return JSNodeHTTPServerSocket::createStructure(vm, globalObject);
|
||||
}
|
||||
|
||||
} // namespace Bun
|
||||
|
||||
Reference in New Issue
Block a user