All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.apphosting.runtime.jetty94.SizeLimitHandler Maven / Gradle / Ivy

/*
 * Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.apphosting.runtime.jetty94;

import java.io.IOException;
import java.nio.ByteBuffer;
import javax.annotation.Nullable;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.server.HttpInput;
import org.eclipse.jetty.server.HttpOutput;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.util.Callback;

/**
 * A handler that can limit the size of message bodies in requests and responses.
 *
 * 

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 HandlerWrapper { private final long requestLimit; private final long responseLimit; /** * @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) { this.requestLimit = requestLimit; this.responseLimit = responseLimit; } protected void checkRequestLimit(long size) { if (requestLimit >= 0 && size > requestLimit) { throw new BadMessageException(413, "Request body is too large: " + size + ">" + requestLimit); } } protected void checkResponseLimit(long size) { if (responseLimit >= 0 && size > responseLimit) { throw new BadMessageException( 500, "Response body is too large: " + size + ">" + responseLimit); } } @Override public void handle( String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { if (requestLimit >= 0 || responseLimit >= 0) { HttpOutput httpOutput = baseRequest.getResponse().getHttpOutput(); HttpOutput.Interceptor interceptor = httpOutput.getInterceptor(); LimitInterceptor limit = new LimitInterceptor(interceptor); if (requestLimit >= 0) { long contentLength = baseRequest.getContentLengthLong(); checkRequestLimit(contentLength); if (contentLength < 0) { baseRequest.getHttpInput().addInterceptor(limit); } } if (responseLimit > 0) { httpOutput.setInterceptor(limit); response = new LimitResponse(response); } } super.handle(target, baseRequest, request, response); } private class LimitInterceptor implements HttpOutput.Interceptor, HttpInput.Interceptor { private final HttpOutput.Interceptor nextOutput; long read; long written; public LimitInterceptor(HttpOutput.Interceptor nextOutput) { this.nextOutput = nextOutput; } @Override public HttpOutput.Interceptor getNextInterceptor() { return nextOutput; } @Override public boolean isOptimizedForDirectBuffers() { return nextOutput.isOptimizedForDirectBuffers(); } @Nullable @Override public HttpInput.Content readFrom(HttpInput.Content content) { if (content == null) { return null; } if (content.hasContent()) { read += content.remaining(); checkResponseLimit(read); } return content; } @Override public void write(ByteBuffer content, boolean last, Callback callback) { if (content.hasRemaining()) { written += content.remaining(); try { checkResponseLimit(written); } catch (Throwable t) { callback.failed(t); return; } } getNextInterceptor().write(content, last, callback); } @Override public void resetBuffer() { written = 0; getNextInterceptor().resetBuffer(); } } private class LimitResponse extends HttpServletResponseWrapper { public LimitResponse(HttpServletResponse response) { super(response); } @Override public void setContentLength(int len) { checkResponseLimit(len); super.setContentLength(len); } @Override public void setContentLengthLong(long len) { checkResponseLimit(len); super.setContentLengthLong(len); } @Override public void setHeader(String name, String value) { if (HttpHeader.CONTENT_LENGTH.is(name)) { checkResponseLimit(Long.parseLong(value)); } super.setHeader(name, value); } @Override public void addHeader(String name, String value) { if (HttpHeader.CONTENT_LENGTH.is(name)) { checkResponseLimit(Long.parseLong(value)); } super.addHeader(name, value); } @Override public void setIntHeader(String name, int value) { if (HttpHeader.CONTENT_LENGTH.is(name)) { checkResponseLimit(value); } super.setIntHeader(name, value); } @Override public void addIntHeader(String name, int value) { if (HttpHeader.CONTENT_LENGTH.is(name)) { checkResponseLimit(value); } super.addIntHeader(name, value); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy