/* * Copyright 2014-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.glowroot.agent.plugin.servlet; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.regex.Pattern; import javax.annotation.Nullable; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.glowroot.agent.plugin.servlet.ServletAspect.HttpServletRequest; // shallow copies are necessary because request may not be thread safe, which may affect ability // to see detail from active traces // // shallow copies are also necessary because servlet container may clear out the objects after the // request is complete (e.g. tomcat does this) in order to reuse them, in which case this detail // would need to be captured synchronously at end of request anyways (although then it could be // captured only if trace met threshold for storage...) class DetailCapture { private DetailCapture() {} static ImmutableMap<String, Object> captureRequestParameters( Map</*@Nullable*/ String, /*@Nullable*/ String /*@Nullable*/[]> requestParameters) { ImmutableList<Pattern> capturePatterns = ServletPluginProperties.captureRequestParameters(); ImmutableList<Pattern> maskPatterns = ServletPluginProperties.maskRequestParameters(); ImmutableMap.Builder<String, Object> map = ImmutableMap.builder(); for (Entry</*@Nullable*/ String, /*@Nullable*/ String /*@Nullable*/[]> entry : requestParameters .entrySet()) { String name = entry.getKey(); if (name == null) { continue; } // converted to lower case for case-insensitive matching (patterns are lower case) String keyLowerCase = name.toLowerCase(Locale.ENGLISH); if (!matchesOneOf(keyLowerCase, capturePatterns)) { continue; } if (matchesOneOf(keyLowerCase, maskPatterns)) { map.put(name, "****"); continue; } @Nullable String[] values = entry.getValue(); if (values != null) { set(map, name, values); } } return map.build(); } static ImmutableMap<String, Object> captureRequestParameters(HttpServletRequest request) { Enumeration<? extends /*@Nullable*/Object> e = request.getParameterNames(); if (e == null) { return ImmutableMap.of(); } ImmutableList<Pattern> capturePatterns = ServletPluginProperties.captureRequestParameters(); ImmutableList<Pattern> maskPatterns = ServletPluginProperties.maskRequestParameters(); ImmutableMap.Builder<String, Object> map = ImmutableMap.builder(); while (e.hasMoreElements()) { Object nameObj = e.nextElement(); if (nameObj == null) { continue; } if (!(nameObj instanceof String)) { continue; } String name = (String) nameObj; // converted to lower case for case-insensitive matching (patterns are lower case) String keyLowerCase = name.toLowerCase(Locale.ENGLISH); if (!matchesOneOf(keyLowerCase, capturePatterns)) { continue; } if (matchesOneOf(keyLowerCase, maskPatterns)) { map.put(name, "****"); continue; } @Nullable String[] values = request.getParameterValues(name); if (values != null) { set(map, name, values); } } return map.build(); } private static void set(ImmutableMap.Builder<String, Object> map, String name, @Nullable String[] values) { if (values == null) { return; } if (values.length == 1) { String value = values[0]; if (value != null) { map.put(name, value); } } else { List</*@Nullable*/ String> list = new ArrayList</*@Nullable*/ String>(values.length); Collections.addAll(list, values); map.put(name, list); } } static ImmutableMap<String, Object> captureRequestHeaders(HttpServletRequest request) { ImmutableList<Pattern> capturePatterns = ServletPluginProperties.captureRequestHeaders(); if (capturePatterns.isEmpty()) { return ImmutableMap.of(); } Map<String, Object> requestHeaders = Maps.newHashMap(); Enumeration</*@Nullable*/ String> headerNames = request.getHeaderNames(); if (headerNames == null) { return ImmutableMap.of(); } for (Enumeration</*@Nullable*/ String> e = headerNames; e.hasMoreElements();) { String name = e.nextElement(); if (name == null) { continue; } // converted to lower case for case-insensitive matching (patterns are lower case) String keyLowerCase = name.toLowerCase(Locale.ENGLISH); if (!matchesOneOf(keyLowerCase, capturePatterns)) { continue; } Enumeration</*@Nullable*/ String> values = request.getHeaders(name); if (values != null) { captureRequestHeader(name, values, requestHeaders); } } return ImmutableMap.copyOf(requestHeaders); } static boolean matchesOneOf(String key, List<Pattern> patterns) { for (Pattern pattern : patterns) { if (pattern.matcher(key).matches()) { return true; } } return false; } private static void captureRequestHeader(String name, Enumeration</*@Nullable*/ String> values, Map<String, Object> requestHeaders) { if (!values.hasMoreElements()) { requestHeaders.put(name, ""); } else { String value = values.nextElement(); if (!values.hasMoreElements()) { requestHeaders.put(name, Strings.nullToEmpty(value)); } else { List<String> list = Lists.newArrayList(); list.add(Strings.nullToEmpty(value)); while (values.hasMoreElements()) { list.add(Strings.nullToEmpty(values.nextElement())); } requestHeaders.put(name, ImmutableList.copyOf(list)); } } } }