Class RequestContext

All Implemented Interfaces:
Serializable, ConcurrentMap<String,Object>, Map<String,Object>

public class RequestContext extends ConcurrentHashMap<String,Object>
The Request Context holds request, response, state information and data to access and share. The RequestContext lives for the duration of the request and is ThreadLocal. extensions of RequestContext can be substituted by setting the contextClass. Most methods here are convenience wrapper methods; the RequestContext is an extension of a ConcurrentHashMap
See Also:
  • Constructor Details

    • RequestContext

      public RequestContext()
  • Method Details

    • getTraceHeaders

      public Map<String,String> getTraceHeaders()
      Buffer for the X-frontcache-trace-* headers. Includes are resolved on the include-processor's worker threads, but HttpServletResponse (and Jetty's underlying HttpFields) is NOT thread safe - concurrent setHeader() calls corrupt the field list (NPE in HttpFields$Mutable$Wrapper.put) and a late/timed-out include can hit an already committed or recycled response. So worker threads only fill this map; the request thread flushes it onto the response right before the body is written.
      Returns:
      the shared (thread safe) trace header buffer of this request
    • getBoolean

      public boolean getBoolean(String key)
      Convenience method to return a boolean value for a given key
      Parameters:
      key -
      Returns:
      true or false depending what was set. default is false
    • getBoolean

      public boolean getBoolean(String key, boolean defaultResponse)
      Convenience method to return a boolean value for a given key
      Parameters:
      key -
      defaultResponse -
      Returns:
      true or false depending what was set. default defaultResponse
    • set

      public void set(String key)
      sets a key value to Boolen.TRUE
      Parameters:
      key -
    • set

      public void set(String key, Object value)
      puts the key, value into the map. a null value will remove the key from the map
      Parameters:
      key -
      value -
    • getRequest

      public jakarta.servlet.http.HttpServletRequest getRequest()
      Returns:
      the HttpServletRequest from the "request" key
    • setRequest

      public void setRequest(jakarta.servlet.http.HttpServletRequest request)
      sets the HttpServletRequest into the "request" key
      Parameters:
      request -
    • getResponse

      public jakarta.servlet.http.HttpServletResponse getResponse()
      Returns:
      the HttpServletResponse from the "response" key
    • setResponse

      public void setResponse(jakarta.servlet.http.HttpServletResponse response)
      sets the "response" key to the HttpServletResponse passed in
      Parameters:
      response -
    • getThrowable

      public Throwable getThrowable()
      returns a set throwable
      Returns:
      a set throwable
    • setThrowable

      public void setThrowable(Throwable th)
      sets a throwable
      Parameters:
      th -
    • setFrontCacheHost

      public void setFrontCacheHost(String frontCacheHost)
      sets frontCacheHost
      Parameters:
      frontCacheHost - a URL
    • getFrontCacheHost

      public String getFrontCacheHost()
      Returns:
      "frontCacheHost" URL
    • setFrontCacheProtocol

      public void setFrontCacheProtocol(String frontCacheProtocol)
      Parameters:
      frontCacheProtocol -
    • getFrontCacheProtocol

      public String getFrontCacheProtocol()
      Returns:
    • setPublicHost

      public void setPublicHost(String publicHost)
      The site's public hostname (front-cache.default-domain), or null when the node has none configured. This is NOT getFrontCacheHost() and the two must not be confused - that is the whole of docs/archive/guard-redirect-url-host-fix.md. getFrontCacheHost() is the Host header as it arrived, which is what predicates match, what the log lines record and what the cache key is built from; a node is reachable under several of them (the CDN's, an internal alias, its own name, a bare IP). This one is the single name we are willing to hand to a browser. Null means "not configured" - callers fall back to the arriving host. See FrontCacheEngine.getPublicDomain(), which is what maps the unset/placeholder property values onto null.
    • getPublicHost

      public String getPublicHost()
      Returns:
      the site's public hostname, or null when none is configured
    • setPublicProtocol

      public void setPublicProtocol(String publicProtocol)
      The scheme the client actually used, recovered from X-Forwarded-Proto (trusted peers only) or front-cache.public-scheme. Separate from getFrontCacheProtocol(), which stays the scheme of the connection as this process saw it. Behind a TLS-terminating proxy the two differ, and the cache key is built from the latter - so overloading it would silently re-key the whole cache. Only URLs we emit to a client read this one.
    • getPublicProtocol

      public String getPublicProtocol()
      Returns:
      the public scheme, or null when it could not be established (fall back to getFrontCacheProtocol())
    • setOriginURL

      public void setOriginURL(URL originURL)
    • getOriginURL

      public URL getOriginURL()
    • setResponseBody

      public void setResponseBody(String body)
      sets the "responseBody" value as a String. This is the response sent back to the client.
      Parameters:
      body -
    • getResponseBody

      public String getResponseBody()
      Returns:
      the String response body to be snt back to the requesting client
    • setResponseDataStream

      public void setResponseDataStream(InputStream responseDataStream)
      sets the InputStream of the response into the responseDataStream
      Parameters:
      responseDataStream -
    • setResponseGZipped

      public void setResponseGZipped(boolean gzipped)
      sets the flag responseGZipped if the response is gzipped
      Parameters:
      gzipped -
    • getResponseGZipped

      public boolean getResponseGZipped()
      Returns:
      true if responseGZipped is true (the response is gzipped)
    • getResponseDataStream

      public InputStream getResponseDataStream()
      Returns:
      the InputStream Response
    • getResponseStatusCode

      public int getResponseStatusCode()
      returns the response status code. Default is 200
      Returns:
    • setResponseStatusCode

      public void setResponseStatusCode(int nStatusCode)
      Use this instead of response.setStatusCode()
      Parameters:
      nStatusCode -
    • getOriginResponseHeaders

      public Map<String, List<String>> getOriginResponseHeaders()
      the Origin response headers
      Returns:
      the Listinvalid input: '<'Pairinvalid input: '<'String, String>> of headers sent back from the origin
    • isCacheableResponse

      public boolean isCacheableResponse()
      check if response has "Content-Type" header with "text" inside
      Returns:
    • isCacheableRequest

      public boolean isCacheableRequest()
      Returns:
    • addOriginResponseHeader

      public void addOriginResponseHeader(String name, String value)
      adds a header to the origin response headers
      Parameters:
      name -
      value -
    • getOriginContentLength

      public Long getOriginContentLength()
      returns the content-length of the origin response
      Returns:
      the content-length of the origin response
    • setOriginContentLength

      public void setOriginContentLength(Long v)
      sets the content-length from the origin response
      Parameters:
      v -
    • setRequestURI

      public void setRequestURI(String uri)
    • getRequestURI

      public String getRequestURI()
    • setRequestQueryString

      public void setRequestQueryString(String requestQueryString)
    • getRequestQueryString

      public String getRequestQueryString()
    • setOriginContentLength

      public void setOriginContentLength(String v)
      sets the content-length from the origin response
      Parameters:
      v - parses the string into an int
    • isChunkedRequestBody

      public boolean isChunkedRequestBody()
      Returns:
      true if the request body is chunked
    • setChunkedRequestBody

      public void setChunkedRequestBody()
      sets chunkedRequestBody to true
    • isGzipRequested

      public boolean isGzipRequested()
      Returns:
      true is the client request can accept gzip encoding. Checks the "accept-encoding" header
    • getRequestQueryParams

      public Map<String, List<String>> getRequestQueryParams()
      Returns:
      Mapinvalid input: '<'String, List> of the request Query Parameters
    • setRequestQueryParams

      public void setRequestQueryParams(Map<String, List<String>> qp)
      sets the request query params list
      Parameters:
      qp - Mapinvalid input: '<'String, List> qp
    • getHttpClientResponse

      public org.apache.hc.core5.http.ClassicHttpResponse getHttpClientResponse()
    • setHttpClientResponse

      public void setHttpClientResponse(org.apache.hc.core5.http.ClassicHttpResponse response)
    • releaseHttpClientResponse

      public void releaseHttpClientResponse()
      Releases the origin response opened by FC_BypassCache, closing it once BOTH parties are done. The response is opened on a command worker thread but its body is streamed by the request thread, so neither thread can close it unilaterally. On a command TIMEOUT the request thread finishes early (through the fallback) while the worker is still inside httpclient.execute() - a blocking socket read ignores the interrupt - and then stashes a response nobody is left to close, leaking the pooled connection permanently (leased entries are never reclaimed by closeExpiredConnections). Both threads call this in a finally; whichever arrives last does the close, so the connection is returned exactly once no matter which one wins the race.
    • setFallbackWebResponse

      public void setFallbackWebResponse(WebResponse webResponse)
      The fallback to serve instead of the origin response, parked here by FC_BypassCache.getFallback(). The fallback is resolved on the command worker thread but written by the request thread, which is the only thread that may touch the servlet output stream in bypass mode. Writing it from getFallback() instead raced with the request thread's own writeResponse(): the fallback body went out first, then addResponseHeaders() stamped the context status over it - which defaults to 500 - and any origin body that arrived late was appended to it. Whenever the fallback was smaller than the container's response buffer (32KB in Jetty) the response was not yet committed when that happened, so the status actually took effect and the client got a 500.
    • getFallbackWebResponse

      public WebResponse getFallbackWebResponse()
    • setFrontCacheHttpPort

      public void setFrontCacheHttpPort(String frontCacheHttpPort)
    • getFrontCacheHttpPort

      public String getFrontCacheHttpPort()
    • setFrontCacheHttpsPort

      public void setFrontCacheHttpsPort(String frontCacheHttpsPort)
    • getFrontCacheHttpsPort

      public String getFrontCacheHttpsPort()
    • setFilterChain

      public void setFilterChain(jakarta.servlet.FilterChain filterChain)
    • getFilterChain

      public jakarta.servlet.FilterChain getFilterChain()
    • isFilterMode

      public boolean isFilterMode()
      Check if run as ServletFilter
      Returns:
    • setFrontCacheId

      public void setFrontCacheId(String frontCacheId)
      Parameters:
      frontCacheId -
    • getFrontCacheId

      public String getFrontCacheId()
      Returns:
    • setFallbackUsed

      public void setFallbackUsed()
    • setFallbackUsed

      public void setFallbackUsed(String reason)
    • isFallbackUsed

      public boolean isFallbackUsed()
    • getFallbackReason

      public String getFallbackReason()
      Returns:
      the failure category (short-circuited|timeout|rejected|bad-request|failure) when known, otherwise "error". Only meaningful when isFallbackUsed() is true.
    • setRequestType

      public void setRequestType(String frontcacheRequestType)
    • getRequestType

      public String getRequestType()
    • setRequestId

      public void setRequestId(String frontcacheRequestId)
    • getRequestId

      public String getRequestId()
    • setRequestFromFrontcache

      public void setRequestFromFrontcache()
    • getRequestFromFrontcache

      public boolean getRequestFromFrontcache()
    • getCurrentRequestURL

      public String getCurrentRequestURL()
    • setCurrentRequestURL

      public void setCurrentRequestURL(String currentRequestURL)
    • getCookieNames

      public Set<String> getCookieNames()
      Names of the cookies the client sent, parsed once per request and memoized. Used by the guard rules (org.frontcache.guard), which run on every request before cache and origin - so a request pays for cookie parsing only when some rule asks about a cookie, and never more than once. Names only: guard rules test presence and must not read, compare or log cookie values.
      Returns:
      cookie names, empty when there is no (or no longer a) usable servlet request
    • getClientType

      public String getClientType()
    • setClientType

      public void setClientType(String currentClientType)
    • getClientIP

      public String getClientIP()
      The client IP of this request, resolved once. FCUtils.getClientIP(HttpServletRequest) walks up to thirteen candidate headers, and it used to be called afresh for every log line - once per request, again per include, again on the origin call. Resolving it once per exchange makes those free. The engine stamps it in init(), on the request thread while the servlet request is still live, which is also what makes it CORRECT for work that outlives the request: an async include runs after the container has recycled the request, where a late resolve can only answer "unresolved". A context built outside the engine (a copy, a test) resolves lazily here instead.
    • setClientIP

      public void setClientIP(String clientIP)
    • getResolvedClientIP

      public String getResolvedClientIP()
      The trustworthy client address, resolved once per request. Deliberately NOT getClientIP(). That one walks thirteen client-supplied headers and returns the first non-empty value, which is right for a log column and would let any client mint itself a fresh counter per request - or claim a crawler's address - by sending its own X-Forwarded-For. This goes through ClientIpResolver, which reads a forwarding header only from a configured trusted proxy and walks it right to left. The two can legitimately disagree, which is why the guard log line carries this one explicitly. Two callers, both of which must never see a spoofable value: the rate limiter's counter key, and the client-ip: predicate used by guard rules and by bots.conf classification.
      Returns:
      canonical client address, or null when it cannot be established
    • getRateLimitKey

      public String getRateLimitKey()
      Returns:
      getResolvedClientIP() - the name the rate limiter knows it by
    • getUserAgent

      public String getUserAgent()
      The User-Agent header, read once per request. Memoized for the same reason the cookie names are: bot classification asks several rules for it on every request, and an async include's servlet request may be recycled by the time one of them asks - in which case the answer is "no user agent", not an exception.
      Returns:
      the header value, or null when there is none (or it is no longer readable)
    • getClientTypeRule

      public String getClientTypeRule()
      Name of the bots.conf rule that decided getClientType(). Kept for diagnostics; the client type itself is what everything downstream reads.
    • setClientTypeRule

      public void setClientTypeRule(String ruleName)
    • setWarmRequest

      public void setWarmRequest(String mode)
      Marks this request as the cache warmer's - set by the engine only after the warm token checked out (see org.frontcache.warmer.WarmRequest). Nothing a client sends can set it.
      Parameters:
      mode - fill | refresh
    • isWarmRequest

      public boolean isWarmRequest()
    • getWarmMode

      public String getWarmMode()
      Returns:
      fill | refresh, or null when this is not a warm request
    • setWarmResult

      public void setWarmResult(String result)
      What the cache processor did with a warm request - reported to the warmer as x-frontcache-warm-result. Recorded on warm requests only.
    • getWarmResult

      public String getWarmResult()
    • setGuardProbe

      public void setGuardProbe()
      Marks a synthetic context built for a load-time guard self-check (redirect-loop detection, the exemption warnings) rather than for a real request. Predicates with side effects - today, the rate limiter - must do nothing for one of these.
    • isGuardProbe

      public boolean isGuardProbe()
    • setGuardRetryAfterSeconds

      public void setGuardRetryAfterSeconds(int seconds)
      Seconds until a rate-limited client's window rolls over, stashed by the rate predicate and emitted as Retry-After by the guard response.
    • getGuardRetryAfterSeconds

      public int getGuardRetryAfterSeconds()
      Returns:
      the Retry-After value in seconds, or 0 when the matching rule had nothing to say
    • copy

      public RequestContext copy()
    • setDomain

      public void setDomain(String domain)
      The site this node serves (front-cache.default-domain), stamped on the context by the engine. Used for request logging, cache-entry ownership and the command group key.
    • getDomain

      public String getDomain()
    • setLogToHTTPHeaders

      public void setLogToHTTPHeaders()
    • getLogToHTTPHeaders

      public boolean getLogToHTTPHeaders()
    • setIncludeLevel

      public void setIncludeLevel(String includeLevel)
    • getIncludeLevel

      public String getIncludeLevel()
    • setToplevelCached

      public void setToplevelCached()
    • isToplevelCached

      public boolean isToplevelCached()
    • toString

      public String toString()
      A bounded, one-line summary for diagnostics - NOT a dump of the map. This used to be super.toString() (every entry, including the HttpServletRequest, the HttpServletResponse, the open origin ClassicHttpResponse and the whole origin header map) put through a freshly-compiled regex to squash newlines. The reason that mattered is where it is called from: all four command fallbacks log it - so it runs on EVERY request precisely when the origin is failing and every request is taking the fallback path. The log line meant to help diagnose an outage was amplifying the load during one, and it dragged whole request/response objects through their own toString() to do it. Fixed fields, so there is nothing to escape: none of them can contain a newline, and the URI and query string are the only caller-influenced values - they are length-capped rather than pattern-matched.
      Overrides:
      toString in class ConcurrentHashMap<String,Object>