Skip to content

Commit

Permalink
Refactor, fix checkstyle and javadoc
Browse files Browse the repository at this point in the history
  • Loading branch information
andriy-dmytruk committed Aug 16, 2024
1 parent b62f243 commit 5a6975b
Show file tree
Hide file tree
Showing 11 changed files with 123 additions and 62 deletions.
2 changes: 1 addition & 1 deletion config/checkstyle/suppressions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@

<suppress checks="MissingJavadocType" files=".*doc-examples.*" />

<suppress checks=".*" files=".*/http-poja/src/main/java/io/micronaut/http/poja/util/QueryStringDecoder.java" />
<suppress checks=".*" files=".*/http-poja-common/src/main/java/io/micronaut/http/poja/util/QueryStringDecoder.java" />
</suppressions>
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package io.micronaut.http.poja.llhttp;

import io.micronaut.context.ApplicationContext;
import io.micronaut.context.annotation.DefaultImplementation;
import io.micronaut.core.convert.ConversionService;
import io.micronaut.http.codec.MediaTypeCodecRegistry;
import io.micronaut.http.poja.PojaHttpServerlessApplication;
Expand All @@ -41,6 +40,7 @@
* Implementation of {@link PojaHttpServerlessApplication} for Apache.
*
* @author Andriy Dmytruk.
* @since 4.10.0
*/
@Singleton
public class ApacheServerlessApplication
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@
* An implementation of the POJA Http Request based on Apache.
*
* @param <B> Body type
* @author Andriy Dmytruk.
* @author Andriy Dmytruk
* @since 4.10.0
*/
public class ApacheServletHttpRequest<B> extends PojaHttpRequest<B, ClassicHttpRequest, ClassicHttpResponse> {

Expand All @@ -72,6 +73,15 @@ public class ApacheServletHttpRequest<B> extends PojaHttpRequest<B, ClassicHttpR

private final ByteBody byteBody;

/**
* Create an Apache-based request.
*
* @param inputStream The input stream
* @param conversionService The conversion service
* @param codecRegistry The media codec registry
* @param ioExecutor The executor service
* @param response The response
*/
public ApacheServletHttpRequest(
InputStream inputStream,
ConversionService conversionService,
Expand Down Expand Up @@ -104,8 +114,8 @@ public ApacheServletHttpRequest(
OptionalLong optionalContentLength = contentLength >= 0 ? OptionalLong.of(contentLength) : OptionalLong.empty();
try {
InputStream bodyStream = inputStream;
if (sessionInputBuffer.available() > 0) {
byte[] data = new byte[sessionInputBuffer.available()];
if (sessionInputBuffer.length() > 0) {
byte[] data = new byte[sessionInputBuffer.length()];
sessionInputBuffer.read(data, inputStream);

bodyStream = new CombinedInputStream(
Expand Down Expand Up @@ -189,6 +199,57 @@ public void setConversionService(@NonNull ConversionService conversionService) {

}

private SimpleCookies parseCookies(ClassicHttpRequest request, ConversionService conversionService) {
SimpleCookies cookies = new SimpleCookies(conversionService);

// Manually parse cookies from the response headers
for (Header header : request.getHeaders(MultiValueHeaders.COOKIE)) {
String cookie = header.getValue();

String name = null;
int start = 0;
for (int i = 0; i < cookie.length(); ++i) {
if (i < cookie.length() - 1 && cookie.charAt(i) == ';' && cookie.charAt(i + 1) == ' ') {
if (name != null) {
cookies.put(name, Cookie.of(name, cookie.substring(start, i)));
name = null;
start = i + 2;
++i;
}
} else if (cookie.charAt(i) == '=') {
name = cookie.substring(start, i);
start = i + 1;
}
}
if (name != null) {
cookies.put(name, Cookie.of(name, cookie.substring(start)));
}
}
return cookies;
}

private static MultiValueHeaders createHeaders(
Header[] headers, ConversionService conversionService
) {
Map<String, List<String>> map = new HashMap<>();
for (Header header: headers) {
if (!map.containsKey(header.getName())) {
map.put(header.getName(), new ArrayList<>(1));
}
map.get(header.getName()).add(header.getValue());
}
return new MultiValueHeaders(map, conversionService);
}

private static MultiValuesQueryParameters parseQueryParameters(URI uri, ConversionService conversionService) {
Map<CharSequence, List<String>> map = new URIBuilder(uri).getQueryParams().stream()
.collect(Collectors.groupingBy(
NameValuePair::getName,
Collectors.mapping(NameValuePair::getValue, Collectors.toList())
));
return new MultiValuesQueryParameters(map, conversionService);
}

/**
* An input stream that would initially delegate to the first input stream
* and then to the second one. Created specifically to be used with {@link ByteBody}.
Expand Down Expand Up @@ -243,55 +304,4 @@ public void close() throws IOException {
}
}

private SimpleCookies parseCookies(ClassicHttpRequest request, ConversionService conversionService) {
SimpleCookies cookies = new SimpleCookies(conversionService);

// Manually parse cookies from the response headers
for (Header header : request.getHeaders(MultiValueHeaders.COOKIE)) {
String cookie = header.getValue();

String name = null;
int start = 0;
for (int i = 0; i < cookie.length(); ++i) {
if (i < cookie.length() - 1 && cookie.charAt(i) == ';' && cookie.charAt(i + 1) == ' ') {
if (name != null) {
cookies.put(name, Cookie.of(name, cookie.substring(start, i)));
name = null;
start = i + 2;
++i;
}
} else if (cookie.charAt(i) == '=') {
name = cookie.substring(start, i);
start = i + 1;
}
}
if (name != null) {
cookies.put(name, Cookie.of(name, cookie.substring(start)));
}
}
return cookies;
}

private static MultiValueHeaders createHeaders(
Header[] headers, ConversionService conversionService
) {
Map<String, List<String>> map = new HashMap<>();
for (Header header: headers) {
if (!map.containsKey(header.getName())) {
map.put(header.getName(), new ArrayList<>(1));
}
map.get(header.getName()).add(header.getValue());
}
return new MultiValueHeaders(map, conversionService);
}

private static MultiValuesQueryParameters parseQueryParameters(URI uri, ConversionService conversionService) {
Map<CharSequence, List<String>> map = new URIBuilder(uri).getQueryParams().stream()
.collect(Collectors.groupingBy(
NameValuePair::getName,
Collectors.mapping(NameValuePair::getValue, Collectors.toList())
));
return new MultiValuesQueryParameters(map, conversionService);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
import org.apache.hc.core5.http.message.BasicHttpResponse;

import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
Expand All @@ -45,6 +44,7 @@
*
* @param <T> The body type
* @author Andriy Dmytruk
* @since 4.10.0
*/
public class ApacheServletHttpResponse<T> extends PojaHttpResponse<T, ClassicHttpResponse> {

Expand All @@ -56,6 +56,11 @@ public class ApacheServletHttpResponse<T> extends PojaHttpResponse<T, ClassicHtt
private final MutableConvertibleValues<Object> attributes = new MutableConvertibleValuesMap<>();
private T bodyObject;

/**
* Create an Apache-based response.
*
* @param conversionService The conversion service
*/
public ApacheServletHttpResponse(ConversionService conversionService) {
this.headers = new SimpleHttpHeaders(conversionService);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
* @param <REQ> The POJA request type
* @param <RES> The POJA response type
* @author Andriy
* @since 4.10.0
*/
public abstract class PojaHttpRequest<B, REQ, RES>
implements ServletHttpRequest<REQ, B>, ServerHttpRequest<B>, ServletExchange<REQ, RES>, MutableHttpRequest<B> {
Expand Down Expand Up @@ -134,6 +135,11 @@ public <T> T consumeBody(Function<InputStream, T> consumer) {
}
}

/**
* A method used for retrieving form data. Can be overridden by specific implementations.
*
* @return The form data as multi-values.
*/
protected ConvertibleMultiValues<?> getFormData() {
return consumeBody(inputStream -> {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
*
* @param <T> The body type
* @param <RES> The POJA response type
* @author Andriy Dmytruk
* @since 4.10.0
*/
public abstract class PojaHttpResponse<T, RES> implements ServletHttpResponse<RES, T> {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@
* A base class for POJA serverless applications.
* It implements {@link EmbeddedApplication} for POSIX serverless environments.
*
* @param <REQ> The request type
* @param <RES> The response type
* @author Andriy Dmytruk.
* @since 4.10.0
*/
public abstract class PojaHttpServerlessApplication<REQ, RES> implements EmbeddedApplication<PojaHttpServerlessApplication> {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ public class LimitingInputStream extends InputStream {
private final InputStream stream;
private final long maxSize;

/**
* Create the limiting input stream.
*
* @param stream The delegate stream
* @param maxSize The maximum size to read
*/
public LimitingInputStream(InputStream stream, long maxSize) {
this.maxSize = maxSize;
this.stream = stream;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
/*
* Copyright 2017-2024 original authors
*
* 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 io.micronaut.http.poja.util;

import io.micronaut.core.annotation.NonNull;
Expand All @@ -8,7 +23,6 @@
import io.micronaut.http.MutableHttpHeaders;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
/*
* Copyright 2017-2024 original authors
*
* 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 io.micronaut.http.poja.util;

import io.micronaut.core.annotation.NonNull;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,21 @@
* Splits an HTTP query string into a path string and key-value parameter pairs.
* This decoder is for one time use only. Create a new instance for each URI:
* <pre>
* {@link QueryStringDecoder} decoder = new {@link QueryStringDecoder}("/hello?recipient=world&x=1;y=2");
* {@link QueryStringDecoder} decoder = new {@link QueryStringDecoder}("/hello?recipient=world&amp;x=1;y=2");
* assert decoder.path().equals("/hello");
* assert decoder.parameters().get("recipient").get(0).equals("world");
* assert decoder.parameters().get("x").get(0).equals("1");
* assert decoder.parameters().get("y").get(0).equals("2");
* </pre>
*
* This decoder can also decode the content of an HTTP POST request whose
* content type is <tt>application/x-www-form-urlencoded</tt>:
* content type is <code>application/x-www-form-urlencoded</code>:
* <pre>
* {@link QueryStringDecoder} decoder = new {@link QueryStringDecoder}("recipient=world&x=1;y=2", false);
* {@link QueryStringDecoder} decoder = new {@link QueryStringDecoder}("recipient=world&amp;x=1;y=2", false);
* ...
* </pre>
*
* <h3>HashDOS vulnerability fix</h3>
* <b>HashDOS vulnerability fix</b>
*
* As a workaround to the <a href="https://netty.io/s/hashdos">HashDOS</a> vulnerability, the decoder
* limits the maximum number of decoded key-value parameter pairs, up to {@literal 1024} by
Expand Down

0 comments on commit 5a6975b

Please sign in to comment.