* Fix #9953 handled HEAD Fix #9953 so that if a Handler self handles HEAD by not writing content, then the length checks do not fire if 0 bytes have been written. * updates from review
This commit is contained in:
parent
f6e963c841
commit
62e6cf2b76
|
@ -1176,16 +1176,15 @@ public class HttpChannelState implements HttpChannel, Components
|
||||||
{
|
{
|
||||||
long length = BufferUtil.length(content);
|
long length = BufferUtil.length(content);
|
||||||
|
|
||||||
long totalWritten;
|
|
||||||
HttpChannelState httpChannelState;
|
HttpChannelState httpChannelState;
|
||||||
HttpStream stream = null;
|
HttpStream stream = null;
|
||||||
Throwable failure = null;
|
Throwable failure;
|
||||||
MetaData.Response responseMetaData = null;
|
MetaData.Response responseMetaData = null;
|
||||||
try (AutoLock ignored = _request._lock.lock())
|
try (AutoLock ignored = _request._lock.lock())
|
||||||
{
|
{
|
||||||
httpChannelState = _request.lockedGetHttpChannelState();
|
httpChannelState = _request.lockedGetHttpChannelState();
|
||||||
long committedContentLength = httpChannelState._committedContentLength;
|
long committedContentLength = httpChannelState._committedContentLength;
|
||||||
totalWritten = _contentBytesWritten + length;
|
long totalWritten = _contentBytesWritten + length;
|
||||||
long contentLength = committedContentLength >= 0 ? committedContentLength : getHeaders().getLongField(HttpHeader.CONTENT_LENGTH);
|
long contentLength = committedContentLength >= 0 ? committedContentLength : getHeaders().getLongField(HttpHeader.CONTENT_LENGTH);
|
||||||
|
|
||||||
if (_writeCallback != null)
|
if (_writeCallback != null)
|
||||||
|
@ -1193,11 +1192,14 @@ public class HttpChannelState implements HttpChannel, Components
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
failure = getFailure(httpChannelState);
|
failure = getFailure(httpChannelState);
|
||||||
if (failure == null && contentLength >= 0)
|
if (failure == null && contentLength >= 0 && totalWritten != contentLength)
|
||||||
{
|
{
|
||||||
// If the content length were not compatible with what was written, then we need to abort.
|
// If the content length were not compatible with what was written, then we need to abort.
|
||||||
String lengthError = (totalWritten > contentLength) ? "written %d > %d content-length"
|
String lengthError = null;
|
||||||
: (last && totalWritten < contentLength) ? "written %d < %d content-length" : null;
|
if (totalWritten > contentLength)
|
||||||
|
lengthError = "written %d > %d content-length";
|
||||||
|
else if (last && !(totalWritten == 0 && HttpMethod.HEAD.is(_request.getMethod())))
|
||||||
|
lengthError = "written %d < %d content-length";
|
||||||
if (lengthError != null)
|
if (lengthError != null)
|
||||||
{
|
{
|
||||||
String message = lengthError.formatted(totalWritten, contentLength);
|
String message = lengthError.formatted(totalWritten, contentLength);
|
||||||
|
@ -1439,7 +1441,7 @@ public class HttpChannelState implements HttpChannel, Components
|
||||||
long totalWritten = response._contentBytesWritten;
|
long totalWritten = response._contentBytesWritten;
|
||||||
long committedContentLength = httpChannelState._committedContentLength;
|
long committedContentLength = httpChannelState._committedContentLength;
|
||||||
|
|
||||||
if (committedContentLength >= 0 && committedContentLength != totalWritten)
|
if (committedContentLength >= 0 && committedContentLength != totalWritten && !(totalWritten == 0 && HttpMethod.HEAD.is(_request.getMethod())))
|
||||||
failure = new IOException("content-length %d != %d written".formatted(committedContentLength, totalWritten));
|
failure = new IOException("content-length %d != %d written".formatted(committedContentLength, totalWritten));
|
||||||
|
|
||||||
// is the request fully consumed?
|
// is the request fully consumed?
|
||||||
|
|
|
@ -36,6 +36,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import org.awaitility.Awaitility;
|
import org.awaitility.Awaitility;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
import org.eclipse.jetty.http.HttpStatus;
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
import org.eclipse.jetty.http.HttpTester;
|
import org.eclipse.jetty.http.HttpTester;
|
||||||
import org.eclipse.jetty.io.AbstractConnection;
|
import org.eclipse.jetty.io.AbstractConnection;
|
||||||
|
@ -1334,6 +1335,75 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHeadHandled() throws Exception
|
||||||
|
{
|
||||||
|
startServer(new Handler.Abstract()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public boolean handle(Request request, Response response, Callback callback) throws Exception
|
||||||
|
{
|
||||||
|
response.getHeaders().put(HttpHeader.CONTENT_LENGTH, 10);
|
||||||
|
if (HttpMethod.HEAD.is(request.getMethod()))
|
||||||
|
{
|
||||||
|
if (request.getHttpURI().getCanonicalPath().equals("/writeNull"))
|
||||||
|
response.write(true, null, callback);
|
||||||
|
else
|
||||||
|
callback.succeeded();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Content.Sink.write(response, true, "123456789\n", callback);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_httpConfiguration.setSendDateHeader(false);
|
||||||
|
_httpConfiguration.setSendServerVersion(false);
|
||||||
|
_httpConfiguration.setSendXPoweredBy(false);
|
||||||
|
|
||||||
|
try (Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort()))
|
||||||
|
{
|
||||||
|
OutputStream os = client.getOutputStream();
|
||||||
|
InputStream is = client.getInputStream();
|
||||||
|
|
||||||
|
os.write("""
|
||||||
|
GET / HTTP/1.1
|
||||||
|
Host: localhost
|
||||||
|
|
||||||
|
HEAD / HTTP/1.1
|
||||||
|
Host: localhost
|
||||||
|
|
||||||
|
HEAD /writeNull HTTP/1.1
|
||||||
|
Host: localhost
|
||||||
|
|
||||||
|
GET / HTTP/1.1
|
||||||
|
Host: localhost
|
||||||
|
Connection: close
|
||||||
|
|
||||||
|
""".getBytes(StandardCharsets.ISO_8859_1));
|
||||||
|
|
||||||
|
String in = IO.toString(is);
|
||||||
|
assertThat(in.replace("\r", ""), is("""
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Length: 10
|
||||||
|
|
||||||
|
123456789
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Length: 10
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Length: 10
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Length: 10
|
||||||
|
Connection: close
|
||||||
|
|
||||||
|
123456789
|
||||||
|
"""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBlockedClient() throws Exception
|
public void testBlockedClient() throws Exception
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue