Commit 76979454 authored by Sebastiaan Deckers's avatar Sebastiaan Deckers 馃悜

feat: Gopher over HTTPS

parent 3886cf4f
......@@ -130,9 +130,7 @@ Example:
Default: `false`
Settings for DNS over HTTPS support.
Set to `true` for defaults (local DNS resolver) or an object containing custom configuration.
DNS over HTTPS (DoH) support. Set to `true` for defaults (local DNS resolver) or an object containing custom configuration.
### `doh.protocol`
......@@ -170,6 +168,37 @@ Default: `10000`
Number of milliseconds to wait for a response from the DNS resolver.
## `goh`
Default: `false`
[Gopher over HTTPS](https://gopher.commons.host) (GoH) support. Set to `true` or an object to enable.
## `goh.allowHTTP1`
Default: `false`
If `false` only HTTP/2 (or later) clients are accepted. Set to `true` to also accept requests from HTTP/1 clients.
## `goh.unsafeAllowNonStandardPort`
Default: `false`
If `false` the relay is restricted to the standard Gopher port `70`. If `true` the middleware accepts URLs with any port number. Allowing any port is potentially unsafe and not recommended. The middleware does not validate the response, effectively becoming an open TCP/IP proxy.
## `goh.unsafeAllowPrivateAddress`
Default: `false`
If `false` connection attempts to any private IPv4 or IPv6 address are denied. This is important for security when operating a public GoH service to avoid exposing LAN hosts to malicious external users. Set to `true` to allow connections to remote hosts with private IP addresses.
## `goh.timeout`
Default: `10000`
The number of milliseconds to keep idle Gopher sessions active. Defaults to 10 seconds. The HTTP connection (aka session) is not closed, only the TCP/IP socket to the Gopher server and its corresponding HTTP/2 streams with the HTTP user agent.
## `log`
Settings for the [Pino](https://getpino.io)-based logger.
......
......@@ -2,6 +2,7 @@ const connect = require('connect')
const { fingerprint } = require('./middleware/fingerprint')
const { logger } = require('./middleware/logger')
const { playdoh } = require('playdoh')
const { goh } = require('goh')
const { hostOptions } = require('./middleware/hostOptions')
const { allowedMethods } = require('./middleware/allowedMethods')
const { allowCors } = require('./middleware/allowCors')
......@@ -16,9 +17,12 @@ module.exports.app = (options, files) => {
const app = connect()
app.use(fingerprint(options))
app.use(logger(options.log))
if (options.doh !== false) {
if (typeof options.doh === 'object') {
app.use(playdoh(options.doh))
}
if (typeof options.goh === 'object') {
app.use(goh(options.goh))
}
app.use(hostOptions(options, files))
app.use(allowedMethods(['GET', 'HEAD', 'OPTIONS']))
app.use(allowCors())
......
......@@ -12,6 +12,7 @@ module.exports.defaultOptions = {
webroot: ''
},
doh: false,
goh: false,
placeholder: {
hostNotFound: ''
},
......
const test = require('blue-tape')
const { Master } = require('..')
const { join } = require('path')
const { h2 } = require('./helpers/receive')
const { createServer } = require('net')
const eventToPromise = require('event-to-promise')
let gopherServer
test('start mock Gopher server', async (t) => {
gopherServer = createServer((socket) => {
socket.once('data', (chunk) => {
socket.end('1Hello, World!\t\t\t\r\n.\r\n')
})
})
gopherServer.listen(0)
await eventToPromise(gopherServer, 'listening')
})
let master
test('start server', async (t) => {
const cwd = join(__dirname, 'fixtures')
const options = {
goh: {
allowHTTP1: false,
unsafeAllowNonStandardPort: true,
unsafeAllowPrivateAddress: true,
timeout: 10000
}
}
master = new Master({ cwd, options })
await master.listen()
})
test('Make a Gopher over HTTPS request', async (t) => {
const gopherUrl = `gopher://localhost:${gopherServer.address().port}/1/`
const url = `https://localhost:8443/?url=${encodeURIComponent(gopherUrl)}`
const headers = { accept: 'application/gopher' }
const response = await h2(url, { headers })
t.ok(response.ok)
t.is(response.status, 200)
t.is(response.headers.get('content-type'), 'application/gopher')
t.deepEquals(await response.text(), '1Hello, World!\t\t\t\r\n.\r\n')
})
test('stop server', async (t) => master.close())
test('stop resolver', (t) => gopherServer.close(t.end))
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment