Skip to content

Commit

Permalink
Use Apache APIs for body validation
Browse files Browse the repository at this point in the history
  • Loading branch information
andriy-dmytruk committed Aug 23, 2024
1 parent 55d2b07 commit fc416a9
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import io.micronaut.context.ApplicationContext;
import io.micronaut.core.convert.ConversionService;
import io.micronaut.core.io.buffer.ByteBufferFactory;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.MediaType;
import io.micronaut.http.codec.MediaTypeCodecRegistry;
Expand All @@ -26,6 +27,7 @@
import io.micronaut.inject.qualifiers.Qualifiers;
import io.micronaut.runtime.ApplicationConfiguration;
import io.micronaut.scheduling.TaskExecutors;
import io.micronaut.servlet.http.ByteArrayBufferFactory;
import io.micronaut.servlet.http.ServletHttpHandler;
import jakarta.inject.Singleton;
import org.apache.hc.core5.http.ClassicHttpResponse;
Expand Down Expand Up @@ -53,6 +55,7 @@ public class ApacheServerlessApplication
private final ConversionService conversionService;
private final MediaTypeCodecRegistry codecRegistry;
private final ExecutorService ioExecutor;
private final ByteBufferFactory<?, ?> byteBufferFactory;
private final ApacheServletConfiguration configuration;

/**
Expand All @@ -68,6 +71,7 @@ public ApacheServerlessApplication(ApplicationContext applicationContext,
codecRegistry = applicationContext.getBean(MediaTypeCodecRegistry.class);
ioExecutor = applicationContext.getBean(ExecutorService.class, Qualifiers.byName(TaskExecutors.BLOCKING));
configuration = applicationContext.getBean(ApacheServletConfiguration.class);
byteBufferFactory = ByteArrayBufferFactory.INSTANCE;
}

@Override
Expand All @@ -79,7 +83,7 @@ protected void handleSingleRequest(
ApacheServletHttpResponse<?> response = new ApacheServletHttpResponse<>(conversionService);
try {
ApacheServletHttpRequest exchange = new ApacheServletHttpRequest<>(
in, conversionService, codecRegistry, ioExecutor, response, configuration
in, conversionService, codecRegistry, ioExecutor, byteBufferFactory, response, configuration
);
servletHttpHandler.service(exchange);
} catch (ApacheServletBadRequestException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,34 @@
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.convert.ConversionService;
import io.micronaut.core.io.buffer.ByteBufferFactory;
import io.micronaut.http.HttpHeaders;
import io.micronaut.http.HttpMethod;
import io.micronaut.http.MutableHttpHeaders;
import io.micronaut.http.MutableHttpParameters;
import io.micronaut.http.MutableHttpRequest;
import io.micronaut.http.body.ByteBody;
import io.micronaut.http.body.stream.InputStreamByteBody;
import io.micronaut.http.codec.MediaTypeCodecRegistry;
import io.micronaut.http.cookie.Cookie;
import io.micronaut.http.cookie.Cookies;
import io.micronaut.http.poja.PojaHttpRequest;
import io.micronaut.http.poja.llhttp.exception.ApacheServletBadRequestException;
import io.micronaut.http.poja.util.LimitingInputStream;
import io.micronaut.http.poja.util.MultiValueHeaders;
import io.micronaut.http.poja.util.MultiValuesQueryParameters;
import io.micronaut.http.simple.cookies.SimpleCookies;
import io.micronaut.servlet.http.body.InputStreamByteBody;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.impl.io.ChunkedInputStream;
import org.apache.hc.core5.http.impl.io.ContentLengthInputStream;
import org.apache.hc.core5.http.impl.io.DefaultHttpRequestParser;
import org.apache.hc.core5.http.impl.io.SessionInputBufferImpl;
import org.apache.hc.core5.http.io.entity.EmptyInputStream;
import org.apache.hc.core5.net.URIBuilder;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
Expand All @@ -67,6 +69,8 @@
@Internal
public final class ApacheServletHttpRequest<B> extends PojaHttpRequest<B, ClassicHttpRequest, ClassicHttpResponse> {

private static final String TRANSFER_ENCODING_CHUNKED = "chunked";

private final ClassicHttpRequest request;

private final HttpMethod method;
Expand All @@ -92,6 +96,7 @@ public ApacheServletHttpRequest(
ConversionService conversionService,
MediaTypeCodecRegistry codecRegistry,
ExecutorService ioExecutor,
ByteBufferFactory<?, ?> byteBufferFactory,
ApacheServletHttpResponse<?> response,
ApacheServletConfiguration configuration
) {
Expand Down Expand Up @@ -119,29 +124,33 @@ public ApacheServletHttpRequest(

long contentLength = getContentLength();
OptionalLong optionalContentLength = contentLength >= 0 ? OptionalLong.of(contentLength) : OptionalLong.empty();
try {
InputStream bodyStream = inputStream;
if (sessionInputBuffer.length() > 0) {
byte[] data = new byte[sessionInputBuffer.length()];
sessionInputBuffer.read(data, inputStream);

bodyStream = new CombinedInputStream(
new ByteArrayInputStream(data),
inputStream
);
}
if (contentLength > 0) {
bodyStream = new LimitingInputStream(bodyStream, contentLength);
} else {
// Empty
bodyStream = new ByteArrayInputStream(new byte[0]);
}
byteBody = InputStreamByteBody.create(
bodyStream, optionalContentLength, ioExecutor
);
} catch (IOException e) {
throw new ApacheServletBadRequestException("Could not parse request body", e);
InputStream bodyStream = createBodyStream(inputStream, contentLength, sessionInputBuffer);
byteBody = InputStreamByteBody.create(
bodyStream, optionalContentLength, ioExecutor, byteBufferFactory
);
}

/**
* Create body stream.
* Based on org.apache.hc.core5.http.impl.io.BHttpConnectionBase#createContentOutputStream.
*
* @param inputStream The input stream
* @param contentLength The content length
* @param sessionInputBuffer The input buffer
* @return The body stream
*/
private InputStream createBodyStream(InputStream inputStream, long contentLength, SessionInputBufferImpl sessionInputBuffer) {
InputStream bodyStream;
if (contentLength > 0) {
bodyStream = new ContentLengthInputStream(sessionInputBuffer, inputStream, contentLength);
} else if (contentLength == 0) {
bodyStream = EmptyInputStream.INSTANCE;
} else if (TRANSFER_ENCODING_CHUNKED.equalsIgnoreCase(headers.get(HttpHeaders.TRANSFER_ENCODING))) {
bodyStream = new ChunkedInputStream(sessionInputBuffer, inputStream);
} else {
bodyStream = EmptyInputStream.INSTANCE;
}
return bodyStream;
}

@Override
Expand Down

0 comments on commit fc416a9

Please sign in to comment.