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

Commit ffea32e

Browse files
committed
Clean up
1 parent dc94a0d commit ffea32e

File tree

5 files changed

+124
-89
lines changed

5 files changed

+124
-89
lines changed

server/api.ts

Lines changed: 8 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,11 @@ import type { MultipartFormData } from 'https://deno.land/[email protected]/mime/multip
44
import { MultipartReader } from 'https://deno.land/[email protected]/mime/multipart.ts'
55
import log from '../shared/log.ts'
66
import type { APIRequest, ServerRequest, ServerResponse } from '../types.ts'
7-
8-
let brotli: ((data: Uint8Array) => Uint8Array) | null = null
9-
let gzip: ((data: Uint8Array) => Uint8Array) | null = null
7+
import compress from './compress.ts'
108

119
type Response = {
1210
status: number
1311
headers: Headers
14-
compress: boolean
1512
done: boolean
1613
}
1714

@@ -22,7 +19,7 @@ export class Request implements APIRequest {
2219
#cookies: ReadonlyMap<string, string>
2320
#resp: Response
2421

25-
constructor(req: ServerRequest, params: Record<string, string>, query: URLSearchParams, compress: boolean = true) {
22+
constructor(req: ServerRequest, params: Record<string, string>, query: URLSearchParams) {
2623
this.#req = req
2724
this.#params = params
2825
this.#query = query
@@ -39,7 +36,6 @@ export class Request implements APIRequest {
3936
headers: new Headers({
4037
Server: 'Aleph.js',
4138
}),
42-
compress,
4339
done: false
4440
}
4541
}
@@ -53,7 +49,7 @@ export class Request implements APIRequest {
5349
}
5450

5551
get hostname(): string {
56-
return (this.#req.conn.remoteAddr as Deno.NetAddr).hostname;
52+
return (this.#req.conn.remoteAddr as Deno.NetAddr).hostname
5753
}
5854

5955
get headers(): Headers {
@@ -172,39 +168,12 @@ export class Request implements APIRequest {
172168
contentType = 'text/plain; charset=utf-8'
173169
this.#resp.headers.set('Content-Type', contentType)
174170
}
175-
if (this.#resp.compress) {
176-
let shouldCompress = false
177-
if (contentType) {
178-
if (contentType.startsWith('text/')) {
179-
shouldCompress = true
180-
} else if (/^application\/(javascript|json|xml|wasm)/i.test(contentType)) {
181-
shouldCompress = true
182-
} else if (/^image\/svg\+xml/i.test(contentType)) {
183-
shouldCompress = true
184-
}
185-
}
186-
if (shouldCompress && body.length > 1024) {
187-
const ae = this.headers.get('accept-encoding') || ''
188-
if (ae.includes('br')) {
189-
this.#resp.headers.set('Vary', 'Origin')
190-
this.#resp.headers.set('Content-Encoding', 'br')
191-
if (brotli === null) {
192-
const { compress } = await import('https://deno.land/x/[email protected]/mod.ts')
193-
brotli = compress
194-
}
195-
body = brotli(body)
196-
} else if (ae.includes('gzip')) {
197-
this.#resp.headers.set('Vary', 'Origin')
198-
this.#resp.headers.set('Content-Encoding', 'gzip')
199-
if (gzip === null) {
200-
const denoflate = await import('https://deno.land/x/[email protected]/mod.ts')
201-
gzip = (data: Uint8Array) => denoflate.gzip(data, undefined)
202-
}
203-
body = gzip(body)
204-
}
205-
}
171+
if (contentType) {
172+
body = compress.apply(this.#req, this.#resp, contentType, body)
173+
}
174+
if (!this.#resp.headers.has('Date')) {
175+
this.#resp.headers.set('Date', (new Date).toUTCString())
206176
}
207-
this.#resp.headers.set('Date', (new Date).toUTCString())
208177
this.#resp.done = true
209178
try {
210179
await this.respond({

server/compress.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import type { ServerRequest, ServerResponse } from '../types.ts'
2+
3+
const brotliMod = 'https://deno.land/x/[email protected]/mod.ts'
4+
const flateMod = 'https://deno.land/x/[email protected]/mod.ts'
5+
6+
class Compression {
7+
#brotli: ((data: Uint8Array) => Uint8Array) | null = null
8+
#gzip: ((data: Uint8Array) => Uint8Array) | null = null
9+
#ready: boolean = false
10+
11+
async init() {
12+
if (this.#brotli === null) {
13+
const { compress } = await import(brotliMod)
14+
this.#brotli = compress
15+
}
16+
if (this.#gzip === null) {
17+
const denoflate = await import(flateMod)
18+
this.#gzip = (data: Uint8Array) => denoflate.gzip(data, undefined)
19+
}
20+
this.#ready = true
21+
}
22+
23+
apply(req: ServerRequest, resp: ServerResponse, contentType: string, content: Uint8Array): Uint8Array {
24+
if (!this.#ready) {
25+
return content
26+
}
27+
28+
let shouldCompress = false
29+
if (contentType) {
30+
if (contentType.startsWith('text/')) {
31+
shouldCompress = true
32+
} else if (/^application\/(javascript|json|xml|wasm)/i.test(contentType)) {
33+
shouldCompress = true
34+
} else if (/^image\/svg\+xml/i.test(contentType)) {
35+
shouldCompress = true
36+
}
37+
}
38+
39+
if (shouldCompress && content.length > 1024) {
40+
const ae = req.headers.get('accept-encoding') || ''
41+
if (ae.includes('br') && this.#brotli !== null) {
42+
resp.headers?.set('Vary', 'Origin')
43+
resp.headers?.set('Content-Encoding', 'br')
44+
return this.#brotli(content)
45+
} else if (ae.includes('gzip') && this.#gzip !== null) {
46+
resp.headers?.set('Vary', 'Origin')
47+
resp.headers?.set('Content-Encoding', 'gzip')
48+
return this.#gzip(content)
49+
}
50+
}
51+
52+
return content
53+
}
54+
}
55+
56+
export default new Compression()

server/mod.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from './app.ts'
22
export * from './server.ts'
3+
export * from './stdserver.ts'

server/server.ts

Lines changed: 3 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { join } from 'https://deno.land/[email protected]/path/mod.ts'
2-
import { serve as stdServe, serveTLS } from 'https://deno.land/[email protected]/http/server.ts'
32
import { acceptWebSocket, isWebSocketCloseEvent } from 'https://deno.land/[email protected]/ws/mod.ts'
43
import { trimModuleExt } from '../framework/core/module.ts'
54
import { rewriteURL } from '../framework/core/routing.ts'
@@ -28,10 +27,10 @@ export class Server {
2827
}
2928

3029
const app = this.#app
31-
const { basePath, compress, headers, rewrites } = app.config
30+
const { basePath, headers, rewrites } = app.config
3231
const url = rewriteURL(r.url, basePath, rewrites)
3332
const pathname = decodeURI(url.pathname)
34-
const req = new Request(r, {}, url.searchParams, !app.isDev && compress)
33+
const req = new Request(r, {}, url.searchParams)
3534

3635
// set custom headers
3736
for (const key in headers) {
@@ -158,7 +157,7 @@ export class Server {
158157
const [{ params, query }, { jsFile, hash }] = route
159158
const { default: handle } = await import(`file://${jsFile}#${hash.slice(0, 6)}`)
160159
if (util.isFunction(handle)) {
161-
await handle(new Request(req, params, query, !app.isDev && compress))
160+
await handle(new Request(req, params, query))
162161
} else {
163162
req.status(500).json({ status: 500, message: 'bad api handler' })
164163
}
@@ -186,49 +185,3 @@ export class Server {
186185
}
187186
}
188187
}
189-
190-
/** Options for creating a standard Aleph server. */
191-
export type ServeOptions = {
192-
/** The Aleph Server Application to serve. */
193-
app: Application
194-
/** The port to listen on. */
195-
port: number
196-
/** A literal IP address or host name that can be resolved to an IP address.
197-
* If not specified, defaults to `0.0.0.0`. */
198-
hostname?: string
199-
/** Server certificate file. */
200-
certFile?: string
201-
/** Server public key file. */
202-
keyFile?: string
203-
}
204-
205-
/** Create a standard Aleph server. */
206-
export async function serve({ app, port, hostname, certFile, keyFile }: ServeOptions) {
207-
const server = new Server(app)
208-
await app.ready
209-
210-
while (true) {
211-
try {
212-
let s: AsyncIterable<ServerRequest>
213-
if (certFile && keyFile) {
214-
s = serveTLS({ port, hostname, certFile, keyFile })
215-
} else {
216-
s = stdServe({ port, hostname })
217-
}
218-
log.info(`Server ready on http://${hostname || 'localhost'}:${port}${app.config.basePath}`)
219-
for await (const r of s) {
220-
server.handle(r)
221-
}
222-
} catch (err) {
223-
if (err instanceof Deno.errors.AddrInUse) {
224-
if (!app.isDev) {
225-
log.fatal(`port ${port} already in use!`)
226-
}
227-
log.warn(`port ${port} already in use, try ${port + 1}...`)
228-
port++
229-
} else {
230-
log.fatal(err.message)
231-
}
232-
}
233-
}
234-
}

server/stdserver.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { serve as stdServe, serveTLS } from 'https://deno.land/[email protected]/http/server.ts'
2+
import log from '../shared/log.ts'
3+
import type { ServerRequest } from '../types.ts'
4+
import { Application } from './app.ts'
5+
import compress from './compress.ts'
6+
import { Server } from './server.ts'
7+
8+
/** Options for creating a standard Aleph server. */
9+
export type ServeOptions = {
10+
/** The Aleph Server Application to serve. */
11+
app: Application
12+
/** The port to listen on. */
13+
port: number
14+
/** A literal IP address or host name that can be resolved to an IP address.
15+
* If not specified, defaults to `0.0.0.0`. */
16+
hostname?: string
17+
/** Server certificate file. */
18+
certFile?: string
19+
/** Server public key file. */
20+
keyFile?: string
21+
}
22+
23+
/** Create a standard Aleph server. */
24+
export async function serve({ app, port, hostname, certFile, keyFile }: ServeOptions) {
25+
const server = new Server(app)
26+
await app.ready
27+
28+
if (!app.isDev && app.config.compress) {
29+
await compress.init()
30+
}
31+
32+
while (true) {
33+
try {
34+
let s: AsyncIterable<ServerRequest>
35+
if (certFile && keyFile) {
36+
s = serveTLS({ port, hostname, certFile, keyFile })
37+
} else {
38+
s = stdServe({ port, hostname })
39+
}
40+
log.info(`Server ready on http://${hostname || 'localhost'}:${port}${app.config.basePath}`)
41+
for await (const r of s) {
42+
server.handle(r)
43+
}
44+
} catch (err) {
45+
if (err instanceof Deno.errors.AddrInUse) {
46+
if (!app.isDev) {
47+
log.fatal(`port ${port} already in use!`)
48+
}
49+
log.warn(`port ${port} already in use, try ${port + 1}...`)
50+
port++
51+
} else {
52+
log.fatal(err.message)
53+
}
54+
}
55+
}
56+
}

0 commit comments

Comments
 (0)