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}