Skip to content

switch to http-proxy-3 #572

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ on:
jobs:
# Audit dependencies for known vulnerabilities
audit-dependencies:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
Expand All @@ -50,21 +50,18 @@ jobs:
npm audit --production --audit-level=moderate

test:
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
strategy:
fail-fast: false # Do not cancel all jobs if one fails
matrix:
# IMPORTANT: Make sure to update package.json's engines.node field to
# always require at least the oldest version, as well as our
# README.md file under the install section.
node_version:
# Removing node 10 is dropping support for ubuntu 20.04 LTS
- "10"
- "12"
- "14"
- "16"
# Removing node 18 is dropping support for ubuntu 24.04 LTS, debian 12
- "18"
- "20"
- "22"
- current

steps:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ functionality to [JupyterHub] deployments.

## Install

Prerequisite: [Node.js](https://nodejs.org/en/download/) ≥ 10
Prerequisite: [Node.js](https://nodejs.org/en/download/) ≥ 18

If you're installing `configurable-http-proxy` in Linux, you can follow [the instruction of nodesource](https://github.com/nodesource/distributions#installation-instructions) to install arbitrary version of Node.js.

Expand Down
70 changes: 33 additions & 37 deletions lib/configproxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,10 @@ var http = require("http"),
fs = require("fs"),
path = require("path"),
EventEmitter = require("events").EventEmitter,
httpProxy = require("http-proxy-node16"),
httpProxy = require("http-proxy-3"),
winston = require("winston"),
util = require("util"),
URL = require("url"),
defaultLogger = require("./log").defaultLogger,
querystring = require("querystring"),
metrics = require("./metrics");

function bound(that, method) {
Expand Down Expand Up @@ -311,23 +309,20 @@ class ConfigurableProxy extends EventEmitter {
}
// GET returns routing table as JSON dict
var that = this;
var parsed = URL.parse(req.url);
var parsed = new URL(req.url, "https://example.com");
var inactiveSince = null;
if (parsed.query) {
var query = querystring.parse(parsed.query);
if (query.inactive_since !== undefined) {
// camelCaseify
query.inactiveSince = query.inactive_since;
}

if (query.inactiveSince !== undefined) {
var timestamp = Date.parse(query.inactiveSince);
if (isFinite(timestamp)) {
inactiveSince = new Date(timestamp);
} else {
fail(req, res, 400, "Invalid datestamp '" + query.inactiveSince + "' must be ISO8601.");
return;
}
var inactiveSinceParam = parsed.searchParams.get("inactiveSince");
if (!inactiveSinceParam) {
// camelCaseify old inactive_since
inactiveSinceParam = parsed.searchParams.get("inactive_since");
}
if (inactiveSinceParam) {
var timestamp = Date.parse(inactiveSinceParam);
if (isFinite(timestamp)) {
inactiveSince = new Date(timestamp);
} else {
fail(req, res, 400, "Invalid datestamp '" + inactiveSinceParam + "' must be ISO8601.");
return;
}
}
res.writeHead(200, { "Content-Type": "application/json" });
Expand Down Expand Up @@ -393,7 +388,7 @@ class ConfigurableProxy extends EventEmitter {
var metricsTimerEnd = this.metrics.findTargetForReqSummary.startTimer();
// return proxy target for a given url path
var basePath = this.hostRouting ? "/" + parseHost(req) : "";
var path = basePath + decodeURIComponent(URL.parse(req.url).pathname);
var path = basePath + decodeURIComponent(new URL(req.url, "http://example.com").pathname);
var route = await this._routes.getTarget(path);
metricsTimerEnd();
if (route) {
Expand Down Expand Up @@ -460,26 +455,27 @@ class ConfigurableProxy extends EventEmitter {
return;
}
if (this.errorTarget) {
var urlSpec = URL.parse(this.errorTarget);
var urlSpec = new URL(this.errorTarget);
// error request is $errorTarget/$code?url=$requestUrl
urlSpec.search = "?" + querystring.encode({ url: req.url });
urlSpec.searchParams.set("url", req.url);
urlSpec.pathname = urlSpec.pathname + code.toString();
var secure = /https/gi.test(urlSpec.protocol) ? true : false;
var url = URL.format(urlSpec);
this.log.debug("Requesting custom error page: %s", urlSpec.format());
var url = urlSpec.toString();
this.log.debug("Requesting custom error page: %s", url);

// construct request target from urlSpec
var target = URL.parse(url);
target.method = "GET";
// construct request options
var options = {
method: "GET",
};

// add client SSL config if error target is using https
if (secure && this.options.clientSsl) {
target.key = this.options.clientSsl.key;
target.cert = this.options.clientSsl.cert;
target.ca = this.options.clientSsl.ca;
options.key = this.options.clientSsl.key;
options.cert = this.options.clientSsl.cert;
options.ca = this.options.clientSsl.ca;
}

var errorRequest = (secure ? https : http).request(target, function (upstream) {
var errorRequest = (secure ? https : http).request(url, options, function (upstream) {
if (res.writableEnded) return; // response already done
["content-type", "content-encoding"].map(function (key) {
if (!upstream.headers[key]) return;
Expand Down Expand Up @@ -556,15 +552,15 @@ class ConfigurableProxy extends EventEmitter {
req.url = req.url.slice(prefix.length);
}

target = URL.parse(target);
target = new URL(target);
var proxyOptions = { target: target };
if (that.options.clientSsl) {
target.key = that.options.clientSsl.key;
target.cert = that.options.clientSsl.cert;
target.ca = that.options.clientSsl.ca;
proxyOptions.key = that.options.clientSsl.key;
proxyOptions.cert = that.options.clientSsl.cert;
proxyOptions.ca = that.options.clientSsl.ca;
}

// add config argument
var proxyOptions = { target: target };
if (target.protocol.slice(-2) === "s:") {
proxyOptions.agent = that.httpsAgent;
} else {
Expand Down Expand Up @@ -657,7 +653,7 @@ class ConfigurableProxy extends EventEmitter {
function pushPathArg(arg) {
args.push(arg === undefined ? arg : decodeURIComponent(arg));
}
var path = URL.parse(req.url).pathname;
var path = new URL(req.url, "https://example.com").pathname;
for (var i = 0; i < this.apiHandlers.length; i++) {
var pat = this.apiHandlers[i][0];
var match = pat.exec(path);
Expand Down
10 changes: 4 additions & 6 deletions lib/testutil.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
"use strict";

var http = require("http");
var URL = require("url");
var extend = require("util")._extend;
var WebSocketServer = require("ws").WebSocketServer;
var querystring = require("querystring");

var configproxy = require("./configproxy");
var defaultLogger = require("./log").defaultLogger;
Expand Down Expand Up @@ -112,17 +110,17 @@ exports.setupProxy = function (port, options, paths) {
if (options.errorTarget) {
countdown++;
var errorServer = http.createServer(function (req, res) {
var parsed = URL.parse(req.url);
var query = querystring.parse(parsed.query);
var query = new URL(req.url, "http://example.com").searchParams;
res.setHeader("Content-Type", "text/plain");
req.on("data", function () {});
req.on("end", function () {
res.write(query.url);
res.write(query.get("url"));
res.end();
});
});
errorServer.on("listening", onlisten);
errorServer.listen(URL.parse(options.errorTarget).port, ip);
const errorUrl = new URL(options.errorTarget);
errorServer.listen(errorUrl.port, ip);
servers.push(errorServer);
}

Expand Down
Loading