Skip to content

Commit

Permalink
Edited implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Hage Yaapa committed Jun 5, 2018
1 parent b3a7256 commit c24e20b
Show file tree
Hide file tree
Showing 9 changed files with 196 additions and 135 deletions.
40 changes: 17 additions & 23 deletions packages/http-server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,40 @@ This package implements the HTTP / HTTPS server endpoint for LoopBack 4 apps.

## Overview

This is an internal package used by `RestServer` for creating its HTTP / HTTPS server.
This is an internal package used by LoopBack 4 for creating HTTP / HTTPS server.

## Installation

To use this package, you'll need to install `@loopback/http-server`.

```sh
npm i @loopback/htp-server
npm i @loopback/http-server
```

## Usage

`@loopback/http-server` should be instantiated with an instance of `RestServer`, HTTP / HTTPS options object, and a request handler function.
`@loopback/http-server` should be instantiated with a request handler function, and an HTTP / HTTPS options object.

```js
import {RestServer} from '@loopback/rest';
import {Application} from '@loopback/core';

const app = new Application();
const restServer = new RestServer(app);
const httpServer = new HttpServer(restServer, {port: 3000, host: ''}, (req, res) => {});
const httpServer = new HttpServer((req, res) => { res.end('Hello world')}, {port: 3000, host: ''});
```

Call the `start()` method to start the server.

```js
httpServer.start()
```
Instance methods of `HttpServer`.

Call the `stop()` method to stop the server.
| Method | Description |
| ------- | -------------------- |
| `start()` | Starts the server |
| `stop()` | Stops the server |

Use the `listening` property to check whether the server is listening for connections or not.
Instance properties of `HttpServer`.

```js
if (httpServer.listening) {
console.log('Server is running');
} else {
console.log('Server is not running');
}
```
| Property | Description |
| ----------- | ---------------------- |
| `address` | Address details |
| `host` | host of the server |
| `port` | port of the server |
| `protocol` | protocol of the server |
| `url` | url the server |

## Contributions

Expand Down
16 changes: 8 additions & 8 deletions packages/http-server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@loopback/http-server",
"version": "0.0.0",
"version": "0.0.1",
"description": "",
"engines": {
"node": ">=8"
Expand All @@ -12,35 +12,35 @@
"build:current": "lb-tsc",
"build:dist8": "lb-tsc es2017",
"build:dist10": "lb-tsc es2018",
"clean": "lb-clean loopback-core*.tgz dist* package api-docs",
"clean": "lb-clean loopback-http-server*.tgz dist* package api-docs",
"pretest": "npm run build:current",
"integration": "lb-mocha \"DIST/test/integration/**/*.js\"",
"test": "lb-mocha \"DIST/test/unit/**/*.js\" \"DIST/test/integration/**/*.js\" \"DIST/test/acceptance/**/*.js\"",
"unit": "lb-mocha \"DIST/test/unit/**/*.js\"",
"verify": "npm pack && tar xf loopback-core*.tgz && tree package && npm run clean"
"verify": "npm pack && tar xf loopback-http-server*.tgz && tree package && npm run clean"
},
"author": "IBM",
"copyright.owner": "IBM Corp.",
"license": "MIT",
"dependencies": {
"@loopback/dist-util": "^0.3.1",
"@loopback/rest": "^0.10.4",
"p-event": "^2.0.0"
},
"devDependencies": {
"@loopback/build": "^0.6.5",
"@loopback/core": "^0.8.4",
"@loopback/testlab": "^0.10.4",
"@types/debug": "^0.0.30",
"@types/node": "^10.1.2",
"@types/p-event": "^1.3.0"
"@types/p-event": "^1.3.0",
"@types/request-promise-native": "^1.0.14",
"request-promise-native": "^1.0.5"
},
"files": [
"README.md",
"index.js",
"index.d.ts",
"dist/src",
"api-docs",
"dist*/src",
"dist*/index*",
"src"
],
"repository": {
Expand Down
109 changes: 69 additions & 40 deletions packages/http-server/src/http-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,83 +3,112 @@
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {createServer, Server} from 'http';
import {RestServer, HttpRequestListener} from '@loopback/rest';
import {createServer, Server, ServerRequest, ServerResponse} from 'http';
import {AddressInfo} from 'net';
import * as pEvent from 'p-event';

export type HttpRequestListener = (
req: ServerRequest,
res: ServerResponse,
) => void;

/**
* Object for specifyig the HTTP / HTTPS server options
*/
export type HttpOptions = {
port: number;
host: string | undefined;
export type HttpServerOptions = {
port?: number;
host?: string;
protocol?: HttpProtocol;
};

export type HttpProtocol = 'http' | 'https'; // Will be extended to `http2` in the future

/**
* HTTP / HTTPS server used by LoopBack's RestServer
*
* @export
* @class HttpServer
*/
export class HttpServer {
private restServer: RestServer;
private httpPort: number;
private httpHost: string | undefined;
private _port: number;
private _host?: string;
/**
* Protocol, default to `http`
*/
private _protocol: HttpProtocol;
private _address: AddressInfo;
private httpRequestListener: HttpRequestListener;
private httpServer: Server;

/**
* @param restServer
* @param httpOptions
* @param httpServerOptions
* @param httpRequestListener
*/
constructor(
restServer: RestServer,
httpOptions: HttpOptions,
httpRequestListener: HttpRequestListener,
httpServerOptions?: HttpServerOptions,
) {
this.restServer = restServer;
this.httpPort = httpOptions.port;
this.httpHost = httpOptions.host;
this.httpServer = createServer(httpRequestListener);
this._port = (httpServerOptions && httpServerOptions.port) || 0;
this._host = (httpServerOptions && httpServerOptions.host) || undefined;
this._protocol =
(httpServerOptions && httpServerOptions.protocol) || 'http';
this.httpRequestListener = httpRequestListener;
}

/**
* Starts the HTTP / HTTPS server
*/
public start(): Promise<void> {
this.httpServer.listen(this.httpPort, this.httpHost);
return new Promise<void>(async (resolve, reject) => {
try {
await pEvent(this.httpServer, 'listening');
const address = this.httpServer.address() as AddressInfo;
this.restServer.bind('rest.port').to(address.port);
resolve();
} catch (e) {
reject();
}
});
public async start(port?: number, host?: string) {
this.httpServer = createServer(this.httpRequestListener);
this._port = typeof port === 'undefined' ? 0 : this._port;
this._host = host || this._host;
this.httpServer.listen(this._port, this._host);
await pEvent(this.httpServer, 'listening');
this._address = this.httpServer.address() as AddressInfo;
this._host = this._host || this._address.address;
this._port = this._address.port;
}

/**
* Stops the HTTP / HTTPS server
*/
public stop(): Promise<void> {
public async stop() {
this.httpServer.close();
return new Promise<void>(async (resolve, reject) => {
try {
await pEvent(this.httpServer, 'close');
resolve();
} catch (e) {
reject();
}
});
await pEvent(this.httpServer, 'close');
}

/**
* Protocol of the HTTP / HTTPS server
*/
public get protocol(): HttpProtocol {
return this._protocol;
}

/**
* Port number of the HTTP / HTTPS server
*/
public get port(): number {
return this._port;
}

/**
* Host of the HTTP / HTTPS server
*/
public get host(): string | undefined {
return this._host;
}

/**
* URL of the HTTP / HTTPS server
*/
public get url(): string {
return `${this._protocol}://${this.host}:${this.port}`;
}

/**
* Whether the HTTP / HTTPS server is listening or not
* Address of the HTTP / HTTPS server
*/
public get listening(): Boolean {
return this.httpServer.listening;
public get address(): AddressInfo {
return this._address;
}
}
77 changes: 65 additions & 12 deletions packages/http-server/test/integration/http-server.integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,81 @@
// Node module: @loopback/http-server
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
import {RestServer, RestComponent} from '@loopback/rest';
import {Application, ApplicationConfig} from '@loopback/core';
import * as assert from 'assert';
import {HttpServer} from '../../';
import {supertest, expect} from '@loopback/testlab';
import * as makeRequest from 'request-promise-native';
import {ServerRequest, ServerResponse} from 'http';

describe('HttpServer', () => {
describe('HttpServer (integration)', () => {
it('starts server', async () => {
const server = await givenAServer();
const server = new HttpServer(dummyRequestHandler);
await server.start();
assert(server.listening, 'Server not started');
supertest(server.url)
.get('/')
.expect(200);
await server.stop();
});

it('stops server', async () => {
const server = await givenAServer();
const server = new HttpServer(dummyRequestHandler);
await server.start();
await server.stop();
assert(!server.listening, 'Server not stopped');
await expect(
makeRequest({
uri: server.url,
}),
).to.be.rejectedWith(/ECONNREFUSED/);
});

async function givenAServer(options?: ApplicationConfig) {
const app = new Application(options);
app.component(RestComponent);
return await app.getServer(RestServer);
it('exports port', async () => {
const server = new HttpServer(dummyRequestHandler);
await server.start();
expect(server)
.to.have.property('port')
.which.is.a.Number()
.which.is.greaterThan(0);
await server.stop();
});

it('exports host', async () => {
const server = new HttpServer(dummyRequestHandler);
await server.start();
expect(server)
.to.have.property('host')
.which.is.a.String();
await server.stop();
});

it('exports protocol', async () => {
const server = new HttpServer(dummyRequestHandler);
await server.start();
expect(server)
.to.have.property('protocol')
.which.is.a.String()
.match(/http|https/);
await server.stop();
});

it('exports url', async () => {
const server = new HttpServer(dummyRequestHandler);
await server.start();
expect(server)
.to.have.property('url')
.which.is.a.String()
.match(/http|https\:\/\//);
await server.stop();
});

it('exports address', async () => {
const server = new HttpServer(dummyRequestHandler);
await server.start();
expect(server)
.to.have.property('address')
.which.is.an.Object();
await server.stop();
});

function dummyRequestHandler(req: ServerRequest, res: ServerResponse): void {
res.end();
}
});
30 changes: 0 additions & 30 deletions packages/http-server/test/unit/http-server.unit.ts

This file was deleted.

Loading

0 comments on commit c24e20b

Please sign in to comment.