Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
import org.apache.hc.core5.http.io.HttpMessageWriterFactory;
import org.apache.hc.core5.http.io.ResponseOutOfOrderStrategy;
import org.apache.hc.core5.http.message.MessageSupport;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.util.Args;

/**
Expand Down Expand Up @@ -209,7 +210,7 @@ public void sendRequestEntity(final ClassicHttpRequest request) throws HttpExcep
if (len == ContentLengthStrategy.UNDEFINED) {
throw new LengthRequiredException();
}
try (final OutputStream outStream = createContentOutputStream(
final OutputStream outStream = createContentOutputStream(
len, this.outbuffer, new OutputStream() {

final OutputStream socketOutputStream = socketHolder.getOutputStream();
Expand Down Expand Up @@ -281,15 +282,22 @@ public void close() throws IOException {
socketOutputStream.close();
}

}, entity.getTrailers())) {
}, entity.getTrailers());
try {
entity.writeTo(outStream);
Comment thread
ok2c marked this conversation as resolved.
Resolved
outStream.close();
} catch (final ResponseOutOfOrderException ex) {
if (len > 0) {
if (len != 0) {
Comment thread
ok2c marked this conversation as resolved.
Resolved
this.consistent = false;
}
} catch (final IOException | RuntimeException ex) {
this.consistent = false;
close(CloseMode.IMMEDIATE);
throw ex;
}
}


@Override
public boolean isConsistent() {
return this.consistent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import org.apache.hc.core5.http.io.HttpMessageWriter;
import org.apache.hc.core5.http.io.HttpMessageWriterFactory;
import org.apache.hc.core5.http.io.HttpServerConnection;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.util.Args;

/**
Expand Down Expand Up @@ -193,8 +194,14 @@ public void sendResponseEntity(final ClassicHttpResponse response)
return;
}
final long len = this.outgoingContentStrategy.determineLength(response);
try (final OutputStream outStream = createContentOutputStream(len, this.outbuffer, socketHolder.getOutputStream(), entity.getTrailers())) {
final OutputStream outStream = createContentOutputStream(
len, this.outbuffer, socketHolder.getOutputStream(), entity.getTrailers());
try {
entity.writeTo(outStream);
outStream.close();
} catch (final IOException | RuntimeException ex) {
close(CloseMode.IMMEDIATE);
throw ex;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

Expand Down Expand Up @@ -230,6 +232,67 @@ void testReadResponseNoEntity() throws Exception {
Assertions.assertInstanceOf(IdentityInputStream.class, content);
}

@Test
void testEarlyResponseDoesNotAbortConnection() throws Exception {
final ByteArrayOutputStream output = new ByteArrayOutputStream();
final ByteArrayInputStream input = new ByteArrayInputStream(
"HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n"
.getBytes(StandardCharsets.US_ASCII));
Mockito.when(socket.getOutputStream()).thenReturn(output);
Mockito.when(socket.getInputStream()).thenReturn(input);

final org.apache.hc.core5.http.io.ResponseOutOfOrderStrategy strategy =
Mockito.mock(org.apache.hc.core5.http.io.ResponseOutOfOrderStrategy.class);
Mockito.when(strategy.isEarlyResponseDetected(
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyLong(), Mockito.anyLong()))
.thenReturn(true);

conn = new DefaultBHttpClientConnection(
Http1Config.DEFAULT, null, null,
DefaultContentLengthStrategy.INSTANCE,
DefaultContentLengthStrategy.INSTANCE,
strategy,
DefaultHttpRequestWriterFactory.INSTANCE,
DefaultHttpResponseParserFactory.INSTANCE);
conn.bind(socket);

final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/stuff");
request.addHeader("Transfer-Encoding", "chunked");
request.setEntity(new ByteArrayEntity(new byte[16384], ContentType.TEXT_PLAIN, true));

conn.sendRequestHeader(request);
Assertions.assertDoesNotThrow(() -> conn.sendRequestEntity(request));
Assertions.assertTrue(conn.isOpen());
Assertions.assertFalse(conn.isConsistent());
Assertions.assertEquals(400, conn.receiveResponseHeader().getCode());
Assertions.assertFalse(new String(output.toByteArray(), StandardCharsets.US_ASCII)
.endsWith("0\r\n\r\n"));
}

@Test
void testChunkedEntityFailureDoesNotTerminateMessage() throws Exception {
final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
Mockito.when(socket.getOutputStream()).thenReturn(outStream);
conn.bind(socket);

final HttpEntity entity = Mockito.mock(HttpEntity.class);
Mockito.doAnswer(invocation -> {
final OutputStream out = invocation.getArgument(0);
out.write("123".getBytes(StandardCharsets.US_ASCII));
throw new EOFException("source stream failed");
}).when(entity).writeTo(Mockito.any(OutputStream.class));

final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/stuff");
request.addHeader("Transfer-Encoding", "chunked");
request.setEntity(entity);
conn.sendRequestHeader(request);
Assertions.assertThrows(EOFException.class, () -> conn.sendRequestEntity(request));
Assertions.assertFalse(conn.isConsistent());
Assertions.assertFalse(conn.isOpen());
Assertions.assertFalse(new String(outStream.toByteArray(), StandardCharsets.US_ASCII)
.endsWith("0\r\n\r\n"));
}

@Test
void testWriteRequestHead() throws Exception {
final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

Expand Down Expand Up @@ -210,6 +212,29 @@ void testReadRequestNoEntity() throws Exception {
Assertions.assertNull(entity);
}

@Test
void testChunkedEntityFailureDoesNotTerminateMessage() throws Exception {
final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
Mockito.when(socket.getOutputStream()).thenReturn(outStream);
conn.bind(socket);

final HttpEntity entity = Mockito.mock(HttpEntity.class);
Mockito.doAnswer(invocation -> {
final OutputStream out = invocation.getArgument(0);
out.write("123".getBytes(StandardCharsets.US_ASCII));
throw new EOFException("source stream failed");
}).when(entity).writeTo(Mockito.any(OutputStream.class));

final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
response.addHeader("Transfer-Encoding", "chunked");
response.setEntity(entity);
conn.sendResponseHeader(response);
Assertions.assertThrows(EOFException.class, () -> conn.sendResponseEntity(response));
Assertions.assertFalse(conn.isOpen());
Assertions.assertFalse(new String(outStream.toByteArray(), StandardCharsets.US_ASCII)
.endsWith("0\r\n\r\n"));
}

@Test
void testWriteResponseHead() throws Exception {
final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
Expand Down
Loading