001/*- 002 * #%L 003 * Smile CDR - CDR 004 * %% 005 * Copyright (C) 2016 - 2024 Smile CDR, Inc. 006 * %% 007 * All rights reserved. 008 * #L% 009 */ 010package ca.cdr.api.util; 011 012import jakarta.annotation.Nullable; 013import jakarta.servlet.http.HttpServletRequest; 014import org.slf4j.Logger; 015import org.slf4j.LoggerFactory; 016import org.springframework.http.HttpHeaders; 017import org.springframework.web.util.ForwardedHeaderUtils; 018 019import java.net.InetSocketAddress; 020import java.net.URI; 021import java.net.URISyntaxException; 022import java.util.Enumeration; 023 024import static org.apache.commons.lang3.StringUtils.isBlank; 025import static org.apache.commons.lang3.StringUtils.isNotBlank; 026 027public class HttpRequestUtil { 028 private static final Logger ourLog = LoggerFactory.getLogger(HttpRequestUtil.class); 029 030 /** 031 * Spec header. see: 032 * <a href="https://datatracker.ietf.org/doc/html/rfc7239">Forwarded spec</a> 033 */ 034 private HttpRequestUtil() {} 035 036 /** 037 * Unknown identifier 038 */ 039 private static final String UNKNOWN = "unknown"; 040 041 /** 042 * Gets the best attempt at the originating 043 * client ip address from the incoming HttpServletRequest. 044 * 045 * NB: Because proxies can remove or change ip addresses (depending on 046 * implementation), this cannot be <b>guaranteed</b> to be the originating 047 * client ip address. But if the request is made directly, or proxies 048 * are handling them in standardized ways, its most likely going to be. 049 * 050 * In order of priority, this method will prioritize: 051 * * Forwarded header as defined in <a href="https://datatracker.ietf.org/doc/html/rfc7239">Forwarded spec</a> 052 * * X-Forwarded-for header (a standard, but not a spec) 053 * * the address on the request itself 054 * 055 * @param theRequest - the request 056 * @return the ip address of the client or first recorded ip address. 057 */ 058 public static String getRemoteAddressOfServletRequest(HttpServletRequest theRequest) { 059 URI uri; 060 try { 061 uri = new URI(theRequest.getRequestURL().toString()); 062 } catch (URISyntaxException ex) { 063 ourLog.warn( 064 "Unable to acquire remote address from request. This can affect log tracing, but should not affect functionality.", 065 ex); 066 return null; 067 } 068 HttpHeaders headers = new HttpHeaders(); 069 Enumeration<String> headerNames = theRequest.getHeaderNames(); 070 while (headerNames.hasMoreElements()) { 071 String h = headerNames.nextElement(); 072 Enumeration<String> values = theRequest.getHeaders(h); 073 while (values.hasMoreElements()) { 074 headers.add(h, values.nextElement()); 075 } 076 } 077 return getRemoteAddressFromHeaders(uri, headers, theRequest.getRemoteAddr()); 078 } 079 080 public static String getRemoteAddressFromHeaders(URI theUri, HttpHeaders theHeaders, @Nullable String theDefault) { 081 InetSocketAddress addr = ForwardedHeaderUtils.parseForwardedFor(theUri, theHeaders, null); 082 String originIp = null; 083 if (addr != null && isNotBlank(addr.getHostName())) { 084 originIp = addr.getHostName(); 085 } 086 087 if (isBlank(originIp)) { 088 originIp = theDefault; 089 } 090 091 if (isBlank(originIp)) { 092 originIp = UNKNOWN; 093 } 094 095 return originIp; 096 } 097}