UNPKG

5.63 kBJavaScriptView Raw
1'use strict'
2
3const fs = require('fs')
4const path = require('path')
5const util = require('util')
6const IConfiguration = require('./iconfiguration')
7const { check } = require('./mapping')
8const checkMethod = require('./checkMethod')
9const { parse } = require('./schema')
10const {
11 $configurationInterface,
12 $configurationRequests,
13 $handlerMethod,
14 $handlerSchema
15} = require('./symbols')
16
17const readFileAsync = util.promisify(fs.readFile)
18const statAsync = util.promisify(fs.stat)
19
20const defaultHandlers = {
21 custom: require('./handlers/custom'),
22 file: require('./handlers/file'),
23 status: require('./handlers/status'),
24 url: require('./handlers/url'),
25 use: require('./handlers/use')
26}
27
28const defaults = {
29 hostname: undefined,
30 port: 5000,
31 'max-redirect': 10,
32 mappings: [{
33 match: /^\/proxy\/(https?)\/(.*)/,
34 url: '$1://$2',
35 'unsecure-cookies': true
36 }, {
37 match: '(.*)',
38 file: './$1'
39 }]
40}
41
42function applyDefaults (configuration) {
43 Object.keys(defaults).forEach(property => {
44 if (!Object.prototype.hasOwnProperty.call(configuration, property)) {
45 configuration[property] = defaults[property]
46 }
47 })
48}
49
50function getHandler (handlers, types, mapping) {
51 for (let index = 0; index < types.length; ++index) {
52 const type = types[index]
53 const redirect = mapping[type]
54 if (redirect !== undefined) {
55 return {
56 handler: handlers[type],
57 redirect,
58 type
59 }
60 }
61 }
62 return {}
63}
64
65function checkHandler (handler, type) {
66 if (handler.schema) {
67 handler[$handlerSchema] = parse(handler.schema)
68 delete handler.schema
69 }
70 if (handler.method) {
71 checkMethod(handler, $handlerMethod)
72 delete handler.method
73 }
74 if (typeof handler.redirect !== 'function') {
75 throw new Error('Invalid "' + type + '" handler: redirect is not a function')
76 }
77}
78
79function validateHandler (type) {
80 const handlers = this.handlers
81 let handler = handlers[type]
82 if (typeof handler === 'string') {
83 handler = require(handler)
84 handlers[type] = handler
85 }
86 checkHandler(handler, type)
87 Object.freeze(handler)
88}
89
90function setHandlers (configuration) {
91 if (configuration.handlers) {
92 // Default hanlders can't be overridden
93 configuration.handlers = Object.assign({}, configuration.handlers, defaultHandlers)
94 } else {
95 configuration.handlers = defaultHandlers
96 }
97 Object.keys(configuration.handlers).forEach(validateHandler.bind(configuration))
98 configuration.handler = getHandler.bind(null, configuration.handlers, Object.keys(configuration.handlers))
99}
100
101async function readSslFile (configuration, filePath) {
102 if (path.isAbsolute(filePath)) {
103 return (await readFileAsync(filePath)).toString()
104 }
105 return (await readFileAsync(path.join(configuration.ssl.cwd, filePath))).toString()
106}
107
108async function checkProtocol (configuration) {
109 if (configuration.ssl) {
110 configuration.protocol = 'https'
111 configuration.ssl.cert = await readSslFile(configuration, configuration.ssl.cert)
112 configuration.ssl.key = await readSslFile(configuration, configuration.ssl.key)
113 } else {
114 configuration.protocol = 'http'
115 }
116}
117
118async function checkMappings (configuration) {
119 const configurationInterface = new IConfiguration(configuration)
120 configuration[$configurationInterface] = configurationInterface
121 for await (const mapping of configuration.mappings) {
122 await check(configuration, mapping)
123 }
124}
125
126function setCwd (folderPath, configuration) {
127 if (configuration.handlers) {
128 Object.keys(configuration.handlers).forEach(prefix => {
129 var handler = configuration.handlers[prefix]
130 if (typeof handler === 'string' && handler.match(/^\.\.?\//)) {
131 configuration.handlers[prefix] = path.join(folderPath, handler)
132 }
133 })
134 }
135 if (configuration.mappings) {
136 configuration.mappings.forEach(mapping => {
137 if (!mapping.cwd) {
138 mapping.cwd = folderPath
139 }
140 })
141 }
142 if (configuration.ssl && !configuration.ssl.cwd) {
143 configuration.ssl.cwd = folderPath
144 }
145}
146
147function extend (filePath, configuration) {
148 const folderPath = path.dirname(filePath)
149 setCwd(folderPath, configuration)
150 if (configuration.extend) {
151 const basefilePath = path.join(folderPath, configuration.extend)
152 delete configuration.extend
153 return readFileAsync(basefilePath)
154 .then(buffer => JSON.parse(buffer.toString()))
155 .then(baseConfiguration => {
156 // Only merge mappings
157 const baseMappings = baseConfiguration.mappings
158 const mergedConfiguration = Object.assign(baseConfiguration, configuration)
159 if (baseMappings !== mergedConfiguration.mappings) {
160 mergedConfiguration.mappings = [...configuration.mappings, ...baseMappings]
161 }
162 return extend(basefilePath, mergedConfiguration)
163 })
164 }
165 return configuration
166}
167
168module.exports = {
169 async check (configuration) {
170 const checkedConfiguration = Object.assign({}, configuration)
171 applyDefaults(checkedConfiguration)
172 setHandlers(checkedConfiguration)
173 await checkProtocol(checkedConfiguration)
174 await checkMappings(checkedConfiguration)
175 checkedConfiguration[$configurationRequests] = {
176 hold: Promise.resolve(),
177 promises: []
178 }
179 return checkedConfiguration
180 },
181
182 async read (fileName) {
183 let filePath
184 if (path.isAbsolute(fileName)) {
185 filePath = fileName
186 } else {
187 filePath = path.join(process.cwd(), fileName)
188 }
189 return statAsync(filePath)
190 .then(() => readFileAsync(filePath).then(buffer => JSON.parse(buffer.toString())))
191 .then(configuration => extend(filePath, configuration))
192 }
193}