001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2010–2011 ApexIdentity Inc.
015 * Portions Copyright 2011-2014 ForgeRock AS.
016 */
017
018package org.forgerock.openig.filter;
019
020import static org.forgerock.openig.util.Json.*;
021
022import java.io.IOException;
023import java.util.Collections;
024import java.util.List;
025import java.util.Map;
026
027import org.forgerock.json.fluent.JsonValue;
028import org.forgerock.openig.handler.Handler;
029import org.forgerock.openig.handler.HandlerException;
030import org.forgerock.openig.heap.GenericHeaplet;
031import org.forgerock.openig.heap.HeapException;
032import org.forgerock.openig.http.Exchange;
033import org.forgerock.openig.http.Headers;
034import org.forgerock.openig.http.Message;
035import org.forgerock.openig.http.MessageType;
036import org.forgerock.openig.util.CaseInsensitiveSet;
037
038/**
039 * Removes headers from and adds headers to a message.
040 */
041public class HeaderFilter extends GenericFilter {
042
043    /** Indicates the type of message in the exchange to filter headers for. */
044    private final MessageType messageType;
045
046    /** The names of header fields to remove from the message. */
047    private final CaseInsensitiveSet removedHeaders = new CaseInsensitiveSet();
048
049    /** Header fields to add to the message. */
050    private final Headers addedHeaders = new Headers();
051
052    /**
053     * Builds a HeaderFilter processing either the incoming or outgoing message.
054     * @param messageType {@link MessageType#REQUEST} or {@link MessageType#RESPONSE}
055     */
056    public HeaderFilter(final MessageType messageType) {
057        this.messageType = messageType;
058    }
059
060    /**
061     * Returns the names of header fields to remove from the message.
062     * @return the names of header fields to remove from the message.
063     */
064    public CaseInsensitiveSet getRemovedHeaders() {
065        return removedHeaders;
066    }
067
068    /**
069     * Returns the header fields to add to the message.
070     * This is a essentially a Map of String to a List of String, each listed value representing
071     * an expression that will be evaluated.
072     * @return the header fields to add to the message.
073     */
074    public Headers getAddedHeaders() {
075        return addedHeaders;
076    }
077
078    /**
079     * Removes all specified headers, then adds all specified headers.
080     *
081     * @param message the message to remove headers from and add headers to.
082     */
083    private void process(Message<?> message, Exchange exchange) {
084        for (String s : this.removedHeaders) {
085            message.getHeaders().remove(s);
086        }
087        for (String key : this.addedHeaders.keySet()) {
088            for (String value : this.addedHeaders.get(key)) {
089                JsonValue jsonValue = new JsonValue(value);
090                message.getHeaders().add(key, (String) asExpression(jsonValue).eval(exchange));
091            }
092        }
093    }
094
095    @Override
096    public void filter(Exchange exchange, Handler next) throws HandlerException, IOException {
097        if (messageType == MessageType.REQUEST) {
098            process(exchange.request, exchange);
099        }
100        next.handle(exchange);
101        if (messageType == MessageType.RESPONSE) {
102            process(exchange.response, exchange);
103        }
104    }
105
106    /** Creates and initializes a header filter in a heap environment. */
107    public static class Heaplet extends GenericHeaplet {
108        @Override
109        public Object create() throws HeapException {
110            HeaderFilter filter = new HeaderFilter(config.get("messageType")
111                                                         .required()
112                                                         .asEnum(MessageType.class));
113            filter.removedHeaders.addAll(config.get("remove")
114                                         .defaultTo(Collections.emptyList())
115                                         .asList(String.class));
116            JsonValue add = config.get("add")
117                    .defaultTo(Collections.emptyMap())
118                    .expect(Map.class);
119            for (String key : add.keys()) {
120                List<String> values = add.get(key).required().asList(String.class);
121                filter.addedHeaders.addAll(key, values);
122            }
123            return filter;
124        }
125    }
126}