001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.rest.server; 018 019 020import java.util.function.*; 021 022import org.apache.juneau.commons.utils.*; 023import static org.apache.juneau.commons.utils.Shorts.*; 024 025/** 026 * Configuration for the always-on request-id correlation resolver run at {@link RestSession} build time. 027 * 028 * <p> 029 * This is the public write-surface for tuning how the framework mints or honors the {@code X-Request-Id} correlation id 030 * for every request — including 404 / early-error paths that never reach a {@code @RestStartCall} hook. Register a 031 * configured instance as a bean, or return one from a {@code @Bean} factory method on the resource; the resolver picks it 032 * up via {@link RestContext#getRequestIdSettings()}. 033 * 034 * <h5 class='topic'>Resolution behavior</h5> 035 * <ol> 036 * <li>If the request already carries a non-empty id under {@link #getAttributeKey() attributeKey} (idempotent re-entry 037 * or a parent filter), that value is honored unchanged. 038 * <li>Otherwise, if an incoming {@code X-Request-Id} header is present, it is 039 * {@linkplain org.apache.juneau.http.DebugTextSanitizer#sanitize(String, int) sanitized} and length-capped to 040 * {@link org.apache.juneau.http.RequestIdConstants#MAX_LEN}; the sanitized value is honored when it is non-empty, 041 * not truncated, and accepted by {@link #getValidator() validator}. 042 * <li>Otherwise a fresh id is minted via {@link #getIdSupplier() idSupplier}. 043 * </ol> 044 * 045 * <h5 class='topic'>Default validator — sanitize-and-accept</h5> 046 * <p> 047 * Unlike a reject-and-remint regex, the default validator {@linkplain Builder#validator(Predicate) accepts} any candidate 048 * that survives sanitization within the length cap. The sanitizer already neutralizes CR/LF/control-character 049 * header-injection payloads (escaping them to inert, ASCII-safe {@code \\uXXXX}), so the default honors a cleaned form 050 * rather than discarding a slightly-dirty id. Supply a stricter validator (e.g. UUID-only) to opt into reject-and-remint. 051 * 052 * <h5 class='section'>See Also:</h5><ul> 053 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerLoggingAndDebugging">Logging / Debugging</a> 054 * </ul> 055 * 056 * @since 10.0.0 057 */ 058@SuppressWarnings({ 059 "java:S1192" // Duplicated literals read more clearly inline than as constants. 060}) 061public class RequestIdSettings { 062 063 private final Predicate<String> validator; 064 private final String attributeKey; 065 private final Supplier<String> idSupplier; 066 067 /** 068 * Constructor. 069 * 070 * @param b The builder configuring these settings. Must not be <jk>null</jk>. 071 */ 072 protected RequestIdSettings(Builder b) { 073 reqnn("builder", b); 074 this.validator = b.validator; 075 this.attributeKey = b.attributeKey; 076 this.idSupplier = b.idSupplier; 077 } 078 079 /** 080 * Creates a new builder. 081 * 082 * @return A new builder. 083 */ 084 public static Builder create() { 085 return new Builder(); 086 } 087 088 /** 089 * Returns the predicate applied to a sanitized incoming id to decide whether to honor it. 090 * 091 * @return The validator predicate. Never <jk>null</jk>. 092 */ 093 public Predicate<String> getValidator() { return validator; } 094 095 /** 096 * Returns the servlet-request attribute key under which the resolved id is stashed. 097 * 098 * @return The attribute key. Never <jk>null</jk>. 099 */ 100 public String getAttributeKey() { return attributeKey; } 101 102 /** 103 * Returns the supplier used to mint a fresh id when none is honored from the request. 104 * 105 * @return The id supplier. Never <jk>null</jk>. 106 */ 107 public Supplier<String> getIdSupplier() { return idSupplier; } 108 109 /** 110 * Builder for {@link RequestIdSettings}. 111 */ 112 public static class Builder { 113 114 Predicate<String> validator = s -> true; 115 String attributeKey = "requestId"; 116 Supplier<String> idSupplier = Uuid7::createString; 117 118 /** 119 * Constructor. 120 */ 121 protected Builder() {} 122 123 /** 124 * Sets the predicate applied to a <b>sanitized</b> incoming {@code X-Request-Id} value. 125 * 126 * <p> 127 * Defaults to accept-all (sanitize-and-accept): any candidate that survives sanitization within 128 * {@link org.apache.juneau.http.RequestIdConstants#MAX_LEN} is honored. Supply a stricter predicate to 129 * reject-and-remint values that do not match a required shape. 130 * 131 * @param value The predicate, tested against the sanitized candidate. Must not be <jk>null</jk>. 132 * @return This object. 133 */ 134 public Builder validator(Predicate<String> value) { 135 reqnn("value", value); 136 validator = value; 137 return this; 138 } 139 140 /** 141 * Overrides the servlet-request attribute key under which the resolved id is stashed. 142 * 143 * <p> 144 * Defaults to {@code "requestId"}. Override only when coexisting with a third-party 145 * filter that publishes the id under a different key. 146 * 147 * @param value The attribute key. Must not be <jk>null</jk> or blank. 148 * @return This object. 149 */ 150 public Builder attributeKey(String value) { 151 reqnn("value", value); 152 if (value.isBlank()) 153 throw new IllegalArgumentException("Argument 'value' must not be blank."); 154 attributeKey = value; 155 return this; 156 } 157 158 /** 159 * Sets the supplier used to mint a new id when none is honored from the request. 160 * 161 * <p> 162 * Defaults to a time-ordered {@linkplain Uuid7 RFC 9562 version-7 UUID}. Swap in a smaller / shorter id 163 * scheme when payload size matters. 164 * 165 * @param value The supplier. Must not be <jk>null</jk>. 166 * @return This object. 167 */ 168 public Builder idSupplier(Supplier<String> value) { 169 reqnn("value", value); 170 idSupplier = value; 171 return this; 172 } 173 174 /** 175 * Builds the settings. 176 * 177 * @return A new {@link RequestIdSettings}. 178 */ 179 public RequestIdSettings build() { 180 return new RequestIdSettings(this); 181 } 182 } 183}