Skip to content

Commit b20bc2d

Browse files
author
wlanboy
committed
InterruptedException-Handling, escapeJson , Redirect Loop, Overwrite Headers,
1 parent 1226bd1 commit b20bc2d

1 file changed

Lines changed: 45 additions & 8 deletions

File tree

src/main/java/com/wlanboy/javahttpclient/client/ClientService.java

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import java.net.http.HttpResponse.BodyHandlers;
1414
import java.time.Duration;
1515
import java.util.ArrayList;
16+
import java.util.HashSet;
1617
import java.util.LinkedHashMap;
1718
import java.util.List;
1819
import java.util.Map;
@@ -34,6 +35,7 @@ public class ClientService {
3435
);
3536
private static final Set<Integer> REDIRECT_CODES = Set.of(301, 302, 303, 307, 308);
3637
private static final int MAX_REDIRECTS = 10;
38+
private static final int MAX_REDIRECT_CHAIN_HEADER_LENGTH = 4096;
3739

3840
private final HttpClient client;
3941
private final HttpClient clientHttp11;
@@ -54,7 +56,9 @@ public ClientService() {
5456
public ResponseEntity<String> sendRequest(JavaHttpRequest requestData, HttpHeaders incomingHeaders) {
5557
try {
5658
List<Map<String, Object>> redirectChain = new ArrayList<>();
59+
Set<URI> visitedUris = new HashSet<>();
5760
URI currentUri = URI.create(requestData.url());
61+
visitedUris.add(currentUri);
5862
String currentMethod = requestData.method().name();
5963
String currentBody = requestData.body();
6064

@@ -98,6 +102,11 @@ record RequestResult(HttpClient activeClient, HttpResponse<String> response, boo
98102
if (location == null) break;
99103

100104
URI nextUri = currentUri.resolve(location);
105+
if (visitedUris.contains(nextUri)) {
106+
logger.warn("Redirect-Loop erkannt: {} wurde bereits besucht", nextUri);
107+
break;
108+
}
109+
visitedUris.add(nextUri);
101110

102111
Map<String, Object> step = new LinkedHashMap<>();
103112
step.put("from", currentUri.toString());
@@ -130,19 +139,25 @@ record RequestResult(HttpClient activeClient, HttpResponse<String> response, boo
130139
if (usedFallback) h.set("X-Protocol-Fallback", "HTTP/1.1");
131140
if (resolvedIps != null) h.set("X-Resolved-IP", resolvedIps);
132141
if (!redirectChain.isEmpty()) {
133-
h.set("X-Redirect-Chain", serializeChain(redirectChain));
142+
String chain = serializeChain(redirectChain);
143+
if (chain.length() > MAX_REDIRECT_CHAIN_HEADER_LENGTH) {
144+
chain = chain.substring(0, MAX_REDIRECT_CHAIN_HEADER_LENGTH - 3) + "...";
145+
}
146+
h.set("X-Redirect-Chain", chain);
134147
}
135148
})
136149
.body(response.body());
137150

151+
} catch (InterruptedException e) {
152+
Thread.currentThread().interrupt();
153+
String errorDetail = formatErrorResponse(e);
154+
logger.error("HTTP Request unterbrochen: {}", errorDetail);
155+
return ResponseEntity.status(502)
156+
.header("Content-Type", "text/plain; charset=UTF-8")
157+
.body(errorDetail);
138158
} catch (Exception e) {
139159
String errorDetail = formatErrorResponse(e);
140160
logger.error("HTTP Request fehlgeschlagen: {}", errorDetail);
141-
142-
if (e instanceof InterruptedException) {
143-
Thread.currentThread().interrupt();
144-
}
145-
146161
return ResponseEntity.status(502)
147162
.header("Content-Type", "text/plain; charset=UTF-8")
148163
.body(errorDetail);
@@ -170,8 +185,15 @@ private HttpRequest buildRequest(URI uri, String method, String body,
170185
}
171186

172187
if (requestData != null && requestData.copyHeaders() && incomingHeaders != null) {
188+
Set<String> customKeys = (requestData.customHeaders() != null)
189+
? requestData.customHeaders().keySet().stream()
190+
.map(String::toLowerCase)
191+
.collect(java.util.stream.Collectors.toSet())
192+
: Set.of();
173193
incomingHeaders.forEach((key, value) -> {
174-
if (!BAD_HEADERS.contains(key.toLowerCase()) && !value.isEmpty()) {
194+
if (!BAD_HEADERS.contains(key.toLowerCase())
195+
&& !customKeys.contains(key.toLowerCase())
196+
&& !value.isEmpty()) {
175197
try {
176198
builder.header(key, value.get(0));
177199
} catch (IllegalArgumentException e) {
@@ -221,7 +243,22 @@ private String serializeChain(List<Map<String, Object>> chain) {
221243
}
222244

223245
private String escapeJson(String s) {
224-
return s.replace("\\", "\\\\").replace("\"", "\\\"");
246+
StringBuilder sb = new StringBuilder(s.length() + 16);
247+
for (int i = 0; i < s.length(); i++) {
248+
char c = s.charAt(i);
249+
switch (c) {
250+
case '\\' -> sb.append("\\\\");
251+
case '"' -> sb.append("\\\"");
252+
case '\n' -> sb.append("\\n");
253+
case '\r' -> sb.append("\\r");
254+
case '\t' -> sb.append("\\t");
255+
default -> {
256+
if (c < 0x20) sb.append(String.format("\\u%04x", (int) c));
257+
else sb.append(c);
258+
}
259+
}
260+
}
261+
return sb.toString();
225262
}
226263

227264
private String formatErrorResponse(Exception e) {

0 commit comments

Comments
 (0)