Skip to content

Commit

Permalink
[JENKINS-73090] Handle CR from LineTransformationOutputStream
Browse files Browse the repository at this point in the history
  • Loading branch information
jglick committed May 1, 2024
1 parent 33858d1 commit 009ba54
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
* @since 1.349
*/
public abstract class LineTransformationOutputStream extends OutputStream {
private boolean sawCR;
private ByteArrayOutputStream2 buf = new ByteArrayOutputStream2();

/**
Expand All @@ -53,8 +54,15 @@ public abstract class LineTransformationOutputStream extends OutputStream {

@Override
public void write(int b) throws IOException {
if (sawCR && b != '\n') {
eol();
}
buf.write(b);
if (b == LF) eol();
if (b == '\n') {
eol();
} else if (b == '\r') {
sawCR = true;
}
}

private void eol() throws IOException {
Expand All @@ -65,6 +73,7 @@ private void eol() throws IOException {
buf = new ByteArrayOutputStream2();
else
buf.reset();
sawCR = false;
}

@Override
Expand Down Expand Up @@ -110,8 +119,6 @@ protected String trimEOL(String line) {
return line;
}

private static final int LF = 0x0A;

/**
* Convenience subclass for cases where you wish to process lines being sent to an underlying stream.
* {@link #eol} will typically {@link OutputStream#write(byte[], int, int)} to {@link #out}.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* The MIT License
*
* Copyright 2024 CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package hudson.console;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicLong;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import org.junit.Test;

public final class LineTransformationOutputStreamTest {

@Test public void nl() throws Exception {
test("\n");
}

@Test public void crnl() throws Exception {
test("\r\n");
}

@Test public void cr() throws Exception {
test("\r");
}

private void test(String linefeed) throws Exception {
var count = new AtomicLong();
long max = 1_000_000; // to see OOME in cr without fix: 1_000_000_000
try (var counter = new LineTransformationOutputStream() {
@Override protected void eol(byte[] b, int len) throws IOException {
count.addAndGet(Integer.parseInt(trimEOL(new String(b, 0, len))));
}
}) {
for (long i = 0; i < max; i++) {
counter.write((i + linefeed).getBytes(StandardCharsets.UTF_8));
}
}
assertThat(count.get(), is((max * (max - 1)) / 2));
}

}

0 comments on commit 009ba54

Please sign in to comment.