Skip to content

Commit af8a2ea

Browse files
committed
Merge branch 'master' of github.com:archilogic-com/lambda-expressless
2 parents 3060487 + 50c1e6d commit af8a2ea

File tree

5 files changed

+106
-5
lines changed

5 files changed

+106
-5
lines changed

src/lambda-wrapper.spec.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,6 @@ describe('Lambda Wrapper', () => {
130130
})
131131
const lambdaHandler = ApiGatewayHandler(router)
132132
const out = await lambdaHandler(proxyRequest, {})
133-
console.log(out)
134133
})
135134

136135
it('should handle next(error)', async () => {

src/request.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@ const ReadableStream = require('stream').Readable;
22
const accepts = require('accepts');
33
const typeis = require('type-is');
44

5+
function byteLength(str) {
6+
// returns the byte length of an utf8 string
7+
let s = str.length
8+
for (let i=str.length-1; i>=0; i--) {
9+
const code = str.charCodeAt(i)
10+
if (code > 0x7f && code <= 0x7ff) s++
11+
else if (code > 0x7ff && code <= 0xffff) s+=2
12+
}
13+
return s
14+
}
15+
516
/**
617
*
718
*/
@@ -43,7 +54,7 @@ class Request extends ReadableStream {
4354
this.params = event.pathParameters;
4455

4556
if (!this.get('Content-Length') && event.body) {
46-
this.headers['content-length'] = event.body.length;
57+
this.headers['content-length'] = byteLength (event.body);
4758
}
4859

4960
this.protocol = this.get('X-Forwarded-Proto')

src/request.spec.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,22 @@ describe('Request object', () => {
141141
});
142142

143143
it('should handle content-length header if its not provided', () => {
144-
delete event.headers['content-length'];
145-
delete event.multiValueHeaders['content-length'];
144+
delete event.headers['Content-Length'];
145+
delete event.multiValueHeaders['Content-Length'];
146+
const body = JSON.stringify(requestObject);
147+
event.body = body
146148

147149
const request = new Request(event);
148-
const body = JSON.stringify(requestObject);
149150
expect(request.get('content-length')).toBe(body.length);
150151
})
152+
153+
it('should handle non-ascii content-length if header is not provided', () => {
154+
delete event.headers['Content-Length'];
155+
delete event.multiValueHeaders['Content-Length'];
156+
const body = JSON.stringify({text:"árvíztűrőtükörfúrógép"});
157+
event.body = body
158+
159+
const request = new Request(event);
160+
expect(request.get('content-length')).toBe(41);
161+
})
151162
});

src/response.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const EventEmitter = require('events');
22

33
const AWS_RES_BODY = Symbol('body')
44
const AWS_RES_HEADERS = Symbol('body')
5+
const AWS_RES_MULTI_VALUE_HEADERS = Symbol('body')
56
const ON_FINISHED = Symbol('body')
67

78
/**
@@ -20,6 +21,7 @@ class Response extends EventEmitter {
2021
this.statusCode = 200
2122
// Non-Express compatible props: Use symbols to avoid name clashes
2223
this[AWS_RES_HEADERS] = {}
24+
this[AWS_RES_MULTI_VALUE_HEADERS] = undefined
2325
this[AWS_RES_BODY] = ''
2426
this[ON_FINISHED] = onFinished
2527
}
@@ -40,6 +42,7 @@ class Response extends EventEmitter {
4042
headers: this[AWS_RES_HEADERS],
4143
body: bodyStr
4244
}
45+
if (this[AWS_RES_MULTI_VALUE_HEADERS]) apiGatewayResult.multiValueHeaders = this[AWS_RES_MULTI_VALUE_HEADERS]
4346
this[ON_FINISHED](apiGatewayResult)
4447
}
4548

@@ -152,6 +155,58 @@ class Response extends EventEmitter {
152155
return this;
153156
}
154157

158+
/**
159+
* Set cookie
160+
*
161+
* @param {string} key Cookie name
162+
* @param {string} value Cookie value
163+
* @param {{domain: string, expires: Date, maxAge: number, path: string, sameSite: string, httpOnly:boolean, secure: boolean}} options
164+
*/
165+
cookie(key, value, options={}) {
166+
if (! options['path']) {
167+
options['path'] = '/'
168+
}
169+
let cookieStr = `${key}=${value}`;
170+
for (const optKey in options) {
171+
switch (optKey.toLocaleLowerCase()) {
172+
case 'domain':
173+
cookieStr += `; Domain=${options[optKey]}`
174+
break
175+
case 'expires':
176+
cookieStr += `; Expires=${options[optKey].toUTCString()}`
177+
break
178+
case 'maxage':
179+
cookieStr += `; Max-Age=${options[optKey]}`
180+
break
181+
case 'path':
182+
cookieStr += `; Path=${options[optKey]}`
183+
break
184+
case 'samesite':
185+
cookieStr += `; SameSite=${options[optKey]}`
186+
break
187+
case 'httponly':
188+
if (options[optKey]) {
189+
cookieStr += '; HttpOnly'
190+
}
191+
break
192+
case 'secure':
193+
if (options[optKey]) {
194+
cookieStr += '; Secure'
195+
}
196+
break
197+
default:
198+
console.warn (`Warning: Cookie paramterer ${optKey} not supported`)
199+
}
200+
}
201+
if (!this[AWS_RES_MULTI_VALUE_HEADERS]) {
202+
this[AWS_RES_MULTI_VALUE_HEADERS] = {'Set-Cookie': [cookieStr]};
203+
} else {
204+
const cookies = this[AWS_RES_MULTI_VALUE_HEADERS]['Set-Cookie'] || []
205+
cookies.push (cookieStr)
206+
}
207+
return this;
208+
}
209+
155210
/**
156211
* Set status code for response
157212
*

src/response.spec.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,31 @@ describe('Response object', () => {
4646
res.end()
4747
});
4848

49+
it('set cookies', done => {
50+
const res = new Response(null, out => {
51+
expect(out.multiValueHeaders).toEqual({
52+
'Set-Cookie': [
53+
'foo=1234; Path=/',
54+
'bar=5678; Path=/docs',
55+
'foo2=1234; Domain=example.com; Path=/',
56+
'foo3=1234; Expires=Fri, 25 Sep 2020 22:00:00 GMT; Path=/',
57+
'foo4=1234; Max-Age=456879; Path=/',
58+
'foo5=1234; Secure; SameSite=None; Path=/',
59+
'foo6=1234; HttpOnly; Path=/'
60+
]
61+
})
62+
done()
63+
});
64+
res.cookie('foo', '1234');
65+
res.cookie('bar', '5678', {path: '/docs'})
66+
res.cookie('foo2', '1234', {domain: 'example.com'})
67+
res.cookie('foo3', '1234', {expires: new Date(2020, 8, 26)})
68+
res.cookie('foo4', '1234', {maxAge: 456879})
69+
res.cookie('foo5', '1234', {secure: true, sameSite: 'None'})
70+
res.cookie('foo6', '1234', {httpOnly: true})
71+
res.end()
72+
});
73+
4974
it('can chain status method', done => {
5075
const res = new Response(null, out => {
5176
expect(out.statusCode).toBe(201);

0 commit comments

Comments
 (0)