1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
|
'use strict'
const {createServer: createTlsServer} = require('tls')
const {EventEmitter} = require('events')
const createParser = require('./lib/request-parser')
const createResponse = require('./lib/response')
const {
ALPN_ID,
MIN_TLS_VERSION,
} = require('./lib/util')
const createGeminiServer = (opt = {}, onRequest) => {
if (typeof opt === 'function') {
onRequest = opt
opt = {}
}
const {
tlsOpt,
} = {
tlsOpt: {},
...opt,
}
const onConnection = (socket) => {
const req = createParser()
socket.pipe(req)
socket.once('error', (err) => {
socket.unpipe(req)
req.destroy(err)
})
const close = () => {
socket.destroy()
req.destroy()
}
let timeout = setTimeout(close, 20 * 1000)
req.once('header', (header) => {
clearTimeout(timeout)
// prepare req
req.socket = socket
req.url = header.url
const url = new URL(header.url, 'http://foo/')
req.path = url.pathname
// todo: req.abort(), req.destroy()
// prepare res
const res = createResponse()
res.pipe(socket)
res.once('error', (err) => {
console.error('error', err)
res.unpipe(socket)
socket.destroy(err)
})
Object.defineProperty(res, 'socket', {value: socket})
onRequest(req, res)
server.emit('request', req, res)
})
}
const server = createTlsServer({
ALPNProtocols: [ALPN_ID],
minVersion: MIN_TLS_VERSION,
requestCert: !!alwaysRequireClientCert,
// > Gemini requests typically will be made without a client
// > certificate being sent to the server. If a requested resource
// > is part of a server-side application which requires persistent
// > state, a Gemini server can [...] request that the client repeat
// the request with a "transient certificate" to initiate a client
// > certificate section.
rejectUnauthorized: false,
...tlsOpt,
}, onConnection)
return server
}
module.exports = createGeminiServer
|