Skip to content

Commit e74ab47

Browse files
committed
Fix pipe issues in Windows docker-compose interception
Windows named pipes aren't handled well by Python (seemingly) when used in docker-compose (<v2, which isn't stable yet). When they are closed cleanly (at least from Node's POV...) it throws an error, either complaining that it's closed on read or on write (which gets reported as an odd "incompatible API" error incorrectly, see https://github.com/kenneth-git/compose/blob/d06e15323a44b3a544969cb77b517d0dac295a94/compose/cli/errors.py#L67-L72 for background). On the other hand, it does properly close down attach connections all by itself, which works around the need for actively closing the connection on our end, so we can just disable that for this specific case and everything seems to work.
1 parent 9d3355f commit e74ab47

File tree

1 file changed

+21
-8
lines changed

1 file changed

+21
-8
lines changed

src/interceptors/docker/docker-proxy.ts

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ if (process.platform !== 'win32') {
4343
const API_VERSION_MATCH = /^\/v?([\.\d]+)\//;
4444
const CREATE_CONTAINER_MATCHER = /^\/[^\/]+\/containers\/create/;
4545
const START_CONTAINER_MATCHER = /^\/[^\/]+\/containers\/([^\/]+)\/start/;
46-
const BUILD_IMAGE_MATCHER = /^\/[^\/]+\/build/;
46+
const BUILD_IMAGE_MATCHER = /^\/[^\/]+\/build$/;
47+
const EVENTS_MATCHER = /^\/[^\/]+\/events$/;
4748
const ATTACH_CONTAINER_MATCHER = /^\/[^\/]+\/containers\/([^\/]+)\/attach/;
4849
const CONTAINER_LIST_MATCHER = /^\/[^\/]+\/containers\/json/;
4950

@@ -185,7 +186,10 @@ async function createDockerProxy(proxyPort: number, httpsConfig: { certPath: str
185186
}
186187
}
187188

188-
if (reqPath.match(CONTAINER_LIST_MATCHER)) {
189+
if (
190+
reqPath.match(CONTAINER_LIST_MATCHER) ||
191+
reqPath.match(EVENTS_MATCHER)
192+
) {
189193
const filterString = reqUrl.searchParams.get('filters') ?? '{}';
190194

191195
try {
@@ -312,20 +316,29 @@ async function createDockerProxy(proxyPort: number, httpsConfig: { certPath: str
312316
});
313317

314318
const attachMatch = ATTACH_CONTAINER_MATCHER.exec(req.url!);
315-
if (attachMatch) {
319+
320+
// Python Docker compose (every version since 2016 at least) uses its own user agent, and
321+
// has problems with unexpected closure of attach requests
322+
const isPythonDockerCompose = (req.headers['user-agent'] ?? '').startsWith('docker-compose/');
323+
324+
if (attachMatch && process.platform === 'win32' && !isPythonDockerCompose) {
325+
// On Windows for some reason attach doesn't exit by itself when containers do. To handle
326+
// that, we watch for exit ourselves, kill the attach shortly afterwards, in case it isn't dead
327+
// already.
328+
329+
// This only affects Windows, and it's disabled for Python docker-compose, which doesn't handle
330+
// clean closure well (throwing pipe errors) but which does know how to clean up attach connections
331+
// all by itself (it tracks container events to close from the client side).
332+
316333
const abortController = new AbortController();
317334

318335
docker.getContainer(attachMatch[1]).wait({
319336
condition: 'next-exit',
320337
abortSignal: abortController.signal
321338
}).then(() => {
322-
// The container has exited. On Windows for some reason attach doesn't exit by itself, even
323-
// after the container has actually ended. To handle that, we watch for exit here, and
324-
// kill the attach shortly afterwards, if it isn't dead already. We do this on all platforms,
325-
// not just Windows, for consistency & reliability in case this comes up elsewhere.
326339
setTimeout(() => {
327340
socket.end();
328-
}, 100); // Slightly delay, in case there's more output on the way
341+
}, 500); // Slightly delay, in case there's more output/clean close on the way
329342
}).catch((err) => {
330343
if (abortController.signal.aborted) return; // If we aborted, we don't care about errors
331344
console.log("Error waiting for container exit on attach", err);

0 commit comments

Comments
 (0)