Skip to content

Commit

Permalink
feat: switch httpclient to httpclient2 for retry feature (#3606)
Browse files Browse the repository at this point in the history
  • Loading branch information
atian25 authored and dead-horse committed Apr 11, 2019
1 parent 879fe93 commit eead318
Show file tree
Hide file tree
Showing 14 changed files with 87 additions and 22 deletions.
2 changes: 1 addition & 1 deletion app/extend/context.js
Expand Up @@ -39,7 +39,7 @@ const proto = module.exports = {
/**
* Shortcut for httpclient.curl
*
* @method Context#curl
* @function Context#curl
* @param {String|Object} url - request url address.
* @param {Object} [options] - options for request.
* @return {Object} see {@link ContextHttpClient#curl}
Expand Down
4 changes: 2 additions & 2 deletions app/extend/helper.js
Expand Up @@ -7,7 +7,7 @@ module.exports = {

/**
* Generate URL path(without host) for route. Takes the route name and a map of named params.
* @method Helper#pathFor
* @function Helper#pathFor
* @param {String} name - Router Name
* @param {Object} params - Other params
*
Expand All @@ -25,7 +25,7 @@ module.exports = {

/**
* Generate full URL(with host) for route. Takes the route name and a map of named params.
* @method Helper#urlFor
* @function Helper#urlFor
* @param {String} name - Router name
* @param {Object} params - Other params
* @example
Expand Down
18 changes: 14 additions & 4 deletions app/extend/request.js
Expand Up @@ -95,9 +95,9 @@ module.exports = {
},

/**
* Get or set the request remote IPv4 address
* Get the request remote IPv4 address
* @member {String} Request#ip
* @param {String} ip - IPv4 address
* @return {String} IPv4 address
* @example
* ```js
* this.request.ip
Expand All @@ -116,6 +116,17 @@ module.exports = {
return this._ip;
},

/**
* Set the request remote IPv4 address
* @member {String} Request#ip
* @param {String} ip - IPv4 address
* @example
* ```js
* this.request.ip
* => '127.0.0.1'
* => '111.10.2.1'
* ```
*/
set ip(ip) {
this._ip = ip;
},
Expand Down Expand Up @@ -226,10 +237,9 @@ module.exports = {
/**
* Set query-string as an object.
*
* @method Request#query
* @function Request#query
* @param {Object} obj set querystring and query object for request.
* @return {void}
* @api public
*/
set query(obj) {
this.querystring = querystring.stringify(obj);
Expand Down
12 changes: 11 additions & 1 deletion app/extend/response.js
Expand Up @@ -76,7 +76,7 @@ module.exports = {
* And access log should save 500 not 302,
* then the `realStatus` can help us find out the real status code.
* @member {Number} Response#realStatus
* @param {Number} status The status code to be set.
* @return {Number} The status code to be set.
*/
get realStatus() {
if (this[REAL_STATUS]) {
Expand All @@ -85,6 +85,16 @@ module.exports = {
return this.status;
},

/**
* Set a real status code.
*
* e.g.: Using 302 status redirect to the global error page
* instead of show current 500 status page.
* And access log should save 500 not 302,
* then the `realStatus` can help us find out the real status code.
* @member {Number} Response#realStatus
* @param {Number} status The status code to be set.
*/
set realStatus(status) {
this[REAL_STATUS] = status;
},
Expand Down
12 changes: 6 additions & 6 deletions index.d.ts
Expand Up @@ -7,13 +7,13 @@ import { Readable } from 'stream';
import { Socket } from 'net';
import { IncomingMessage, ServerResponse } from 'http';
import { EggLogger, EggLoggers, LoggerLevel as EggLoggerLevel, EggContextLogger } from 'egg-logger';
import { HttpClient2, RequestOptions } from 'urllib';
import { HttpClient2, RequestOptions2 as RequestOptions } from 'urllib';
import {
EggCoreBase,
FileLoaderOption,
EggLoader as CoreLoader,
EggCoreOptions as CoreOptions,
EggLoaderOptions as CoreLoaderOptions,
EggLoader as CoreLoader,
EggCoreOptions as CoreOptions,
EggLoaderOptions as CoreLoaderOptions,
BaseContextClass as CoreBaseContextClass,
} from 'egg-core';
import EggCookies = require('egg-cookies');
Expand Down Expand Up @@ -636,7 +636,7 @@ declare module 'egg' {

truncated: boolean;
}

interface GetFileStreamOptions {
requireFile?: boolean; // required file submit, default is true
defCharset?: string;
Expand Down Expand Up @@ -664,7 +664,7 @@ declare module 'egg' {
* special properties (e.g: encrypted). So we must remove this property and
* create our own with the same name.
* @see https://github.com/eggjs/egg/pull/2958
*
*
* However, the latest version of Koa has "[key: string]: any" on the
* context, and there'll be a type error for "keyof koa.Context".
* So we have to directly inherit from "KoaApplication.BaseContext" and
Expand Down
2 changes: 1 addition & 1 deletion lib/agent.js
Expand Up @@ -14,7 +14,7 @@ const EGG_PATH = Symbol.for('egg#eggPath');
*/
class Agent extends EggApplication {
/**
* @constructor
* @class
* @param {Object} options - see {@link EggApplication}
*/
constructor(options = {}) {
Expand Down
2 changes: 1 addition & 1 deletion lib/application.js
Expand Up @@ -52,7 +52,7 @@ function escapeHeaderValue(value) {
class Application extends EggApplication {

/**
* @constructor
* @class
* @param {Object} options - see {@link EggApplication}
*/
constructor(options = {}) {
Expand Down
6 changes: 5 additions & 1 deletion lib/core/base_context_logger.js
Expand Up @@ -5,7 +5,7 @@ const CALL = Symbol('BaseContextLogger#call');
class BaseContextLogger {

/**
* @constructor
* @class
* @param {Context} ctx - context instance
* @param {String} pathName - class path name
* @since 1.0.0
Expand All @@ -29,6 +29,7 @@ class BaseContextLogger {

/**
* @member {Function} BaseContextLogger#debug
* @param {...any} args - log msg
* @since 1.2.0
*/
debug(...args) {
Expand All @@ -37,6 +38,7 @@ class BaseContextLogger {

/**
* @member {Function} BaseContextLogger#info
* @param {...any} args - log msg
* @since 1.2.0
*/
info(...args) {
Expand All @@ -45,6 +47,7 @@ class BaseContextLogger {

/**
* @member {Function} BaseContextLogger#warn
* @param {...any} args - log msg
* @since 1.2.0
*/
warn(...args) {
Expand All @@ -53,6 +56,7 @@ class BaseContextLogger {

/**
* @member {Function} BaseContextLogger#error
* @param {...any} args - log msg
* @since 1.2.0
*/
error(...args) {
Expand Down
2 changes: 1 addition & 1 deletion lib/core/httpclient.js
Expand Up @@ -5,7 +5,7 @@ const HttpsAgent = require('agentkeepalive').HttpsAgent;
const urllib = require('urllib');
const ms = require('humanize-ms');

class HttpClient extends urllib.HttpClient {
class HttpClient extends urllib.HttpClient2 {
constructor(app) {
normalizeConfig(app);
const config = app.config.httpclient;
Expand Down
4 changes: 2 additions & 2 deletions lib/egg.js
Expand Up @@ -33,7 +33,7 @@ const CLUSTER_CLIENTS = Symbol.for('egg#clusterClients');
class EggApplication extends EggCore {

/**
* @constructor
* @class
* @param {Object} options
* - {Object} [type] - type of instance, Agent and Application both extend koa, type can determine what it is.
* - {String} [baseDir] - app root dir, default is `process.cwd()`
Expand Down Expand Up @@ -508,7 +508,7 @@ class EggApplication extends EggCore {

/**
* Create egg context
* @method EggApplication#createContext
* @function EggApplication#createContext
* @param {Req} req - node native Request object
* @param {Res} res - node native Response object
* @return {Context} context object
Expand Down
4 changes: 2 additions & 2 deletions test/app/extend/context.test.js
Expand Up @@ -293,8 +293,8 @@ describe('test/app/extend/context.test.js', () => {
.get('/error')
.expect(200)
.expect('hello error');
assert(errorHadEmit);
await sleep(5000);
assert(errorHadEmit);
const logdir = app.config.logger.dir;
const log = fs.readFileSync(path.join(logdir, 'common-error.log'), 'utf8');
assert(/ENOENT: no such file or directory/.test(log));
Expand Down Expand Up @@ -359,8 +359,8 @@ describe('test/app/extend/context.test.js', () => {
.get('/error')
.expect(200)
.expect('hello error');
assert(errorHadEmit);
await sleep(5000);
assert(errorHadEmit);
const logdir = app.config.logger.dir;
const log = fs.readFileSync(path.join(logdir, 'common-error.log'), 'utf8');
assert(/ENOENT: no such file or directory/.test(log));
Expand Down
3 changes: 3 additions & 0 deletions test/fixtures/apps/httpclient-retry/package.json
@@ -0,0 +1,3 @@
{
"name": "httpclient-retry"
}
27 changes: 27 additions & 0 deletions test/lib/core/httpclient.test.js
Expand Up @@ -417,4 +417,31 @@ describe('test/lib/core/httpclient.test.js', () => {
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);
});
});
});
11 changes: 11 additions & 0 deletions test/utils.js
Expand Up @@ -50,6 +50,7 @@ exports.startLocalServer = () => {
if (localServer) {
return resolve('http://127.0.0.1:' + localServer.address().port);
}
let retry = false;
localServer = http.createServer((req, res) => {
req.resume();
req.on('end', () => {
Expand All @@ -62,6 +63,16 @@ exports.startLocalServer = () => {
res.end(`${req.method} ${req.url}`);
}, 10000);
return;
} else if (req.url === '/retry') {
if (!retry) {
retry = true;
res.statusCode = 500;
res.end();
} else {
res.statusCode = 200;
res.end('retry suc');
}
return;
} else {
res.end(`${req.method} ${req.url}`);
}
Expand Down

0 comments on commit eead318

Please sign in to comment.