Skip to content

Commit eb9df39

Browse files
committed
adding onClientConnectionTerminated callback
1 parent 79e25a4 commit eb9df39

File tree

6 files changed

+108
-4
lines changed

6 files changed

+108
-4
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,15 @@ Called when an http response is received from the source.
111111
The default behavior is `pump(stream, res)`, which will be disabled if the
112112
option is specified.
113113

114+
##### onClientConnectionTerminated(res, err, response)
115+
Called when the client HTTP connection to the proxy server unexpectedly terminates before the downstream service response is sent.
116+
```js
117+
// internal implementation
118+
if (!res.socket || res.socket.destroyed || res.writableEnded) {
119+
return onClientConnectionTerminated(res, err, response)
120+
}
121+
```
122+
114123
##### rewriteRequestHeaders(req, headers)
115124
Called to rewrite the headers of the request, before them being sent to the downstream server.
116125
It must return the new headers object.

demos/gateway.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ const { proxy } = require('../index')({
55
})
66

77
const service = require('restana')()
8-
service.all('/service/*', (req, res) => proxy(req, res, req.url, {}))
8+
service.all('/service/*', (req, res) => proxy(req, res, req.url, {
9+
onClientConnectionTerminated (res, _err, response) {
10+
console.log('Client connection unexpectedly terminated:' + req.url)
11+
}
12+
}))
913

1014
service.start(8080)

demos/service.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,8 @@ service.get('/service/get', (req, res) => res.send('Hello World!'))
99

1010
service.post('/service/post', (req, res) => res.send(req.body))
1111

12+
service.get('/service/long', (req, res) => {
13+
setTimeout(() => res.end(), 10000)
14+
})
15+
1216
service.start(3000)

index.d.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as Http from 'http';
22
import * as Https from 'https';
33
import { Stream } from 'pump';
44

5-
interface Options {
5+
type Options = {
66
base?: string;
77
cacheURLs?: number;
88
requests?: {
@@ -14,13 +14,20 @@ interface Options {
1414
rejectUnauthorized?: boolean;
1515
}
1616

17+
type ProxyRequestResponse = {
18+
statusCode: Number;
19+
headers: Http.OutgoingHttpHeaders;
20+
stream: Stream;
21+
}
22+
1723
declare function fastProxy(options?: Options): {
1824
proxy(
1925
originReq: Http.IncomingMessage,
2026
originRes: Http.ServerResponse,
2127
source: string,
2228
opts?: {
2329
base?: string;
30+
onClientConnectionTerminated?(res: Http.ServerResponse, err: Error, response: ProxyRequestResponse): void;
2431
onResponse?(req: Http.IncomingMessage, res: Http.ServerResponse, stream: Stream): void;
2532
rewriteRequestHeaders?(req: Http.IncomingMessage, headers: Http.IncomingHttpHeaders): Http.IncomingHttpHeaders;
2633
rewriteHeaders?(headers: Http.OutgoingHttpHeaders): Http.OutgoingHttpHeaders;

index.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ function fastProxy (opts = {}) {
2828
opts = opts || {}
2929
const reqOpts = opts.request || {}
3030
const onResponse = opts.onResponse
31+
const onClientConnectionTerminated = opts.onClientConnectionTerminated || onClientConnectionTerminatedNoOp
3132
const rewriteHeaders = opts.rewriteHeaders || rewriteHeadersNoOp
3233
const rewriteRequestHeaders = opts.rewriteRequestHeaders || rewriteRequestHeadersNoOp
3334

@@ -88,8 +89,8 @@ function fastProxy (opts = {}) {
8889
request: reqOpts
8990
}
9091
request(reqParams, (err, response) => {
91-
if (res.socket.destroyed || res.writableEnded) {
92-
return
92+
if (!res.socket || res.socket.destroyed || res.writableEnded) {
93+
return onClientConnectionTerminated(res, err, response)
9394
}
9495

9596
if (err) {
@@ -156,6 +157,10 @@ function rewriteHeadersNoOp (headers) {
156157
return headers
157158
}
158159

160+
function onClientConnectionTerminatedNoOp (_err, response) {
161+
162+
}
163+
159164
function rewriteRequestHeadersNoOp (req, headers) {
160165
return headers
161166
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/* global describe, it */
2+
'use strict'
3+
4+
const http = require('http')
5+
const { expect } = require('chai')
6+
let gateway, service, close, proxy
7+
let clientTerminated = false
8+
9+
describe('Client connection terminated', () => {
10+
it('init', async () => {
11+
const fastProxy = require('../index')({
12+
base: 'http://127.0.0.1:3000'
13+
})
14+
15+
proxy = fastProxy.proxy
16+
close = fastProxy.close
17+
})
18+
19+
it('init & start gateway', async () => {
20+
// init gateway
21+
gateway = require('restana')()
22+
23+
gateway.all('/service/*', function (req, res) {
24+
proxy(req, res, req.url, {
25+
onClientConnectionTerminated (res, _err, response) {
26+
clientTerminated = true
27+
}
28+
})
29+
})
30+
31+
await gateway.start(8080)
32+
})
33+
34+
it('init & start remote service', async () => {
35+
// init remote service
36+
service = require('restana')()
37+
38+
service.get('/service/long', (req, res) => {
39+
setTimeout(() => res.end(), 500)
40+
})
41+
42+
await service.start(3000)
43+
})
44+
45+
it('should invoke onClientConnectionTerminated callback', async () => {
46+
const options = {
47+
host: 'localhost',
48+
path: '/service/long',
49+
port: 8080,
50+
method: 'GET',
51+
headers: {
52+
'Content-Length': 0
53+
}
54+
}
55+
const req = http.request(options)
56+
req.on('error', () => {})
57+
req.end()
58+
59+
await sleep(100)
60+
req.destroy()
61+
62+
await sleep(1000)
63+
expect(clientTerminated).to.equal(true)
64+
})
65+
66+
it('close all', async () => {
67+
close()
68+
await gateway.close()
69+
await service.close()
70+
})
71+
})
72+
73+
function sleep (ms) {
74+
return new Promise(resolve => setTimeout(resolve, ms))
75+
}

0 commit comments

Comments
 (0)