package com.vincent.rsf.httpaudit.web; import com.vincent.rsf.httpaudit.model.HttpAuditDecision; import com.vincent.rsf.httpaudit.service.HttpAuditOutboundRecorder; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import org.springframework.util.StreamUtils; import java.io.IOException; import java.nio.charset.StandardCharsets; /** * RestTemplate 出站审计 */ @Slf4j @RequiredArgsConstructor public class OutboundHttpAuditInterceptor implements ClientHttpRequestInterceptor { private static final String FN_REST = "HTTP出站(RestTemplate)"; private final HttpAuditOutboundRecorder outboundRecorder; @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { if (!outboundRecorder.isAuditEnabled()) { return execution.execute(request, body); } String url = request.getURI().toString(); String method = request.getMethod().name(); String reqText = body == null || body.length == 0 ? "" : new String(body, StandardCharsets.UTF_8); HttpAuditDecision dec = outboundRecorder.decideOutbound(url, method, reqText); long t0 = System.currentTimeMillis(); ClientHttpResponse raw; try { raw = execution.execute(request, body); } catch (IOException e) { outboundRecorder.saveOutbound(FN_REST, url, method, reqText, dec, null, null, t0, e); throw e; } byte[] respBytes; try { respBytes = StreamUtils.copyToByteArray(raw.getBody()); } catch (IOException e) { outboundRecorder.saveOutbound(FN_REST, url, method, reqText, dec, raw.getRawStatusCode(), null, t0, e); throw e; } if (!dec.isAudit()) { return new CopiedBodyClientHttpResponse(raw, respBytes); } String resText = new String(respBytes, StandardCharsets.UTF_8); outboundRecorder.saveOutbound(FN_REST, url, method, reqText, dec, raw.getRawStatusCode(), resText, t0, null); return new CopiedBodyClientHttpResponse(raw, respBytes); } private static class CopiedBodyClientHttpResponse implements ClientHttpResponse { private final ClientHttpResponse delegate; private final byte[] body; CopiedBodyClientHttpResponse(ClientHttpResponse delegate, byte[] body) { this.delegate = delegate; this.body = body != null ? body : new byte[0]; } @Override public org.springframework.http.HttpStatus getStatusCode() throws IOException { return delegate.getStatusCode(); } @Override public int getRawStatusCode() throws IOException { return delegate.getRawStatusCode(); } @Override public String getStatusText() throws IOException { return delegate.getStatusText(); } @Override public void close() { delegate.close(); } @Override public org.springframework.http.HttpHeaders getHeaders() { return delegate.getHeaders(); } @Override public java.io.InputStream getBody() throws IOException { return new java.io.ByteArrayInputStream(body); } } }