From c2e40d0dc9425ba1750710808aad979eaa572789 Mon Sep 17 00:00:00 2001 From: Joshua Cline Date: Sun, 8 Nov 2020 16:39:17 -0800 Subject: [PATCH 1/4] Initial middleware. --- websocket/enable_websockets.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 websocket/enable_websockets.ts diff --git a/websocket/enable_websockets.ts b/websocket/enable_websockets.ts new file mode 100644 index 0000000..0f334c5 --- /dev/null +++ b/websocket/enable_websockets.ts @@ -0,0 +1,23 @@ +import type { Middleware, Context } from "https://deno.land/x/oak@v6.3.1/mod.ts"; +import { WebSocket, acceptable } from 'https://deno.land/std@0.76.0/ws/mod.ts' +export type handler = (socket: WebSocket, url: URL, headers: Headers) => Promise; +export class WebSocketMiddleware { + public handler: handler; + + constructor(handler: handler) { + this.handler = handler; + } + + private async real_middleware(ctx: Context, next: () => Promise) { + if (acceptable(ctx.request)) { + let ws = await ctx.upgrade(); + await this.handler(ws, ctx.request.url, ctx.request.headers); + } else { + return await next(); + } + } + + public middleware(): Middleware { + return async (ctx, next) => { await this.real_middleware(ctx, next) }; + } +} \ No newline at end of file From 82f96da7ea2c8bd792c163f0783259bf66896c27 Mon Sep 17 00:00:00 2001 From: Joshua Cline Date: Sun, 8 Nov 2020 16:42:08 -0800 Subject: [PATCH 2/4] Made into a single function for simplicity. --- websocket/enable_websockets.ts | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/websocket/enable_websockets.ts b/websocket/enable_websockets.ts index 0f334c5..125f030 100644 --- a/websocket/enable_websockets.ts +++ b/websocket/enable_websockets.ts @@ -1,23 +1,14 @@ import type { Middleware, Context } from "https://deno.land/x/oak@v6.3.1/mod.ts"; import { WebSocket, acceptable } from 'https://deno.land/std@0.76.0/ws/mod.ts' -export type handler = (socket: WebSocket, url: URL, headers: Headers) => Promise; -export class WebSocketMiddleware { - public handler: handler; +export type WebsocketHandler = (socket: WebSocket, url: URL, headers: Headers) => Promise; - constructor(handler: handler) { - this.handler = handler; - } - - private async real_middleware(ctx: Context, next: () => Promise) { - if (acceptable(ctx.request)) { +export function WebSocketMiddleware(handler: WebsocketHandler){ + return async function real_middleware(ctx: Context, next: () => Promise) { + if(acceptable(ctx.request)) { let ws = await ctx.upgrade(); - await this.handler(ws, ctx.request.url, ctx.request.headers); + await handler(ws, ctx.request.url, ctx.request.headers); } else { return await next(); } - } - - public middleware(): Middleware { - return async (ctx, next) => { await this.real_middleware(ctx, next) }; - } + }; } \ No newline at end of file From a108ec070ca572935bcb2511d630275ad3b9d151 Mon Sep 17 00:00:00 2001 From: Joshua Cline Date: Sun, 8 Nov 2020 16:43:45 -0800 Subject: [PATCH 3/4] Typo. --- websocket/enable_websockets.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/websocket/enable_websockets.ts b/websocket/enable_websockets.ts index 125f030..9675e2a 100644 --- a/websocket/enable_websockets.ts +++ b/websocket/enable_websockets.ts @@ -1,8 +1,8 @@ import type { Middleware, Context } from "https://deno.land/x/oak@v6.3.1/mod.ts"; import { WebSocket, acceptable } from 'https://deno.land/std@0.76.0/ws/mod.ts' -export type WebsocketHandler = (socket: WebSocket, url: URL, headers: Headers) => Promise; +export type WebSocketHandler = (socket: WebSocket, url: URL, headers: Headers) => Promise; -export function WebSocketMiddleware(handler: WebsocketHandler){ +export function WebSocketMiddleware(handler: WebSocketHandler){ return async function real_middleware(ctx: Context, next: () => Promise) { if(acceptable(ctx.request)) { let ws = await ctx.upgrade(); From 7de733c7075bbcf3a304870fbf8cfd5485592b28 Mon Sep 17 00:00:00 2001 From: Joshua Cline Date: Sun, 8 Nov 2020 18:07:04 -0800 Subject: [PATCH 4/4] Documentation and added to mod.ts --- mod.ts | 1 + websocket/README.md | 49 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 websocket/README.md diff --git a/mod.ts b/mod.ts index 018c92a..edb0f38 100644 --- a/mod.ts +++ b/mod.ts @@ -1,3 +1,4 @@ // Copyright 2020 the oak authors. All rights reserved. MIT license. export { responseTimeHeader } from "./observability/response_time_header.ts"; +export { WebSocketMiddleware } from "./websocket/enable_websockets.ts"; \ No newline at end of file diff --git a/websocket/README.md b/websocket/README.md new file mode 100644 index 0000000..b7c127c --- /dev/null +++ b/websocket/README.md @@ -0,0 +1,49 @@ +The following is a re-implementation of the example server in [std/ws](https://deno.land/std@0.76.0/ws): + +```ts +import { WebSocketMiddleware } from "https://deno.land/x/oak-middleware/mod.ts"; +import { Application } from "https://deno.land/x/oak/mod.ts"; +import { + isWebSocketCloseEvent, + isWebSocketPingEvent, + WebSocket, +} from "https://deno.land/std@0.76.0/ws/mod.ts"; + +async function handleWs(sock: WebSocket) { + console.log("socket connected!"); + try { + for await (const ev of sock) { + if (typeof ev === "string") { + // text message. + console.log("ws:Text", ev); + await sock.send(ev); + } else if (ev instanceof Uint8Array) { + // binary message. + console.log("ws:Binary", ev); + } else if (isWebSocketPingEvent(ev)) { + const [, body] = ev; + // ping. + console.log("ws:Ping", body); + } else if (isWebSocketCloseEvent(ev)) { + // close. + const { code, reason } = ev; + console.log("ws:Close", code, reason); + } + } + } catch (err) { + console.error(`failed to receive frame: ${err}`); + + if (!sock.isClosed) { + await sock.close(1000).catch(console.error); + } + } +} + +const app = new App(); +app.use(WebSocketMiddleware(handleWs)); + +// other middleware + +await app.listen(":80"); + +``` \ No newline at end of file