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