@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/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/cloudEventsDispatcher.ts","../src/webPubSubEventHandler.ts"],"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","// 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', [ endpoint ] {\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 * },\n * });\n * ```\n *\n * @param hub The name of the hub to listen to\n * @param allowedEndpoints The allowed endpoints for the incoming CloudEvents request\n * @param options Options to configure the event handler\n */\n constructor(\n private hub: string,\n allowedEndpoints: string[],\n options?: WebPubSubEventHandlerOptions\n ) {\n const path = (options?.path ?? `/api/webpubsub/hubs/${hub}/`).toLowerCase();\n this.path = path.endsWith(\"/\") ? path : path + \"/\";\n this._cloudEventsHandler = new CloudEventsDispatcher(this.hub, allowedEndpoints, 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 === this.path) {\n if (req.method === \"OPTIONS\") {\n if (this._cloudEventsHandler.processValidateRequest(req, res)) {\n return;\n }\n } else if (req.method === \"POST\") {\n try {\n if (await this._cloudEventsHandler.processRequest(req, res)) {\n return;\n }\n } catch (err) {\n next(err);\n return;\n }\n }\n }\n\n next();\n };\n }\n}\n"],"names":["URL","HTTP"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/logger.ts","../src/utils.ts","../src/cloudEventsDispatcher.ts","../src/webPubSubEventHandler.ts"],"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","// 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","// 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","// 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"],"names":["createClientLogger","utils.toBase64JsonString","utils.fromBase64JsonString","utils.getHttpHeader","URL","utils.convertHttpToEvent","HTTP"],"mappings":"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"}
@@ -5,10 +5,11 @@
5
5
  */
6
6
  import { WebPubSubEventHandler } from "@azure/web-pubsub-express";
7
7
  import express from "express";
8
- const handler = new WebPubSubEventHandler("chat", ["https://xxx.webpubsub.azure.com"], {
9
- dumpRequest: false,
8
+ const handler = new WebPubSubEventHandler("chat", {
10
9
  handleConnect(req, res) {
11
10
  console.log(req);
11
+ // You can set the state for the connection, it lasts throughout the lifetime of the connection
12
+ res.setState("calledTime", 1);
12
13
  res.success();
13
14
  // or fail
14
15
  // res.fail(401);
@@ -17,9 +18,13 @@ const handler = new WebPubSubEventHandler("chat", ["https://xxx.webpubsub.azure.
17
18
  console.log(connectedRequest);
18
19
  },
19
20
  handleUserEvent(req, res) {
20
- console.log(req);
21
+ var calledTime = req.context.states.calledTime++;
22
+ console.log(calledTime);
23
+ // You can also set the state here
24
+ res.setState("calledTime", calledTime);
21
25
  res.success("Hello", "text");
22
- }
26
+ },
27
+ allowedEndpoints: ["https://xxx.webpubsub.azure.com"]
23
28
  });
24
29
  const app = express();
25
30
  app.use(handler.getMiddleware());
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../samples-dev/server.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAElC;;GAEG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,MAAM,OAAO,GAAG,IAAI,qBAAqB,CAAC,MAAM,EAAE,CAAC,iCAAiC,CAAC,EAAE;IACrF,WAAW,EAAE,KAAK;IAClB,aAAa,CAAC,GAAG,EAAE,GAAG;QACpB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjB,GAAG,CAAC,OAAO,EAAE,CAAC;QACd,UAAU;QACV,iBAAiB;IACnB,CAAC;IACD,WAAW,CAAC,gBAAgB;QAC1B,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAChC,CAAC;IACD,eAAe,CAAC,GAAG,EAAE,GAAG;QACtB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjB,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC/B,CAAC;CACF,CAAC,CAAC;AAEH,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AAEtB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;AAEjC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,CACpB,OAAO,CAAC,GAAG,CAAC,0DAA0D,OAAO,CAAC,IAAI,EAAE,CAAC,CACtF,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\n/**\n * @summary Demonstrates handling Web PubSub CloudEvents with Express\n */\n\nimport { WebPubSubEventHandler } from \"@azure/web-pubsub-express\";\nimport express from \"express\";\n\nconst handler = new WebPubSubEventHandler(\"chat\", [\"https://xxx.webpubsub.azure.com\"], {\n dumpRequest: false,\n handleConnect(req, res) {\n console.log(req);\n res.success();\n // or fail\n // res.fail(401);\n },\n onConnected(connectedRequest) {\n console.log(connectedRequest);\n },\n handleUserEvent(req, res) {\n console.log(req);\n res.success(\"Hello\", \"text\");\n }\n});\n\nconst app = express();\n\napp.use(handler.getMiddleware());\n\napp.listen(3000, () =>\n console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`)\n);\n"]}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../samples-dev/server.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAElC;;GAEG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,MAAM,OAAO,GAAG,IAAI,qBAAqB,CAAC,MAAM,EAAE;IAChD,aAAa,CAAC,GAAG,EAAE,GAAG;QACpB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjB,+FAA+F;QAC/F,GAAG,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;QAC9B,GAAG,CAAC,OAAO,EAAE,CAAC;QACd,UAAU;QACV,iBAAiB;IACnB,CAAC;IACD,WAAW,CAAC,gBAAgB;QAC1B,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAChC,CAAC;IACD,eAAe,CAAC,GAAG,EAAE,GAAG;QACtB,IAAI,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACxB,kCAAkC;QAClC,GAAG,CAAC,QAAQ,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;QACvC,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC/B,CAAC;IACD,gBAAgB,EAAE,CAAC,iCAAiC,CAAC;CACtD,CAAC,CAAC;AAEH,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AAEtB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;AAEjC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,CACpB,OAAO,CAAC,GAAG,CAAC,0DAA0D,OAAO,CAAC,IAAI,EAAE,CAAC,CACtF,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\n/**\n * @summary Demonstrates handling Web PubSub CloudEvents with Express\n */\n\nimport { WebPubSubEventHandler } from \"@azure/web-pubsub-express\";\nimport express from \"express\";\n\nconst handler = new WebPubSubEventHandler(\"chat\", {\n handleConnect(req, res) {\n console.log(req);\n // You can set the state for the connection, it lasts throughout the lifetime of the connection\n res.setState(\"calledTime\", 1);\n res.success();\n // or fail\n // res.fail(401);\n },\n onConnected(connectedRequest) {\n console.log(connectedRequest);\n },\n handleUserEvent(req, res) {\n var calledTime = req.context.states.calledTime++;\n console.log(calledTime);\n // You can also set the state here\n res.setState(\"calledTime\", calledTime);\n res.success(\"Hello\", \"text\");\n },\n allowedEndpoints: [\"https://xxx.webpubsub.azure.com\"]\n});\n\nconst app = express();\n\napp.use(handler.getMiddleware());\n\napp.listen(3000, () =>\n console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`)\n);\n"]}
@@ -1,265 +1,258 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT license.
3
- import { __awaiter } from "tslib";
4
3
  import { HTTP } from "cloudevents";
5
4
  import { URL } from "url";
5
+ import { logger } from "./logger";
6
+ import * as utils from "./utils";
7
+ var EventType;
8
+ (function (EventType) {
9
+ EventType[EventType["Connect"] = 0] = "Connect";
10
+ EventType[EventType["Connected"] = 1] = "Connected";
11
+ EventType[EventType["Disconnected"] = 2] = "Disconnected";
12
+ EventType[EventType["UserEvent"] = 3] = "UserEvent";
13
+ })(EventType || (EventType = {}));
14
+ function getConnectResponseHandler(connectRequest, response) {
15
+ const states = connectRequest.context.states;
16
+ let modified = false;
17
+ const handler = {
18
+ setState(name, value) {
19
+ states[name] = value;
20
+ modified = true;
21
+ },
22
+ success(res) {
23
+ response.statusCode = 200;
24
+ if (modified) {
25
+ response.setHeader("ce-connectionState", utils.toBase64JsonString(states));
26
+ }
27
+ if (res === undefined) {
28
+ response.end();
29
+ }
30
+ else {
31
+ response.setHeader("Content-Type", "application/json; charset=utf-8");
32
+ response.end(JSON.stringify(res));
33
+ }
34
+ },
35
+ fail(code, detail) {
36
+ response.statusCode = code;
37
+ response.end(detail !== null && detail !== void 0 ? detail : "");
38
+ }
39
+ };
40
+ return handler;
41
+ }
42
+ function getUserEventResponseHandler(userRequest, response) {
43
+ const states = userRequest.context.states;
44
+ let modified = false;
45
+ const handler = {
46
+ setState(name, value) {
47
+ modified = true;
48
+ states[name] = value;
49
+ },
50
+ success(data, dataType) {
51
+ response.statusCode = 200;
52
+ if (modified) {
53
+ response.setHeader("ce-connectionState", utils.toBase64JsonString(states));
54
+ }
55
+ switch (dataType) {
56
+ case "json":
57
+ response.setHeader("Content-Type", "application/json; charset=utf-8");
58
+ break;
59
+ case "text":
60
+ response.setHeader("Content-Type", "text/plain; charset=utf-8");
61
+ break;
62
+ default:
63
+ response.setHeader("Content-Type", "application/octet-stream");
64
+ break;
65
+ }
66
+ response.end(data !== null && data !== void 0 ? data : "");
67
+ },
68
+ fail(code, detail) {
69
+ response.statusCode = code;
70
+ response.end(detail !== null && detail !== void 0 ? detail : "");
71
+ }
72
+ };
73
+ return handler;
74
+ }
75
+ function getContext(ce, origin) {
76
+ const context = {
77
+ signature: ce["signature"],
78
+ userId: ce["userid"],
79
+ hub: ce["hub"],
80
+ connectionId: ce["connectionid"],
81
+ eventName: ce["eventname"],
82
+ origin: origin,
83
+ states: utils.fromBase64JsonString(ce["connectionstate"])
84
+ };
85
+ // TODO: validation
86
+ return context;
87
+ }
88
+ function tryGetWebPubSubEvent(req) {
89
+ // check ce-type to see if it is a valid WebPubSub CloudEvent request
90
+ const prefix = "azure.webpubsub.";
91
+ const connect = "azure.webpubsub.sys.connect";
92
+ const connected = "azure.webpubsub.sys.connected";
93
+ const disconnectd = "azure.webpubsub.sys.disconnected";
94
+ const userPrefix = "azure.webpubsub.user.";
95
+ const type = utils.getHttpHeader(req, "ce-type");
96
+ if (!(type === null || type === void 0 ? void 0 : type.startsWith(prefix))) {
97
+ return undefined;
98
+ }
99
+ if (type.startsWith(userPrefix)) {
100
+ return EventType.UserEvent;
101
+ }
102
+ switch (type) {
103
+ case connect:
104
+ return EventType.Connect;
105
+ case connected:
106
+ return EventType.Connected;
107
+ case disconnectd:
108
+ return EventType.Disconnected;
109
+ default:
110
+ return undefined;
111
+ }
112
+ }
113
+ function isWebPubSubRequest(req) {
114
+ return utils.getHttpHeader(req, "ce-awpsversion") !== undefined;
115
+ }
6
116
  /**
7
117
  * @internal
8
118
  */
9
119
  export class CloudEventsDispatcher {
10
- constructor(hub, allowedEndpoints, eventHandler) {
11
- var _a;
120
+ constructor(hub, eventHandler) {
12
121
  this.hub = hub;
13
122
  this.eventHandler = eventHandler;
14
- this._dumpRequest = (_a = eventHandler === null || eventHandler === void 0 ? void 0 : eventHandler.dumpRequest) !== null && _a !== void 0 ? _a : false;
15
- this._allowedOrigins = allowedEndpoints.map((endpoint) => endpoint === "*" ? "*" : new URL(endpoint).host);
123
+ this._allowAll = true;
124
+ this._allowedOrigins = [];
125
+ if (Array.isArray(eventHandler)) {
126
+ throw new Error("Unexpected WebPubSubEventHandlerOptions");
127
+ }
128
+ if ((eventHandler === null || eventHandler === void 0 ? void 0 : eventHandler.allowedEndpoints) !== undefined) {
129
+ this._allowedOrigins = eventHandler.allowedEndpoints.map((endpoint) => new URL(endpoint).host.toLowerCase());
130
+ this._allowAll = false;
131
+ }
16
132
  }
17
- processValidateRequest(req, res) {
18
- if (req.headers["webhook-request-origin"]) {
19
- res.setHeader("WebHook-Allowed-Origin", this._allowedOrigins);
20
- res.end();
21
- return true;
133
+ handlePreflight(req, res) {
134
+ var _a;
135
+ if (!isWebPubSubRequest(req)) {
136
+ return false;
137
+ }
138
+ const origin = (_a = utils.getHttpHeader(req, "webhook-request-origin")) === null || _a === void 0 ? void 0 : _a.toLowerCase();
139
+ if (origin === undefined) {
140
+ logger.warning("Expecting webhook-request-origin header.");
141
+ res.statusCode = 400;
142
+ }
143
+ else if (this._allowAll || this._allowedOrigins.indexOf(origin) > -1) {
144
+ res.setHeader("WebHook-Allowed-Origin", origin);
22
145
  }
23
146
  else {
24
- return false;
147
+ logger.warning("Origin does not match the allowed origins: " + this._allowedOrigins);
148
+ res.statusCode = 400;
25
149
  }
150
+ res.end();
151
+ return true;
26
152
  }
27
- processRequest(request, response) {
153
+ async handleRequest(request, response) {
28
154
  var _a, _b, _c, _d, _e;
29
- return __awaiter(this, void 0, void 0, function* () {
30
- // check if it is a valid WebPubSub cloud events
31
- const origin = this.getSingleHeader(request, "webhook-request-origin");
32
- if (origin === undefined) {
33
- return false;
34
- }
35
- const eventType = this.tryGetWebPubSubEvent(request);
36
- if (eventType === undefined) {
37
- return false;
38
- }
39
- // check if hub matches
40
- if (request.headers["ce-hub"] !== this.hub) {
41
- return false;
42
- }
43
- // No need to read body if handler is not specified
44
- switch (eventType) {
45
- case EventType.Connect:
46
- if (!((_a = this.eventHandler) === null || _a === void 0 ? void 0 : _a.handleConnect)) {
47
- response.statusCode = 401;
48
- response.end("Connect event handler is not configured.");
49
- return true;
50
- }
51
- break;
52
- case EventType.Connected:
53
- if (!((_b = this.eventHandler) === null || _b === void 0 ? void 0 : _b.onConnected)) {
54
- response.end();
55
- return true;
56
- }
57
- break;
58
- case EventType.Disconnected:
59
- if (!((_c = this.eventHandler) === null || _c === void 0 ? void 0 : _c.onDisconnected)) {
60
- response.end();
61
- return true;
62
- }
63
- break;
64
- case EventType.UserEvent:
65
- if (!((_d = this.eventHandler) === null || _d === void 0 ? void 0 : _d.handleUserEvent)) {
66
- response.end();
67
- return true;
68
- }
69
- break;
70
- default:
71
- console.warn(`Unknown EventType ${eventType}`);
72
- return false;
73
- }
74
- const eventRequest = yield this.convertHttpToEvent(request);
75
- const receivedEvent = HTTP.toEvent(eventRequest);
76
- if (this._dumpRequest) {
77
- console.log(receivedEvent);
78
- }
79
- switch (eventType) {
80
- case EventType.Connect: {
81
- const connectRequest = receivedEvent.data;
82
- connectRequest.context = this.GetContext(receivedEvent, origin);
83
- this.eventHandler.handleConnect(connectRequest, {
84
- success(res) {
85
- response.statusCode = 200;
86
- if (res === undefined) {
87
- response.end();
88
- }
89
- else {
90
- response.setHeader("Content-Type", "application/json; charset=utf-8");
91
- response.end(JSON.stringify(res));
92
- }
93
- },
94
- fail(code, detail) {
95
- response.statusCode = code;
96
- response.end(detail !== null && detail !== void 0 ? detail : "");
97
- }
98
- });
155
+ if (!isWebPubSubRequest(request)) {
156
+ return false;
157
+ }
158
+ // check if it is a valid WebPubSub cloud events
159
+ const origin = utils.getHttpHeader(request, "webhook-request-origin");
160
+ if (origin === undefined) {
161
+ return false;
162
+ }
163
+ const eventType = tryGetWebPubSubEvent(request);
164
+ if (eventType === undefined) {
165
+ return false;
166
+ }
167
+ // check if hub matches
168
+ const hub = utils.getHttpHeader(request, "ce-hub");
169
+ if (hub !== this.hub) {
170
+ return false;
171
+ }
172
+ // No need to read body if handler is not specified
173
+ switch (eventType) {
174
+ case EventType.Connect:
175
+ if (!((_a = this.eventHandler) === null || _a === void 0 ? void 0 : _a.handleConnect)) {
176
+ response.end();
99
177
  return true;
100
178
  }
101
- case EventType.Connected: {
102
- // for unblocking events, we responds to the service as early as possible
179
+ break;
180
+ case EventType.Connected:
181
+ if (!((_b = this.eventHandler) === null || _b === void 0 ? void 0 : _b.onConnected)) {
103
182
  response.end();
104
- const connectedRequest = receivedEvent.data;
105
- connectedRequest.context = this.GetContext(receivedEvent, origin);
106
- this.eventHandler.onConnected(connectedRequest);
107
183
  return true;
108
184
  }
109
- case EventType.Disconnected: {
110
- // for unblocking events, we responds to the service as early as possible
185
+ break;
186
+ case EventType.Disconnected:
187
+ if (!((_c = this.eventHandler) === null || _c === void 0 ? void 0 : _c.onDisconnected)) {
111
188
  response.end();
112
- const disconnectedRequest = receivedEvent.data;
113
- disconnectedRequest.context = this.GetContext(receivedEvent, origin);
114
- this.eventHandler.onDisconnected(disconnectedRequest);
115
189
  return true;
116
190
  }
117
- case EventType.UserEvent: {
118
- let userRequest;
119
- if (receivedEvent.data_base64 !== undefined) {
120
- userRequest = {
121
- context: this.GetContext(receivedEvent, origin),
122
- data: Buffer.from(receivedEvent.data_base64, "base64"),
123
- dataType: "binary"
124
- };
125
- }
126
- else if (receivedEvent.data !== undefined) {
127
- userRequest = {
128
- context: this.GetContext(receivedEvent, origin),
129
- data: receivedEvent.data,
130
- dataType: ((_e = receivedEvent.datacontenttype) === null || _e === void 0 ? void 0 : _e.startsWith("application/json;"))
131
- ? "json"
132
- : "text"
133
- };
134
- }
135
- else {
136
- throw new Error("Unexpected data.");
137
- }
138
- this.eventHandler.handleUserEvent(userRequest, {
139
- success(data, dataType) {
140
- response.statusCode = 200;
141
- switch (dataType) {
142
- case "json":
143
- response.setHeader("Content-Type", "application/json; charset=utf-8");
144
- break;
145
- case "text":
146
- response.setHeader("Content-Type", "text/plain; charset=utf-8");
147
- break;
148
- default:
149
- response.setHeader("Content-Type", "application/octet-stream");
150
- break;
151
- }
152
- response.end(data !== null && data !== void 0 ? data : "");
153
- },
154
- fail(code, detail) {
155
- response.statusCode = code;
156
- response.end(detail !== null && detail !== void 0 ? detail : "");
157
- }
158
- });
191
+ break;
192
+ case EventType.UserEvent:
193
+ if (!((_d = this.eventHandler) === null || _d === void 0 ? void 0 : _d.handleUserEvent)) {
194
+ response.end();
159
195
  return true;
160
196
  }
161
- default:
162
- console.warn(`Unknown EventType ${eventType}`);
163
- return false;
164
- }
165
- });
166
- }
167
- tryGetWebPubSubEvent(req) {
168
- // check ce-type to see if it is a valid WebPubSub CloudEvent request
169
- const prefix = "azure.webpubsub.";
170
- const connect = "azure.webpubsub.sys.connect";
171
- const connected = "azure.webpubsub.sys.connected";
172
- const disconnectd = "azure.webpubsub.sys.disconnected";
173
- const userPrefix = "azure.webpubsub.user.";
174
- const type = this.getSingleHeader(req, "ce-type");
175
- if (!(type === null || type === void 0 ? void 0 : type.startsWith(prefix))) {
176
- return undefined;
177
- }
178
- if (type.startsWith(userPrefix)) {
179
- return EventType.UserEvent;
180
- }
181
- switch (type) {
182
- case connect:
183
- return EventType.Connect;
184
- case connected:
185
- return EventType.Connected;
186
- case disconnectd:
187
- return EventType.Disconnected;
197
+ break;
188
198
  default:
189
- return undefined;
190
- }
191
- }
192
- getSingleHeader(req, key) {
193
- const value = req.headers[key];
194
- if (value === undefined) {
195
- return undefined;
196
- }
197
- if (typeof value === "string") {
198
- return value;
199
+ logger.warning(`Unknown EventType ${eventType}`);
200
+ return false;
199
201
  }
200
- return value[0];
201
- }
202
- GetContext(ce, origin) {
203
- const context = {
204
- signature: ce["signature"],
205
- userId: ce["userid"],
206
- hub: ce["hub"],
207
- connectionId: ce["connectionid"],
208
- eventName: ce["eventname"],
209
- origin: origin
210
- };
211
- // TODO: validation
212
- return context;
213
- }
214
- convertHttpToEvent(request) {
215
- return __awaiter(this, void 0, void 0, function* () {
216
- const normalized = {
217
- headers: {},
218
- body: ""
219
- };
220
- if (request.headers) {
221
- for (const key in request.headers) {
222
- if (Object.prototype.hasOwnProperty.call(request.headers, key)) {
223
- const element = request.headers[key];
224
- if (element === undefined) {
225
- continue;
226
- }
227
- if (typeof element === "string") {
228
- normalized.headers[key] = element;
229
- }
230
- else {
231
- normalized.headers[key] = element.join(",");
232
- }
233
- }
202
+ const eventRequest = await utils.convertHttpToEvent(request);
203
+ const receivedEvent = HTTP.toEvent(eventRequest);
204
+ logger.verbose(receivedEvent);
205
+ switch (eventType) {
206
+ case EventType.Connect: {
207
+ const connectRequest = receivedEvent.data;
208
+ connectRequest.context = getContext(receivedEvent, origin);
209
+ this.eventHandler.handleConnect(connectRequest, getConnectResponseHandler(connectRequest, response));
210
+ return true;
211
+ }
212
+ case EventType.Connected: {
213
+ // for unblocking events, we responds to the service as early as possible
214
+ response.end();
215
+ const connectedRequest = receivedEvent.data;
216
+ connectedRequest.context = getContext(receivedEvent, origin);
217
+ this.eventHandler.onConnected(connectedRequest);
218
+ return true;
219
+ }
220
+ case EventType.Disconnected: {
221
+ // for unblocking events, we responds to the service as early as possible
222
+ response.end();
223
+ const disconnectedRequest = receivedEvent.data;
224
+ disconnectedRequest.context = getContext(receivedEvent, origin);
225
+ this.eventHandler.onDisconnected(disconnectedRequest);
226
+ return true;
227
+ }
228
+ case EventType.UserEvent: {
229
+ let userRequest;
230
+ if (receivedEvent.data_base64 !== undefined) {
231
+ userRequest = {
232
+ context: getContext(receivedEvent, origin),
233
+ data: Buffer.from(receivedEvent.data_base64, "base64"),
234
+ dataType: "binary"
235
+ };
236
+ }
237
+ else if (receivedEvent.data !== undefined) {
238
+ userRequest = {
239
+ context: getContext(receivedEvent, origin),
240
+ data: receivedEvent.data,
241
+ dataType: ((_e = receivedEvent.datacontenttype) === null || _e === void 0 ? void 0 : _e.startsWith("application/json;"))
242
+ ? "json"
243
+ : "text"
244
+ };
234
245
  }
246
+ else {
247
+ throw new Error("Unexpected data.");
248
+ }
249
+ this.eventHandler.handleUserEvent(userRequest, getUserEventResponseHandler(userRequest, response));
250
+ return true;
235
251
  }
236
- normalized.body = yield this.readRequestBody(request);
237
- return normalized;
238
- });
239
- }
240
- readRequestBody(req) {
241
- return new Promise(function (resolve, reject) {
242
- const chunks = [];
243
- req.on("data", function (chunk) {
244
- chunks.push(chunk);
245
- });
246
- req.on("end", function () {
247
- const buffer = Buffer.concat(chunks);
248
- resolve(buffer.toString());
249
- });
250
- // reject on request error
251
- req.on("error", function (err) {
252
- // This is not a "Second reject", just a different sort of failure
253
- reject(err);
254
- });
255
- });
252
+ default:
253
+ logger.warning(`Unknown EventType ${eventType}`);
254
+ return false;
255
+ }
256
256
  }
257
257
  }
258
- var EventType;
259
- (function (EventType) {
260
- EventType[EventType["Connect"] = 0] = "Connect";
261
- EventType[EventType["Connected"] = 1] = "Connected";
262
- EventType[EventType["Disconnected"] = 2] = "Disconnected";
263
- EventType[EventType["UserEvent"] = 3] = "UserEvent";
264
- })(EventType || (EventType = {}));
265
258
  //# sourceMappingURL=cloudEventsDispatcher.js.map