@azure/web-pubsub-express 1.0.0-beta.1 → 1.0.1-alpha.20211215.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +66 -0
- package/README.md +123 -30
- package/dist/index.js +310 -242
- package/dist/index.js.map +1 -1
- package/dist-esm/samples-dev/server.js +9 -4
- package/dist-esm/samples-dev/server.js.map +1 -1
- package/dist-esm/src/cloudEventsDispatcher.js +225 -232
- package/dist-esm/src/cloudEventsDispatcher.js.map +1 -1
- package/dist-esm/src/cloudEventsProtocols.js.map +1 -1
- package/dist-esm/src/logger.js +10 -0
- package/dist-esm/src/logger.js.map +1 -0
- package/dist-esm/src/utils.js +68 -0
- package/dist-esm/src/utils.js.map +1 -0
- package/dist-esm/src/webPubSubEventHandler.js +11 -12
- package/dist-esm/src/webPubSubEventHandler.js.map +1 -1
- package/dist-esm/test/connect.spec.js +131 -81
- package/dist-esm/test/connect.spec.js.map +1 -1
- package/dist-esm/test/connected.spec.js +43 -53
- package/dist-esm/test/connected.spec.js.map +1 -1
- package/dist-esm/test/ctor.spec.js +9 -3
- package/dist-esm/test/ctor.spec.js.map +1 -1
- package/dist-esm/test/disconnected.spec.js +43 -53
- package/dist-esm/test/disconnected.spec.js.map +1 -1
- package/dist-esm/test/user.spec.js +121 -115
- package/dist-esm/test/user.spec.js.map +1 -1
- package/dist-esm/test/validate.spec.js +26 -10
- package/dist-esm/test/validate.spec.js.map +1 -1
- package/package.json +18 -38
- package/types/web-pubsub-express.d.ts +36 -16
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloudEventsDispatcher.js","sourceRoot":"","sources":["../../src/cloudEventsDispatcher.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { CloudEvent, Message, HTTP } from \"cloudevents\";\nimport { IncomingMessage, ServerResponse } from \"http\";\nimport { URL } from \"url\";\nimport {\n ConnectRequest,\n ConnectResponse,\n UserEventRequest,\n DisconnectedRequest,\n ConnectedRequest,\n ConnectionContext,\n WebPubSubEventHandlerOptions\n} from \"./cloudEventsProtocols\";\n\n/**\n * @internal\n */\nexport class CloudEventsDispatcher {\n private readonly _dumpRequest: boolean;\n private readonly _allowedOrigins: string[];\n constructor(\n private hub: string,\n allowedEndpoints: string[],\n private eventHandler?: WebPubSubEventHandlerOptions\n ) {\n this._dumpRequest = eventHandler?.dumpRequest ?? false;\n this._allowedOrigins = allowedEndpoints.map((endpoint) =>\n endpoint === \"*\" ? \"*\" : new URL(endpoint).host\n );\n }\n\n public processValidateRequest(req: IncomingMessage, res: ServerResponse): boolean {\n if (req.headers[\"webhook-request-origin\"]) {\n res.setHeader(\"WebHook-Allowed-Origin\", this._allowedOrigins);\n res.end();\n return true;\n } else {\n return false;\n }\n }\n\n public async processRequest(\n request: IncomingMessage,\n response: ServerResponse\n ): Promise<boolean> {\n // check if it is a valid WebPubSub cloud events\n const origin = this.getSingleHeader(request, \"webhook-request-origin\");\n if (origin === undefined) {\n return false;\n }\n\n const eventType = this.tryGetWebPubSubEvent(request);\n if (eventType === undefined) {\n return false;\n }\n\n // check if hub matches\n if (request.headers[\"ce-hub\"] !== this.hub) {\n return false;\n }\n\n // No need to read body if handler is not specified\n switch (eventType) {\n case EventType.Connect:\n if (!this.eventHandler?.handleConnect) {\n response.statusCode = 401;\n response.end(\"Connect event handler is not configured.\");\n return true;\n }\n break;\n case EventType.Connected:\n if (!this.eventHandler?.onConnected) {\n response.end();\n return true;\n }\n break;\n case EventType.Disconnected:\n if (!this.eventHandler?.onDisconnected) {\n response.end();\n return true;\n }\n break;\n case EventType.UserEvent:\n if (!this.eventHandler?.handleUserEvent) {\n response.end();\n return true;\n }\n break;\n default:\n console.warn(`Unknown EventType ${eventType}`);\n return false;\n }\n\n const eventRequest = await this.convertHttpToEvent(request);\n const receivedEvent = HTTP.toEvent(eventRequest);\n\n if (this._dumpRequest) {\n console.log(receivedEvent);\n }\n\n switch (eventType) {\n case EventType.Connect: {\n const connectRequest = receivedEvent.data as ConnectRequest;\n connectRequest.context = this.GetContext(receivedEvent, origin);\n this.eventHandler.handleConnect!(connectRequest, {\n success(res?: ConnectResponse): void {\n response.statusCode = 200;\n if (res === undefined) {\n response.end();\n } else {\n response.setHeader(\"Content-Type\", \"application/json; charset=utf-8\");\n response.end(JSON.stringify(res));\n }\n },\n fail(code: 400 | 401 | 500, detail?: string): void {\n response.statusCode = code;\n response.end(detail ?? \"\");\n }\n });\n return true;\n }\n case EventType.Connected: {\n // for unblocking events, we responds to the service as early as possible\n response.end();\n const connectedRequest = receivedEvent.data as ConnectedRequest;\n connectedRequest.context = this.GetContext(receivedEvent, origin);\n this.eventHandler.onConnected!(connectedRequest);\n return true;\n }\n case EventType.Disconnected: {\n // for unblocking events, we responds to the service as early as possible\n response.end();\n const disconnectedRequest = receivedEvent.data as DisconnectedRequest;\n disconnectedRequest.context = this.GetContext(receivedEvent, origin);\n this.eventHandler.onDisconnected!(disconnectedRequest);\n return true;\n }\n case EventType.UserEvent: {\n let userRequest: UserEventRequest;\n if (receivedEvent.data_base64 !== undefined) {\n userRequest = {\n context: this.GetContext(receivedEvent, origin),\n data: Buffer.from(receivedEvent.data_base64, \"base64\"),\n dataType: \"binary\"\n };\n } else if (receivedEvent.data !== undefined) {\n userRequest = {\n context: this.GetContext(receivedEvent, origin),\n data: receivedEvent.data as string,\n dataType: receivedEvent.datacontenttype?.startsWith(\"application/json;\")\n ? \"json\"\n : \"text\"\n };\n } else {\n throw new Error(\"Unexpected data.\");\n }\n\n this.eventHandler.handleUserEvent!(userRequest, {\n success(data?: string | ArrayBuffer, dataType?: \"binary\" | \"text\" | \"json\"): void {\n response.statusCode = 200;\n switch (dataType) {\n case \"json\":\n response.setHeader(\"Content-Type\", \"application/json; charset=utf-8\");\n break;\n case \"text\":\n response.setHeader(\"Content-Type\", \"text/plain; charset=utf-8\");\n break;\n default:\n response.setHeader(\"Content-Type\", \"application/octet-stream\");\n break;\n }\n response.end(data ?? \"\");\n },\n fail(code: 400 | 401 | 500, detail?: string): void {\n response.statusCode = code;\n response.end(detail ?? \"\");\n }\n });\n return true;\n }\n default:\n console.warn(`Unknown EventType ${eventType}`);\n return false;\n }\n }\n\n private tryGetWebPubSubEvent(req: IncomingMessage): EventType | undefined {\n // check ce-type to see if it is a valid WebPubSub CloudEvent request\n const prefix = \"azure.webpubsub.\";\n const connect = \"azure.webpubsub.sys.connect\";\n const connected = \"azure.webpubsub.sys.connected\";\n const disconnectd = \"azure.webpubsub.sys.disconnected\";\n const userPrefix = \"azure.webpubsub.user.\";\n const type = this.getSingleHeader(req, \"ce-type\");\n if (!type?.startsWith(prefix)) {\n return undefined;\n }\n if (type.startsWith(userPrefix)) {\n return EventType.UserEvent;\n }\n switch (type) {\n case connect:\n return EventType.Connect;\n case connected:\n return EventType.Connected;\n case disconnectd:\n return EventType.Disconnected;\n default:\n return undefined;\n }\n }\n\n private getSingleHeader(req: IncomingMessage, key: string): string | undefined {\n const value = req.headers[key];\n if (value === undefined) {\n return undefined;\n }\n\n if (typeof value === \"string\") {\n return value;\n }\n\n return value[0];\n }\n\n private GetContext(ce: CloudEvent, origin: string): ConnectionContext {\n const context = {\n signature: ce[\"signature\"] as string,\n userId: ce[\"userid\"] as string,\n hub: ce[\"hub\"] as string,\n connectionId: ce[\"connectionid\"] as string,\n eventName: ce[\"eventname\"] as string,\n origin: origin\n };\n\n // TODO: validation\n return context;\n }\n\n private async convertHttpToEvent(request: IncomingMessage): Promise<Message> {\n const normalized: Message = {\n headers: {},\n body: \"\"\n };\n if (request.headers) {\n for (const key in request.headers) {\n if (Object.prototype.hasOwnProperty.call(request.headers, key)) {\n const element = request.headers[key];\n if (element === undefined) {\n continue;\n }\n if (typeof element === \"string\") {\n normalized.headers[key] = element;\n } else {\n normalized.headers[key] = element.join(\",\");\n }\n }\n }\n }\n\n normalized.body = await this.readRequestBody(request);\n return normalized;\n }\n\n private readRequestBody(req: IncomingMessage): Promise<string> {\n return new Promise(function(resolve, reject) {\n const chunks: any = [];\n req.on(\"data\", function(chunk) {\n chunks.push(chunk);\n });\n req.on(\"end\", function() {\n const buffer = Buffer.concat(chunks);\n resolve(buffer.toString());\n });\n // reject on request error\n req.on(\"error\", function(err) {\n // This is not a \"Second reject\", just a different sort of failure\n reject(err);\n });\n });\n }\n}\n\nenum EventType {\n Connect,\n Connected,\n Disconnected,\n UserEvent\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cloudEventsDispatcher.js","sourceRoot":"","sources":["../../src/cloudEventsDispatcher.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { HTTP, CloudEvent } from \"cloudevents\";\nimport { IncomingMessage, ServerResponse } from \"http\";\nimport { URL } from \"url\";\nimport { logger } from \"./logger\";\nimport * as utils from \"./utils\";\n\nimport {\n ConnectRequest,\n ConnectResponse,\n UserEventRequest,\n DisconnectedRequest,\n ConnectedRequest,\n ConnectionContext,\n ConnectResponseHandler,\n UserEventResponseHandler,\n WebPubSubEventHandlerOptions\n} from \"./cloudEventsProtocols\";\n\nenum EventType {\n Connect,\n Connected,\n Disconnected,\n UserEvent\n}\n\nfunction getConnectResponseHandler(\n connectRequest: ConnectRequest,\n response: ServerResponse\n): ConnectResponseHandler {\n const states: Record<string, any> = connectRequest.context.states;\n let modified = false;\n const handler = {\n setState(name: string, value: unknown): void {\n states[name] = value;\n modified = true;\n },\n success(res?: ConnectResponse): void {\n response.statusCode = 200;\n if (modified) {\n response.setHeader(\"ce-connectionState\", utils.toBase64JsonString(states));\n }\n if (res === undefined) {\n response.end();\n } else {\n response.setHeader(\"Content-Type\", \"application/json; charset=utf-8\");\n response.end(JSON.stringify(res));\n }\n },\n fail(code: 400 | 401 | 500, detail?: string): void {\n response.statusCode = code;\n response.end(detail ?? \"\");\n }\n };\n\n return handler;\n}\n\nfunction getUserEventResponseHandler(\n userRequest: UserEventRequest,\n response: ServerResponse\n): UserEventResponseHandler {\n const states: Record<string, any> = userRequest.context.states;\n let modified = false;\n const handler = {\n setState(name: string, value: unknown): void {\n modified = true;\n states[name] = value;\n },\n success(data?: string | ArrayBuffer, dataType?: \"binary\" | \"text\" | \"json\"): void {\n response.statusCode = 200;\n if (modified) {\n response.setHeader(\"ce-connectionState\", utils.toBase64JsonString(states));\n }\n\n switch (dataType) {\n case \"json\":\n response.setHeader(\"Content-Type\", \"application/json; charset=utf-8\");\n break;\n case \"text\":\n response.setHeader(\"Content-Type\", \"text/plain; charset=utf-8\");\n break;\n default:\n response.setHeader(\"Content-Type\", \"application/octet-stream\");\n break;\n }\n response.end(data ?? \"\");\n },\n fail(code: 400 | 401 | 500, detail?: string): void {\n response.statusCode = code;\n response.end(detail ?? \"\");\n }\n };\n return handler;\n}\n\nfunction getContext(ce: CloudEvent, origin: string): ConnectionContext {\n const context = {\n signature: ce[\"signature\"] as string,\n userId: ce[\"userid\"] as string,\n hub: ce[\"hub\"] as string,\n connectionId: ce[\"connectionid\"] as string,\n eventName: ce[\"eventname\"] as string,\n origin: origin,\n states: utils.fromBase64JsonString(ce[\"connectionstate\"] as string)\n };\n\n // TODO: validation\n return context;\n}\n\nfunction tryGetWebPubSubEvent(req: IncomingMessage): EventType | undefined {\n // check ce-type to see if it is a valid WebPubSub CloudEvent request\n const prefix = \"azure.webpubsub.\";\n const connect = \"azure.webpubsub.sys.connect\";\n const connected = \"azure.webpubsub.sys.connected\";\n const disconnectd = \"azure.webpubsub.sys.disconnected\";\n const userPrefix = \"azure.webpubsub.user.\";\n const type = utils.getHttpHeader(req, \"ce-type\");\n if (!type?.startsWith(prefix)) {\n return undefined;\n }\n if (type.startsWith(userPrefix)) {\n return EventType.UserEvent;\n }\n switch (type) {\n case connect:\n return EventType.Connect;\n case connected:\n return EventType.Connected;\n case disconnectd:\n return EventType.Disconnected;\n default:\n return undefined;\n }\n}\n\nfunction isWebPubSubRequest(req: IncomingMessage): boolean {\n return utils.getHttpHeader(req, \"ce-awpsversion\") !== undefined;\n}\n\n/**\n * @internal\n */\nexport class CloudEventsDispatcher {\n private readonly _allowAll: boolean = true;\n private readonly _allowedOrigins: Array<string> = [];\n constructor(private hub: string, private eventHandler?: WebPubSubEventHandlerOptions) {\n if (Array.isArray(eventHandler)) {\n throw new Error(\"Unexpected WebPubSubEventHandlerOptions\");\n }\n if (eventHandler?.allowedEndpoints !== undefined) {\n this._allowedOrigins = eventHandler.allowedEndpoints.map((endpoint) =>\n new URL(endpoint).host.toLowerCase()\n );\n this._allowAll = false;\n }\n }\n\n public handlePreflight(req: IncomingMessage, res: ServerResponse): boolean {\n if (!isWebPubSubRequest(req)) {\n return false;\n }\n const origin = utils.getHttpHeader(req, \"webhook-request-origin\")?.toLowerCase();\n\n if (origin === undefined) {\n logger.warning(\"Expecting webhook-request-origin header.\");\n res.statusCode = 400;\n } else if (this._allowAll || this._allowedOrigins.indexOf(origin!) > -1) {\n res.setHeader(\"WebHook-Allowed-Origin\", origin!);\n } else {\n logger.warning(\"Origin does not match the allowed origins: \" + this._allowedOrigins);\n res.statusCode = 400;\n }\n\n res.end();\n return true;\n }\n\n public async handleRequest(request: IncomingMessage, response: ServerResponse): Promise<boolean> {\n if (!isWebPubSubRequest(request)) {\n return false;\n }\n\n // check if it is a valid WebPubSub cloud events\n const origin = utils.getHttpHeader(request, \"webhook-request-origin\");\n if (origin === undefined) {\n return false;\n }\n\n const eventType = tryGetWebPubSubEvent(request);\n if (eventType === undefined) {\n return false;\n }\n\n // check if hub matches\n const hub = utils.getHttpHeader(request, \"ce-hub\");\n if (hub !== this.hub) {\n return false;\n }\n\n // No need to read body if handler is not specified\n switch (eventType) {\n case EventType.Connect:\n if (!this.eventHandler?.handleConnect) {\n response.end();\n return true;\n }\n break;\n case EventType.Connected:\n if (!this.eventHandler?.onConnected) {\n response.end();\n return true;\n }\n break;\n case EventType.Disconnected:\n if (!this.eventHandler?.onDisconnected) {\n response.end();\n return true;\n }\n break;\n case EventType.UserEvent:\n if (!this.eventHandler?.handleUserEvent) {\n response.end();\n return true;\n }\n break;\n default:\n logger.warning(`Unknown EventType ${eventType}`);\n return false;\n }\n\n const eventRequest = await utils.convertHttpToEvent(request);\n const receivedEvent = HTTP.toEvent(eventRequest);\n\n logger.verbose(receivedEvent);\n\n switch (eventType) {\n case EventType.Connect: {\n const connectRequest = receivedEvent.data as ConnectRequest;\n connectRequest.context = getContext(receivedEvent, origin);\n this.eventHandler.handleConnect!(\n connectRequest,\n getConnectResponseHandler(connectRequest, response)\n );\n return true;\n }\n case EventType.Connected: {\n // for unblocking events, we responds to the service as early as possible\n response.end();\n const connectedRequest = receivedEvent.data as ConnectedRequest;\n connectedRequest.context = getContext(receivedEvent, origin);\n this.eventHandler.onConnected!(connectedRequest);\n return true;\n }\n case EventType.Disconnected: {\n // for unblocking events, we responds to the service as early as possible\n response.end();\n const disconnectedRequest = receivedEvent.data as DisconnectedRequest;\n disconnectedRequest.context = getContext(receivedEvent, origin);\n this.eventHandler.onDisconnected!(disconnectedRequest);\n return true;\n }\n case EventType.UserEvent: {\n let userRequest: UserEventRequest;\n if (receivedEvent.data_base64 !== undefined) {\n userRequest = {\n context: getContext(receivedEvent, origin),\n data: Buffer.from(receivedEvent.data_base64, \"base64\"),\n dataType: \"binary\"\n };\n } else if (receivedEvent.data !== undefined) {\n userRequest = {\n context: getContext(receivedEvent, origin),\n data: receivedEvent.data as string,\n dataType: receivedEvent.datacontenttype?.startsWith(\"application/json;\")\n ? \"json\"\n : \"text\"\n };\n } else {\n throw new Error(\"Unexpected data.\");\n }\n\n this.eventHandler.handleUserEvent!(\n userRequest,\n getUserEventResponseHandler(userRequest, response)\n );\n return true;\n }\n default:\n logger.warning(`Unknown EventType ${eventType}`);\n return false;\n }\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloudEventsProtocols.js","sourceRoot":"","sources":["../../src/cloudEventsProtocols.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\n/**\n * Response of the connect event.\n */\nexport interface ConnectResponse {\n /**\n * Set the groups the connection would like to join.\n */\n groups?: string[];\n /**\n * Set the roles the connection belongs to.\n */\n roles?: string[];\n /**\n * Set the userId for the connection.\n */\n userId?: string;\n /**\n * Set the subprotocol for the connection to complete WebSocket handshake.\n */\n subprotocol?: string;\n}\n\n/**\n * The connection context representing the client WebSocket connection.\n */\nexport interface ConnectionContext {\n /**\n * The hub the connection belongs to.\n */\n hub: string;\n /**\n * The Id of the connection.\n */\n connectionId: string;\n /**\n * The event name of this CloudEvents request.\n */\n eventName: string;\n /**\n * The origin this CloudEvents request comes from.\n */\n origin: string;\n /**\n * The user id of the connection.\n */\n userId?: string;\n /**\n * The subprotocol of this connection.\n */\n subprotocol?: string;\n}\n\n/**\n * Request for the connect event.\n */\nexport interface ConnectRequest {\n /**\n * The context of current CloudEvents request.\n */\n context: ConnectionContext;\n /**\n * The claims that the client WebSocket connection has when it connects.\n */\n claims?: Record<string, string[]>;\n /**\n * The queries that the client WebSocket connection has when it connects.\n */\n queries?: Record<string, string[]>;\n /**\n * The subprotocols that the client WebSocket connection uses to do handshake.\n */\n subprotocols?: string[];\n /**\n * The client certificate info that the client WebSocket connection uses to connect.\n */\n clientCertificates?: Certificate[];\n}\n\n/**\n * The client certificate.\n */\nexport interface Certificate {\n /**\n * The thumbprint of the certificate.\n */\n thumbprint: string;\n}\n\n/**\n * Request for the connected event.\n */\nexport interface ConnectedRequest {\n /**\n * The context of current CloudEvents request.\n */\n context: ConnectionContext;\n}\n\n/**\n * Request for the user event.\n */\nexport type UserEventRequest =\n | {\n /**\n * The context of current CloudEvents request.\n */\n context: ConnectionContext;\n\n /**\n * The content data.\n */\n data: string;\n /**\n * The type of the data.\n */\n dataType: \"text\" | \"json\";\n }\n | {\n /**\n * The context of current CloudEvents request.\n */\n context: ConnectionContext;\n\n /**\n * The content data.\n */\n data: ArrayBuffer;\n /**\n * The type of the data.\n */\n dataType: \"binary\";\n };\n\n/**\n * Request for the disconnected event.\n */\nexport interface DisconnectedRequest {\n /**\n * The context of current CloudEvents request.\n */\n context: ConnectionContext;\n /**\n * The reason that the connection disconnects.\n */\n reason?: string;\n}\n\n/**\n * The handler to set connect event response\n */\nexport interface ConnectResponseHandler {\n /**\n * Return success response to the service.\n * @param response The response for the connect event.\n */\n success(response?: ConnectResponse): void;\n /**\n * Return failed response and the service will reject the client WebSocket connection.\n * @param code Code can be 400 user error, 401 unauthorized and 500 server error.\n * @param detail The detail of the error.\n */\n fail(code: 400 | 401 | 500, detail?: string): void;\n}\n\n/**\n * The handler to set user event response\n */\nexport interface UserEventResponseHandler {\n /**\n * Return success response with data to be delivered to the client WebSocket connection.\n * @param data The payload data to be returned to the client.\n * @param dataType The type of the payload data.\n */\n success(data?: string | ArrayBuffer, dataType?: \"binary\" | \"text\" | \"json\"): void;\n /**\n * Return failed response and the service will close the client WebSocket connection.\n * @param code Code can be 400 user error, 401 unauthorized and 500 server error.\n * @param detail The detail of the error.\n */\n fail(code: 400 | 401 | 500, detail?: string): void;\n}\n\n/**\n * The options for the CloudEvents handler.\n */\nexport interface WebPubSubEventHandlerOptions {\n /**\n * Custom serving path for the path of the CloudEvents handler.\n */\n path?: string;\n\n /**\n *
|
|
1
|
+
{"version":3,"file":"cloudEventsProtocols.js","sourceRoot":"","sources":["../../src/cloudEventsProtocols.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\n/**\n * Response of the connect event.\n */\nexport interface ConnectResponse {\n /**\n * Set the groups the connection would like to join.\n */\n groups?: string[];\n /**\n * Set the roles the connection belongs to.\n */\n roles?: string[];\n /**\n * Set the userId for the connection.\n */\n userId?: string;\n /**\n * Set the subprotocol for the connection to complete WebSocket handshake.\n */\n subprotocol?: string;\n}\n\n/**\n * The connection context representing the client WebSocket connection.\n */\nexport interface ConnectionContext {\n /**\n * The hub the connection belongs to.\n */\n hub: string;\n /**\n * The Id of the connection.\n */\n connectionId: string;\n /**\n * The event name of this CloudEvents request.\n */\n eventName: string;\n /**\n * The origin this CloudEvents request comes from.\n */\n origin: string;\n /**\n * The user id of the connection.\n */\n userId?: string;\n /**\n * The subprotocol of this connection.\n */\n subprotocol?: string;\n /**\n * Get the additional states for the connection, such states are perserved throughout the lifetime of the connection.\n */\n states: Record<string, any>;\n}\n\n/**\n * Request for the connect event.\n */\nexport interface ConnectRequest {\n /**\n * The context of current CloudEvents request.\n */\n context: ConnectionContext;\n /**\n * The claims that the client WebSocket connection has when it connects.\n */\n claims?: Record<string, string[]>;\n /**\n * The queries that the client WebSocket connection has when it connects.\n */\n queries?: Record<string, string[]>;\n /**\n * The headers that the client WebSocket connection has when it connects.\n */\n headers?: Record<string, string[]>;\n /**\n * The subprotocols that the client WebSocket connection uses to do handshake.\n */\n subprotocols?: string[];\n /**\n * The client certificate info that the client WebSocket connection uses to connect.\n */\n clientCertificates?: Certificate[];\n}\n\n/**\n * The client certificate.\n */\nexport interface Certificate {\n /**\n * The thumbprint of the certificate.\n */\n thumbprint: string;\n}\n\n/**\n * Request for the connected event.\n */\nexport interface ConnectedRequest {\n /**\n * The context of current CloudEvents request.\n */\n context: ConnectionContext;\n}\n\n/**\n * Request for the user event.\n */\nexport type UserEventRequest =\n | {\n /**\n * The context of current CloudEvents request.\n */\n context: ConnectionContext;\n\n /**\n * The content data.\n */\n data: string;\n /**\n * The type of the data.\n */\n dataType: \"text\" | \"json\";\n }\n | {\n /**\n * The context of current CloudEvents request.\n */\n context: ConnectionContext;\n\n /**\n * The content data.\n */\n data: ArrayBuffer;\n /**\n * The type of the data.\n */\n dataType: \"binary\";\n };\n\n/**\n * Request for the disconnected event.\n */\nexport interface DisconnectedRequest {\n /**\n * The context of current CloudEvents request.\n */\n context: ConnectionContext;\n /**\n * The reason that the connection disconnects.\n */\n reason?: string;\n}\n\n/**\n * The handler to set connect event response\n */\nexport interface ConnectResponseHandler {\n /**\n * Set the state of the connection\n * @param name - The name of the state\n * @param value - The value of the state\n */\n setState(name: string, value: unknown): void;\n /**\n * Return success response to the service.\n * @param response - The response for the connect event.\n */\n success(response?: ConnectResponse): void;\n /**\n * Return failed response and the service will reject the client WebSocket connection.\n * @param code - Code can be 400 user error, 401 unauthorized and 500 server error.\n * @param detail - The detail of the error.\n */\n fail(code: 400 | 401 | 500, detail?: string): void;\n}\n\n/**\n * The handler to set user event response\n */\nexport interface UserEventResponseHandler {\n /**\n * Set the state of the connection\n * @param name - The name of the state\n * @param value - The value of the state\n */\n setState(name: string, value: unknown): void;\n /**\n * Return success response with data to be delivered to the client WebSocket connection.\n * @param data - The payload data to be returned to the client.\n * @param dataType - The type of the payload data.\n */\n success(data?: string | ArrayBuffer, dataType?: \"binary\" | \"text\" | \"json\"): void;\n /**\n * Return failed response and the service will close the client WebSocket connection.\n * @param code - Code can be 400 user error, 401 unauthorized and 500 server error.\n * @param detail - The detail of the error.\n */\n fail(code: 400 | 401 | 500, detail?: string): void;\n}\n\n/**\n * The options for the CloudEvents handler.\n */\nexport interface WebPubSubEventHandlerOptions {\n /**\n * Custom serving path for the path of the CloudEvents handler.\n */\n path?: string;\n\n /**\n * Handle 'connect' event, the service waits for the response to proceed.\n */\n handleConnect?: (connectRequest: ConnectRequest, connectResponse: ConnectResponseHandler) => void;\n\n /**\n * Handle user events, the service waits for the response to proceed.\n */\n handleUserEvent?: (\n userEventRequest: UserEventRequest,\n userEventResponse: UserEventResponseHandler\n ) => void;\n\n /**\n * Event trigger for \"connected\" unblocking event. This is an unblocking event and the service does not wait for the response.\n */\n onConnected?: (connectedRequest: ConnectedRequest) => void;\n\n /**\n *\n * Event triggers for \"disconnected\" unblocking event. This is an unblocking event and the service does not wait for the response.\n */\n onDisconnected?: (disconnectedRequest: DisconnectedRequest) => void;\n\n /**\n * If not specified, by default allow all the endpoints, otherwise only allow specified endpoints\n */\n allowedEndpoints?: string[];\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT license.
|
|
3
|
+
import { createClientLogger } from "@azure/logger";
|
|
4
|
+
/**
|
|
5
|
+
* The \@azure/logger configuration for this package.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export const logger = createClientLogger("web-pubsub-express");
|
|
10
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/logger.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAElC,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEnD;;;;GAIG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,kBAAkB,CAAC,oBAAoB,CAAC,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { createClientLogger } from \"@azure/logger\";\n\n/**\n * The \\@azure/logger configuration for this package.\n *\n * @internal\n */\nexport const logger = createClientLogger(\"web-pubsub-express\");\n"]}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT license.
|
|
3
|
+
function isJsonObject(obj) {
|
|
4
|
+
return obj && typeof obj === "object" && !Array.isArray(obj);
|
|
5
|
+
}
|
|
6
|
+
export function toBase64JsonString(obj) {
|
|
7
|
+
return Buffer.from(JSON.stringify(obj)).toString("base64");
|
|
8
|
+
}
|
|
9
|
+
export function fromBase64JsonString(base64String) {
|
|
10
|
+
if (base64String === undefined) {
|
|
11
|
+
return {};
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
const buf = Buffer.from(base64String, "base64").toString();
|
|
15
|
+
const parsed = JSON.parse(buf);
|
|
16
|
+
return isJsonObject(parsed) ? parsed : {};
|
|
17
|
+
}
|
|
18
|
+
catch (e) {
|
|
19
|
+
console.warn("Unexpected state format:" + e);
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function getHttpHeader(req, key) {
|
|
24
|
+
const value = req.headers[key];
|
|
25
|
+
if (value === undefined) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
if (typeof value === "string") {
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
return value[0];
|
|
32
|
+
}
|
|
33
|
+
export async function convertHttpToEvent(request) {
|
|
34
|
+
const normalized = {
|
|
35
|
+
headers: {},
|
|
36
|
+
body: ""
|
|
37
|
+
};
|
|
38
|
+
if (request.headers) {
|
|
39
|
+
for (const key in request.headers) {
|
|
40
|
+
if (Object.prototype.hasOwnProperty.call(request.headers, key)) {
|
|
41
|
+
const element = request.headers[key];
|
|
42
|
+
if (element !== undefined) {
|
|
43
|
+
normalized.headers[key.toLowerCase()] = element;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
normalized.body = await readRequestBody(request);
|
|
49
|
+
return normalized;
|
|
50
|
+
}
|
|
51
|
+
export function readRequestBody(req) {
|
|
52
|
+
return new Promise(function (resolve, reject) {
|
|
53
|
+
const chunks = [];
|
|
54
|
+
req.on("data", function (chunk) {
|
|
55
|
+
chunks.push(chunk);
|
|
56
|
+
});
|
|
57
|
+
req.on("end", function () {
|
|
58
|
+
const buffer = Buffer.concat(chunks);
|
|
59
|
+
resolve(buffer.toString());
|
|
60
|
+
});
|
|
61
|
+
// reject on request error
|
|
62
|
+
req.on("error", function (err) {
|
|
63
|
+
// This is not a "Second reject", just a different sort of failure
|
|
64
|
+
reject(err);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { IncomingMessage } from \"http\";\nimport { Message } from \"cloudevents\";\n\nfunction isJsonObject(obj: any): boolean {\n return obj && typeof obj === \"object\" && !Array.isArray(obj);\n}\n\nexport function toBase64JsonString(obj: Record<string, any>): string {\n return Buffer.from(JSON.stringify(obj)).toString(\"base64\");\n}\n\nexport function fromBase64JsonString(base64String: string): Record<string, any> {\n if (base64String === undefined) {\n return {};\n }\n\n try {\n const buf = Buffer.from(base64String, \"base64\").toString();\n const parsed = JSON.parse(buf);\n return isJsonObject(parsed) ? parsed : {};\n } catch (e) {\n console.warn(\"Unexpected state format:\" + e);\n return {};\n }\n}\n\nexport function getHttpHeader(req: IncomingMessage, key: string): string | undefined {\n const value = req.headers[key];\n if (value === undefined) {\n return undefined;\n }\n\n if (typeof value === \"string\") {\n return value;\n }\n\n return value[0];\n}\n\nexport async function convertHttpToEvent(request: IncomingMessage): Promise<Message> {\n const normalized: Message = {\n headers: {},\n body: \"\"\n };\n if (request.headers) {\n for (const key in request.headers) {\n if (Object.prototype.hasOwnProperty.call(request.headers, key)) {\n const element = request.headers[key];\n if (element !== undefined) {\n normalized.headers[key.toLowerCase()] = element;\n }\n }\n }\n }\n\n normalized.body = await readRequestBody(request);\n return normalized;\n}\n\nexport function readRequestBody(req: IncomingMessage): Promise<string> {\n return new Promise(function(resolve, reject) {\n const chunks: any = [];\n req.on(\"data\", function(chunk) {\n chunks.push(chunk);\n });\n req.on(\"end\", function() {\n const buffer = Buffer.concat(chunks);\n resolve(buffer.toString());\n });\n // reject on request error\n req.on(\"error\", function(err) {\n // This is not a \"Second reject\", just a different sort of failure\n reject(err);\n });\n });\n}\n"]}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// Copyright (c) Microsoft Corporation.
|
|
2
2
|
// Licensed under the MIT license.
|
|
3
|
-
import { __awaiter } from "tslib";
|
|
4
3
|
import { CloudEventsDispatcher } from "./cloudEventsDispatcher";
|
|
5
4
|
/**
|
|
6
5
|
* The handler to handle incoming CloudEvents messages
|
|
@@ -14,7 +13,7 @@ export class WebPubSubEventHandler {
|
|
|
14
13
|
* import express from "express";
|
|
15
14
|
* import { WebPubSubEventHandler } from "@azure/web-pubsub-express";
|
|
16
15
|
* const endpoint = "https://xxxx.webpubsubdev.azure.com"
|
|
17
|
-
* const handler = new WebPubSubEventHandler('chat',
|
|
16
|
+
* const handler = new WebPubSubEventHandler('chat', {
|
|
18
17
|
* handleConnect: (req, res) => {
|
|
19
18
|
* console.log(JSON.stringify(req));
|
|
20
19
|
* return {};
|
|
@@ -26,39 +25,39 @@ export class WebPubSubEventHandler {
|
|
|
26
25
|
* console.log(JSON.stringify(req));
|
|
27
26
|
* res.success("Hey " + req.data, req.dataType);
|
|
28
27
|
* };
|
|
28
|
+
* allowedEndpoints: [ endpoint ]
|
|
29
29
|
* },
|
|
30
30
|
* });
|
|
31
31
|
* ```
|
|
32
32
|
*
|
|
33
|
-
* @param hub The name of the hub to listen to
|
|
34
|
-
* @param
|
|
35
|
-
* @param options Options to configure the event handler
|
|
33
|
+
* @param hub - The name of the hub to listen to
|
|
34
|
+
* @param options - Options to configure the event handler
|
|
36
35
|
*/
|
|
37
|
-
constructor(hub,
|
|
36
|
+
constructor(hub, options) {
|
|
38
37
|
var _a;
|
|
39
38
|
this.hub = hub;
|
|
40
39
|
const path = ((_a = options === null || options === void 0 ? void 0 : options.path) !== null && _a !== void 0 ? _a : `/api/webpubsub/hubs/${hub}/`).toLowerCase();
|
|
41
40
|
this.path = path.endsWith("/") ? path : path + "/";
|
|
42
|
-
this._cloudEventsHandler = new CloudEventsDispatcher(this.hub,
|
|
41
|
+
this._cloudEventsHandler = new CloudEventsDispatcher(this.hub, options);
|
|
43
42
|
}
|
|
44
43
|
/**
|
|
45
44
|
* Get the middleware to process the CloudEvents requests
|
|
46
45
|
*/
|
|
47
46
|
getMiddleware() {
|
|
48
|
-
return (req, res, next) =>
|
|
47
|
+
return async (req, res, next) => {
|
|
49
48
|
// Request originalUrl can contain query while baseUrl + path not
|
|
50
49
|
let requestUrl = (req.baseUrl + req.path).toLowerCase();
|
|
51
50
|
// normalize the Url
|
|
52
51
|
requestUrl = requestUrl.endsWith("/") ? requestUrl : requestUrl + "/";
|
|
53
|
-
if (requestUrl
|
|
52
|
+
if (requestUrl.startsWith(this.path)) {
|
|
54
53
|
if (req.method === "OPTIONS") {
|
|
55
|
-
if (this._cloudEventsHandler.
|
|
54
|
+
if (this._cloudEventsHandler.handlePreflight(req, res)) {
|
|
56
55
|
return;
|
|
57
56
|
}
|
|
58
57
|
}
|
|
59
58
|
else if (req.method === "POST") {
|
|
60
59
|
try {
|
|
61
|
-
if (
|
|
60
|
+
if (await this._cloudEventsHandler.handleRequest(req, res)) {
|
|
62
61
|
return;
|
|
63
62
|
}
|
|
64
63
|
}
|
|
@@ -69,7 +68,7 @@ export class WebPubSubEventHandler {
|
|
|
69
68
|
}
|
|
70
69
|
}
|
|
71
70
|
next();
|
|
72
|
-
}
|
|
71
|
+
};
|
|
73
72
|
}
|
|
74
73
|
}
|
|
75
74
|
//# sourceMappingURL=webPubSubEventHandler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webPubSubEventHandler.js","sourceRoot":"","sources":["../../src/webPubSubEventHandler.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC
|
|
1
|
+
{"version":3,"file":"webPubSubEventHandler.js","sourceRoot":"","sources":["../../src/webPubSubEventHandler.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAIlC,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAGhE;;GAEG;AACH,MAAM,OAAO,qBAAqB;IAQhC;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,YAAoB,GAAW,EAAE,OAAsC;;QAAnD,QAAG,GAAH,GAAG,CAAQ;QAC7B,MAAM,IAAI,GAAG,CAAC,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,IAAI,mCAAI,uBAAuB,GAAG,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;QAC5E,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,GAAG,CAAC;QACnD,IAAI,CAAC,mBAAmB,GAAG,IAAI,qBAAqB,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC1E,CAAC;IAED;;OAEG;IACI,aAAa;QAClB,OAAO,KAAK,EACV,GAAoB,EACpB,GAAqB,EACrB,IAA0B,EACX,EAAE;YACjB,iEAAiE;YACjE,IAAI,UAAU,GAAG,CAAC,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;YAExD,oBAAoB;YACpB,UAAU,GAAG,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,GAAG,GAAG,CAAC;YACtE,IAAI,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;gBACpC,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE;oBAC5B,IAAI,IAAI,CAAC,mBAAmB,CAAC,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE;wBACtD,OAAO;qBACR;iBACF;qBAAM,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE;oBAChC,IAAI;wBACF,IAAI,MAAM,IAAI,CAAC,mBAAmB,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE;4BAC1D,OAAO;yBACR;qBACF;oBAAC,OAAO,GAAG,EAAE;wBACZ,IAAI,CAAC,GAAG,CAAC,CAAC;wBACV,OAAO;qBACR;iBACF;aACF;YAED,IAAI,EAAE,CAAC;QACT,CAAC,CAAC;IACJ,CAAC;CACF","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport express from \"express-serve-static-core\";\n\nimport { CloudEventsDispatcher } from \"./cloudEventsDispatcher\";\nimport { WebPubSubEventHandlerOptions } from \"./cloudEventsProtocols\";\n\n/**\n * The handler to handle incoming CloudEvents messages\n */\nexport class WebPubSubEventHandler {\n /**\n * The path this CloudEvents handler listens to\n */\n public readonly path: string;\n\n private _cloudEventsHandler: CloudEventsDispatcher;\n\n /**\n * Creates an instance of a WebPubSubEventHandler for handling incoming CloudEvents messages.\n *\n * Example usage:\n * ```ts\n * import express from \"express\";\n * import { WebPubSubEventHandler } from \"@azure/web-pubsub-express\";\n * const endpoint = \"https://xxxx.webpubsubdev.azure.com\"\n * const handler = new WebPubSubEventHandler('chat', {\n * handleConnect: (req, res) => {\n * console.log(JSON.stringify(req));\n * return {};\n * },\n * onConnected: req => {\n * console.log(JSON.stringify(req));\n * },\n * handleUserEvent: (req, res) => {\n * console.log(JSON.stringify(req));\n * res.success(\"Hey \" + req.data, req.dataType);\n * };\n * allowedEndpoints: [ endpoint ]\n * },\n * });\n * ```\n *\n * @param hub - The name of the hub to listen to\n * @param options - Options to configure the event handler\n */\n constructor(private hub: string, options?: WebPubSubEventHandlerOptions) {\n const path = (options?.path ?? `/api/webpubsub/hubs/${hub}/`).toLowerCase();\n this.path = path.endsWith(\"/\") ? path : path + \"/\";\n this._cloudEventsHandler = new CloudEventsDispatcher(this.hub, options);\n }\n\n /**\n * Get the middleware to process the CloudEvents requests\n */\n public getMiddleware(): express.RequestHandler {\n return async (\n req: express.Request,\n res: express.Response,\n next: express.NextFunction\n ): Promise<void> => {\n // Request originalUrl can contain query while baseUrl + path not\n let requestUrl = (req.baseUrl + req.path).toLowerCase();\n\n // normalize the Url\n requestUrl = requestUrl.endsWith(\"/\") ? requestUrl : requestUrl + \"/\";\n if (requestUrl.startsWith(this.path)) {\n if (req.method === \"OPTIONS\") {\n if (this._cloudEventsHandler.handlePreflight(req, res)) {\n return;\n }\n } else if (req.method === \"POST\") {\n try {\n if (await this._cloudEventsHandler.handleRequest(req, res)) {\n return;\n }\n } catch (err) {\n next(err);\n return;\n }\n }\n }\n\n next();\n };\n }\n}\n"]}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { __awaiter } from "tslib";
|
|
2
1
|
// Copyright (c) Microsoft Corporation.
|
|
3
2
|
// Licensed under the MIT license.
|
|
4
3
|
/* eslint-disable no-invalid-this */
|
|
@@ -7,9 +6,11 @@ import { assert } from "chai";
|
|
|
7
6
|
import { IncomingMessage, ServerResponse } from "http";
|
|
8
7
|
import { Socket } from "net";
|
|
9
8
|
import * as sinon from "sinon";
|
|
10
|
-
|
|
9
|
+
import { toBase64JsonString } from "../src/utils";
|
|
10
|
+
function buildRequest(req, hub, connectionId, userId, states) {
|
|
11
11
|
req.headers["webhook-request-origin"] = "xxx.webpubsub.azure.com";
|
|
12
12
|
req.headers["Content-Type"] = "application/json; charset=utf-8";
|
|
13
|
+
req.headers["ce-awpsversion"] = "1.0";
|
|
13
14
|
req.headers["ce-specversion"] = "1.0";
|
|
14
15
|
req.headers["ce-type"] = "azure.webpubsub.sys.connect";
|
|
15
16
|
req.headers["ce-source"] = `/hubs/${hub}/client/${connectionId}`;
|
|
@@ -21,6 +22,7 @@ function buildRequest(req, hub, connectionId, userId) {
|
|
|
21
22
|
req.headers["ce-connectionId"] = connectionId;
|
|
22
23
|
req.headers["ce-hub"] = hub;
|
|
23
24
|
req.headers["ce-event"] = "connect";
|
|
25
|
+
req.headers["ce-connectionState"] = states;
|
|
24
26
|
}
|
|
25
27
|
function mockBody(req, body) {
|
|
26
28
|
req.emit("data", Buffer.from(body, "utf-8"));
|
|
@@ -33,97 +35,145 @@ describe("Can handle connect event", function () {
|
|
|
33
35
|
req = new IncomingMessage(new Socket());
|
|
34
36
|
res = new ServerResponse(req);
|
|
35
37
|
});
|
|
36
|
-
it("Should not handle the request if request is not cloud events", function () {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
it("Should not handle the request if request is not cloud events", async function () {
|
|
39
|
+
const endSpy = sinon.spy(res.end);
|
|
40
|
+
const dispatcher = new CloudEventsDispatcher("hub1");
|
|
41
|
+
const result = await dispatcher.handleRequest(req, res);
|
|
42
|
+
assert.isFalse(result);
|
|
43
|
+
assert.isTrue(endSpy.notCalled);
|
|
44
|
+
});
|
|
45
|
+
it("Should not handle the request if hub does not match", async function () {
|
|
46
|
+
const endSpy = sinon.spy(res, "end");
|
|
47
|
+
buildRequest(req, "hub", "conn1");
|
|
48
|
+
const dispatcher = new CloudEventsDispatcher("hub1");
|
|
49
|
+
const result = await dispatcher.handleRequest(req, res);
|
|
50
|
+
assert.isFalse(result);
|
|
51
|
+
assert.isTrue(endSpy.notCalled);
|
|
52
|
+
});
|
|
53
|
+
it("Should response with 200 when option is not specified", async function () {
|
|
54
|
+
const endSpy = sinon.spy(res, "end");
|
|
55
|
+
buildRequest(req, "hub", "conn1");
|
|
56
|
+
const dispatcher = new CloudEventsDispatcher("hub");
|
|
57
|
+
const result = await dispatcher.handleRequest(req, res);
|
|
58
|
+
assert.isTrue(result, "should handle");
|
|
59
|
+
assert.isTrue(endSpy.calledOnce, "should call once");
|
|
60
|
+
assert.equal(200, res.statusCode, "should be 200");
|
|
44
61
|
});
|
|
45
|
-
it("Should
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
62
|
+
it("Should response with 200 when handler is not specified", async function () {
|
|
63
|
+
const endSpy = sinon.spy(res, "end");
|
|
64
|
+
buildRequest(req, "hub", "conn1");
|
|
65
|
+
const dispatcher = new CloudEventsDispatcher("hub", {});
|
|
66
|
+
const result = await dispatcher.handleRequest(req, res);
|
|
67
|
+
assert.isTrue(result, "should handle");
|
|
68
|
+
assert.isTrue(endSpy.calledOnce, "should call once");
|
|
69
|
+
assert.equal(200, res.statusCode, "should be 200");
|
|
70
|
+
});
|
|
71
|
+
it("Should response with error when handler returns error", async function () {
|
|
72
|
+
const endSpy = sinon.spy(res, "end");
|
|
73
|
+
buildRequest(req, "hub", "conn1");
|
|
74
|
+
const dispatcher = new CloudEventsDispatcher("hub", {
|
|
75
|
+
handleConnect: async (_, response) => {
|
|
76
|
+
response.fail(400);
|
|
77
|
+
}
|
|
53
78
|
});
|
|
79
|
+
const process = dispatcher.handleRequest(req, res);
|
|
80
|
+
mockBody(req, JSON.stringify({}));
|
|
81
|
+
const result = await process;
|
|
82
|
+
assert.isTrue(result, "should handle");
|
|
83
|
+
assert.isTrue(endSpy.calledOnce, "should call once");
|
|
84
|
+
assert.equal(400, res.statusCode, "should be error");
|
|
54
85
|
});
|
|
55
|
-
it("Should response with
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
assert.isTrue(endSpy.calledOnce, "should call once");
|
|
63
|
-
assert.equal(401, res.statusCode, "should be 401");
|
|
86
|
+
it("Should response with success when handler returns success", async function () {
|
|
87
|
+
const endSpy = sinon.spy(res, "end");
|
|
88
|
+
buildRequest(req, "hub", "conn1");
|
|
89
|
+
const dispatcher = new CloudEventsDispatcher("hub", {
|
|
90
|
+
handleConnect: async (_, response) => {
|
|
91
|
+
response.success();
|
|
92
|
+
}
|
|
64
93
|
});
|
|
94
|
+
const process = dispatcher.handleRequest(req, res);
|
|
95
|
+
mockBody(req, JSON.stringify({}));
|
|
96
|
+
const result = await process;
|
|
97
|
+
assert.isTrue(result, "should handle");
|
|
98
|
+
assert.isTrue(endSpy.calledOnce, "should call once");
|
|
99
|
+
assert.equal(200, res.statusCode, "should be success");
|
|
65
100
|
});
|
|
66
|
-
it("Should response with
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
assert.isTrue(endSpy.calledOnce, "should call once");
|
|
74
|
-
assert.equal(401, res.statusCode, "should be 401");
|
|
101
|
+
it("Should response with success when handler returns success value", async function () {
|
|
102
|
+
const endSpy = sinon.spy(res, "end");
|
|
103
|
+
buildRequest(req, "hub", "conn1");
|
|
104
|
+
const dispatcher = new CloudEventsDispatcher("hub", {
|
|
105
|
+
handleConnect: async (_, response) => {
|
|
106
|
+
response.success({ userId: "vic" });
|
|
107
|
+
}
|
|
75
108
|
});
|
|
109
|
+
const process = dispatcher.handleRequest(req, res);
|
|
110
|
+
mockBody(req, JSON.stringify({}));
|
|
111
|
+
const result = await process;
|
|
112
|
+
assert.isTrue(result, "should handle");
|
|
113
|
+
assert.isTrue(endSpy.calledOnce, "should call once");
|
|
114
|
+
assert.equal(200, res.statusCode, "should be success");
|
|
76
115
|
});
|
|
77
|
-
it("Should
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
var result = yield process;
|
|
89
|
-
assert.isTrue(result, "should handle");
|
|
90
|
-
assert.isTrue(endSpy.calledOnce, "should call once");
|
|
91
|
-
assert.equal(400, res.statusCode, "should be error");
|
|
116
|
+
it("Should be able to set connection state", async function () {
|
|
117
|
+
const endSpy = sinon.spy(res, "end");
|
|
118
|
+
buildRequest(req, "hub", "conn1");
|
|
119
|
+
const dispatcher = new CloudEventsDispatcher("hub", {
|
|
120
|
+
handleConnect: async (_, response) => {
|
|
121
|
+
response.setState("key1", "val1");
|
|
122
|
+
response.setState("key2", "val2");
|
|
123
|
+
response.setState("key1", ["val3"]);
|
|
124
|
+
response.setState("key3", "");
|
|
125
|
+
response.success({ userId: "vic" });
|
|
126
|
+
}
|
|
92
127
|
});
|
|
128
|
+
const process = dispatcher.handleRequest(req, res);
|
|
129
|
+
mockBody(req, JSON.stringify({}));
|
|
130
|
+
const result = await process;
|
|
131
|
+
assert.isTrue(result, "should handle");
|
|
132
|
+
assert.isTrue(endSpy.calledOnce, "should call once");
|
|
133
|
+
assert.equal(toBase64JsonString({
|
|
134
|
+
key1: ["val3"],
|
|
135
|
+
key2: "val2",
|
|
136
|
+
key3: ""
|
|
137
|
+
}), res.getHeader("ce-connectionState"), "should contain multiple state headers");
|
|
93
138
|
});
|
|
94
|
-
it("Should
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
139
|
+
it("Should be able to get the connection states if it exists in the header", async function () {
|
|
140
|
+
const endSpy = sinon.spy(res, "end");
|
|
141
|
+
const states = toBase64JsonString({
|
|
142
|
+
key1: ["val3"],
|
|
143
|
+
key2: "val2",
|
|
144
|
+
key3: ""
|
|
145
|
+
});
|
|
146
|
+
buildRequest(req, "hub1", "conn1", undefined, states);
|
|
147
|
+
const dispatcher = new CloudEventsDispatcher("hub1", {
|
|
148
|
+
handleConnect: (request, response) => {
|
|
149
|
+
assert.equal("val3", request.context.states["key1"][0]);
|
|
150
|
+
assert.equal("val2", request.context.states["key2"]);
|
|
151
|
+
assert.equal("", request.context.states["key3"]);
|
|
152
|
+
response.success();
|
|
153
|
+
}
|
|
109
154
|
});
|
|
155
|
+
const process = dispatcher.handleRequest(req, res);
|
|
156
|
+
mockBody(req, JSON.stringify({}));
|
|
157
|
+
const result = await process;
|
|
158
|
+
assert.isTrue(result, "should handle");
|
|
159
|
+
assert.isTrue(endSpy.calledOnce, "should call once");
|
|
160
|
+
assert.equal(200, res.statusCode, "should be success");
|
|
110
161
|
});
|
|
111
|
-
it("
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
});
|
|
120
|
-
var process = dispatcher.processRequest(req, res);
|
|
121
|
-
mockBody(req, JSON.stringify({}));
|
|
122
|
-
var result = yield process;
|
|
123
|
-
assert.isTrue(result, "should handle");
|
|
124
|
-
assert.isTrue(endSpy.calledOnce, "should call once");
|
|
125
|
-
assert.equal(200, res.statusCode, "should be success");
|
|
162
|
+
it("Invalid state header gets ignored", async function () {
|
|
163
|
+
const endSpy = sinon.spy(res, "end");
|
|
164
|
+
buildRequest(req, "hub1", "conn1", undefined, "");
|
|
165
|
+
const dispatcher = new CloudEventsDispatcher("hub1", {
|
|
166
|
+
handleConnect: (request, response) => {
|
|
167
|
+
assert.deepEqual({}, request.context.states);
|
|
168
|
+
response.success();
|
|
169
|
+
}
|
|
126
170
|
});
|
|
171
|
+
const process = dispatcher.handleRequest(req, res);
|
|
172
|
+
mockBody(req, JSON.stringify({}));
|
|
173
|
+
const result = await process;
|
|
174
|
+
assert.isTrue(result, "should handle");
|
|
175
|
+
assert.isTrue(endSpy.calledOnce, "should call once");
|
|
176
|
+
assert.equal(200, res.statusCode, "should be success");
|
|
127
177
|
});
|
|
128
178
|
});
|
|
129
179
|
//# sourceMappingURL=connect.spec.js.map
|