/* * Apache HTTPD & NGINX Access log parsing made easy * Copyright (C) 2011-2019 Niels Basjes * * 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 * * https://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 nl.basjes.parse.httpdlog.dissectors; import nl.basjes.parse.core.Casts; import nl.basjes.parse.core.Dissector; import nl.basjes.parse.core.Parsable; import nl.basjes.parse.core.ParsedField; import nl.basjes.parse.core.exceptions.DissectionFailure; import java.net.HttpCookie; import java.util.ArrayList; import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Set; import static nl.basjes.parse.core.Casts.STRING_ONLY; public class ResponseSetCookieListDissector extends Dissector { // -------------------------------------------- private static final String INPUT_TYPE = "HTTP.SETCOOKIES"; @Override public String getInputType() { return INPUT_TYPE; } // -------------------------------------------- /** This should output all possible types */ @Override public List<String> getPossibleOutput() { List<String> result = new ArrayList<>(); result.add("HTTP.SETCOOKIE:*"); return result; } // -------------------------------------------- private final Set<String> requestedCookies = new HashSet<>(16); @Override public EnumSet<Casts> prepareForDissect(final String inputname, final String outputname) { requestedCookies.add(extractFieldName(inputname, outputname)); return STRING_ONLY; } // -------------------------------------------- private boolean wantAllCookies = false; @Override public void prepareForRun() { wantAllCookies = requestedCookies.contains("*"); } // -------------------------------------------- private final int minimalExpiresLength = "expires=XXXXXXX".length(); // Cache the compiled pattern private static final String SPLIT_BY = ", "; @Override public void dissect(final Parsable<?> parsable, final String inputname) throws DissectionFailure { final ParsedField field = parsable.getParsableField(INPUT_TYPE, inputname); final String fieldValue = field.getValue().getString(); if (fieldValue == null || fieldValue.isEmpty()){ return; // Nothing to do here } // This input is a ', ' separated list. // But the expires field can contain a ',' // and HttpCookie.parse(...) doesn't always work :( String[] parts = fieldValue.split(SPLIT_BY); String previous=""; for (String part:parts) { int expiresIndex = part.toLowerCase().indexOf("expires="); if (expiresIndex != -1) { if (part.length() - minimalExpiresLength < expiresIndex) { previous = part; continue; } } String value = part; if (!previous.isEmpty()) { value = previous+ SPLIT_BY +part; previous=""; } List<HttpCookie> cookies = HttpCookie.parse(value); for (HttpCookie cookie : cookies) { cookie.setVersion(1); String cookieName = cookie.getName().toLowerCase(); if (wantAllCookies || requestedCookies.contains(cookieName)) { parsable.addDissection(inputname, "HTTP.SETCOOKIE", cookieName, value); } } } } // -------------------------------------------- }