import { IncomingMessage, Server } from 'node:http';
import { Socket } from 'node:net';
import { WebSocketServer } from 'ws';
import { assert } from "../../../shared/src/asserts.js";
import { serializableSubset } from "./http.js";
import { MESSAGE_TYPES, } from "./processes.js";
import { closeWithError, PROTOCOL_ERROR } from "./ws.js";
/**
 * Installs websocket handoff logic from either an http.Server
 * receiving requests, or a parent Worker process
 * that is handing off requests to this process.
 */
export function createWebSocketHandoffHandler(lc, handoff, serverOptions) {
    const wss = new WebSocketServer(serverOptions ?? {
        noServer: true,
    });
    return (message, socket, head) => {
        let sent = false;
        function send({ payload, sender }) {
            assert(!sent, 'Handoff callback already invoked');
            sent = true;
            const data = [
                'handoff',
                {
                    message: serializableSubset(message),
                    head,
                    payload,
                },
            ];
            // "This event is guaranteed to be passed an instance of the <net.Socket> class"
            // https://nodejs.org/api/http.html#event-upgrade
            sender.send(data, socket);
        }
        function onError(error) {
            // Returning an error on the HTTP handshake looks like a hanging connection
            // (at least from Chrome) and doesn't report any meaningful error in the browser.
            // Instead, finish the upgrade to a websocket and then close it with an error.
            wss.handleUpgrade(message, socket, head, ws => closeWithError(lc, ws, error, PROTOCOL_ERROR));
        }
        try {
            const spec = handoff(message, send, onError);
            if (spec) {
                send(spec);
            }
        }
        catch (error) {
            onError(error);
        }
    };
}
/**
 * Installs websocket handoff logic from either an http.Server
 * receiving requests, or a parent Worker process
 * that is handing off requests to this process.
 */
export function installWebSocketHandoff(lc, handoff, source, serverOptions) {
    const handle = createWebSocketHandoffHandler(lc, handoff, serverOptions);
    if (source instanceof Server) {
        // handoff messages from an HTTP server
        source.on('upgrade', handle);
    }
    else {
        // handoff messages from this worker's parent.
        source.onMessageType('handoff', (msg, socket) => {
            const { message, head } = msg;
            handle(message, socket, Buffer.from(head));
        });
    }
}
export function installWebSocketReceiver(lc, server, receive, receiver) {
    receiver.onMessageType('handoff', (msg, socket) => {
        // Per https://nodejs.org/api/child_process.html#subprocesssendmessage-sendhandle-options-callback
        //
        // > Any 'message' handlers in the subprocess should verify that socket
        // > exists, as the connection may have been closed during the time it
        // > takes to send the connection to the child.
        if (!socket) {
            lc.warn?.('websocket closed during handoff');
            return;
        }
        const { message, head, payload } = msg;
        server.handleUpgrade(message, socket, Buffer.from(head), ws => receive(ws, payload, message));
    });
}
//# sourceMappingURL=websocket-handoff.js.map