@@ -5,14 +5,23 @@ import * as fs from 'fs';
55import { expect } from 'chai' ;
66
77import * as Docker from 'dockerode' ;
8+ import fetch from 'node-fetch' ;
89
910import { setupInterceptor , itIsAvailable } from './interceptor-test-utils' ;
1011import { delay } from '../../src/util/promise' ;
1112
1213const docker = new Docker ( ) ;
1314const DOCKER_FIXTURES = path . join ( __dirname , '..' , 'fixtures' , 'docker' ) ;
1415
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+
1625 const imageName = `test-${ dockerFolder } :latest` ;
1726
1827 const dockerContext = {
@@ -46,27 +55,38 @@ async function buildAndRun(dockerFolder: string, argument: string) {
4655 // Run the container, using its default entrypoint
4756 const container = await docker . createContainer ( {
4857 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
5264 } ) ;
5365
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+ } ) ;
5973
60- stream . setEncoding ( 'utf8' ) ;
61- stream . pipe ( process . stdout ) ;
74+ stream . setEncoding ( 'utf8' ) ;
75+ stream . pipe ( process . stdout ) ;
6276
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+ }
6485
65- container . start ( ) ;
86+ console . log ( 'Container created' ) ;
6687
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 ;
7090
7191 return container . id ;
7292}
@@ -108,7 +128,9 @@ describe('Docker single-container interceptor', function () {
108128 const { interceptor, server } = await interceptorSetup ;
109129 const mainRule = await server . forGet ( 'https://example.com' ) . thenReply ( 404 ) ;
110130
111- const containerId = await buildAndRun ( target . toLowerCase ( ) , 'https://example.com' ) ;
131+ const containerId = await buildAndRun ( target . toLowerCase ( ) , {
132+ arguments : [ 'https://example.com' ]
133+ } ) ;
112134
113135 await delay ( 500 ) ;
114136 expect (
@@ -117,12 +139,41 @@ describe('Docker single-container interceptor', function () {
117139
118140 await interceptor . activate ( server . port , { containerId } ) ;
119141 console . log ( 'Container intercepted' ) ;
120-
121142 await new Promise ( ( resolve ) => server . on ( 'response' , resolve ) ) ;
122143
123144 const seenRequests = await mainRule . getSeenRequests ( ) ;
124145 expect ( seenRequests . map ( r => r . url ) ) . to . include ( 'https://example.com/' ) ;
125146 } ) ;
126147 } ) ;
127148
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+
128179} ) ;
0 commit comments