blob: 4a9828561724ee8bde9e76e8045412c04963430a [file] [log] [blame]
BjornMagnussonXAa5491572021-05-04 09:21:24 +02001// ============LICENSE_START===============================================
2// Copyright (C) 2021 Nordix Foundation. All rights reserved.
3// ========================================================================
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15// ============LICENSE_END=================================================
16
17// Basic http/https proxy
18// Call the the proxy on 8080/8433 for http/https
19// The destination (proxied) protocol may be http or https
20// Proxy healthcheck on 8081/8434 for http/https - answers with statistics in json
21
22const http = require('http');
23const net = require('net');
24const urlp = require('url');
25const process = require('process')
26const https = require('https');
27const fs = require('fs');
28
29// Proxy server port for http
30const proxyport = 8080;
31// Proxy server port for https
32const proxyporthttps = 8433;
33// Proyx server alive check, port for http
34const aliveport = 8081;
35// Proyx server alive check, port for https
36const aliveporthttps = 8434;
37
38// Default https destination port
39const defaulthttpsport = "443";
40
41// Certs etc for https
42const httpsoptions = {
43 key: fs.readFileSync('cert/key.crt'),
44 cert: fs.readFileSync('cert/cert.crt'),
45 passphrase: fs.readFileSync('cert/pass', 'utf8')
46};
47
48const stats = {
49 'http-requests-initiated': 0,
50 'http-requests-failed': 0,
51 'https-requests-initiated': 0,
52 'https-requests-failed': 0
53};
54
55// handle a http proxy request
56function httpclientrequest(clientrequest, clientresponse) {
57 stats['http-requests-initiated']++;
58
59 if (clientrequest.url == "/" ) {
60 console.log("Catch bad url in http request: "+clientrequest.url)
61 clientresponse.end();
62 return;
63 }
64 // Extract destination information
65 const clientrequesturl = new URL(clientrequest.url);
66
67 var proxyrequestoptions = {
68 'host': clientrequesturl.hostname,
69 'port': clientrequesturl.port,
70 'method': clientrequest.method,
71 'path': clientrequesturl.pathname+clientrequesturl.search,
72 'agent': clientrequest.agent,
73 'auth': clientrequest.auth,
74 'headers': clientrequest.headers
75 };
76
77 // Setup connection to destination
78 var proxyrequest = http.request(
79 proxyrequestoptions,
80 function (proxyresponse) {
81 clientresponse.writeHead(proxyresponse.statusCode, proxyresponse.headers);
82 proxyresponse.on('data', function (chunk) {
83 clientresponse.write(chunk);
84 });
85 proxyresponse.on('end', function () {
86 clientresponse.end();
87 });
88
89 }
90 );
91
92 // Handle the connection and data transfer between source and desitnation
93 proxyrequest.on('error', function (error) {
94 clientresponse.writeHead(500);
95 stats['http-requests-failed']++;
96 console.log(error);
97 clientresponse.write("<h1>500 Error</h1>\r\n" + "<p>Error was <pre>" + error + "</pre></p>\r\n" + "</body></html>\r\n");
98 clientresponse.end();
99 });
100 clientrequest.addListener('data', function (chunk) {
101 proxyrequest.write(chunk);
102 });
103 clientrequest.addListener('end', function () {
104 proxyrequest.end();
105 });
106}
107
BjornMagnussonXA674793d2021-05-06 19:49:17 +0200108// Function to add a 'connect' message listener to a http server
109function addhttpsconnect(httpserver) {
110 httpserver.addListener(
BjornMagnussonXAa5491572021-05-04 09:21:24 +0200111 'connect',
112 function (request, socketrequest, bodyhead) {
113
114
115 stats['https-requests-initiated']++;
116 // Extract destination information
117 var res = request['url'].split(":")
118 var hostname = res[0]
119 var port = defaulthttpsport;
120 if (res[1] != null) {
121 port = res[1]
122 }
123
124 // Setup connection to destination
125 var httpversion = request['httpVersion'];
126 var proxysocket = new net.Socket();
127
128 proxysocket.connect(
129 parseInt(port), hostname,
130 function () {
131 proxysocket.write(bodyhead);
132 socketrequest.write("HTTP/" + httpversion + " 200 Connection established\r\n\r\n");
133 }
134 );
135
136 // Handle the connection and data transfer between source and desitnation
137 proxysocket.on('data', function (chunk) {
138 socketrequest.write(chunk);
139 });
140 proxysocket.on('end', function () {
141 socketrequest.end();
142 });
143
144 socketrequest.on('data', function (chunk) {
145 proxysocket.write(chunk);
146 });
147 socketrequest.on('end', function () {
148 proxysocket.end();
149 });
150
151 proxysocket.on('error', function (err) {
152 stats['https-requests-failed']++;
153 console.log(err);
154 socketrequest.write("HTTP/" + httpversion + " 500 Connection error\r\n\r\n");
155 socketrequest.end();
156 });
157 socketrequest.on('error', function (err) {
158 stats['https-requests-failed']++;
159 console.log(err);
160 proxysocket.end();
161 });
162 }
163 );
164}
165
BjornMagnussonXA674793d2021-05-06 19:49:17 +0200166function main() {
167
168 // -------------------- Alive server ----------------------------------
169 // Responde with '200' and statistics for any path on the alive address
170 const alivelistener = function (req, res) {
171 console.log(stats)
172 res.writeHead(200, { 'Content-Type': 'application/json' });
173 res.write(JSON.stringify(stats))
174 res.end();
175 };
176
177 // The alive server - for healthckeck
178 const aliveserver = http.createServer(alivelistener);
179
180 // The alive server - for healthckeck
181 const aliveserverhttps = https.createServer(httpsoptions, alivelistener);
182
183 //Handle heatlhcheck requests
184 aliveserver.listen(aliveport, () => {
185 console.log('alive server on: '+aliveport);
186 console.log(' example: curl localhost:'+aliveport)
187 });
188
189 //Handle heatlhcheck requests
190 aliveserverhttps.listen(aliveporthttps, () => {
191 console.log('alive server on: '+aliveporthttps);
192 console.log(' example: curl -k https://localhost:'+aliveporthttps)
193 });
194
195 // -------------------- Proxy server ---------------------------------
196
197 // The proxy server
198 const proxyserver = http.createServer(httpclientrequest).listen(proxyport);
199 console.log('http/https proxy for http proxy calls on port ' + proxyport);
200 console.log(' example: curl --proxy http://localhost:8080 http://100.110.120.130:1234')
201 console.log(' example: curl -k --proxy http//localhost:8080 https://100.110.120.130:5678')
202
203 // handle a http proxy request - https listener
204 addhttpsconnect(proxyserver);
205
206 const proxyserverhttps = https.createServer(httpsoptions, httpclientrequest).listen(proxyporthttps);
207 console.log('http/https proxy for https proxy calls on port ' + proxyporthttps);
208 console.log(' example: curl --proxy-insecure --proxy https://localhost:8433 http://100.110.120.130:1234')
209 console.log(' example: curl --proxy-insecure --proxy https://localhost:8433 https://100.110.120.130:5678')
210
211 // handle a https proxy request - https listener
212 addhttpsconnect(proxyserverhttps);
213
214}
215
BjornMagnussonXAa5491572021-05-04 09:21:24 +0200216//Handle ctrl c when running in interactive mode
217process.on('SIGINT', () => {
218 console.info("Interrupted")
219 process.exit(0)
220})
221
222main();