Skip to content

Commit bc3c025

Browse files
authored
Merge pull request #392 from EXXETA/391-support-target-uris-with-https-protocol
Support target URIs with https: protocol
2 parents 947e6e6 + 44231b8 commit bc3c025

3 files changed

Lines changed: 25 additions & 29 deletions

File tree

docker/README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ as well that the container running openapi-cop has access to the target server (
2626
The image accepts the following environment variables, which correspond to the
2727
same [openapi-cop CLI flags](https://github.com/EXXETA/openapi-cop#cli-usage):
2828

29-
- `TARGET`: The URI of the target server. Must include the port, e.g. http:\/\/somehostname:1234. Note that HTTPS is not
30-
currently supported. If you wish to use HTTPS, put openapi-cop behind a SSL proxy.
29+
- `TARGET`: Full base path of the target API (format: http(s)://host:port/basePath).
3130
- `FILE`: The file path or URI pointing to the OpenAPI definition file. Supports JSON or YAML.
3231
- `DEFAULT_FORBID_ADDITIONAL_PROPERTIES`: When set, additional properties that are not present in the OpenAPI definition
3332
are not allowed.

src/app.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
toOasRequest,
2525
} from './util';
2626
import {dereference, hasErrors, resolve, Validator} from './validation';
27+
import {URL} from "url";
2728

2829
interface BuildOptions {
2930
targetUrl: string;
@@ -107,11 +108,16 @@ export async function buildApp(
107108
operation,
108109
);
109110

111+
const patchedHeaders = {
112+
...req.headers,
113+
host: new URL(targetUrl).hostname,
114+
};
115+
110116
const options: rp.Options = {
111117
url: targetUrl.replace(/\/$/, '') + req.params[0],
112118
qs: req.query,
113119
method: req.method,
114-
headers: req.headers,
120+
headers: patchedHeaders,
115121
gzip: true,
116122
resolveWithFullResponse: true,
117123
simple: false,
@@ -158,6 +164,8 @@ export async function buildApp(
158164
// unmodified server response
159165
res.status(statusCode).send(serverResponse.body);
160166
} else {
167+
// Replace response payload with parsed payload due to practicality
168+
serverResponse.body = parsedResponseBody;
161169
// when not silent, render validation results on error
162170
res.status(500).json({
163171
error: {
@@ -214,9 +222,9 @@ export async function buildApp(
214222

215223
/**
216224
* Builds the proxy and runs it on the given port.
217-
* @param proxy The port on which the proxy will run.
225+
* @param port Port number on which to run the proxy.
218226
* @param host The host name or IP address of the proxy server.
219-
* @param targetUrl The URL the proxy routes from.
227+
* @param targetUrl Full base path of the target API (format: http(s)://host:port/basePath).
220228
* @param apiDocFile The OpenAPI document path used to perform validation.
221229
* @param defaultForbidAdditionalProperties Whether additional properties are
222230
* allowed in requests and responses.

src/cli.ts

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#!/usr/bin/env node
2+
import {URL} from "url";
3+
24
const debugMod = require('debug');
35
const debug = debugMod('openapi-cop:proxy');
46
debug.log = console.log.bind(console); // output to stdout
@@ -19,7 +21,7 @@ program //
1921
.option('-p, --port <port>', 'port number on which to run the proxy', 8888)
2022
.option(
2123
'-t, --target <target>',
22-
'full base path of the target API (format: http://host:port/basePath)',
24+
'full base path of the target API (format: http(s)://host:port/basePath)',
2325
)
2426
.option(
2527
'--default-forbid-additional-properties',
@@ -45,40 +47,27 @@ if (program.verbose) {
4547

4648
// Validate CLI arguments
4749
if (!program.file) {
48-
console.log('Did not provide a OpenAPI file path.\n');
50+
console.error('Did not provide a OpenAPI file path.\n');
4951
program.outputHelp();
5052
process.exit();
5153
}
5254

5355
if (!program.target) {
54-
console.log('Did not provide a target server URL.\n');
56+
console.error('Did not provide a target server URL.\n');
5557
program.outputHelp();
5658
process.exit();
5759
}
5860

59-
const targetPortMatch = (program.target as string).match(
60-
/\w+:\/\/[\w.-]+:(\d{1,5})(\/|$)/,
61-
);
62-
const targetPort: string = targetPortMatch !== null ? targetPortMatch[1] : '';
61+
const targetUrl = new URL(program.target);
6362

64-
if (isNaN(Number(targetPort))) {
65-
// Check for implied port numbers
66-
if (!program.target.startsWith('http://')) {
67-
console.log('Did not provide a port number within the target URL.\n');
68-
program.outputHelp();
69-
process.exit();
70-
}
63+
const defaultPorts: { [key: string]: number } = {
64+
'http:': 80,
65+
'https:': 443
7166
}
72-
if (program.target.startsWith('https://')) {
73-
console.log('HTTPS is not supported. Not possible to modify requests/responses when the channel is encrypted. Consider to use openapi-cop behind a SSL proxy.\n');
74-
process.exit();
75-
}
76-
if (
77-
program.target.indexOf('//localhost') !== -1 &&
78-
program.target.indexOf('//0.0.0.0') !== -1 &&
79-
program.port === Number(targetPort)
80-
) {
81-
console.log('Cannot proxy locally to the same port!');
67+
const targetPort = (targetUrl.port !== '')? Number(targetUrl.port) : defaultPorts[targetUrl.protocol];
68+
69+
if ((targetUrl.hostname === 'localhost' || targetUrl.hostname === '0.0.0.0') && Number(program.port) === targetPort) {
70+
console.error('Cannot proxy to the same local port: ' + program.port);
8271
process.exit();
8372
}
8473

0 commit comments

Comments
 (0)