001package io.prometheus.metrics.exporter.common; 002 003import io.prometheus.metrics.annotations.StableApi; 004import io.prometheus.metrics.model.registry.PrometheusScrapeRequest; 005import java.io.UnsupportedEncodingException; 006import java.util.ArrayList; 007import java.util.Enumeration; 008import javax.annotation.Nullable; 009 010@StableApi 011public interface PrometheusHttpRequest extends PrometheusScrapeRequest { 012 013 /** See {@code jakarta.servlet.http.HttpServletRequest.getQueryString()} */ 014 String getQueryString(); 015 016 /** See {@code jakarta.servlet.http.HttpServletRequest.getHeaders(String)} */ 017 Enumeration<String> getHeaders(String name); 018 019 /** See {@code jakarta.servlet.http.HttpServletRequest.getMethod()} */ 020 String getMethod(); 021 022 /** See {@code jakarta.servlet.http.HttpServletRequest.getHeader(String)} */ 023 @Nullable 024 default String getHeader(String name) { 025 Enumeration<String> headers = getHeaders(name); 026 if (headers == null || !headers.hasMoreElements()) { 027 return null; 028 } else { 029 return headers.nextElement(); 030 } 031 } 032 033 /** See {@code jakarta.servlet.ServletRequest.getParameter(String)} */ 034 @Nullable 035 default String getParameter(String name) { 036 String[] values = getParameterValues(name); 037 if (values == null || values.length == 0) { 038 return null; 039 } else { 040 return values[0]; 041 } 042 } 043 044 /** 045 * See {@code jakarta.servlet.ServletRequest.getParameterValues(String)}. 046 * 047 * <p>For safety, the default implementation applies two fixed limits: {@code maxQueryStringLength 048 * = 64 * 1024} (65,536 characters) and {@code maxQueryParameterCount = 1024} ({@code &}-separated 049 * parameter pairs). These implementation values are not exposed as runtime configuration. 050 * Requests that exceed either limit or contain invalid percent-encoding are rejected by the 051 * scrape handler with HTTP {@code 400 Bad Request}. 052 */ 053 @Override 054 @Nullable 055 // decode with Charset is only available in Java 10+, but we want to support Java 8 056 @SuppressWarnings("JdkObsolete") 057 default String[] getParameterValues(String name) { 058 try { 059 int maxQueryStringLength = 64 * 1024; 060 int maxQueryParameterCount = 1024; 061 ArrayList<String> result = new ArrayList<>(); 062 String queryString = getQueryString(); 063 if (queryString != null) { 064 if (queryString.length() > maxQueryStringLength) { 065 throw new InvalidQueryParameterException( 066 "Query string too long: " 067 + queryString.length() 068 + " characters (max " 069 + maxQueryStringLength 070 + ")"); 071 } 072 int start = 0; 073 int parameterCount = 0; 074 for (int end = queryString.indexOf('&'); start <= queryString.length(); ) { 075 parameterCount++; 076 if (parameterCount > maxQueryParameterCount) { 077 throw new InvalidQueryParameterException( 078 "Too many query parameters: " 079 + parameterCount 080 + " (max " 081 + maxQueryParameterCount 082 + ")"); 083 } 084 String pair = 085 end == -1 ? queryString.substring(start) : queryString.substring(start, end); 086 int idx = pair.indexOf("="); 087 if (idx != -1 088 && InvalidQueryParameterException.urlDecode(pair.substring(0, idx)).equals(name)) { 089 result.add(InvalidQueryParameterException.urlDecode(pair.substring(idx + 1))); 090 } 091 if (end == -1) { 092 break; 093 } 094 start = end + 1; 095 end = queryString.indexOf('&', start); 096 } 097 } 098 if (result.isEmpty()) { 099 // Servlet API: getParameterValues() returns null if the parameter does not exist. 100 return null; 101 } else { 102 return result.toArray(new String[0]); 103 } 104 } catch (UnsupportedEncodingException e) { 105 // UTF-8 encoding not supported. 106 throw new RuntimeException(e); 107 } 108 } 109}