Skip to content

Commit

Permalink
feat: support allowH2 on urllib@4
Browse files Browse the repository at this point in the history
  • Loading branch information
fengmk2 committed Sep 15, 2024
1 parent ce4fd67 commit 6a67a1d
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 2 deletions.
4 changes: 3 additions & 1 deletion config/config.default.js
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,8 @@ module.exports = appInfo => {
* @property {Number} httpsAgent.freeSocketTimeout - httpss agent socket keepalive max free time, default is 4000 ms.
* @property {Number} httpsAgent.maxSockets - https agent max socket number of one host, default is `Number.MAX_SAFE_INTEGER` @ses https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER
* @property {Number} httpsAgent.maxFreeSockets - https agent max free socket number of one host, default is 256.
* @property {Boolean} useHttpClientNext - use urllib@3 HttpClient
* @property {Boolean} useHttpClientNext - use urllib@3 HttpClient, default is false
* @property {Boolean} allowH2 - use urllib@4 HttpClient and enable H2, default is false
*/
config.httpclient = {
enableDNSCache: false,
Expand All @@ -326,6 +327,7 @@ module.exports = appInfo => {
maxFreeSockets: 256,
},
useHttpClientNext: false,
allowH2: false,
};

/**
Expand Down
38 changes: 38 additions & 0 deletions lib/core/httpclient4.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const { HttpClient } = require('urllib4');
const ms = require('humanize-ms');

class HttpClient4 extends HttpClient {
constructor(app) {
normalizeConfig(app);
const config = app.config.httpclient;
super({
app,
defaultArgs: config.request,
allowH2: config.allowH2,
});
this.app = app;
}

async request(url, options) {
options = options || {};
if (options.ctx && options.ctx.tracer) {
options.tracer = options.ctx.tracer;
} else {
options.tracer = options.tracer || this.app.tracer;
}
return await super.request(url, options);
}

async curl(...args) {
return await this.request(...args);
}
}

function normalizeConfig(app) {
const config = app.config.httpclient;
if (typeof config.request.timeout === 'string') {
config.request.timeout = ms(config.request.timeout);
}
}

module.exports = HttpClient4;
6 changes: 5 additions & 1 deletion lib/egg.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const Messenger = require('./core/messenger');
const DNSCacheHttpClient = require('./core/dnscache_httpclient');
const HttpClient = require('./core/httpclient');
const HttpClientNext = require('./core/httpclient_next');
const HttpClient4 = require('./core/httpclient4');
const createLoggers = require('./core/logger');
const Singleton = require('./core/singleton');
const utils = require('./core/utils');
Expand Down Expand Up @@ -51,6 +52,7 @@ class EggApplication extends EggCore {
this.ContextHttpClient = ContextHttpClient;
this.HttpClient = HttpClient;
this.HttpClientNext = HttpClientNext;
this.HttpClient4 = HttpClient4;

this.loader.loadConfig();

Expand Down Expand Up @@ -293,7 +295,9 @@ class EggApplication extends EggCore {
*/
get httpclient() {
if (!this[HTTPCLIENT]) {
if (this.config.httpclient.useHttpClientNext) {
if (this.config.httpclient.allowH2) {
this[HTTPCLIENT] = new this.HttpClient4(this);
} else if (this.config.httpclient.useHttpClientNext) {
this[HTTPCLIENT] = new this.HttpClientNext(this);
} else if (this.config.httpclient.enableDNSCache) {
this[HTTPCLIENT] = new DNSCacheHttpClient(this);
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"sendmessage": "^2.0.0",
"urllib": "^2.33.0",
"urllib-next": "npm:urllib@^3.22.4",
"urllib4": "npm:urllib@^4.3.0",
"utility": "^2.1.0",
"ylru": "^1.3.2"
},
Expand Down
19 changes: 19 additions & 0 deletions test/fixtures/apps/httpclient-allowH2/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const assert = require('assert');

module.exports = app => {
class CustomHttpClient extends app.HttpClient4 {
request(url, opt) {
return new Promise(resolve => {
assert(/^http/.test(url), 'url should start with http, but got ' + url);
resolve();
}).then(() => {
return super.request(url, opt);
});
}

curl(url, opt) {
return this.request(url, opt);
}
}
app.HttpClient4 = CustomHttpClient;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
exports.httpclient = {
allowH2: true,
request: {
timeout: 99,
},
};
3 changes: 3 additions & 0 deletions test/fixtures/apps/httpclient-allowH2/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "httpclient-overwrite"
}
48 changes: 48 additions & 0 deletions test/lib/core/httpclient.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,54 @@ describe('test/lib/core/httpclient.test.js', () => {
});
});

describe('overwrite httpclient support allowH2=true', () => {
let app;
before(() => {
app = utils.app('apps/httpclient-allowH2');
return app.ready();
});
after(() => app.close());

it('should set request default global timeout to 99ms', async () => {
await assert.rejects(async () => {
await app.httpclient.curl(`${url}/timeout`);
}, err => {
assert.equal(err.name, 'HttpClientRequestTimeoutError');
assert(err.message.includes('Request timeout for 99 ms'));
return true;
});
});

it('should request http1.1 success', async () => {
const result = await app.httpclient.curl(`${url}`, {
dataType: 'text',
});
assert.equal(result.status, 200);
assert.equal(result.data, 'GET /');
});

it('should request http2 success', async () => {
for (let i = 0; i < 10; i++) {
const result = await app.httpclient.curl('https://registry.npmmirror.com', {
dataType: 'json',
timeout: 5000,
});
assert.equal(result.status, 200);
assert.equal(result.headers['content-type'], 'application/json; charset=utf-8');
assert.equal(result.data.sync_model, 'all');
}
});

it('should assert url', async () => {
await assert.rejects(async () => {
await app.httpclient.curl('unknown url');
}, err => {
assert.match(err.message, /url should start with http, but got unknown url/);
return true;
});
});
});

describe('httpclient tracer', () => {
let app;
before(() => {
Expand Down

0 comments on commit 6a67a1d

Please sign in to comment.