Skip to content
This repository was archived by the owner on Jul 6, 2025. It is now read-only.

Commit b696961

Browse files
author
Je
committed
refator: rewrite APIRequest
feat: add brotli compress doc: update jsdoc for types.ts
1 parent d78eb5e commit b696961

File tree

6 files changed

+116
-134
lines changed

6 files changed

+116
-134
lines changed

api.ts

Lines changed: 56 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1+
import { compress as brotli } from 'https://deno.land/x/[email protected]/mod.ts'
12
import { gzipEncode } from 'https://deno.land/x/[email protected]/mod.ts'
23
import log from './log.ts'
3-
import type { ServerRequest } from './std.ts'
4-
import type { APIRequest, APIRequestURL, Response } from './types.ts'
5-
6-
export class Request implements APIRequest {
7-
#req: ServerRequest
8-
#url: APIRequestURL
9-
#cookies: ReadonlyMap<string, string> = new Map()
4+
import { ServerRequest } from './std.ts'
5+
import type { APIRequest } from './types.ts'
6+
7+
export class Request extends ServerRequest implements APIRequest {
8+
#pathname: string
9+
#params: Record<string, string>
10+
#query: URLSearchParams
11+
#cookies: ReadonlyMap<string, string>
1012
#resp = {
1113
status: 200,
1214
headers: new Headers({
@@ -16,65 +18,41 @@ export class Request implements APIRequest {
1618
done: false
1719
}
1820

19-
constructor(req: ServerRequest, url: APIRequestURL) {
20-
this.#req = req
21-
this.#url = url
22-
}
23-
24-
get method(): string {
25-
return this.#req.method
26-
}
27-
28-
get proto() {
29-
return this.#req.proto
30-
}
31-
32-
get protoMinor() {
33-
return this.#req.protoMinor
34-
}
35-
36-
get protoMajor() {
37-
return this.#req.protoMajor
38-
}
39-
40-
get conn() {
41-
return this.#req.conn
42-
}
43-
44-
get r() {
45-
return this.#req.r
46-
}
47-
48-
get w() {
49-
return this.#req.w
50-
}
51-
52-
get done() {
53-
return this.#req.done
54-
}
55-
56-
get contentLength() {
57-
return this.#req.contentLength
58-
}
59-
60-
get body() {
61-
return this.#req.body
62-
}
63-
64-
async respond(r: Response) {
65-
return this.#req.respond(r)
21+
constructor(req: ServerRequest, pathname: string, params: Record<string, string>, query: URLSearchParams) {
22+
super()
23+
this.conn = req.conn
24+
this.r = req.r
25+
this.w = req.w
26+
this.method = req.method
27+
this.url = req.url
28+
this.proto = req.proto
29+
this.protoMinor = req.protoMinor
30+
this.protoMajor = req.protoMajor
31+
this.headers = req.headers
32+
this.done = req.done
33+
this.#pathname = pathname
34+
this.#params = params
35+
this.#query = query
36+
const cookies = new Map()
37+
this.headers.get('cookie')?.split(';').forEach(cookie => {
38+
const p = cookie.trim().split('=')
39+
if (p.length >= 2) {
40+
cookies.set(p.shift()!.trim(), decodeURI(p.join('=')))
41+
}
42+
})
43+
this.#cookies = cookies
6644
}
6745

68-
async finalize() {
69-
return this.#req.finalize()
46+
get pathname(): string {
47+
return this.#pathname
7048
}
7149

72-
get url(): APIRequestURL {
73-
return this.#url
50+
get params(): Record<string, string> {
51+
return this.#params
7452
}
7553

76-
get headers(): Headers {
77-
return this.#req.headers
54+
get query(): URLSearchParams {
55+
return this.#query
7856
}
7957

8058
get cookies(): ReadonlyMap<string, string> {
@@ -102,11 +80,11 @@ export class Request implements APIRequest {
10280
return this
10381
}
10482

105-
json(data: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number) {
106-
return this.send(JSON.stringify(data, replacer, space), 'application/json')
83+
async json(data: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): Promise<void> {
84+
await this.send(JSON.stringify(data, replacer, space), 'application/json; charset=utf-8')
10785
}
10886

109-
async send(data: string | Uint8Array | ArrayBuffer, contentType?: string) {
87+
async send(data: string | Uint8Array | ArrayBuffer, contentType?: string): Promise<void> {
11088
if (this.#resp.done) {
11189
log.warn('ServerRequest: repeat respond calls')
11290
return
@@ -125,41 +103,36 @@ export class Request implements APIRequest {
125103
this.#resp.headers.set('Content-Type', contentType)
126104
} else if (this.#resp.headers.has('Content-Type')) {
127105
contentType = this.#resp.headers.get('Content-Type')!
106+
} else if (typeof data === 'string' && data.length > 0) {
107+
contentType = 'text/plain; charset=utf-8'
128108
}
129109
let isText = false
130110
if (contentType) {
131111
if (contentType.startsWith('text/')) {
132112
isText = true
133-
} else if (/^application\/(javascript|typecript|json|xml)/.test(contentType)) {
113+
} else if (/^application\/(javascript|typecript|json|xml)/i.test(contentType)) {
134114
isText = true
135-
} else if (/^image\/svg+xml/.test(contentType)) {
115+
} else if (/^image\/svg+xml/i.test(contentType)) {
136116
isText = true
137117
}
138118
}
139-
if (isText && body.length > 1024 && this.#req.headers.get('accept-encoding')?.includes('gzip')) {
140-
this.#resp.headers.set('Vary', 'Origin')
141-
this.#resp.headers.set('Content-Encoding', 'gzip')
142-
body = gzipEncode(body)
119+
if (isText && body.length > 1024) {
120+
if (this.headers.get('accept-encoding')?.includes('br')) {
121+
this.#resp.headers.set('Vary', 'Origin')
122+
this.#resp.headers.set('Content-Encoding', 'br')
123+
body = brotli(body)
124+
} else if (this.headers.get('accept-encoding')?.includes('gzip')) {
125+
this.#resp.headers.set('Vary', 'Origin')
126+
this.#resp.headers.set('Content-Encoding', 'gzip')
127+
body = gzipEncode(body)
128+
}
143129
}
144130
this.#resp.headers.set('Date', (new Date).toUTCString())
145131
this.#resp.done = true
146-
return this.#req.respond({
132+
await this.respond({
147133
status: this.#resp.status,
148134
headers: this.#resp.headers,
149135
body
150136
}).catch(err => log.warn('ServerRequest.respond:', err.message))
151137
}
152-
153-
async end(status: number) {
154-
if (this.#resp.done) {
155-
log.warn('ServerRequest: repeat respond calls')
156-
return
157-
}
158-
this.#resp.headers.set('Date', (new Date).toUTCString())
159-
this.#resp.done = true
160-
return this.#req.respond({
161-
status,
162-
headers: this.#resp.headers,
163-
}).catch(err => log.warn('ServerRequest.respond:', err.message))
164-
}
165138
}

cli.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ async function main() {
127127
const port = parseInt(match[2])
128128
listenAndServe({ port }, async (req: ServerRequest) => {
129129
const url = new URL('http://localhost' + req.url)
130-
const resp = new Request(req, { pathname: util.cleanPath(url.pathname), params: {}, query: url.searchParams })
130+
const resp = new Request(req, util.cleanPath(url.pathname), {}, url.searchParams)
131131
const filepath = path.join(cwd, url.pathname)
132132
try {
133133
const info = await Deno.lstat(filepath)
@@ -148,10 +148,10 @@ async function main() {
148148
resp.send(await Deno.readFile(filepath), getContentType(filepath))
149149
} catch (err) {
150150
if (err instanceof Deno.errors.NotFound) {
151-
resp.status(404).send('file not found', 'text/plain')
151+
resp.status(404).send('file not found')
152152
return
153153
}
154-
resp.status(500).send(err.message, 'text/plain')
154+
resp.status(500).send(err.message)
155155
}
156156
})
157157
log.info(`Proxy https://deno.land/x/aleph on http://localhost:${port}`)

project.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ export class Project {
185185
try {
186186
const { default: handle } = await import('file://' + this.#modules.get(moduleID)!.jsFile)
187187
if (util.isFunction(handle)) {
188-
await handle(new Request(req, url))
188+
await handle(new Request(req, url.pathname, url.params, url.query))
189189
} else {
190190
req.respond({
191191
status: 500,

server.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export async function start(appDir: string, port: number, isDev = false, reload
1717
for await (const req of s) {
1818
const url = new URL('http://localhost/' + req.url)
1919
const pathname = util.cleanPath(url.pathname)
20-
const resp = new Request(req, { pathname, params: {}, query: url.searchParams })
20+
const resp = new Request(req, pathname, {}, url.searchParams)
2121

2222
try {
2323
// serve hmr ws
@@ -76,7 +76,7 @@ export async function start(appDir: string, port: number, isDev = false, reload
7676
if (status === 200) {
7777
resp.send(`export default ` + JSON.stringify(data), 'application/javascript; charset=utf-8')
7878
} else {
79-
resp.end(status)
79+
resp.status(status).send('')
8080
}
8181
continue
8282
} else if (pathname.endsWith('.css')) {
@@ -92,7 +92,7 @@ export async function start(appDir: string, port: number, isDev = false, reload
9292
if (mod) {
9393
const etag = req.headers.get('If-None-Match')
9494
if (etag && etag === mod.hash) {
95-
resp.end(304)
95+
resp.status(304).send('')
9696
continue
9797
}
9898

@@ -117,7 +117,7 @@ export async function start(appDir: string, port: number, isDev = false, reload
117117
if (existsFileSync(filePath)) {
118118
const info = await Deno.lstat(filePath)
119119
if (info.mtime?.toUTCString() === req.headers.get('If-Modified-Since')) {
120-
resp.end(304)
120+
resp.status(304).send('')
121121
continue
122122
}
123123

@@ -148,5 +148,3 @@ export async function start(appDir: string, port: number, isDev = false, reload
148148
}
149149
}
150150
}
151-
152-

std.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export { ensureFile } from 'https://deno.land/[email protected]/fs/ensure_file.ts'
55
export { walk } from 'https://deno.land/[email protected]/fs/walk.ts'
66
export { Sha1 } from 'https://deno.land/[email protected]/hash/sha1.ts'
77
export { listenAndServe, serve, ServerRequest } from 'https://deno.land/[email protected]/http/server.ts'
8+
export type { Response } from 'https://deno.land/[email protected]/http/server.ts'
89
export { fromStreamReader } from 'https://deno.land/[email protected]/io/mod.ts'
910
export * as path from 'https://deno.land/[email protected]/path/mod.ts'
1011
export * as ws from 'https://deno.land/[email protected]/ws/mod.ts'

0 commit comments

Comments
 (0)