Skip to content

Commit

Permalink
Fix HawkbitClient multipart data error handling
Browse files Browse the repository at this point in the history
Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
  • Loading branch information
avgustinmm committed Jan 30, 2025
1 parent 9418eb4 commit 701c4e3
Showing 1 changed file with 59 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,19 @@
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.fasterxml.jackson.databind.ObjectMapper;
import feign.Client;
import feign.Contract;
import feign.Feign;
import feign.FeignException;
import feign.Request;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import feign.codec.Decoder;
Expand Down Expand Up @@ -139,14 +143,24 @@ private <T> T service(final Class<T> serviceType, final Tenant tenant, final Con
}
}

private <T> T service0(final Class<T> serviceType, final Tenant tenant, final Controller controller) {
return Feign.builder().client(client)
.encoder(encoder)
.decoder(decoder)
.errorDecoder(errorDecoder)
.contract(contract)
.requestInterceptor(requestInterceptorFn.apply(tenant, controller))
.target(serviceType, controller == null ? hawkBitServer.getMgmtUrl() : hawkBitServer.getDdiUrl());
}

@SuppressWarnings("unchecked")
private <T> T proxy(final Class<T> serviceType, final T service, final Tenant tenant, final Controller controller) {
final ObjectMapper objectMapper = new ObjectMapper();
return (T) Proxy.newProxyInstance(service.getClass().getClassLoader(), new Class<?>[] { serviceType }, (proxy, method, args) -> {
try {
final Class<?>[] parameterTypes = method.getParameterTypes();
if (method.getDeclaringClass() == serviceType && List.of(parameterTypes).contains(MultipartFile.class)) {
return processMultipartFormData(method, args, tenant, controller, parameterTypes, objectMapper);
return callMultipartFormDataRequest(method, args, tenant, controller, parameterTypes, objectMapper);
} else {
return method.invoke(service, args);
}
Expand All @@ -156,9 +170,7 @@ private <T> T proxy(final Class<T> serviceType, final T service, final Tenant te
});
}

private static final String CRLF = "\r\n";

private Object processMultipartFormData(
private Object callMultipartFormDataRequest(
final Method method, final Object[] args,
final Tenant tenant, final Controller controller,
final Class<?>[] parameterTypes, final ObjectMapper objectMapper) throws IOException {
Expand Down Expand Up @@ -207,19 +219,59 @@ private Object processMultipartFormData(
}
out.write(("--" + boundary + "--\r\n").getBytes(StandardCharsets.UTF_8));
}

final int responseCode = conn.getResponseCode();
if (responseCode < 200 || responseCode >= 300) {
throw toFeignException(responseCode, conn, Request.create(
Request.HttpMethod.POST, conn.getURL().toString(),
conn.getHeaderFields().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)),
(Request.Body)null, null));
}

return method.getReturnType() == ResponseEntity.class
? new ResponseEntity<>(
deserialize(
conn.getInputStream(),
(Class<?>) ((ParameterizedType) method.getGenericReturnType()).getActualTypeArguments()[0],
objectMapper),
HttpStatusCode.valueOf(conn.getResponseCode()))
HttpStatusCode.valueOf(responseCode))
: deserialize(conn.getInputStream(), method.getReturnType(), objectMapper);
}
private Object deserialize(final InputStream is, final Class<?> type, final ObjectMapper objectMapper) throws IOException {

private static FeignException toFeignException(final int responseCode, final HttpURLConnection conn, final Request request) throws IOException {
if (responseCode >= 500 && responseCode < 600) {
// server error
return switch (responseCode) {
case 500 -> new FeignException.InternalServerError(conn.getResponseMessage(), request, null, null);
case 501 -> new FeignException.NotImplemented(conn.getResponseMessage(), request, null, null);
case 502 -> new FeignException.BadGateway(conn.getResponseMessage(), request, null, null);
case 503 -> new FeignException.ServiceUnavailable(conn.getResponseMessage(), request, null, null);
case 504 -> new FeignException.GatewayTimeout(conn.getResponseMessage(), request, null, null);
default -> new FeignException.FeignServerException(responseCode, conn.getResponseMessage(), request, null, null);
};
} else {
return switch (responseCode) {
case 400 -> new FeignException.BadRequest(conn.getResponseMessage(), request, null, null);
case 401 -> new FeignException.Unauthorized(conn.getResponseMessage(), request, null, null);
case 403 -> new FeignException.Forbidden(conn.getResponseMessage(), request, null, null);
case 404 -> new FeignException.NotFound(conn.getResponseMessage(), request, null, null);
case 405 -> new FeignException.MethodNotAllowed(conn.getResponseMessage(), request, null, null);
case 406 -> new FeignException.NotAcceptable(conn.getResponseMessage(), request, null, null);
case 409 -> new FeignException.Conflict(conn.getResponseMessage(), request, null, null);
case 410 -> new FeignException.Gone(conn.getResponseMessage(), request, null, null);
case 415 -> new FeignException.UnsupportedMediaType(conn.getResponseMessage(), request, null, null);
case 429 -> new FeignException.TooManyRequests(conn.getResponseMessage(), request, null, null);
case 422 -> new FeignException.UnprocessableEntity(conn.getResponseMessage(), request, null, null);
default -> new FeignException.FeignClientException(responseCode, conn.getResponseMessage(), request, null, null);
};
}
}

private static Object deserialize(final InputStream is, final Class<?> type, final ObjectMapper objectMapper) throws IOException {
return type == void.class || type == Void.class ? null : objectMapper.readValue(is, type);
}

private static final String CRLF = "\r\n";
private void writeMultipartFile(
final MultipartFile multipartFile, final OutputStream out, final String boundary, final Annotation[] parametersAnnotations)
throws IOException {
Expand All @@ -238,7 +290,6 @@ private void writeMultipartFile(
out.write(CRLF.getBytes(StandardCharsets.UTF_8));
}
}

private void writeSimpleFormData(
final Object arg, final OutputStream out, final String boundary, final Annotation[] parameterAnnotations) throws IOException {
if (arg != null) {
Expand All @@ -253,22 +304,12 @@ private void writeSimpleFormData(
}

@SuppressWarnings("unchecked")
private <T extends Annotation> T getAnnotation(final Class<T> annotationClass, final Annotation[] annotations) {
private static <T extends Annotation> T getAnnotation(final Class<T> annotationClass, final Annotation[] annotations) {
for (final Annotation annotation : annotations) {
if (annotation.annotationType().equals(annotationClass)) {
return (T) annotation;
}
}
return null;
}

private <T> T service0(final Class<T> serviceType, final Tenant tenant, final Controller controller) {
return Feign.builder().client(client)
.encoder(encoder)
.decoder(decoder)
.errorDecoder(errorDecoder)
.contract(contract)
.requestInterceptor(requestInterceptorFn.apply(tenant, controller))
.target(serviceType, controller == null ? hawkBitServer.getMgmtUrl() : hawkBitServer.getDdiUrl());
}
}

0 comments on commit 701c4e3

Please sign in to comment.