Compare commits
4 Commits
bf142df383
...
7218a80773
Author | SHA1 | Date |
---|---|---|
|
7218a80773 | |
|
bed23c62f5 | |
|
b8453438b6 | |
|
fba3d9e38d |
|
@ -1,5 +1,11 @@
|
||||||
# @hono/node-ws
|
# @hono/node-ws
|
||||||
|
|
||||||
|
## 1.1.4
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- [#1146](https://github.com/honojs/middleware/pull/1146) [`b8453438b66fc9a6af58e33593e9fa21a96c02a7`](https://github.com/honojs/middleware/commit/b8453438b66fc9a6af58e33593e9fa21a96c02a7) Thanks [@nakasyou](https://github.com/nakasyou)! - enhance WebSocket connection handling with CORS support and connection symbols
|
||||||
|
|
||||||
## 1.1.3
|
## 1.1.3
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@hono/node-ws",
|
"name": "@hono/node-ws",
|
||||||
"version": "1.1.3",
|
"version": "1.1.4",
|
||||||
"description": "WebSocket helper for Node.js",
|
"description": "WebSocket helper for Node.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { serve } from '@hono/node-server'
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import type { ServerType } from '@hono/node-server/dist/types'
|
import type { ServerType } from '@hono/node-server/dist/types'
|
||||||
import { Hono } from 'hono'
|
import { Hono } from 'hono'
|
||||||
|
import { cors } from 'hono/cors'
|
||||||
import type { WSMessageReceive } from 'hono/ws'
|
import type { WSMessageReceive } from 'hono/ws'
|
||||||
import { WebSocket } from 'ws'
|
import { WebSocket } from 'ws'
|
||||||
import { createNodeWebSocket } from '.'
|
import { createNodeWebSocket } from '.'
|
||||||
|
@ -244,4 +245,42 @@ describe('WebSocket helper', () => {
|
||||||
createNodeWebSocket({ app })
|
createNodeWebSocket({ app })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('Should client can connect when use cors()', async () => {
|
||||||
|
app.use(cors())
|
||||||
|
const mainPromise = new Promise<boolean>((resolve) =>
|
||||||
|
app.get(
|
||||||
|
'/',
|
||||||
|
upgradeWebSocket(() => ({
|
||||||
|
onOpen() {
|
||||||
|
resolve(true)
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
new WebSocket('ws://localhost:3030/')
|
||||||
|
|
||||||
|
expect(await mainPromise).toBe(true)
|
||||||
|
})
|
||||||
|
it('Should client can connect even if a response has difference', async () => {
|
||||||
|
app.use(async (c, next) => {
|
||||||
|
c.res = new Response(null, c.res)
|
||||||
|
await next()
|
||||||
|
})
|
||||||
|
const mainPromise = new Promise<boolean>((resolve) =>
|
||||||
|
app.get(
|
||||||
|
'/',
|
||||||
|
upgradeWebSocket(() => ({
|
||||||
|
onOpen() {
|
||||||
|
resolve(true)
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
new WebSocket('ws://localhost:3030/')
|
||||||
|
|
||||||
|
expect(await mainPromise).toBe(true)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -23,6 +23,11 @@ export interface NodeWebSocketInit {
|
||||||
baseUrl?: string | URL
|
baseUrl?: string | URL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const generateConnectionSymbol = () => Symbol('connection')
|
||||||
|
|
||||||
|
/** @example `c.env[CONNECTION_SYMBOL_KEY]` */
|
||||||
|
const CONNECTION_SYMBOL_KEY: unique symbol = Symbol('CONNECTION_SYMBOL_KEY')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create WebSockets for Node.js
|
* Create WebSockets for Node.js
|
||||||
* @param init Options
|
* @param init Options
|
||||||
|
@ -32,7 +37,7 @@ export const createNodeWebSocket = (init: NodeWebSocketInit): NodeWebSocket => {
|
||||||
const wss = new WebSocketServer({ noServer: true })
|
const wss = new WebSocketServer({ noServer: true })
|
||||||
const waiterMap = new Map<
|
const waiterMap = new Map<
|
||||||
IncomingMessage,
|
IncomingMessage,
|
||||||
{ resolve: (ws: WebSocket) => void; response: Response }
|
{ resolve: (ws: WebSocket) => void; connectionSymbol: symbol }
|
||||||
>()
|
>()
|
||||||
|
|
||||||
wss.on('connection', (ws, request) => {
|
wss.on('connection', (ws, request) => {
|
||||||
|
@ -43,9 +48,9 @@ export const createNodeWebSocket = (init: NodeWebSocketInit): NodeWebSocket => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const nodeUpgradeWebSocket = (request: IncomingMessage, response: Response) => {
|
const nodeUpgradeWebSocket = (request: IncomingMessage, connectionSymbol: symbol) => {
|
||||||
return new Promise<WebSocket>((resolve) => {
|
return new Promise<WebSocket>((resolve) => {
|
||||||
waiterMap.set(request, { resolve, response })
|
waiterMap.set(request, { resolve, connectionSymbol })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,14 +67,18 @@ export const createNodeWebSocket = (init: NodeWebSocketInit): NodeWebSocket => {
|
||||||
headers.append(key, Array.isArray(value) ? value[0] : value)
|
headers.append(key, Array.isArray(value) ? value[0] : value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await init.app.request(
|
const env: {
|
||||||
url,
|
incoming: IncomingMessage
|
||||||
{ headers: headers },
|
outgoing: undefined
|
||||||
{ incoming: request, outgoing: undefined }
|
[CONNECTION_SYMBOL_KEY]?: symbol
|
||||||
)
|
} = {
|
||||||
|
incoming: request,
|
||||||
|
outgoing: undefined,
|
||||||
|
}
|
||||||
|
await init.app.request(url, { headers: headers }, env)
|
||||||
const waiter = waiterMap.get(request)
|
const waiter = waiterMap.get(request)
|
||||||
if (!waiter || waiter.response !== response) {
|
|
||||||
|
if (!waiter || waiter.connectionSymbol !== env[CONNECTION_SYMBOL_KEY]) {
|
||||||
socket.end(
|
socket.end(
|
||||||
'HTTP/1.1 400 Bad Request\r\n' +
|
'HTTP/1.1 400 Bad Request\r\n' +
|
||||||
'Connection: close\r\n' +
|
'Connection: close\r\n' +
|
||||||
|
@ -93,9 +102,10 @@ export const createNodeWebSocket = (init: NodeWebSocketInit): NodeWebSocket => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = new Response()
|
const connectionSymbol = generateConnectionSymbol()
|
||||||
|
c.env[CONNECTION_SYMBOL_KEY] = connectionSymbol
|
||||||
;(async () => {
|
;(async () => {
|
||||||
const ws = await nodeUpgradeWebSocket(c.env.incoming, response)
|
const ws = await nodeUpgradeWebSocket(c.env.incoming, connectionSymbol)
|
||||||
let events: WSEvents<WebSocket>
|
let events: WSEvents<WebSocket>
|
||||||
try {
|
try {
|
||||||
events = await createEvents(c)
|
events = await createEvents(c)
|
||||||
|
@ -167,7 +177,7 @@ export const createNodeWebSocket = (init: NodeWebSocketInit): NodeWebSocket => {
|
||||||
})
|
})
|
||||||
})()
|
})()
|
||||||
|
|
||||||
return response
|
return new Response()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,8 +30,8 @@ Also, there is two ways to use this middleware:
|
||||||
app.use(
|
app.use(
|
||||||
'/google',
|
'/google',
|
||||||
googleAuth({
|
googleAuth({
|
||||||
client_id: Bun.env.GOOGLE_ID,
|
client_id: process.env.GOOGLE_ID,
|
||||||
client_secret: Bun.env.GOOGLE_SECRET,
|
client_secret: process.env.GOOGLE_SECRET,
|
||||||
scope: ['openid', 'email', 'profile'],
|
scope: ['openid', 'email', 'profile'],
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -57,8 +57,8 @@ Or
|
||||||
app.get(
|
app.get(
|
||||||
'/google',
|
'/google',
|
||||||
googleAuth({
|
googleAuth({
|
||||||
client_id: Bun.env.GOOGLE_ID,
|
client_id: process.env.GOOGLE_ID,
|
||||||
client_secret: Bun.env.GOOGLE_SECRET,
|
client_secret: process.env.GOOGLE_SECRET,
|
||||||
scope: ['openid', 'email', 'profile'],
|
scope: ['openid', 'email', 'profile'],
|
||||||
}),
|
}),
|
||||||
(c) => {
|
(c) => {
|
||||||
|
@ -77,6 +77,8 @@ app.get(
|
||||||
export default app
|
export default app
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you are using Bun, replace `process.env` to `Bun.env` to resolve Environments at runtime.
|
||||||
|
|
||||||
### Google
|
### Google
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
|
@ -88,8 +90,8 @@ const app = new Hono()
|
||||||
app.use(
|
app.use(
|
||||||
'/google',
|
'/google',
|
||||||
googleAuth({
|
googleAuth({
|
||||||
client_id: Bun.env.GOOGLE_ID,
|
client_id: process.env.GOOGLE_ID,
|
||||||
client_secret: Bun.env.GOOGLE_SECRET,
|
client_secret: process.env.GOOGLE_SECRET,
|
||||||
scope: ['openid', 'email', 'profile'],
|
scope: ['openid', 'email', 'profile'],
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -200,8 +202,8 @@ const app = new Hono()
|
||||||
app.use(
|
app.use(
|
||||||
'/facebook',
|
'/facebook',
|
||||||
facebookAuth({
|
facebookAuth({
|
||||||
client_id: Bun.env.FACEBOOK_ID,
|
client_id: process.env.FACEBOOK_ID,
|
||||||
client_secret: Bun.env.FACEBOOK_SECRET,
|
client_secret: process.env.FACEBOOK_SECRET,
|
||||||
scope: ['email', 'public_profile'],
|
scope: ['email', 'public_profile'],
|
||||||
fields: [
|
fields: [
|
||||||
'email',
|
'email',
|
||||||
|
@ -412,8 +414,8 @@ const app = new Hono()
|
||||||
app.use(
|
app.use(
|
||||||
'/github',
|
'/github',
|
||||||
githubAuth({
|
githubAuth({
|
||||||
client_id: Bun.env.GITHUB_ID,
|
client_id: process.env.GITHUB_ID,
|
||||||
client_secret: Bun.env.GITHUB_SECRET,
|
client_secret: process.env.GITHUB_SECRET,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -441,8 +443,8 @@ const app = new Hono()
|
||||||
app.use(
|
app.use(
|
||||||
'/github',
|
'/github',
|
||||||
githubAuth({
|
githubAuth({
|
||||||
client_id: Bun.env.GITHUB_ID,
|
client_id: process.env.GITHUB_ID,
|
||||||
client_secret: Bun.env.GITHUB_SECRET,
|
client_secret: process.env.GITHUB_SECRET,
|
||||||
scope: ['public_repo', 'read:user', 'user', 'user:email', 'user:follow'],
|
scope: ['public_repo', 'read:user', 'user', 'user:email', 'user:follow'],
|
||||||
oauthApp: true,
|
oauthApp: true,
|
||||||
})
|
})
|
||||||
|
@ -545,8 +547,8 @@ const app = new Hono()
|
||||||
app.use(
|
app.use(
|
||||||
'/linkedin',
|
'/linkedin',
|
||||||
linkedinAuth({
|
linkedinAuth({
|
||||||
client_id: Bun.env.LINKEDIN_ID,
|
client_id: process.env.LINKEDIN_ID,
|
||||||
client_secret: Bun.env.LINKEDIN_SECRET,
|
client_secret: process.env.LINKEDIN_SECRET,
|
||||||
scope: ['email', 'openid', 'profile'],
|
scope: ['email', 'openid', 'profile'],
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -575,8 +577,8 @@ const app = new Hono()
|
||||||
app.use(
|
app.use(
|
||||||
'/linkedin',
|
'/linkedin',
|
||||||
linkedinAuth({
|
linkedinAuth({
|
||||||
client_id: Bun.env.LINKEDIN_ID,
|
client_id: process.env.LINKEDIN_ID,
|
||||||
client_secret: Bun.env.LINKEDIN_SECRET,
|
client_secret: process.env.LINKEDIN_SECRET,
|
||||||
appAuth: true,
|
appAuth: true,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -629,8 +631,8 @@ const app = new Hono()
|
||||||
app.use(
|
app.use(
|
||||||
'/x',
|
'/x',
|
||||||
xAuth({
|
xAuth({
|
||||||
client_id: Bun.env.X_ID,
|
client_id: process.env.X_ID,
|
||||||
client_secret: Bun.env.X_SECRET,
|
client_secret: process.env.X_SECRET,
|
||||||
scope: ['tweet.read', 'users.read', 'offline.access'],
|
scope: ['tweet.read', 'users.read', 'offline.access'],
|
||||||
fields: ['profile_image_url', 'url'],
|
fields: ['profile_image_url', 'url'],
|
||||||
})
|
})
|
||||||
|
@ -786,8 +788,8 @@ const app = new Hono()
|
||||||
app.use(
|
app.use(
|
||||||
'/discord',
|
'/discord',
|
||||||
discordAuth({
|
discordAuth({
|
||||||
client_id: Bun.env.DISCORD_ID,
|
client_id: process.env.DISCORD_ID,
|
||||||
client_secret: Bun.env.DISCORD_SECRET,
|
client_secret: process.env.DISCORD_SECRET,
|
||||||
scope: ['identify', 'email'],
|
scope: ['identify', 'email'],
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue