import { EventEmitter } from "events"; import { SrpServer } from "fast-srp-hap"; import { IncomingMessage, ServerResponse } from "http"; import { AddressInfo, Socket } from "net"; import { CharacteristicValue, Nullable, SessionIdentifier } from "../../types"; /** * @group HAP Accessory Server */ export type HAPUsername = string; /** * @group HAP Accessory Server */ export type EventName = string; /** * Simple struct to hold vars needed to support HAP encryption. * * @group Cryptography */ export declare class HAPEncryption { readonly clientPublicKey: Buffer; readonly secretKey: Buffer; readonly publicKey: Buffer; readonly sharedSecret: Buffer; readonly hkdfPairEncryptionKey: Buffer; accessoryToControllerCount: number; controllerToAccessoryCount: number; accessoryToControllerKey: Buffer; controllerToAccessoryKey: Buffer; incompleteFrame?: Buffer; constructor(clientPublicKey: Buffer, secretKey: Buffer, publicKey: Buffer, sharedSecret: Buffer, hkdfPairEncryptionKey: Buffer); } /** * @group HAP Accessory Server */ export declare const enum EventedHTTPServerEvent { LISTENING = "listening", CONNECTION_OPENED = "connection-opened", REQUEST = "request", CONNECTION_CLOSED = "connection-closed" } /** * @group HAP Accessory Server */ export declare interface EventedHTTPServer { on(event: "listening", listener: (port: number, address: string) => void): this; on(event: "connection-opened", listener: (connection: HAPConnection) => void): this; on(event: "request", listener: (connection: HAPConnection, request: IncomingMessage, response: ServerResponse) => void): this; on(event: "connection-closed", listener: (connection: HAPConnection) => void): this; emit(event: "listening", port: number, address: string): boolean; emit(event: "connection-opened", connection: HAPConnection): boolean; emit(event: "request", connection: HAPConnection, request: IncomingMessage, response: ServerResponse): boolean; emit(event: "connection-closed", connection: HAPConnection): boolean; } /** * EventedHTTPServer provides an HTTP-like server that supports HAP "extensions" for security and events. * * Implementation * -------------- * In order to implement the "custom HTTP" server required by the HAP protocol (see HAPServer.js) without completely * reinventing the wheel, we create both a generic TCP socket server and a standard Node HTTP server. * The TCP socket server acts as a proxy, allowing users of this class to transform data (for encryption) as necessary * and passing through bytes directly to the HTTP server for processing. This way we get Node to do all * the "heavy lifting" of HTTP like parsing headers and formatting responses. * * Events are sent by simply waiting for current HTTP traffic to subside and then sending a custom response packet * directly down the wire via the socket. * * Each connection to the main TCP server gets its own internal HTTP server, so we can track ongoing requests/responses * for safe event insertion. * * @group HAP Accessory Server */ export declare class EventedHTTPServer extends EventEmitter { private static readonly CONNECTION_TIMEOUT_LIMIT; private static readonly MAX_CONNECTION_IDLE_TIME; private readonly tcpServer; /** * Set of all currently connected HAP connections. */ private readonly connections; /** * Session dictionary indexed by username/identifier. The username uniquely identifies every person added to the home. * So there can be multiple sessions open for a single username (multiple devices connected to the same Apple ID). */ private readonly connectionsByUsername; private connectionIdleTimeout?; private connectionLoggingInterval?; constructor(); private scheduleNextConnectionIdleTimeout; address(): AddressInfo; listen(targetPort: number, hostname?: string): void; stop(): void; destroy(): void; /** * Send an event notification for given characteristic and changed value to all connected clients. * If `originator` is specified, the given {@link HAPConnection} will be excluded from the broadcast. * * @param aid - The accessory id of the updated characteristic. * @param iid - The instance id of the updated characteristic. * @param value - The newly set value of the characteristic. * @param originator - If specified, the connection will not get an event message. * @param immediateDelivery - The HAP spec requires some characteristics to be delivery immediately. * Namely, for the {@link Characteristic.ButtonEvent} and the {@link Characteristic.ProgrammableSwitchEvent} characteristics. */ broadcastEvent(aid: number, iid: number, value: Nullable, originator?: HAPConnection, immediateDelivery?: boolean): void; private onConnection; private handleConnectionAuthenticated; private handleConnectionClose; /** * This method is to be called when a given {@link HAPConnection} performs a request that should result in the disconnection * of all other {@link HAPConnection} with the same {@link HAPUsername}. * * The initiator MUST be in the middle of a http request were the response was not served yet. * Otherwise, the initiator connection might reside in a state where it isn't disconnected and can't make any further requests. * * @param initiator - The connection that requested to disconnect all connections of the same username. * @param username - The username for which all connections shall be closed. */ static destroyExistingConnectionsAfterUnpair(initiator: HAPConnection, username: HAPUsername): void; } /** * @private * @group HAP Accessory Server */ export declare const enum HAPConnectionState { CONNECTING = 0,// initial state, setup is going on FULLY_SET_UP = 1,// internal http server is running and connection is established AUTHENTICATED = 2,// encryption is set up TO_BE_TEARED_DOWN = 3,// when in this state, connection should be closed down after response was sent out CLOSING = 4,// close was called CLOSED = 5 } /** * @group HAP Accessory Server */ export declare const enum HAPConnectionEvent { REQUEST = "request", AUTHENTICATED = "authenticated", CLOSED = "closed" } /** * @group HAP Accessory Server */ export declare interface HAPConnection { on(event: "request", listener: (request: IncomingMessage, response: ServerResponse) => void): this; on(event: "authenticated", listener: (username: HAPUsername) => void): this; on(event: "closed", listener: () => void): this; emit(event: "request", request: IncomingMessage, response: ServerResponse): boolean; emit(event: "authenticated", username: HAPUsername): boolean; emit(event: "closed"): boolean; } /** * Manages a single iOS-initiated HTTP connection during its lifetime. * @group HAP Accessory Server */ export declare class HAPConnection extends EventEmitter { /** * @private file-private API */ readonly server: EventedHTTPServer; readonly sessionID: SessionIdentifier; private state; readonly localAddress: string; readonly remoteAddress: string; readonly remotePort: number; readonly networkInterface: string; private readonly tcpSocket; private readonly internalHttpServer; private httpSocket?; private internalHttpServerPort?; private internalHttpServerAddress?; lastSocketOperation: number; private pendingClientSocketData?; private handlingRequest; username?: HAPUsername; encryption?: HAPEncryption; srpServer?: SrpServer; _pairSetupState?: number; _pairVerifyState?: number; private registeredEvents; private eventsTimer?; private readonly queuedEvents; /** * If true, the above {@link queuedEvents} contains events which are set to be delivered immediately! */ private eventsQueuedForImmediateDelivery; timedWritePid?: number; timedWriteTimeout?: NodeJS.Timeout; constructor(server: EventedHTTPServer, clientSocket: Socket); private debugListenerRegistration; addListener(event: string | symbol, listener: (...args: any[]) => void): this; removeListener(event: string | symbol, listener: (...args: any[]) => void): this; off(event: string | symbol, listener: (...args: any[]) => void): this; /** * This method is called once the connection has gone through pair-verify. * As any HomeKit controller will initiate a pair-verify after the pair-setup procedure, this method gets * not called on the initial pair-setup. * * Once this method has been called, the connection is authenticated and encryption is turned on. */ connectionAuthenticated(username: HAPUsername): void; isAuthenticated(): boolean; close(): void; closeConnectionAsOfUnpair(initiator: HAPConnection): void; sendEvent(aid: number, iid: number, value: Nullable, immediateDelivery?: boolean): void; private handleEventsTimeout; private writeQueuedEventNotifications; /** * This will create an EVENT/1.0 notification header with the provided event notification. * If currently an HTTP request is in progress the assembled packet will be * added to the pending events list. * * @param notification - The event which should be sent out */ private writeEventNotification; enableEventNotifications(aid: number, iid: number): void; disableEventNotifications(aid: number, iid: number): void; hasEventNotifications(aid: number, iid: number): boolean; getRegisteredEvents(): Set; clearRegisteredEvents(): void; private encrypt; private decrypt; private onHttpServerListening; /** * This event handler is called when we receive data from a HomeKit controller on our tcp socket. * We store the data if the internal http server is not read yet, or forward it to the http server. */ private onTCPSocketData; /** * This event handler is called when the internal http server receives a request. * Meaning we received data from the HomeKit controller in {@link onTCPSocketData}, which then send the * data unencrypted to the internal http server. And now it landed here, fully parsed as a http request. */ private handleHttpServerRequest; /** * This event handler is called by the socket which is connected to our internal http server. * It is called with the response returned from the http server. * In this method we have to encrypt and forward the message back to the HomeKit controller. */ private handleHttpServerResponse; private handleTCPSocketWriteFulfilled; private onTCPSocketError; private onTCPSocketClose; private onHttpServerError; private onHttpServerClose; private onHttpSocketError; private onHttpSocketClose; getLocalAddress(ipVersion: "ipv4" | "ipv6"): string; private static getLocalNetworkInterface; } //# sourceMappingURL=eventedhttp.d.ts.map