Skip to content

Commit

Permalink
simplify
Browse files Browse the repository at this point in the history
  • Loading branch information
arturobernalg committed Oct 20, 2024
1 parent 0eff61b commit 2924b60
Showing 1 changed file with 19 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,13 @@
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.http.EntityDetails;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpRequestInterceptor;
import org.apache.hc.core5.http.ProtocolException;
import org.apache.hc.core5.http.message.MessageSupport;
import org.apache.hc.core5.util.Args;
import org.apache.hc.core5.util.Tokenizer;


/**
Expand All @@ -66,11 +64,6 @@ public class RequestTE implements HttpRequestInterceptor {
*/
public static final HttpRequestInterceptor INSTANCE = new RequestTE();

/**
* Delimiter used to parse the {@code TE} header, recognizing both commas (',') and semicolons (';') as delimiters.
*/
public static final Tokenizer.Delimiter DELIMITER = Tokenizer.delimiters(',', ';');

/**
* Default constructor.
*/
Expand Down Expand Up @@ -98,85 +91,30 @@ public void process(final HttpRequest request, final EntityDetails entity, final
throws HttpException, IOException {
Args.notNull(request, "HTTP request");

// Fetch all TE headers
final Header[] teHeaders = request.getHeaders(HttpHeaders.TE);
if (teHeaders != null && teHeaders.length > 0) {
for (final Header teHeader : teHeaders) {
validateTEField(teHeader.getValue());
}
validateConnectionHeaders(request);
}
}

/**
* Validates the {@link HttpHeaders#TE} header values for compliance with HTTP/1.1.
* <p>
* Specifically, this method ensures that:
* <ul>
* <li>The {@link HttpHeaders#TE} header does not contain the {@code chunked} transfer coding.</li>
* <li>The {@code trailers} directive is allowed and treated as valid.</li>
* </ul>
*
* @param teValue the value of the {@code TE} header
* @throws HttpException if the {@code TE} header contains invalid values
*/
private void validateTEField(final String teValue) throws HttpException {
final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, teValue.length());

while (!cursor.atEnd()) {
Tokenizer.INSTANCE.skipWhiteSpace(teValue, cursor);

final String member = Tokenizer.INSTANCE.parseToken(teValue, cursor, DELIMITER);

if (member.isEmpty()) {
if (!cursor.atEnd()) {
Tokenizer.INSTANCE.skipWhiteSpace(teValue, cursor);
cursor.updatePos(cursor.getPos() + 1);
}
continue;
}

if ("trailers".equalsIgnoreCase(member)) {
continue;
}

if ("chunked".equalsIgnoreCase(member)) {
throw new ProtocolException("'chunked' transfer coding must not be listed in the TE header for HTTP/1.1.");
}

if (!cursor.atEnd()) {
Tokenizer.INSTANCE.skipWhiteSpace(teValue, cursor);
cursor.updatePos(cursor.getPos() + 1);
final AtomicBoolean hasTE = new AtomicBoolean(false);
final AtomicBoolean hasChunk = new AtomicBoolean(false);
MessageSupport.parseTokens(request, HttpHeaders.TE, token -> {
hasTE.set(true);
if (token.equalsIgnoreCase("chunked")) {
hasChunk.set(true);
}
});
if (hasChunk.get()) {
throw new ProtocolException("'chunked' transfer coding must not be listed in the TE header for HTTP/1.1.");
}
}

/**
* Validates the presence of the {@code Connection: TE} header when the {@link HttpHeaders#TE} header is present.
* <p>
* If the {@link HttpHeaders#TE} header is used, the HTTP/1.1 protocol requires that the {@link HttpHeaders#CONNECTION} header includes the {@code TE} directive to prevent forwarding by intermediaries.
* This method now properly handles multiple {@link HttpHeaders#CONNECTION} headers.
*
* @param request the HTTP request to validate
* @throws HttpException if the {@code Connection: TE} header is missing
*/
private void validateConnectionHeaders(final HttpRequest request) throws HttpException {
final Header[] connectionHeaders = request.getHeaders(HttpHeaders.CONNECTION);
if (connectionHeaders == null || connectionHeaders.length == 0) {
throw new ProtocolException("The 'TE' header is present, but the 'Connection' header is missing.");
}

final AtomicBoolean hasTE = new AtomicBoolean(false);
for (final Header connectionHeader : connectionHeaders) {
MessageSupport.parseTokens(connectionHeader, token -> {
if (hasTE.get()) {
final AtomicBoolean hasConnection = new AtomicBoolean(false);
final AtomicBoolean hasTEinConnection = new AtomicBoolean(false);
MessageSupport.parseTokens(request, HttpHeaders.CONNECTION, token -> {
hasConnection.set(true);
if ("TE".equalsIgnoreCase(token)) {
hasTE.set(true);
hasTEinConnection.set(true);
}
});
}

if (!hasTE.get()) {
throw new ProtocolException("The 'Connection' header must include the 'TE' directive when the 'TE' header is present.");
if (!hasTEinConnection.get()) {
throw new ProtocolException("The 'Connection' header must include the 'TE' directive when the 'TE' header is present.");
}
}
}

}

0 comments on commit 2924b60

Please sign in to comment.