Skip to content

Commit 26dcbe4

Browse files
authored
Merge pull request nullstack#353 from nullstack/request-based-context
Request based context
2 parents 445767f + 7979c1f commit 26dcbe4

File tree

8 files changed

+72
-60
lines changed

8 files changed

+72
-60
lines changed

server/context.js

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,54 @@
1-
const context = {}
1+
import { AsyncLocalStorage } from 'async_hooks'
2+
3+
const internalContext = {}
24

35
const contextProxyHandler = {
46
set(target, name, value, receiver) {
5-
context[name] = value
6-
return Reflect.set(target, name, value, receiver)
7+
const currentContext = asyncLocalStorage.getStore()
8+
if (currentContext) {
9+
currentContext[name] = value
10+
return true
11+
} else {
12+
return Reflect.set(target, name, value, receiver)
13+
}
714
},
15+
get(target, name, receiver) {
16+
const currentContext = asyncLocalStorage.getStore()
17+
if (currentContext) {
18+
return currentContext[name]
19+
} else {
20+
return Reflect.get(target, name, receiver)
21+
}
22+
}
823
}
924

25+
const context = new Proxy(internalContext, contextProxyHandler)
26+
27+
const asyncLocalStorage = new AsyncLocalStorage()
28+
1029
export function generateContext(temporary) {
11-
return new Proxy({ ...context, ...temporary }, contextProxyHandler)
30+
if(temporary) {
31+
Object.assign(context, temporary)
32+
}
33+
return context
34+
}
35+
36+
export function generateCurrentContext(temporary, callback) {
37+
const currentContext = {...internalContext, ...temporary}
38+
asyncLocalStorage.run(currentContext, () => {
39+
callback(currentContext);
40+
});
41+
}
42+
43+
export function getCurrentContext(temporary) {
44+
const currentContext = asyncLocalStorage.getStore()
45+
if (!currentContext) {
46+
return generateContext()
47+
}
48+
if(temporary) {
49+
Object.assign(currentContext, temporary)
50+
}
51+
return currentContext
1252
}
1353

14-
export default context
54+
export default context

server/exposeServerFunctions.js

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,16 @@ import fs from 'fs'
33
import bodyParser from 'body-parser'
44
import path from 'path'
55
import deserialize from '../shared/deserialize'
6-
import { generateContext } from './context'
6+
import { getCurrentContext } from './context'
77
import printError from './printError'
88
import registry from './registry'
9-
import reqres from './reqres'
109

1110
export default function exposeServerFunctions(server) {
1211
for (const method of ['get', 'post', 'put', 'patch', 'delete', 'all']) {
1312
const original = server[method].bind(server)
1413
server[method] = function (...args) {
1514
if (typeof args[1] === 'function' && args[1].name === '_invoke') {
1615
return original(args[0], bodyParser.text({ limit: server.maximumPayloadSize }), async (request, response) => {
17-
reqres.set(request, response)
1816
const params = {}
1917
for (const key of Object.keys(request.params)) {
2018
params[key] = extractParamValue(request.params[key])
@@ -27,14 +25,12 @@ export default function exposeServerFunctions(server) {
2725
Object.assign(params, deserialize(payload))
2826
}
2927
try {
30-
const subcontext = generateContext({ request, response, ...params })
28+
const currentContext = getCurrentContext(params)
3129
const exposedFunction = module.hot ? registry[args[1].hash] : args[1]
32-
const result = await exposedFunction(subcontext)
33-
reqres.clear()
30+
const result = await exposedFunction(currentContext)
3431
response.json(result)
3532
} catch (error) {
3633
printError(error)
37-
reqres.clear()
3834
response.status(500).json({})
3935
}
4036
})

server/project.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ICONS } from 'nullstack/project'
22
import environment from './environment'
3-
import reqres from './reqres'
43
import worker from './worker'
4+
import { getCurrentContext } from './context'
55

66
const project = {}
77

@@ -21,8 +21,9 @@ project.disallow = []
2121
project.icons = ICONS
2222

2323
function getHost() {
24-
if (reqres.request?.headers?.host) {
25-
return reqres.request.headers.host
24+
const currentContext = getCurrentContext()
25+
if (currentContext.request?.headers?.host) {
26+
return currentContext.request.headers.host
2627
}
2728
if (project.domain === 'localhost') {
2829
return `localhost:${process.env.NULLSTACK_SERVER_PORT}`

server/registry.js

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
const registry = {}
22
export default registry
3-
import reqres from "./reqres"
4-
import { generateContext } from "./context"
3+
import { getCurrentContext } from "./context"
54
import Nullstack from '.'
65
import { load } from "./lazy"
76

@@ -34,10 +33,9 @@ function bindStaticProps(klass) {
3433
return klass[propName].call(klass, ...args)
3534
}
3635
const params = args[0] || {}
37-
const { request, response } = reqres
38-
const subcontext = generateContext({ request, response, ...params })
36+
const currentContext = getCurrentContext(params)
3937
await load(klass.hash)
40-
return klass[propName].call(klass, subcontext)
38+
return klass[propName].call(klass, currentContext)
4139
}
4240
if (module.hot) {
4341
_invoke.hash = klass[prop].hash

server/reqres.js

Lines changed: 0 additions & 20 deletions
This file was deleted.

server/server.js

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import express from 'express'
33
import path from 'path'
44
import deserialize from '../shared/deserialize'
55
import prefix from '../shared/prefix'
6-
import context, { generateContext } from './context'
6+
import context, { getCurrentContext, generateCurrentContext } from './context'
77
import emulatePrerender from './emulatePrerender'
88
import environment from './environment'
99
import exposeServerFunctions from './exposeServerFunctions'
@@ -13,7 +13,6 @@ import generateManifest from './manifest'
1313
import { prerender } from './prerender'
1414
import printError from './printError'
1515
import registry from './registry'
16-
import reqres from './reqres'
1716
import generateRobots from './robots'
1817
import template from './template'
1918
import { generateServiceWorker } from './worker'
@@ -31,7 +30,9 @@ server.use(async (request, response, next) => {
3130
typeof context.start === 'function' && (await context.start())
3231
contextStarted = true
3332
}
34-
next()
33+
generateCurrentContext({request, response}, () => {
34+
next()
35+
})
3536
})
3637

3738
emulatePrerender(server)
@@ -120,7 +121,6 @@ server.start = function () {
120121

121122
server.all(`/${prefix}/:hash/:methodName.json`, async (request, response) => {
122123
const payload = request.method === 'GET' ? request.query.payload : request.body
123-
reqres.set(request, response)
124124
const args = deserialize(payload)
125125
const { hash, methodName } = request.params
126126
const [invokerHash, boundHash] = hash.split('-')
@@ -137,25 +137,21 @@ server.start = function () {
137137
const method = registry[key]
138138
if (method !== undefined) {
139139
try {
140-
const subcontext = generateContext({ request, response, ...args })
141-
const result = await method.call(boundKlass, subcontext)
142-
reqres.clear()
140+
const currentContext = getCurrentContext(args)
141+
const result = await method.call(boundKlass, currentContext)
143142
response.json({ result })
144143
} catch (error) {
145144
printError(error)
146-
reqres.clear()
147145
response.status(500).json({})
148146
}
149147
} else {
150-
reqres.clear()
151148
response.status(404).json({})
152149
}
153150
})
154151

155152
if (module.hot) {
156153
server.all(`/${prefix}/:version/:hash/:methodName.json`, async (request, response) => {
157154
const payload = request.method === 'GET' ? request.query.payload : request.body
158-
reqres.set(request, response)
159155
const args = deserialize(payload)
160156
const { version, hash, methodName } = request.params
161157
const [invokerHash, boundHash] = hash.split('-')
@@ -178,17 +174,14 @@ server.start = function () {
178174
const method = registry[key]
179175
if (method !== undefined) {
180176
try {
181-
const subcontext = generateContext({ request, response, ...args })
182-
const result = await method.call(boundKlass, subcontext)
183-
reqres.clear()
177+
const currentContext = getCurrentContext(args)
178+
const result = await method.call(boundKlass, currentContext)
184179
response.json({ result })
185180
} catch (error) {
186181
printError(error)
187-
reqres.clear()
188182
response.status(500).json({})
189183
}
190184
} else {
191-
reqres.clear()
192185
response.status(404).json({})
193186
}
194187
}
@@ -210,15 +203,11 @@ server.start = function () {
210203
if (request.originalUrl.split('?')[0].indexOf('.') > -1) {
211204
return next()
212205
}
213-
reqres.set(request, response)
214206
const scope = await prerender(request, response)
215207
if (!response.headersSent) {
216208
const status = scope.context.page.status
217209
const html = template(scope)
218-
reqres.clear()
219210
response.status(status).send(html)
220-
} else {
221-
reqres.clear()
222211
}
223212
})
224213

tests/server.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ context.startIncrementalValue = 0
7373

7474
setExternalRoute(context.server)
7575

76+
context.server.use((request, response, next) => {
77+
context.url = request.originalUrl
78+
next()
79+
})
80+
7681
context.start = async function () {
7782
await ContextProject.start(context)
7883
await ContextSecrets.start(context)

tests/src/ServerFunctions.njs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ class ServerFunctions extends Nullstack {
2121
this.count = await this.getCountAsOne()
2222
}
2323

24-
static async getCount({ to }) {
24+
static async getCount(context) {
25+
const { to, url } = context
26+
console.log({url})
27+
setInterval(() => console.log("SERVERF:", context.url), 1000)
2528
return to
2629
}
2730

0 commit comments

Comments
 (0)