-
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add jsonp, cookie, clearCookie, download
- Loading branch information
v1rtl
committed
Feb 28, 2021
1 parent
732a9f8
commit 035dd22
Showing
12 changed files
with
330 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { Request as Req } from '../../request.ts' | ||
import { Response as Res } from '../../response.ts' | ||
import { append } from './append.ts' | ||
import * as cookie from 'https://esm.sh/@tinyhttp/cookie' | ||
import { sign } from '../../utils/cookieSignature.ts' | ||
|
||
export const setCookie = <Request extends Req = Req, Response extends Res = Res>( | ||
req: Request & { | ||
secret?: string | string[] | ||
}, | ||
res: Response | ||
) => ( | ||
name: string, | ||
value: string | Record<string, unknown>, | ||
options: cookie.SerializeOptions & | ||
Partial<{ | ||
signed: boolean | ||
}> = {} | ||
): Response => { | ||
const secret = req.secret as string | ||
|
||
const signed = options.signed || false | ||
|
||
if (signed && !secret) throw new Error('cookieParser("secret") required for signed cookies') | ||
|
||
let val = typeof value === 'object' ? 'j:' + JSON.stringify(value) : String(value) | ||
|
||
if (signed) val = 's:' + sign(val, secret) | ||
|
||
if (options.maxAge) { | ||
options.expires = new Date(Date.now() + options.maxAge) | ||
options.maxAge /= 1000 | ||
} | ||
|
||
if (options.path == null) options.path = '/' | ||
|
||
append(res)('Set-Cookie', `${cookie.serialize(name, String(val), options)}`) | ||
|
||
return res | ||
} | ||
|
||
export const clearCookie = <Request extends Req = Req, Response extends Res = Res>(req: Request, res: Response) => ( | ||
name: string, | ||
options?: cookie.SerializeOptions | ||
): Response => { | ||
return setCookie<Request, Response>(req, res)( | ||
name, | ||
'', | ||
Object.assign({}, { expires: new Date(1), path: '/' }, options) | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import { contentDisposition } from 'https://esm.sh/@tinyhttp/content-disposition' | ||
import { SendFileOptions, sendFile } from './sendFile.ts' | ||
import { resolve, extname } from 'https://deno.land/std/path/mod.ts' | ||
import { setContentType, setHeader } from './headers.ts' | ||
import { Request as Req } from '../../request.ts' | ||
import { Response as Res } from '../../response.ts' | ||
|
||
export type DownloadOptions = SendFileOptions & | ||
Partial<{ | ||
headers: Record<string, any> | ||
}> | ||
|
||
type Callback = (err?: any) => void | ||
|
||
export const download = <Request extends Req = Req, Response extends Res = Res>(req: Request, res: Response) => ( | ||
path: string, | ||
filename?: string | Callback, | ||
options?: DownloadOptions | Callback, | ||
cb?: Callback | ||
): Response => { | ||
let done = cb | ||
let name: string | null = filename as string | ||
let opts: DownloadOptions | null = options as DownloadOptions | ||
|
||
// support function as second or third arg | ||
if (typeof filename === 'function') { | ||
done = filename | ||
name = null | ||
} else if (typeof options === 'function') { | ||
done = options | ||
opts = null | ||
} | ||
|
||
// set Content-Disposition when file is sent | ||
const headers: Record<string, any> = { | ||
'Content-Disposition': contentDisposition(name || path) | ||
} | ||
|
||
// merge user-provided headers | ||
if (opts && opts.headers) { | ||
for (const key of Object.keys(opts.headers)) { | ||
if (key.toLowerCase() !== 'content-disposition') headers[key] = opts.headers[key] | ||
} | ||
} | ||
|
||
// merge user-provided options | ||
opts = { ...opts, headers } | ||
|
||
// send file | ||
|
||
return sendFile<Request, Response>(req, res)( | ||
opts.root ? path : resolve(path), | ||
opts, | ||
done || (() => undefined) | ||
) as Response | ||
} | ||
|
||
export const attachment = <Response extends Res = Res>(res: Response) => (filename?: string): Response => { | ||
if (filename) setContentType(res)(extname(filename)) | ||
|
||
setHeader(res)('Content-Disposition', contentDisposition(filename)) | ||
|
||
return res | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { Request } from '../../request.ts' | ||
import { Response } from '../../response.ts' | ||
|
||
export type JSONPOptions = Partial<{ | ||
escape: boolean | ||
replacer: ((this: any, key: string, value: any) => any) | undefined | ||
spaces: string | number | ||
callbackName: string | ||
}> | ||
|
||
function stringify( | ||
value: unknown, | ||
replacer: JSONPOptions['replacer'], | ||
spaces: JSONPOptions['spaces'], | ||
escape: JSONPOptions['escape'] | ||
) { | ||
let json = replacer || spaces ? JSON.stringify(value, replacer, spaces) : JSON.stringify(value) | ||
|
||
if (escape) { | ||
json = json.replace(/[<>&]/g, (c) => { | ||
switch (c.charCodeAt(0)) { | ||
case 0x3c: | ||
return '\\u003c' | ||
case 0x3e: | ||
return '\\u003e' | ||
case 0x26: | ||
return '\\u0026' | ||
default: | ||
return c | ||
} | ||
}) | ||
} | ||
|
||
return json | ||
} | ||
|
||
/** | ||
* Send JSON response with JSONP callback support | ||
* @param req Request | ||
* @param res Response | ||
* @param app App | ||
*/ | ||
export const jsonp = (req: Request, res: Response) => (obj: unknown, opts: JSONPOptions = {}) => { | ||
const val = obj | ||
|
||
const { escape, replacer, spaces, callbackName = 'callback' } = opts | ||
|
||
let body = stringify(val, replacer, spaces, escape) | ||
|
||
let callback = req.query[callbackName] | ||
|
||
if (!res.get('Content-Type')) { | ||
res.set('X-Content-Type-Options', 'nosniff') | ||
res.set('Content-Type', 'application/json') | ||
} | ||
|
||
// jsonp | ||
if (typeof callback === 'string' && callback.length !== 0) { | ||
res.set('X-Content-Type-Options', 'nosniff') | ||
res.set('Content-Type', 'text/javascript') | ||
|
||
// restrict callback charset | ||
callback = callback.replace(/[^[\]\w$.]/g, '') | ||
|
||
// replace chars not allowed in JavaScript that are in JSON | ||
body = body.replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029') | ||
|
||
// the /**/ is a specific security mitigation for "Rosetta Flash JSONP abuse" | ||
// the typeof check is just to reduce client error noise | ||
body = `/**/ typeof ${callback} === 'function' && ${callback}(${body});` | ||
} | ||
|
||
return res.send(body) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.