@@ -5,14 +5,23 @@ import * as fs from 'fs';
5
5
import { expect } from 'chai' ;
6
6
7
7
import * as Docker from 'dockerode' ;
8
+ import fetch from 'node-fetch' ;
8
9
9
10
import { setupInterceptor , itIsAvailable } from './interceptor-test-utils' ;
10
11
import { delay } from '../../src/util/promise' ;
11
12
12
13
const docker = new Docker ( ) ;
13
14
const DOCKER_FIXTURES = path . join ( __dirname , '..' , 'fixtures' , 'docker' ) ;
14
15
15
- async function buildAndRun ( dockerFolder : string , argument : string ) {
16
+ async function buildAndRun ( dockerFolder : string , options : {
17
+ arguments ?: string [ ] ,
18
+ attach ?: boolean ,
19
+ portBindings ?: { [ key : string ] : { } } ,
20
+ } = { } ) {
21
+ const shouldAttach = options . attach ?? true ;
22
+ const containerArguments = options . arguments ?? [ ] ;
23
+ const portBindings = options . portBindings ?? { } ;
24
+
16
25
const imageName = `test-${ dockerFolder } :latest` ;
17
26
18
27
const dockerContext = {
@@ -46,27 +55,38 @@ async function buildAndRun(dockerFolder: string, argument: string) {
46
55
// Run the container, using its default entrypoint
47
56
const container = await docker . createContainer ( {
48
57
Image : imageName ,
49
- HostConfig : { AutoRemove : true } ,
50
- Cmd : [ argument ] ,
51
- Tty : true ,
58
+ HostConfig : {
59
+ AutoRemove : true ,
60
+ PortBindings : portBindings
61
+ } ,
62
+ Cmd : containerArguments ,
63
+ Tty : shouldAttach
52
64
} ) ;
53
65
54
- const stream = await container . attach ( {
55
- stream : true ,
56
- stdout : true ,
57
- stderr : true
58
- } ) ;
66
+ let startupPromise : Promise < void >
67
+ if ( shouldAttach ) {
68
+ const stream = await container . attach ( {
69
+ stream : true ,
70
+ stdout : true ,
71
+ stderr : true
72
+ } ) ;
59
73
60
- stream . setEncoding ( 'utf8' ) ;
61
- stream . pipe ( process . stdout ) ;
74
+ stream . setEncoding ( 'utf8' ) ;
75
+ stream . pipe ( process . stdout ) ;
62
76
63
- console . log ( 'Container created' ) ;
77
+ // The promise that docker.run returns waits for the container to *finish*. To wait until
78
+ // the container has started, we wait for the first stream output:
79
+ startupPromise = new Promise ( ( resolve ) => stream . on ( 'data' , resolve ) ) ;
80
+ } else {
81
+ // When we can't attach to wait for input (e.g. PHP Apache, where window events cause
82
+ // problems) we just wait briefly instead:
83
+ startupPromise = delay ( 500 ) ;
84
+ }
64
85
65
- container . start ( ) ;
86
+ console . log ( 'Container created' ) ;
66
87
67
- // The promise that docker.run returns waits for the container to *finish*. To wait until the
68
- // container has started, we wait for the first stream output:
69
- await new Promise ( ( resolve ) => stream . on ( 'data' , resolve ) ) ;
88
+ await container . start ( ) ;
89
+ await startupPromise ;
70
90
71
91
return container . id ;
72
92
}
@@ -108,7 +128,9 @@ describe('Docker single-container interceptor', function () {
108
128
const { interceptor, server } = await interceptorSetup ;
109
129
const mainRule = await server . forGet ( 'https://example.com' ) . thenReply ( 404 ) ;
110
130
111
- const containerId = await buildAndRun ( target . toLowerCase ( ) , 'https://example.com' ) ;
131
+ const containerId = await buildAndRun ( target . toLowerCase ( ) , {
132
+ arguments : [ 'https://example.com' ]
133
+ } ) ;
112
134
113
135
await delay ( 500 ) ;
114
136
expect (
@@ -117,12 +139,41 @@ describe('Docker single-container interceptor', function () {
117
139
118
140
await interceptor . activate ( server . port , { containerId } ) ;
119
141
console . log ( 'Container intercepted' ) ;
120
-
121
142
await new Promise ( ( resolve ) => server . on ( 'response' , resolve ) ) ;
122
143
123
144
const seenRequests = await mainRule . getSeenRequests ( ) ;
124
145
expect ( seenRequests . map ( r => r . url ) ) . to . include ( 'https://example.com/' ) ;
125
146
} ) ;
126
147
} ) ;
127
148
149
+ // PHP is a special case: we have to make a request to trigger it:
150
+ it ( `should intercept external PHP requests` , async function ( ) {
151
+ this . timeout ( 60000 ) ;
152
+ const { interceptor, server } = await interceptorSetup ;
153
+ const mainRule = await server . forGet ( 'https://example.com' ) . thenReply ( 404 ) ;
154
+
155
+ const containerId = await buildAndRun ( 'php' , {
156
+ attach : false ,
157
+ portBindings : { '80/tcp' : [ { HostPort : '48080' } ] }
158
+ } ) ;
159
+
160
+ await delay ( 500 ) ;
161
+ expect (
162
+ _ . map ( ( ( await interceptor . getMetadata ! ( 'summary' ) ) . targets ) , ( { id } : any ) => id )
163
+ ) . to . include ( containerId ) ;
164
+
165
+ await interceptor . activate ( server . port , { containerId } ) ;
166
+ console . log ( 'Container intercepted' ) ;
167
+
168
+ await delay ( 500 ) ;
169
+ fetch ( 'http://localhost:48080/?target=https://example.com' )
170
+ . catch ( ( ) => { } ) ;
171
+
172
+ // Wait for the resulting request to example.com:
173
+ await new Promise ( ( resolve ) => server . on ( 'response' , resolve ) ) ;
174
+
175
+ const seenRequests = await mainRule . getSeenRequests ( ) ;
176
+ expect ( seenRequests . map ( r => r . url ) ) . to . include ( 'https://example.com/' ) ;
177
+ } ) ;
178
+
128
179
} ) ;
0 commit comments