001package ca.uhn.fhir.rest.server;
002
003/*-
004 * #%L
005 * HAPI FHIR - Server Framework
006 * %%
007 * Copyright (C) 2014 - 2022 Smile CDR, Inc.
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 *
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import ca.uhn.fhir.rest.api.Constants;
024import org.apache.commons.lang3.RandomStringUtils;
025import org.slf4j.Logger;
026import org.slf4j.LoggerFactory;
027
028import javax.annotation.Nullable;
029import javax.servlet.ServletRequest;
030import javax.servlet.http.HttpServletRequest;
031
032import static org.apache.commons.lang3.StringUtils.isBlank;
033import static org.apache.commons.lang3.StringUtils.isNotBlank;
034
035public class ServletRequestTracing {
036        private static final Logger ourLog = LoggerFactory.getLogger(ServletRequestTracing.class);
037        public static final String ATTRIBUTE_REQUEST_ID = ServletRequestTracing.class.getName() + '.' + Constants.HEADER_REQUEST_ID;
038
039        ServletRequestTracing() { }
040        
041        /**
042         * Assign a tracing id to this request, using
043         * the X-Request-ID if present and compatible.
044         *
045         * If none present, generate a 64 random alpha-numeric string that is not
046         * cryptographically secure.
047         *
048         * @param theServletRequest the request to trace
049         * @return the tracing id
050         */
051        public static String getOrGenerateRequestId(ServletRequest theServletRequest) {
052                String requestId = maybeGetRequestId(theServletRequest);
053                if (isBlank(requestId)) {
054                        requestId = RandomStringUtils.randomAlphanumeric(Constants.REQUEST_ID_LENGTH);
055                }
056
057                ourLog.debug("Assigned tracing id {}", requestId);
058
059                theServletRequest.setAttribute(ATTRIBUTE_REQUEST_ID, requestId);
060
061                return requestId;
062        }
063
064        @Nullable
065        public static String maybeGetRequestId(ServletRequest theServletRequest) {
066                // have we already seen this request?
067                String requestId = (String) theServletRequest.getAttribute(ATTRIBUTE_REQUEST_ID);
068
069                if (requestId == null && theServletRequest instanceof HttpServletRequest) {
070                        // Also applies to non-FHIR (e.g. admin-json) requests).
071                        HttpServletRequest request = (HttpServletRequest) theServletRequest;
072                        requestId = request.getHeader(Constants.HEADER_REQUEST_ID);
073                        if (isNotBlank(requestId)) {
074                                for (char nextChar : requestId.toCharArray()) {
075                                        if (!Character.isLetterOrDigit(nextChar)) {
076                                                if (nextChar != '.' && nextChar != '-' && nextChar != '_' && nextChar != ' ') {
077                                                        requestId = null;
078                                                        break;
079                                                }
080                                        }
081                                }
082                        }
083                }
084                return requestId;
085        }
086
087}