1313import java .net .http .HttpResponse .BodyHandlers ;
1414import java .time .Duration ;
1515import java .util .ArrayList ;
16+ import java .util .HashSet ;
1617import java .util .LinkedHashMap ;
1718import java .util .List ;
1819import 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