Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add SizeLimitHandler to Jetty-12 #10071

Merged
merged 3 commits into from
Jul 14, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -410,7 +410,11 @@ public void onFailure(int request, Throwable failure)
if (LOG.isDebugEnabled())
LOG.debug("Request {} failure on {}: {}", request, stream, failure);
if (stream != null)
stream.getHttpChannel().onFailure(new BadMessageException(null, failure));
{
Runnable runnable = stream.getHttpChannel().onFailure(new BadMessageException(null, failure));
if (runnable != null)
getExecutor().execute(runnable);
}
stream = null;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,10 @@ public static Response parseHeadResponse(String response)

public static Response parseResponse(String response)
{
return parseResponse(response, false);
Response r = new Response();
HttpParser parser = new HttpParser(r);
parser.parseNext(BufferUtil.toBuffer(response));
return r;
}

private static Response parseResponse(String response, boolean head)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.server;

import java.nio.ByteBuffer;

import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.HttpException;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.util.Callback;

/**
* A handler that can limit the size of message bodies in requests and responses.
*
* <p>The optional request and response limits are imposed by checking the {@code Content-Length}
* header or observing the actual bytes seen by the handler. Handler order is important, in as much
* as if this handler is before a the {@link org.eclipse.jetty.server.handler.gzip.GzipHandler},
* then it will limit compressed sized, if it as after the {@link
* org.eclipse.jetty.server.handler.gzip.GzipHandler} then the limit is applied to uncompressed
* bytes. If a size limit is exceeded then {@link BadMessageException} is thrown with a {@link
* org.eclipse.jetty.http.HttpStatus#PAYLOAD_TOO_LARGE_413} status.
*/
public class SizeLimitHandler extends Handler.Wrapper
{
private final long _requestLimit;
private final long _responseLimit;
private long _read = 0;
private long _written = 0;

/**
* @param requestLimit The request body size limit in bytes or -1 for no limit
* @param responseLimit The response body size limit in bytes or -1 for no limit
*/
public SizeLimitHandler(long requestLimit, long responseLimit)
{
_requestLimit = requestLimit;
_responseLimit = responseLimit;
}

@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception
{
HttpField contentLengthField = request.getHeaders().getField(HttpHeader.CONTENT_LENGTH);
if (contentLengthField != null)
{
long contentLength = contentLengthField.getLongValue();
if (contentLength > _requestLimit)
throw new BadMessageException(413, "Request body is too large: " + contentLength + ">" + _requestLimit);
}

HttpFields.Mutable.Wrapper httpFields = new HttpFields.Mutable.Wrapper(response.getHeaders())
{
@Override
public HttpField onAddField(HttpField field)
{
if (field.getHeader().is(HttpHeader.CONTENT_LENGTH.asString()))
{
long contentLength = field.getLongValue();
if (contentLength > _responseLimit)
throw new HttpException.RuntimeException(500, "Response body is too large: " + contentLength + ">" + _responseLimit);
}
return super.onAddField(field);
}
};

response = new Response.Wrapper(request, response)
{
@Override
public HttpFields.Mutable getHeaders()
{
return httpFields;
}
};

request.addHttpStreamWrapper(httpStream -> new HttpStream.Wrapper(httpStream)
{
@Override
public Content.Chunk read()
{
Content.Chunk chunk = super.read();
if (chunk == null)
return null;
if (chunk.getFailure() != null)
return chunk;

// Check request content limit.
ByteBuffer content = chunk.getByteBuffer();
if (content != null && content.remaining() > 0)
{
_read += content.remaining();
if (_requestLimit >= 0 && _read > _requestLimit)
{
BadMessageException e = new BadMessageException(413, "Request body is too large: " + _read + ">" + _requestLimit);
request.fail(e);
return null;
}
}

return chunk;
}

@Override
public void send(MetaData.Request request, MetaData.Response response, boolean last, ByteBuffer content, Callback callback)
{
// Check response content limit.
if (content != null && content.remaining() > 0)
{
if (_responseLimit >= 0 && (_written + content.remaining()) > _responseLimit)
{
callback.failed(new HttpException.RuntimeException(500, "Response body is too large: " +
_written + content.remaining() + ">" + _responseLimit));
return;
}
_written += content.remaining();
}

super.send(request, response, last, content, callback);
}
});

return super.handle(request, response, callback);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -980,7 +980,9 @@ public void demand(Runnable demandCallback)
@Override
public void fail(Throwable failure)
{
_httpChannelState.onFailure(failure);
Runnable runnable = _httpChannelState.onFailure(failure);
if (runnable != null)
getContext().execute(runnable);
}

@Override
Expand Down
Loading