com.turbospaces.resteasy.DefaultHttpClientRequestPrinter Maven / Gradle / Ivy
The newest version!
package com.turbospaces.resteasy;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.function.Consumer;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.utils.URIBuilder;
import org.jboss.resteasy.client.jaxrs.internal.ClientConfiguration;
import org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.robtimus.obfuscation.Obfuscator;
import com.github.robtimus.obfuscation.http.HeaderObfuscator;
import com.github.robtimus.obfuscation.http.RequestParameterObfuscator;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.turbospaces.annotations.ApiEndpoint;
import com.turbospaces.cfg.ApplicationProperties;
import com.turbospaces.http.HttpProto;
import com.turbospaces.resteasy.obfuscate.ObfuscateUtil;
import io.netty.handler.codec.http.QueryStringDecoder;
import jakarta.ws.rs.client.ClientRequestContext;
import jakarta.ws.rs.core.Configuration;
import jakarta.ws.rs.core.Cookie;
import jakarta.ws.rs.core.Form;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.ext.ContextResolver;
import jakarta.ws.rs.ext.RuntimeDelegate;
import jakarta.ws.rs.ext.RuntimeDelegate.HeaderDelegate;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class DefaultHttpClientRequestPrinter implements HttpClientRequestPrinter {
private static HeaderDelegate COOKIE_PRINTER = RuntimeDelegate.getInstance().createHeaderDelegate(Cookie.class);
protected final ApplicationProperties props;
protected final StringWriter writer = new StringWriter();
protected final BufferedWriter buffer = new BufferedWriter(writer);
private final LoadingCache objectMappersWithObfuscation = CacheBuilder.newBuilder()
.build(new CacheLoader() {
@Override
public ObjectMapper load(ObjectMapper key) {
return key.copy().registerModule(ObfuscateUtil.LOGGING_OBFUSCATED_MODULE);
}
});
public DefaultHttpClientRequestPrinter(ApplicationProperties props) {
this.props = Objects.requireNonNull(props);
}
@Override
public void writeStart(ClientRequestContext context) throws IOException {
buffer.write("Request:");
buffer.newLine();
}
@Override
public Map> writeUri(ClientRequestContext context) throws IOException {
ClientInvoker clientInvoker = clientInvoker(context);
URI uri = context.getUri();
Map> query = Collections.emptyMap();
if (StringUtils.isNotEmpty(uri.getRawQuery())) {
QueryStringDecoder decoder = new QueryStringDecoder(uri.getRawQuery(), false);
query = decoder.parameters();
}
URIBuilder builder = new URIBuilder().setScheme(uri.getScheme()).setHost(uri.getHost()).setPort(uri.getPort());
builder.setPath(uri.getPath());
if (BooleanUtils.isFalse(query.isEmpty())) {
RequestParameterObfuscator.Builder b = addQueryParamsToMask(clientInvoker, RequestParameterObfuscator.builder());
List l = props.HTTP_QUERY_PARAMS_TO_MASK.get();
if (CollectionUtils.isNotEmpty(l)) {
for (String it : l) {
b.withParameter(it, Obfuscator.all());
}
}
RequestParameterObfuscator obfuscator = b.build();
for (Entry> next : query.entrySet()) {
String name = next.getKey();
if (CollectionUtils.isNotEmpty(next.getValue())) {
for (String value : next.getValue()) {
builder.addParameter(name, obfuscator.obfuscateParameter(name, value).toString());
}
}
}
}
context.setProperty(HttpProto.toCtxName(HttpProto.CONTEXT_URI), builder);
buffer.write(context.getMethod() + ": " + builder);
buffer.newLine();
return query;
}
@Override
public void writeQueryParams(ClientRequestContext context, Map> query) throws IOException {
ClientInvoker clientInvoker = clientInvoker(context);
if (MapUtils.isNotEmpty(query)) {
if (BooleanUtils.isFalse(query.isEmpty())) {
RequestParameterObfuscator.Builder b = addQueryParamsToMask(clientInvoker, RequestParameterObfuscator.builder());
List l = props.HTTP_QUERY_PARAMS_TO_MASK.get();
if (CollectionUtils.isNotEmpty(l)) {
for (String it : l) {
b.withParameter(it, Obfuscator.all());
}
}
RequestParameterObfuscator obfuscator = b.build();
for (Entry> next : query.entrySet()) {
String name = next.getKey();
if (CollectionUtils.isNotEmpty(next.getValue())) {
for (String value : next.getValue()) {
buffer.write(String.format("Query: %s = %s", name, obfuscator.obfuscateParameter(name, value)));
buffer.newLine();
}
}
}
}
}
}
@Override
public void writeHeaders(ClientRequestContext context) throws IOException {
ClientInvoker clientInvoker = clientInvoker(context);
MultivaluedMap headers = context.getStringHeaders();
if (BooleanUtils.isFalse(headers.isEmpty())) {
HeaderObfuscator.Builder b = addHeadersToMask(clientInvoker, HeaderObfuscator.builder());
List l = props.HTTP_HEADERS_TO_MASK.get();
if (CollectionUtils.isNotEmpty(l)) {
for (String it : l) {
b.withHeader(it, Obfuscator.all());
}
}
HeaderObfuscator obfuscator = b.build();
for (Entry> next : headers.entrySet()) {
String name = next.getKey();
if (CollectionUtils.isNotEmpty(next.getValue())) {
for (String value : next.getValue()) {
buffer.write(String.format("Header: %s = %s", name, obfuscator.obfuscateHeader(name, value)));
buffer.newLine();
}
}
}
}
}
@Override
public void writeCookies(ClientRequestContext context) throws IOException {
Map cookies = context.getCookies();
if (BooleanUtils.isFalse(cookies.isEmpty())) {
for (Entry next : cookies.entrySet()) {
Cookie cookie = next.getValue();
String value = COOKIE_PRINTER.toString(cookie);
buffer.write(String.format("Cookie: %s", Obfuscator.all().obfuscateText(value)));
buffer.newLine();
}
}
}
@Override
public void writeBody(ClientRequestContext context) throws IOException {
String body = getBody(context.getEntity(), context.getConfiguration(), context.getMediaType());
if (StringUtils.isNotEmpty(body)) {
buffer.write("Body: " + body);
}
}
private String getBody(Object entity, Configuration config, MediaType mediaType) {
if (Objects.nonNull(entity)) {
try {
ContextResolver resolver = null;
ObjectMapper mapper = null;
if (config instanceof ClientConfiguration) {
resolver = ((ClientConfiguration) config).getContextResolver(ObjectMapper.class, mediaType);
}
if (resolver != null) {
mapper = resolver.getContext(ObjectMapper.class);
}
if (mapper != null) {
return objectMappersWithObfuscation.get(mapper).writeValueAsString(entity);
}
if (entity instanceof Form) {
StringBuffer str = new StringBuffer();
((Form) entity).asMap().forEach((s, strings) -> str.append(s).append("=").append(String.join(",", strings)).append(";"));
return str.toString();
}
return entity.toString();
} catch (Exception e) {
log.error("Error on preparing request body", e);
}
}
return StringUtils.EMPTY;
}
@Override
public void doOutPut(ClientRequestContext context) {
try {
writeStart(context);
Map> query = writeUri(context);
writeHeaders(context);
writeQueryParams(context, query);
writeCookies(context);
writeBody(context);
buffer.flush();
LOGGING_FILTER_LOGGER.debug(writer.toString());
buffer.close();
writer.close();
} catch (IOException err) {
log.error(err.getMessage(), err);
}
}
protected RequestParameterObfuscator.Builder addQueryParamsToMask(ClientInvoker invoker, RequestParameterObfuscator.Builder obfuscator) {
Class> resource = invoker.getDeclaring();
Method method = invoker.getMethod();
Consumer action = new Consumer() {
@Override
public void accept(ApiEndpoint annotation) {
if (Objects.nonNull(annotation)) {
String[] toMask = annotation.queryParamsToMask();
if (toMask != null) {
for (String param : toMask) {
obfuscator.withParameter(param, Obfuscator.all());
}
}
}
}
};
action.accept(resource.getAnnotation(ApiEndpoint.class));
action.accept(method.getAnnotation(ApiEndpoint.class));
return obfuscator;
}
protected HeaderObfuscator.Builder addHeadersToMask(ClientInvoker invoker, HeaderObfuscator.Builder obfuscator) {
Class> resource = invoker.getDeclaring();
Method method = invoker.getMethod();
Consumer action = new Consumer() {
@Override
public void accept(ApiEndpoint annotation) {
if (Objects.nonNull(annotation)) {
String[] toMask = annotation.headersToMask();
if (toMask != null) {
for (String param : toMask) {
obfuscator.withHeader(param, Obfuscator.all());
}
}
}
}
};
action.accept(resource.getAnnotation(ApiEndpoint.class));
action.accept(method.getAnnotation(ApiEndpoint.class));
return obfuscator;
}
}