feat: stopping the server

ref pnpm/pnpm#960
This commit is contained in:
Zoltan Kochan
2018-01-15 03:36:25 +02:00
parent 15f59aaf24
commit bf9bdbfee2
5 changed files with 95 additions and 10 deletions

View File

@@ -39,6 +39,7 @@
"@pnpm/npm-resolver": "^0.3.9",
"@pnpm/tarball-fetcher": "^0.3.1",
"@types/tape": "^4.2.31",
"is-port-reachable": "^2.0.0",
"mos": "^2.0.0-alpha.3",
"mos-plugin-readme": "^1.0.4",
"package-preview": "^1.0.1",

View File

@@ -13,6 +13,7 @@ devDependencies:
'@pnpm/npm-resolver': 0.3.11
'@pnpm/tarball-fetcher': 0.3.3
'@types/tape': 4.2.31
is-port-reachable: 2.0.0
mos: 2.0.0-alpha.3
mos-plugin-readme: 1.0.4
package-preview: 1.0.4
@@ -1711,6 +1712,12 @@ packages:
node: '>=0.10.0'
resolution:
integrity: sha1-caUMhCnfync8kqOQpKA7OfzVHT4=
/is-port-reachable/2.0.0:
dev: true
engines:
node: '>=4'
resolution:
integrity: sha1-VNE9ZUkX60M64+4ty8N3TyzUTrI=
/is-promise/2.1.0:
dev: true
resolution:
@@ -4000,6 +4007,7 @@ specifiers:
'@types/tape': ^4.2.31
'@types/uuid': ^3.4.3
got: ^8.0.1
is-port-reachable: ^2.0.0
mos: ^2.0.0-alpha.3
mos-plugin-readme: ^1.0.4
p-limit: ^1.1.0

View File

@@ -10,12 +10,16 @@ import pLimit = require('p-limit')
import {StoreController} from 'package-store'
import uuid = require('uuid')
export type StoreServerController = StoreController & {
stop (): Promise<void>,
}
export default function (
initOpts: {
remotePrefix: string,
concurrency?: number,
},
): Promise<StoreController> {
): Promise<StoreServerController> {
const remotePrefix = initOpts.remotePrefix
const limitedFetch = fetch.bind(null, pLimit(initOpts.concurrency || 100))
@@ -39,6 +43,7 @@ export default function (
saveState: async () => {
await limitedFetch(`${remotePrefix}/saveState`, {})
},
stop: () => limitedFetch(`${remotePrefix}/stop`, {}),
updateConnections: async (prefix: string, opts: {addDependencies: string[], removeDependencies: string[], prune: boolean}) => {
await limitedFetch(`${remotePrefix}/updateConnections`, {
opts,

View File

@@ -1,7 +1,10 @@
import logger from '@pnpm/logger'
import {
RequestPackageOptions,
WantedDependency,
} from '@pnpm/package-requester'
import http = require('http')
import {IncomingMessage, Server, ServerResponse} from 'http'
import {RequestPackageOptions, WantedDependency} from '@pnpm/package-requester'
import {StoreController} from 'package-store'
interface RequestBody {
@@ -22,6 +25,7 @@ export default function (
path?: string,
port?: number,
hostname?: string,
ignoreStopRequests?: boolean,
},
) {
const manifestPromises = {}
@@ -97,6 +101,17 @@ export default function (
await store.importPackage(importPackageBody.from, importPackageBody.to, importPackageBody.opts)
res.end(JSON.stringify('OK'))
break
case '/stop':
if (opts.ignoreStopRequests) {
res.statusCode = 403
res.end()
break
}
logger.info('Got request to stop the server')
await close()
res.end(JSON.stringify('OK'))
logger.info('Server stopped')
break
default:
res.statusCode = 404
res.end(`${req.url} does not match any route`)
@@ -114,7 +129,10 @@ export default function (
listener = server.listen(opts.port, opts.hostname)
}
return {
close: () => listener.close(() => { return }),
return { close }
function close () {
listener.close()
return store.close()
}
}

View File

@@ -6,12 +6,15 @@ import {
import {
PackageFilesResponse,
} from '@pnpm/package-requester'
import got = require('got')
import isPortReachable = require('is-port-reachable')
import createResolver from '@pnpm/npm-resolver'
import createFetcher from '@pnpm/tarball-fetcher'
import createStore from 'package-store'
test('server', async t => {
const registry = 'https://registry.npmjs.org/'
const registry = 'https://registry.npmjs.org/'
async function createStoreController () {
const rawNpmConfig = { registry }
const store = '.store'
const resolve = createResolver({
@@ -25,16 +28,19 @@ test('server', async t => {
strictSsl: true,
rawNpmConfig,
})
const storeCtrlForServer = await createStore(resolve, fetchers, {
return await createStore(resolve, fetchers, {
networkConcurrency: 1,
store: store,
locks: undefined,
lockStaleDuration: 100,
})
}
test('server', async t => {
const port = 5813
const hostname = '127.0.0.1'
const remotePrefix = `http://${hostname}:${port}`
const storeCtrlForServer = await createStoreController()
const server = createServer(storeCtrlForServer, {
port,
hostname,
@@ -45,7 +51,6 @@ test('server', async t => {
{
downloadPriority: 0,
loggedPkg: {rawSpec: 'sfdf'},
offline: false,
prefix: process.cwd(),
registry,
verifyStoreIntegrity: false,
@@ -65,7 +70,55 @@ test('server', async t => {
await response['finishing']
server.close()
await server.close()
await storeCtrl.close()
t.end()
})
test('stop server with remote call', async t => {
const port = 5813
const hostname = '127.0.0.1'
const remotePrefix = `http://${hostname}:${port}`
const storeCtrlForServer = await createStoreController()
const server = createServer(storeCtrlForServer, {
port,
hostname,
ignoreStopRequests: false,
})
t.ok(await isPortReachable(port), 'server is running')
const response = await got(`${remotePrefix}/stop`, {method: 'POST'})
t.equal(response.statusCode, 200, 'success returned by server stopping endpoint')
t.notOk(await isPortReachable(port), 'server is not running')
t.end()
})
test('disallow stop server with remote call', async t => {
const port = 5813
const hostname = '127.0.0.1'
const remotePrefix = `http://${hostname}:${port}`
const storeCtrlForServer = await createStoreController()
const server = createServer(storeCtrlForServer, {
port,
hostname,
ignoreStopRequests: true,
})
t.ok(await isPortReachable(port), 'server is running')
try {
const response = await got(`${remotePrefix}/stop`, {method: 'POST'})
t.fail('request should have failed')
} catch (err) {
t.equal(err.statusCode, 403, 'server not stopped')
}
t.ok(await isPortReachable(port), 'server is running')
await server.close()
t.end()
})