mirror of
https://github.com/eggjs/egg.git
synced 2024-12-04 07:14:30 +00:00
562 lines
15 KiB
JavaScript
562 lines
15 KiB
JavaScript
'use strict';
|
|
|
|
const assert = require('assert');
|
|
const mm = require('egg-mock');
|
|
const urllib = require('urllib');
|
|
const Httpclient = require('../../../lib/core/httpclient');
|
|
const utils = require('../../utils');
|
|
|
|
describe('test/lib/core/httpclient.test.js', () => {
|
|
let client;
|
|
let url;
|
|
|
|
before(() => {
|
|
client = new Httpclient({
|
|
deprecate: () => {},
|
|
config: {
|
|
httpclient: {
|
|
request: {},
|
|
httpAgent: {},
|
|
httpsAgent: {},
|
|
},
|
|
},
|
|
});
|
|
client.on('request', info => {
|
|
info.args.headers = info.args.headers || {};
|
|
info.args.headers['mock-traceid'] = 'mock-traceid';
|
|
info.args.headers['mock-rpcid'] = 'mock-rpcid';
|
|
});
|
|
});
|
|
before(async () => {
|
|
url = await utils.startLocalServer();
|
|
});
|
|
|
|
afterEach(mm.restore);
|
|
|
|
it('should request ok with log', done => {
|
|
const args = {
|
|
dataType: 'text',
|
|
};
|
|
client.once('response', info => {
|
|
assert(info.req.options.headers['mock-traceid'] === 'mock-traceid');
|
|
assert(info.req.options.headers['mock-rpcid'] === 'mock-rpcid');
|
|
done();
|
|
});
|
|
|
|
client.request(url, args);
|
|
});
|
|
|
|
it('should request callback with log', done => {
|
|
client.once('response', info => {
|
|
assert(info.req.options.headers['mock-traceid'] === 'mock-traceid');
|
|
assert(info.req.options.headers['mock-rpcid'] === 'mock-rpcid');
|
|
done();
|
|
});
|
|
|
|
client.request(url, () => {});
|
|
});
|
|
|
|
it('should request callback with error', done => {
|
|
client.request(url + '/error', { dataType: 'json' }, err => {
|
|
assert(err);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should curl ok with log', done => {
|
|
const args = {
|
|
dataType: 'text',
|
|
};
|
|
client.once('response', info => {
|
|
assert(info.req.options.headers['mock-traceid'] === 'mock-traceid');
|
|
assert(info.req.options.headers['mock-rpcid'] === 'mock-rpcid');
|
|
done();
|
|
});
|
|
|
|
client.curl(url, args);
|
|
});
|
|
|
|
it('should requestThunk ok with log', async () => {
|
|
const args = {
|
|
dataType: 'text',
|
|
};
|
|
client.once('response', info => {
|
|
assert(info.req.options.headers['mock-traceid'] === 'mock-traceid');
|
|
assert(info.req.options.headers['mock-rpcid'] === 'mock-rpcid');
|
|
});
|
|
|
|
await client.requestThunk(url, args);
|
|
});
|
|
|
|
it('should mock ENETUNREACH error', async () => {
|
|
mm(urllib.HttpClient2.prototype, 'request', () => {
|
|
const err = new Error('connect ENETUNREACH 1.1.1.1:80 - Local (127.0.0.1)');
|
|
err.code = 'ENETUNREACH';
|
|
return Promise.reject(err);
|
|
});
|
|
await assert.rejects(async () => {
|
|
await client.request(url);
|
|
}, err => {
|
|
assert(err.name === 'HttpClientError');
|
|
assert(err.code === 'httpclient_ENETUNREACH');
|
|
assert(err.message === 'connect ENETUNREACH 1.1.1.1:80 - Local (127.0.0.1) [ https://eggjs.org/zh-cn/faq/httpclient_ENETUNREACH ]');
|
|
return true;
|
|
});
|
|
});
|
|
|
|
it('should handle timeout error', async () => {
|
|
await assert.rejects(async () => {
|
|
await client.request(url + '/timeout', { timeout: 100 });
|
|
}, err => {
|
|
assert(err.name === 'ResponseTimeoutError');
|
|
return true;
|
|
});
|
|
});
|
|
|
|
describe('httpclient.httpAgent.timeout < 30000', () => {
|
|
let app;
|
|
before(() => {
|
|
app = utils.app('apps/httpclient-agent-timeout-3000');
|
|
return app.ready();
|
|
});
|
|
after(() => app.close());
|
|
|
|
it('should auto reset httpAgent.timeout to 30000', () => {
|
|
// should access httpclient first
|
|
assert(app.httpclient);
|
|
assert(app.config.httpclient.timeout === 3000);
|
|
assert(app.config.httpclient.httpAgent.timeout === 30000);
|
|
assert(app.config.httpclient.httpsAgent.timeout === 30000);
|
|
});
|
|
|
|
it('should set request default global timeout to 10s', () => {
|
|
// should access httpclient first
|
|
assert(app.httpclient);
|
|
assert(app.config.httpclient.request.timeout === 10000);
|
|
});
|
|
|
|
it('should convert compatibility options to agent options', () => {
|
|
// should access httpclient first
|
|
assert(app.httpclient);
|
|
assert(app.config.httpclient.httpAgent.freeSocketTimeout === 2000);
|
|
assert(app.config.httpclient.httpsAgent.freeSocketTimeout === 2000);
|
|
|
|
assert(app.config.httpclient.httpAgent.maxSockets === 100);
|
|
assert(app.config.httpclient.httpsAgent.maxSockets === 100);
|
|
|
|
assert(app.config.httpclient.httpAgent.maxFreeSockets === 100);
|
|
assert(app.config.httpclient.httpsAgent.maxFreeSockets === 100);
|
|
|
|
assert(app.config.httpclient.httpAgent.keepAlive === false);
|
|
assert(app.config.httpclient.httpsAgent.keepAlive === false);
|
|
});
|
|
});
|
|
|
|
describe('httpclient.request.timeout = 100', () => {
|
|
let app;
|
|
before(() => {
|
|
app = utils.app('apps/httpclient-request-timeout-100');
|
|
return app.ready();
|
|
});
|
|
after(() => app.close());
|
|
|
|
it('should set request default global timeout to 100ms', () => {
|
|
return app.httpclient.curl(`${url}/timeout`)
|
|
.catch(err => {
|
|
assert(err);
|
|
assert(err.name === 'ResponseTimeoutError');
|
|
assert(err.message.includes('Response timeout for 100ms'));
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('overwrite httpclient', () => {
|
|
let app;
|
|
before(() => {
|
|
app = utils.app('apps/httpclient-overwrite');
|
|
return app.ready();
|
|
});
|
|
after(() => app.close());
|
|
|
|
it('should set request default global timeout to 100ms', () => {
|
|
return app.httpclient.curl(`${url}/timeout`)
|
|
.catch(err => {
|
|
assert(err);
|
|
assert(err.name === 'ResponseTimeoutError');
|
|
assert(err.message.includes('Response timeout for 100ms'));
|
|
});
|
|
});
|
|
|
|
it('should assert url', () => {
|
|
return app.httpclient.curl('unknown url')
|
|
.catch(err => {
|
|
assert(err);
|
|
assert(err.message.includes('url should start with http, but got unknown url'));
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('httpclient tracer', () => {
|
|
const url = 'https://www.alibaba.com/';
|
|
let app;
|
|
before(() => {
|
|
app = utils.app('apps/httpclient-tracer');
|
|
return app.ready();
|
|
});
|
|
|
|
after(() => app.close());
|
|
|
|
it('should app request auto set tracer', async () => {
|
|
const httpclient = app.httpclient;
|
|
|
|
let reqTracer;
|
|
let resTracer;
|
|
|
|
httpclient.on('request', function(options) {
|
|
reqTracer = options.args.tracer;
|
|
});
|
|
|
|
httpclient.on('response', function(options) {
|
|
resTracer = options.req.args.tracer;
|
|
});
|
|
|
|
let res = await httpclient.request(url, {
|
|
method: 'GET',
|
|
});
|
|
|
|
assert(res.status === 200);
|
|
assert(reqTracer === resTracer);
|
|
|
|
assert(reqTracer.traceId);
|
|
assert(reqTracer.traceId === resTracer.traceId);
|
|
|
|
reqTracer = null;
|
|
resTracer = null;
|
|
|
|
res = await httpclient.request(url);
|
|
|
|
assert(res.status === 200);
|
|
assert(reqTracer === resTracer);
|
|
|
|
assert(reqTracer.traceId);
|
|
assert(reqTracer.traceId === resTracer.traceId);
|
|
});
|
|
|
|
it('should agent request auto set tracer', async () => {
|
|
const httpclient = app.agent.httpclient;
|
|
|
|
let reqTracer;
|
|
let resTracer;
|
|
|
|
httpclient.on('request', function(options) {
|
|
reqTracer = options.args.tracer;
|
|
});
|
|
|
|
httpclient.on('response', function(options) {
|
|
resTracer = options.req.args.tracer;
|
|
});
|
|
|
|
const res = await httpclient.request(url, {
|
|
method: 'GET',
|
|
});
|
|
|
|
assert(res.status === 200);
|
|
assert(reqTracer === resTracer);
|
|
|
|
assert(reqTracer.traceId);
|
|
assert(reqTracer.traceId === resTracer.traceId);
|
|
});
|
|
|
|
it('should app request with ctx and tracer', async () => {
|
|
const httpclient = app.httpclient;
|
|
|
|
let reqTracer;
|
|
let resTracer;
|
|
|
|
httpclient.on('request', function(options) {
|
|
reqTracer = options.args.tracer;
|
|
});
|
|
|
|
httpclient.on('response', function(options) {
|
|
resTracer = options.req.args.tracer;
|
|
});
|
|
|
|
let res = await httpclient.request(url, {
|
|
method: 'GET',
|
|
});
|
|
|
|
assert(res.status === 200);
|
|
|
|
assert(reqTracer.traceId);
|
|
assert(reqTracer.traceId === resTracer.traceId);
|
|
|
|
reqTracer = null;
|
|
resTracer = null;
|
|
res = await httpclient.request(url, {
|
|
method: 'GET',
|
|
ctx: {},
|
|
tracer: {
|
|
id: '1234',
|
|
},
|
|
});
|
|
|
|
assert(res.status === 200);
|
|
assert(reqTracer.id === resTracer.id);
|
|
assert(reqTracer.id === '1234');
|
|
|
|
reqTracer = null;
|
|
resTracer = null;
|
|
res = await httpclient.request(url, {
|
|
method: 'GET',
|
|
ctx: {
|
|
tracer: {
|
|
id: '5678',
|
|
},
|
|
},
|
|
});
|
|
|
|
assert(res.status === 200);
|
|
assert(reqTracer.id === resTracer.id);
|
|
assert(reqTracer.id === '5678');
|
|
});
|
|
});
|
|
|
|
describe.skip('before app ready multi httpclient request tracer', () => {
|
|
let app;
|
|
before(() => {
|
|
app = utils.app('apps/httpclient-tracer');
|
|
});
|
|
|
|
after(() => app.close());
|
|
|
|
it('should app request before ready use same tracer', async () => {
|
|
const httpclient = app.httpclient;
|
|
|
|
let reqTracers = [];
|
|
let resTracers = [];
|
|
|
|
httpclient.on('request', function(options) {
|
|
reqTracers.push(options.args.tracer);
|
|
});
|
|
|
|
httpclient.on('response', function(options) {
|
|
resTracers.push(options.req.args.tracer);
|
|
});
|
|
|
|
let res = await httpclient.request(url, {
|
|
method: 'GET',
|
|
timeout: 20000,
|
|
});
|
|
assert(res.status === 200);
|
|
|
|
res = await httpclient.request('https://github.com', {
|
|
method: 'GET',
|
|
timeout: 20000,
|
|
});
|
|
|
|
assert(res.status === 200);
|
|
|
|
res = await httpclient.request('https://www.npmjs.com', {
|
|
method: 'GET',
|
|
timeout: 20000,
|
|
});
|
|
assert(res.status === 200);
|
|
|
|
assert(reqTracers.length === 3);
|
|
assert(resTracers.length === 3);
|
|
|
|
assert(reqTracers[0] === reqTracers[1]);
|
|
assert(reqTracers[1] === reqTracers[2]);
|
|
|
|
assert(resTracers[0] === reqTracers[2]);
|
|
assert(resTracers[1] === resTracers[0]);
|
|
assert(resTracers[2] === resTracers[1]);
|
|
|
|
assert(reqTracers[0].traceId);
|
|
|
|
reqTracers = [];
|
|
resTracers = [];
|
|
|
|
await app.ready();
|
|
|
|
res = await httpclient.request(url, {
|
|
method: 'GET',
|
|
timeout: 20000,
|
|
});
|
|
assert(res.status === 200);
|
|
|
|
res = await httpclient.request('https://github.com', {
|
|
method: 'GET',
|
|
timeout: 20000,
|
|
});
|
|
assert(res.status === 200);
|
|
|
|
res = await httpclient.request('https://www.npmjs.com', {
|
|
method: 'GET',
|
|
timeout: 20000,
|
|
});
|
|
assert(res.status === 200);
|
|
|
|
assert(reqTracers.length === 3);
|
|
assert(resTracers.length === 3);
|
|
|
|
assert(reqTracers[0] !== reqTracers[1]);
|
|
assert(reqTracers[1] !== reqTracers[2]);
|
|
|
|
assert(resTracers[0] !== reqTracers[2]);
|
|
assert(resTracers[1] !== resTracers[0]);
|
|
assert(resTracers[2] !== resTracers[1]);
|
|
|
|
assert(reqTracers[0].traceId);
|
|
});
|
|
});
|
|
|
|
describe('compatibility freeSocketKeepAliveTimeout', () => {
|
|
it('should convert freeSocketKeepAliveTimeout to freeSocketTimeout', () => {
|
|
let mockApp = {
|
|
config: {
|
|
httpclient: {
|
|
request: {},
|
|
freeSocketKeepAliveTimeout: 1000,
|
|
httpAgent: {},
|
|
httpsAgent: {},
|
|
},
|
|
},
|
|
};
|
|
let client = new Httpclient(mockApp);
|
|
assert(client);
|
|
assert(mockApp.config.httpclient.freeSocketTimeout === 1000);
|
|
assert(!mockApp.config.httpclient.freeSocketKeepAliveTimeout);
|
|
assert(mockApp.config.httpclient.httpAgent.freeSocketTimeout === 1000);
|
|
assert(mockApp.config.httpclient.httpsAgent.freeSocketTimeout === 1000);
|
|
|
|
mockApp = {
|
|
config: {
|
|
httpclient: {
|
|
request: {},
|
|
httpAgent: {
|
|
freeSocketKeepAliveTimeout: 1001,
|
|
},
|
|
httpsAgent: {
|
|
freeSocketKeepAliveTimeout: 1002,
|
|
},
|
|
},
|
|
},
|
|
};
|
|
client = new Httpclient(mockApp);
|
|
assert(client);
|
|
assert(mockApp.config.httpclient.httpAgent.freeSocketTimeout === 1001);
|
|
assert(!mockApp.config.httpclient.httpAgent.freeSocketKeepAliveTimeout);
|
|
assert(mockApp.config.httpclient.httpsAgent.freeSocketTimeout === 1002);
|
|
assert(!mockApp.config.httpclient.httpsAgent.freeSocketKeepAliveTimeout);
|
|
});
|
|
});
|
|
|
|
describe('httpclient retry', () => {
|
|
let app;
|
|
before(() => {
|
|
app = utils.app('apps/httpclient-retry');
|
|
return app.ready();
|
|
});
|
|
after(() => app.close());
|
|
|
|
it('should retry when httpclient fail', async () => {
|
|
let hasRetry = false;
|
|
const res = await app.httpclient.curl(`${url}/retry`, {
|
|
retry: 1,
|
|
retryDelay: 100,
|
|
isRetry(res) {
|
|
const shouldRetry = res.status >= 500;
|
|
if (shouldRetry) {
|
|
hasRetry = true;
|
|
}
|
|
return shouldRetry;
|
|
},
|
|
});
|
|
|
|
assert(hasRetry);
|
|
assert(res.status === 200);
|
|
});
|
|
|
|
it('should callback style retry when httpclient fail', done => {
|
|
let hasRetry = false;
|
|
app.httpclient.request(`${url}/retry`, {
|
|
retry: 1,
|
|
retryDelay: 100,
|
|
isRetry(res) {
|
|
const shouldRetry = res.status >= 500;
|
|
if (shouldRetry) {
|
|
hasRetry = true;
|
|
}
|
|
return shouldRetry;
|
|
},
|
|
}, (err, data, res) => {
|
|
assert(hasRetry);
|
|
assert(res.status === 200);
|
|
assert(data.toString() === 'retry suc');
|
|
done(err);
|
|
});
|
|
});
|
|
|
|
it('should retry when httpclient fail', async () => {
|
|
let hasRetry = false;
|
|
const res = await app.httpclient.curl(`${url}/retry`, {
|
|
retry: 1,
|
|
retryDelay: 100,
|
|
isRetry(res) {
|
|
const shouldRetry = res.status >= 500;
|
|
if (shouldRetry) {
|
|
hasRetry = true;
|
|
}
|
|
return shouldRetry;
|
|
},
|
|
});
|
|
|
|
assert(hasRetry);
|
|
assert(res.status === 200);
|
|
});
|
|
|
|
it('should callback style retry when httpclient fail', done => {
|
|
let hasRetry = false;
|
|
app.httpclient.request(`${url}/retry`, {
|
|
retry: 1,
|
|
retryDelay: 100,
|
|
isRetry(res) {
|
|
const shouldRetry = res.status >= 500;
|
|
if (shouldRetry) {
|
|
hasRetry = true;
|
|
}
|
|
return shouldRetry;
|
|
},
|
|
}, (err, data, res) => {
|
|
assert(hasRetry);
|
|
assert(res.status === 200);
|
|
assert(data.toString() === 'retry suc');
|
|
done(err);
|
|
});
|
|
});
|
|
|
|
it('should thunk style retry when httpclient fail', done => {
|
|
let hasRetry = false;
|
|
app.httpclient.requestThunk(`${url}/retry`, {
|
|
retry: 1,
|
|
retryDelay: 100,
|
|
isRetry(res) {
|
|
const shouldRetry = res.status >= 500;
|
|
if (shouldRetry) {
|
|
hasRetry = true;
|
|
}
|
|
return shouldRetry;
|
|
},
|
|
})((err, { data, status, headers, res }) => {
|
|
assert(hasRetry);
|
|
assert(status === 200);
|
|
assert(res.status === 200);
|
|
assert(data.toString() === 'retry suc');
|
|
assert(headers['x-retry'] === '1');
|
|
done(err);
|
|
});
|
|
});
|
|
});
|
|
});
|