-
Notifications
You must be signed in to change notification settings - Fork 3.7k
feat: Enable using something other than Express for a web server #1809
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| import { HttpServerPort } from './HttpServerPort.mjs' | ||
| import express from 'express' | ||
| import basicAuth from 'express-basic-auth' | ||
|
|
||
| class ExpressHttpServerPort extends HttpServerPort { | ||
| get address() { | ||
| return this.robot.server.address() | ||
| } | ||
| async start(port) { | ||
| const user = process.env.EXPRESS_USER | ||
| const pass = process.env.EXPRESS_PASSWORD | ||
| const stat = process.env.EXPRESS_STATIC | ||
| const address = process.env.EXPRESS_BIND_ADDRESS || process.env.BIND_ADDRESS || '0.0.0.0' | ||
| const limit = process.env.EXPRESS_LIMIT || '100kb' | ||
| const paramLimit = parseInt(process.env.EXPRESS_PARAMETER_LIMIT) || 1000 | ||
| const app = express() | ||
|
|
||
| app.use((req, res, next) => { | ||
| res.setHeader('X-Powered-By', `hubot/${encodeURI(this.robot.name)}`) | ||
| return next() | ||
| }) | ||
|
|
||
| if (user && pass) { | ||
| const authUser = {} | ||
| authUser[user] = pass | ||
| app.use(basicAuth({ users: authUser })) | ||
| } | ||
|
|
||
| app.use(express.json({ limit })) | ||
| app.use(express.urlencoded({ limit, parameterLimit: paramLimit, extended: true })) | ||
|
|
||
| if (stat) { | ||
| app.use(express.static(stat)) | ||
| } | ||
| return new Promise((resolve, reject) => { | ||
| try { | ||
| this.robot.server = app.listen(port, address, () => { | ||
| this.robot.router = app | ||
| this.robot.emit('listening', this.robot.server) | ||
| resolve(this.robot.server) | ||
| }) | ||
| } catch (err) { | ||
| reject(err) | ||
| } | ||
| }) | ||
| } | ||
|
joeyguerra marked this conversation as resolved.
joeyguerra marked this conversation as resolved.
|
||
|
|
||
| async stop() { | ||
| if (!this.robot || !this.robot.server) { | ||
| return | ||
| } | ||
| return new Promise((resolve, reject) => { | ||
| this.robot.server.close(err => { | ||
| if (err) { | ||
| return reject(err) | ||
| } | ||
| resolve() | ||
| }) | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| export default async (robot) => { | ||
| const server = new ExpressHttpServerPort(robot) | ||
| const port = process.env.EXPRESS_PORT || process.env.PORT || 8080 | ||
| await server.start(port) | ||
| return server | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,29 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class HttpServerPort { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| constructor(robot) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.robot = robot | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async stop() {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async start(port) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.port = port | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.robot.server = {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.robot.router = this | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return this.robot.server | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| address() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { port: this.port } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use(handler) {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| get(path, handler) {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| post(path, handler) {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| put(path, handler) {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| patch(path, handler) {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| query(path, handler) {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| delete(path, handler) {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| trace(path, handler) {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| options(path, handler) {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| head(path, handler) {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+15
to
+24
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use(handler) {} | |
| get(path, handler) {} | |
| post(path, handler) {} | |
| put(path, handler) {} | |
| patch(path, handler) {} | |
| query(path, handler) {} | |
| delete(path, handler) {} | |
| trace(path, handler) {} | |
| options(path, handler) {} | |
| head(path, handler) {} | |
| use(handler) { | |
| return this | |
| } | |
| get(path, handler) { | |
| return this | |
| } | |
| post(path, handler) { | |
| return this | |
| } | |
| put(path, handler) { | |
| return this | |
| } | |
| patch(path, handler) { | |
| return this | |
| } | |
| query(path, handler) { | |
| return this | |
| } | |
| delete(path, handler) { | |
| return this | |
| } | |
| trace(path, handler) { | |
| return this | |
| } | |
| options(path, handler) { | |
| return this | |
| } | |
| head(path, handler) { | |
| return this | |
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,8 +3,10 @@ | |
| import { describe, it, beforeEach, afterEach } from 'node:test' | ||
| import assert from 'node:assert/strict' | ||
| import path from 'node:path' | ||
| import http from 'node:http' | ||
|
joeyguerra marked this conversation as resolved.
|
||
| import { Robot, CatchAllMessage, EnterMessage, LeaveMessage, TextMessage, TopicMessage, User, Response } from '../index.mjs' | ||
| import mockAdapter from './fixtures/MockAdapter.mjs' | ||
| import { HttpServerPort } from '../src/ports/HttpServerPort.mjs' | ||
|
|
||
| describe('Robot', () => { | ||
| describe('#http', () => { | ||
|
|
@@ -1041,4 +1043,42 @@ describe('Robot', () => { | |
| delete process.env.PORT | ||
| }) | ||
| }) | ||
|
|
||
| describe('Non-express HTTP server', async () => { | ||
| it('should work with a non-express http server', async () => { | ||
| class HttpServer extends HttpServerPort { | ||
| async start(port) { | ||
| return new Promise((resolve, reject) => { | ||
| try { | ||
| this.robot.server = http.createServer((req, res) => { | ||
| res.writeHead(200, { 'Content-Type': 'text/plain' }) | ||
| res.end('Hello World\n') | ||
| }) | ||
| this.robot.router = this | ||
| this.robot.server = this.robot.server.listen(port, '127.0.0.1', () => { | ||
| this.robot.emit('listening', this.robot.server) | ||
| resolve(this.robot.server) | ||
| }) | ||
| } catch (err) { | ||
| reject(err) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
| const httpDServerFactory = async (robot) => { | ||
| const server = new HttpServer(robot) | ||
| await server.start(process.env.PORT || 0) | ||
| return server | ||
| } | ||
| const robot = new Robot(mockAdapter, httpDServerFactory, 'TestHubot') | ||
|
||
| await robot.loadAdapter() | ||
| await robot.run() | ||
| const port = robot.server.address().port | ||
| const res = await fetch(`http://127.0.0.1:${port}/`) | ||
| assert.equal(res.status, 200) | ||
| assert.equal(await res.text(), 'Hello World\n') | ||
| robot.shutdown() | ||
| delete process.env.PORT | ||
| }) | ||
| }) | ||
| }) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| import test from 'node:test' | ||
| import assert from 'node:assert/strict' | ||
| import { HttpServerPort } from '../../src/ports/HttpServerPort.mjs' | ||
|
|
||
| test('HttpServerPort API', async t => { | ||
| await t.test('start and stop', async () => { | ||
| const server = new HttpServerPort({}) | ||
| await server.start(0) | ||
| assert.equal(server.address().port, 0) | ||
| await server.stop() | ||
| }) | ||
|
|
||
| await t.test('use', async () => { | ||
| const server = new HttpServerPort({}) | ||
| server.use((req, res) => { | ||
| res.end('Hello, World!') | ||
| }) | ||
| await server.start(0) | ||
| await server.stop() | ||
| }) | ||
|
|
||
| await t.test('http methods', async () => { | ||
| const server = new HttpServerPort({}) | ||
| await server.start(0) | ||
| assert.ok(server.get) | ||
| assert.ok(server.post) | ||
| assert.ok(server.put) | ||
| assert.ok(server.patch) | ||
| assert.ok(server.query) | ||
| assert.ok(server.delete) | ||
| assert.ok(server.trace) | ||
| assert.ok(server.options) | ||
| assert.ok(server.head) | ||
| await server.stop() | ||
| }) | ||
| }) | ||
|
joeyguerra marked this conversation as resolved.
|
||
Uh oh!
There was an error while loading. Please reload this page.