@Override public void accept(BufferedSource source) { try { while (!source.exhausted()) { // See https://docs.docker.com/engine/api/v1.37/#operation/ContainerAttach if(!source.request(HEADER_SIZE)) { return; } StreamType streamType = streamType(source.readByte()); source.skip(3); int payloadSize = source.readInt(); if (streamType != StreamType.RAW) { if (!source.request(payloadSize)) { return; } byte[] payload = source.readByteArray(payloadSize); resultCallback.onNext(new Frame(streamType, payload)); } else { resultCallback.onNext(new Frame(streamType, source.readByteArray())); } } } catch (Exception e) { resultCallback.onError(e); } }
@Test public void reconstructBreakedUnicode() throws IOException { String payload = "Тест"; byte[] payloadBytes = payload.getBytes(StandardCharsets.UTF_8); byte[] bytes1 = new byte[(int) (payloadBytes.length * 0.6)]; byte[] bytes2 = new byte[payloadBytes.length - bytes1.length]; System.arraycopy(payloadBytes, 0, bytes1, 0, bytes1.length); System.arraycopy(payloadBytes, bytes1.length, bytes2, 0, bytes2.length); FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); callback.addConsumer(OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.RAW, bytes1)); callback.onNext(new Frame(StreamType.RAW, bytes2)); callback.close(); assertEquals(payload, consumer.toUtf8String()); }
@Test public void passStderrFrameWithoutColors() { FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer(); callback.addConsumer(OutputType.STDERR, consumer); callback.onNext(new Frame(StreamType.STDERR, FRAME_PAYLOAD.getBytes())); assertEquals(LOG_RESULT, consumer.toUtf8String()); }
@Test public void basicConsumer() { FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); BasicConsumer consumer = new BasicConsumer(); callback.addConsumer(OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, FRAME_PAYLOAD.getBytes())); assertEquals(LOG_RESULT, consumer.toString()); }
@Test public void passStdoutFrameWithoutColors() { FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer(); callback.addConsumer(OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, FRAME_PAYLOAD.getBytes())); assertEquals(LOG_RESULT, consumer.toUtf8String()); }
@Test public void passStdoutEmptyLine() { String payload = ""; FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); callback.addConsumer(OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, payload.getBytes())); assertEquals(payload, consumer.toUtf8String()); }
@Test public void passStdoutSingleLine() { String payload = "Test"; FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); callback.addConsumer(OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, payload.getBytes())); assertEquals(payload, consumer.toUtf8String()); }
@Test public void passStderrFrameWithColors() { FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); callback.addConsumer(OutputType.STDERR, consumer); callback.onNext(new Frame(StreamType.STDERR, FRAME_PAYLOAD.getBytes())); assertEquals(FRAME_PAYLOAD, consumer.toUtf8String()); }
@Test public void passStdoutFrameWithColors() { FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); callback.addConsumer(OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, FRAME_PAYLOAD.getBytes())); assertEquals(FRAME_PAYLOAD, consumer.toUtf8String()); }
@Test public void passStdoutSingleLineWithNewline() { String payload = "Test\n"; FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); callback.addConsumer(OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, payload.getBytes())); assertEquals(payload, consumer.toUtf8String()); }
@Test public void passRawFrameWithoutColors() throws TimeoutException, IOException { FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); WaitingConsumer waitConsumer = new WaitingConsumer(); callback.addConsumer(OutputType.STDOUT, waitConsumer); callback.onNext(new Frame(StreamType.RAW, FRAME_PAYLOAD.getBytes())); waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("Test2"), 1, TimeUnit.SECONDS); waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("Тест1"), 1, TimeUnit.SECONDS); Exception exception = null; try { waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("Test3"), 1, TimeUnit.SECONDS); } catch (Exception e) { exception = e; } assertTrue(exception instanceof TimeoutException); callback.close(); waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("Test3"), 1, TimeUnit.SECONDS); }
@Test public void passRawFrameWithColors() throws TimeoutException, IOException { FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); WaitingConsumer waitConsumer = new WaitingConsumer().withRemoveAnsiCodes(false); callback.addConsumer(OutputType.STDOUT, waitConsumer); callback.onNext(new Frame(StreamType.RAW, FRAME_PAYLOAD.getBytes())); waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("\u001B[1;33mTest2\u001B[0m"), 1, TimeUnit.SECONDS); waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("\u001B[0;32mТест1\u001B[0m"), 1, TimeUnit.SECONDS); Exception exception = null; try { waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("\u001B[0;31mTest3\u001B[0m"), 1, TimeUnit.SECONDS); } catch (Exception e) { exception = e; } assertTrue(exception instanceof TimeoutException); callback.close(); waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("\u001B[0;31mTest3\u001B[0m"), 1, TimeUnit.SECONDS); }
@Test public void passStdoutNull() { FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); callback.addConsumer(OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, null)); assertEquals("", consumer.toUtf8String()); }
@Test public void shouldInterruptStreamWhenDockerThreadInterrupted() { Thread.currentThread().interrupt(); streamLog.onNext(new Frame(StreamType.RAW, "added line".getBytes(UTF_8))); assertThat(Thread.interrupted()).isTrue(); }
@Test public void shouldGiveAStreamContainingLineOfFrameFromOtherThread() throws ExecutionException, InterruptedException { CountDownLatch streamStarted = new CountDownLatch(1); executor.submit(() -> streamLog.onNext(new Frame(StreamType.RAW, "added line".getBytes(UTF_8)))); Future<?> streamCompleted = executor.submit(completeStreamOnceStarted(streamStarted)); assertThat(streamLog.stream().peek((l) -> streamStarted.countDown())).contains("added line"); assertExecutionOf(streamCompleted::get).hasNoAssertionFailures(); }
@Test public void shouldReadLineAsUtf8() throws ExecutionException, InterruptedException { CountDownLatch streamFinished = new CountDownLatch(2); String originalString = "use of accents é"; byte[] utf8String = originalString.getBytes(UTF_8); byte[] isoString = originalString.getBytes(ISO_8859_1); String misinterpretedString = new String(isoString, UTF_8); executor.submit(() -> streamLog.onNext(new Frame(StreamType.RAW, utf8String))); executor.submit(() -> streamLog.onNext(new Frame(StreamType.RAW, isoString))); Future<?> streamCompleted = executor.submit(completeStreamOnceStarted(streamFinished)); assertThat(streamLog.stream().peek((l) -> streamFinished.countDown())) .hasSize(2) .containsOnlyOnce(originalString, misinterpretedString); assertExecutionOf(streamCompleted::get).hasNoAssertionFailures(); }