Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slightly more complete undici polyfill #15360

Merged
merged 2 commits into from
Nov 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion src/bun.js/bindings/Undici.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
#include "JavaScriptCore/AggregateError.h"
#include "JavaScriptCore/JSFunction.h"
#include "JSDOMFile.h"
#include "JSWebSocket.h"
#include "JSCloseEvent.h"
#include "JSErrorEvent.h"
#include "JSMessageEvent.h"

namespace Bun {

Expand All @@ -27,7 +31,7 @@ JSC::JSValue createUndiciInternalBinding(Zig::GlobalObject* globalObject)
{
auto& vm = globalObject->vm();

auto* obj = constructEmptyObject(globalObject);
auto* obj = constructEmptyObject(globalObject, globalObject->objectPrototype(), 11);
obj->putDirectIndex(
globalObject, 0,
globalObject->JSResponseConstructor());
Expand All @@ -52,6 +56,18 @@ JSC::JSValue createUndiciInternalBinding(Zig::GlobalObject* globalObject)
obj->putDirectIndex(
globalObject, 7,
JSURLSearchParams::getConstructor(vm, globalObject));
obj->putDirectIndex(
globalObject, 8,
JSWebSocket::getConstructor(vm, globalObject));
obj->putDirectIndex(
globalObject, 9,
JSCloseEvent::getConstructor(vm, globalObject));
obj->putDirectIndex(
globalObject, 10,
JSErrorEvent::getConstructor(vm, globalObject));
obj->putDirectIndex(
globalObject, 11,
JSMessageEvent::getConstructor(vm, globalObject));

return obj;
}
Expand Down
283 changes: 230 additions & 53 deletions src/js/thirdparty/undici.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,23 @@ const File = bindings[4];
const URL = bindings[5];
const AbortSignal = bindings[6];
const URLSearchParams = bindings[7];
const WebSocket = bindings[8];
const CloseEvent = bindings[9];
const ErrorEvent = bindings[10];
const MessageEvent = bindings[11];

class FileReader extends EventTarget {
constructor() {
throw new Error("Not implemented yet!");
super();
}

static EMPTY = 0;
static LOADING = 1;
static DONE = 2;
}

function notImplemented() {
throw new Error("Not implemented in bun");
throw new Error("This function is not yet implemented in Bun");
}

/**
Expand Down Expand Up @@ -230,102 +238,271 @@ async function request(
}

function stream() {
throw new Error("Not implemented in bun");
notImplemented();
}
function pipeline() {
throw new Error("Not implemented in bun");
notImplemented();
}
function connect() {
throw new Error("Not implemented in bun");
notImplemented();
}
function upgrade() {
throw new Error("Not implemented in bun");
notImplemented();
}

class MockClient {
constructor() {}
}
class MockPool {
constructor() {}
}
class MockAgent {
constructor() {}
}

function mockErrors() {}

class Dispatcher extends EventEmitter {}
class Agent extends Dispatcher {}
class Pool extends Dispatcher {
request() {}
}
class BalancedPool extends Dispatcher {}
class Client extends Dispatcher {
request() {}
}

class DispatcherBase extends EventEmitter {}

class ProxyAgent extends DispatcherBase {
constructor() {
throw new Error("Not implemented in bun");
super();
}
}
class MockPool {

class EnvHttpProxyAgent extends DispatcherBase {
constructor() {
throw new Error("Not implemented in bun");
super();
}
}
class MockAgent {

class RetryAgent extends Dispatcher {
constructor() {
throw new Error("Not implemented in bun");
super();
}
}

function mockErrors() {
throw new Error("Not implemented in bun");
class RetryHandler {
constructor() {}
}

function Undici() {
throw new Error("Not implemented in bun");
class DecoratorHandler {
constructor() {}
}

class Dispatcher extends EventEmitter {}
class Agent extends Dispatcher {}
class Pool extends Dispatcher {
request() {
throw new Error("Not implemented in bun");
}
class RedirectHandler {
constructor() {}
}
class BalancedPool extends Dispatcher {}
class Client extends Dispatcher {
request() {
throw new Error("Not implemented in bun");

function createRedirectInterceptor() {
return new RedirectHandler();
}

const interceptors = {
redirect: () => {},
retry: () => {},
dump: () => {},
};

// Error classes
class UndiciError extends Error {}
class AbortError extends UndiciError {}
class HTTPParserError extends Error {}
class HeadersTimeoutError extends UndiciError {}
class HeadersOverflowError extends UndiciError {}
class BodyTimeoutError extends UndiciError {}
class RequestContentLengthMismatchError extends UndiciError {}
class ConnectTimeoutError extends UndiciError {}
class ResponseStatusCodeError extends UndiciError {}
class InvalidArgumentError extends UndiciError {}
class InvalidReturnValueError extends UndiciError {}
class RequestAbortedError extends AbortError {}
class ClientDestroyedError extends UndiciError {}
class ClientClosedError extends UndiciError {}
class InformationalError extends UndiciError {}
class SocketError extends UndiciError {}
class NotSupportedError extends UndiciError {}
class ResponseContentLengthMismatchError extends UndiciError {}
class BalancedPoolMissingUpstreamError extends UndiciError {}
class ResponseExceededMaxSizeError extends UndiciError {}
class RequestRetryError extends UndiciError {}
class SecureProxyConnectionError extends UndiciError {}
class MockNotMatchedError extends UndiciError {}

const errors = {
AbortError,
HTTPParserError,
UndiciError,
HeadersTimeoutError,
HeadersOverflowError,
BodyTimeoutError,
RequestContentLengthMismatchError,
ConnectTimeoutError,
ResponseStatusCodeError,
InvalidArgumentError,
InvalidReturnValueError,
RequestAbortedError,
ClientDestroyedError,
ClientClosedError,
InformationalError,
SocketError,
NotSupportedError,
ResponseContentLengthMismatchError,
BalancedPoolMissingUpstreamError,
ResponseExceededMaxSizeError,
RequestRetryError,
SecureProxyConnectionError,
};

const util = {
parseHeaders: () => {
notImplemented();
},
headerNameToString: () => {
notImplemented();
},
};

class EventSource extends EventTarget {
static CONNECTING = 0;
static OPEN = 1;
static CLOSED = 2;

constructor() {
super();
}
}

Undici.Dispatcher = Dispatcher;
Undici.Pool = Pool;
Undici.BalancedPool = BalancedPool;
Undici.Client = Client;
Undici.Agent = Agent;

Undici.buildConnector =
Undici.errors =
Undici.setGlobalDispatcher =
Undici.getGlobalDispatcher =
Undici.request =
Undici.stream =
Undici.pipeline =
Undici.connect =
Undici.upgrade =
Undici.MockClient =
Undici.MockPool =
Undici.MockAgent =
Undici.mockErrors =
notImplemented;

Undici.fetch = fetch;

export default {
// Add missing cookie functions
function deleteCookie() {
notImplemented();
}

function getCookies() {
notImplemented();
}

function getSetCookies() {
notImplemented();
}

function setCookie() {
notImplemented();
}

// Add missing MIME type functions
function parseMIMEType() {
notImplemented();
}

function serializeAMimeType() {
notImplemented();
}

let globalDispatcher;

// Add missing dispatcher functions
function setGlobalDispatcher(dispatcher) {
globalDispatcher = dispatcher;
}

function getGlobalDispatcher() {
return (globalDispatcher ??= new Dispatcher());
}

// Add missing origin functions
function setGlobalOrigin() {}

function getGlobalOrigin() {}

// Create empty CacheStorage
const caches = {};

/**
* Builds a connector function for making network connections
* @param {Object} [options] Configuration options for the connector
* @param {boolean} [options.rejectUnauthorized] Whether to reject unauthorized SSL/TLS connections
* @param {number} [options.connectTimeout] Connection timeout in milliseconds
* @param {number} [options.maxCachedSessions] Maximum number of cached TLS sessions
* @param {boolean} [options.allowH2] Whether to allow HTTP/2 connections
* @returns {function} A connector function
*/
function buildConnector(options = {}) {
const { rejectUnauthorized = true, connectTimeout, maxCachedSessions = 100, allowH2 = false } = options;

/**
* @param {Object} options
* @param {string} options.hostname
* @param {number} options.port
* @param {string} [options.servername]
* @param {AbortSignal} [options.signal]
*/
return function connect({ hostname, port, servername, signal }) {
notImplemented();
};
}

// Update the exports to match the exact structure
const moduleExports = {
Agent,
BalancedPool,
buildConnector,
caches,
Client,
CloseEvent,
connect,
createRedirectInterceptor,
DecoratorHandler,
deleteCookie,
Dispatcher,
EnvHttpProxyAgent,
ErrorEvent,
errors,
EventSource,
fetch,
File,
FileReader,
FormData,
getCookies,
getGlobalDispatcher,
getGlobalOrigin,
getSetCookies,
Headers,
interceptors,
MessageEvent,
MockAgent,
MockClient,
mockErrors,
MockPool,
parseMIMEType,
pipeline,
Pool,
request,
ProxyAgent,
RedirectHandler,
Request,
request,
Response,
RetryAgent,
RetryHandler,
serializeAMimeType,
setCookie,
setGlobalDispatcher,
setGlobalOrigin,
stream,
Undici,
upgrade,
URL,
URLSearchParams,
util,
WebSocket,
};

moduleExports.default = moduleExports;
export default moduleExports;
Loading