UNPKG

11.1 kBTypeScriptView Raw
1import { EventEmitter } from "events";
2import { SrpServer } from "fast-srp-hap";
3import { IncomingMessage, ServerResponse } from "http";
4import { AddressInfo, Socket } from "net";
5import { CharacteristicValue, Nullable, SessionIdentifier } from "../../types";
6/**
7 * @group HAP Accessory Server
8 */
9export type HAPUsername = string;
10/**
11 * @group HAP Accessory Server
12 */
13export type EventName = string;
14/**
15 * Simple struct to hold vars needed to support HAP encryption.
16 *
17 * @group Cryptography
18 */
19export declare class HAPEncryption {
20 readonly clientPublicKey: Buffer;
21 readonly secretKey: Buffer;
22 readonly publicKey: Buffer;
23 readonly sharedSecret: Buffer;
24 readonly hkdfPairEncryptionKey: Buffer;
25 accessoryToControllerCount: number;
26 controllerToAccessoryCount: number;
27 accessoryToControllerKey: Buffer;
28 controllerToAccessoryKey: Buffer;
29 incompleteFrame?: Buffer;
30 constructor(clientPublicKey: Buffer, secretKey: Buffer, publicKey: Buffer, sharedSecret: Buffer, hkdfPairEncryptionKey: Buffer);
31}
32/**
33 * @group HAP Accessory Server
34 */
35export declare const enum EventedHTTPServerEvent {
36 LISTENING = "listening",
37 CONNECTION_OPENED = "connection-opened",
38 REQUEST = "request",
39 CONNECTION_CLOSED = "connection-closed"
40}
41/**
42 * @group HAP Accessory Server
43 */
44export declare interface EventedHTTPServer {
45 on(event: "listening", listener: (port: number, address: string) => void): this;
46 on(event: "connection-opened", listener: (connection: HAPConnection) => void): this;
47 on(event: "request", listener: (connection: HAPConnection, request: IncomingMessage, response: ServerResponse) => void): this;
48 on(event: "connection-closed", listener: (connection: HAPConnection) => void): this;
49 emit(event: "listening", port: number, address: string): boolean;
50 emit(event: "connection-opened", connection: HAPConnection): boolean;
51 emit(event: "request", connection: HAPConnection, request: IncomingMessage, response: ServerResponse): boolean;
52 emit(event: "connection-closed", connection: HAPConnection): boolean;
53}
54/**
55 * EventedHTTPServer provides an HTTP-like server that supports HAP "extensions" for security and events.
56 *
57 * Implementation
58 * --------------
59 * In order to implement the "custom HTTP" server required by the HAP protocol (see HAPServer.js) without completely
60 * reinventing the wheel, we create both a generic TCP socket server and a standard Node HTTP server.
61 * The TCP socket server acts as a proxy, allowing users of this class to transform data (for encryption) as necessary
62 * and passing through bytes directly to the HTTP server for processing. This way we get Node to do all
63 * the "heavy lifting" of HTTP like parsing headers and formatting responses.
64 *
65 * Events are sent by simply waiting for current HTTP traffic to subside and then sending a custom response packet
66 * directly down the wire via the socket.
67 *
68 * Each connection to the main TCP server gets its own internal HTTP server, so we can track ongoing requests/responses
69 * for safe event insertion.
70 *
71 * @group HAP Accessory Server
72 */
73export declare class EventedHTTPServer extends EventEmitter {
74 private static readonly CONNECTION_TIMEOUT_LIMIT;
75 private static readonly MAX_CONNECTION_IDLE_TIME;
76 private readonly tcpServer;
77 /**
78 * Set of all currently connected HAP connections.
79 */
80 private readonly connections;
81 /**
82 * Session dictionary indexed by username/identifier. The username uniquely identifies every person added to the home.
83 * So there can be multiple sessions open for a single username (multiple devices connected to the same Apple ID).
84 */
85 private readonly connectionsByUsername;
86 private connectionIdleTimeout?;
87 private connectionLoggingInterval?;
88 constructor();
89 private scheduleNextConnectionIdleTimeout;
90 address(): AddressInfo;
91 listen(targetPort: number, hostname?: string): void;
92 stop(): void;
93 destroy(): void;
94 /**
95 * Send an event notification for given characteristic and changed value to all connected clients.
96 * If `originator` is specified, the given {@link HAPConnection} will be excluded from the broadcast.
97 *
98 * @param aid - The accessory id of the updated characteristic.
99 * @param iid - The instance id of the updated characteristic.
100 * @param value - The newly set value of the characteristic.
101 * @param originator - If specified, the connection will not get an event message.
102 * @param immediateDelivery - The HAP spec requires some characteristics to be delivery immediately.
103 * Namely, for the {@link Characteristic.ButtonEvent} and the {@link Characteristic.ProgrammableSwitchEvent} characteristics.
104 */
105 broadcastEvent(aid: number, iid: number, value: Nullable<CharacteristicValue>, originator?: HAPConnection, immediateDelivery?: boolean): void;
106 private onConnection;
107 private handleConnectionAuthenticated;
108 private handleConnectionClose;
109 /**
110 * This method is to be called when a given {@link HAPConnection} performs a request that should result in the disconnection
111 * of all other {@link HAPConnection} with the same {@link HAPUsername}.
112 *
113 * The initiator MUST be in the middle of a http request were the response was not served yet.
114 * Otherwise, the initiator connection might reside in a state where it isn't disconnected and can't make any further requests.
115 *
116 * @param initiator - The connection that requested to disconnect all connections of the same username.
117 * @param username - The username for which all connections shall be closed.
118 */
119 static destroyExistingConnectionsAfterUnpair(initiator: HAPConnection, username: HAPUsername): void;
120}
121/**
122 * @private
123 * @group HAP Accessory Server
124 */
125export declare const enum HAPConnectionState {
126 CONNECTING = 0,// initial state, setup is going on
127 FULLY_SET_UP = 1,// internal http server is running and connection is established
128 AUTHENTICATED = 2,// encryption is set up
129 TO_BE_TEARED_DOWN = 3,// when in this state, connection should be closed down after response was sent out
130 CLOSING = 4,// close was called
131 CLOSED = 5
132}
133/**
134 * @group HAP Accessory Server
135 */
136export declare const enum HAPConnectionEvent {
137 REQUEST = "request",
138 AUTHENTICATED = "authenticated",
139 CLOSED = "closed"
140}
141/**
142 * @group HAP Accessory Server
143 */
144export declare interface HAPConnection {
145 on(event: "request", listener: (request: IncomingMessage, response: ServerResponse) => void): this;
146 on(event: "authenticated", listener: (username: HAPUsername) => void): this;
147 on(event: "closed", listener: () => void): this;
148 emit(event: "request", request: IncomingMessage, response: ServerResponse): boolean;
149 emit(event: "authenticated", username: HAPUsername): boolean;
150 emit(event: "closed"): boolean;
151}
152/**
153 * Manages a single iOS-initiated HTTP connection during its lifetime.
154 * @group HAP Accessory Server
155 */
156export declare class HAPConnection extends EventEmitter {
157 /**
158 * @private file-private API
159 */
160 readonly server: EventedHTTPServer;
161 readonly sessionID: SessionIdentifier;
162 private state;
163 readonly localAddress: string;
164 readonly remoteAddress: string;
165 readonly remotePort: number;
166 readonly networkInterface: string;
167 private readonly tcpSocket;
168 private readonly internalHttpServer;
169 private httpSocket?;
170 private internalHttpServerPort?;
171 private internalHttpServerAddress?;
172 lastSocketOperation: number;
173 private pendingClientSocketData?;
174 private handlingRequest;
175 username?: HAPUsername;
176 encryption?: HAPEncryption;
177 srpServer?: SrpServer;
178 _pairSetupState?: number;
179 _pairVerifyState?: number;
180 private registeredEvents;
181 private eventsTimer?;
182 private readonly queuedEvents;
183 /**
184 * If true, the above {@link queuedEvents} contains events which are set to be delivered immediately!
185 */
186 private eventsQueuedForImmediateDelivery;
187 timedWritePid?: number;
188 timedWriteTimeout?: NodeJS.Timeout;
189 constructor(server: EventedHTTPServer, clientSocket: Socket);
190 private debugListenerRegistration;
191 addListener(event: string | symbol, listener: (...args: any[]) => void): this;
192 removeListener(event: string | symbol, listener: (...args: any[]) => void): this;
193 off(event: string | symbol, listener: (...args: any[]) => void): this;
194 /**
195 * This method is called once the connection has gone through pair-verify.
196 * As any HomeKit controller will initiate a pair-verify after the pair-setup procedure, this method gets
197 * not called on the initial pair-setup.
198 *
199 * Once this method has been called, the connection is authenticated and encryption is turned on.
200 */
201 connectionAuthenticated(username: HAPUsername): void;
202 isAuthenticated(): boolean;
203 close(): void;
204 closeConnectionAsOfUnpair(initiator: HAPConnection): void;
205 sendEvent(aid: number, iid: number, value: Nullable<CharacteristicValue>, immediateDelivery?: boolean): void;
206 private handleEventsTimeout;
207 private writeQueuedEventNotifications;
208 /**
209 * This will create an EVENT/1.0 notification header with the provided event notification.
210 * If currently an HTTP request is in progress the assembled packet will be
211 * added to the pending events list.
212 *
213 * @param notification - The event which should be sent out
214 */
215 private writeEventNotification;
216 enableEventNotifications(aid: number, iid: number): void;
217 disableEventNotifications(aid: number, iid: number): void;
218 hasEventNotifications(aid: number, iid: number): boolean;
219 getRegisteredEvents(): Set<EventName>;
220 clearRegisteredEvents(): void;
221 private encrypt;
222 private decrypt;
223 private onHttpServerListening;
224 /**
225 * This event handler is called when we receive data from a HomeKit controller on our tcp socket.
226 * We store the data if the internal http server is not read yet, or forward it to the http server.
227 */
228 private onTCPSocketData;
229 /**
230 * This event handler is called when the internal http server receives a request.
231 * Meaning we received data from the HomeKit controller in {@link onTCPSocketData}, which then send the
232 * data unencrypted to the internal http server. And now it landed here, fully parsed as a http request.
233 */
234 private handleHttpServerRequest;
235 /**
236 * This event handler is called by the socket which is connected to our internal http server.
237 * It is called with the response returned from the http server.
238 * In this method we have to encrypt and forward the message back to the HomeKit controller.
239 */
240 private handleHttpServerResponse;
241 private handleTCPSocketWriteFulfilled;
242 private onTCPSocketError;
243 private onTCPSocketClose;
244 private onHttpServerError;
245 private onHttpServerClose;
246 private onHttpSocketError;
247 private onHttpSocketClose;
248 getLocalAddress(ipVersion: "ipv4" | "ipv6"): string;
249 private static getLocalNetworkInterface;
250}
251//# sourceMappingURL=eventedhttp.d.ts.map
\No newline at end of file