View Javadoc
1   /*
2    * The contents of this file are subject to the terms of the Common Development and
3    * Distribution License (the License). You may not use this file except in compliance with the
4    * License.
5    *
6    * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7    * specific language governing permission and limitations under the License.
8    *
9    * When distributing Covered Software, include this CDDL Header Notice in each file and include
10   * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11   * Header, with the fields enclosed by brackets [] replaced by your own identifying
12   * information: "Portions copyright [year] [name of copyright owner]".
13   *
14   * Copyright 2012-2015 ForgeRock AS.
15   */
16  
17  package org.forgerock.json.resource.http;
18  
19  import java.util.Collections;
20  import java.util.LinkedHashMap;
21  import java.util.List;
22  import java.util.Map;
23  import java.util.Set;
24  import java.util.TreeMap;
25  
26  import org.forgerock.services.context.AbstractContext;
27  import org.forgerock.services.context.Context;
28  import org.forgerock.http.protocol.Header;
29  import org.forgerock.json.JsonValue;
30  import org.forgerock.util.Factory;
31  import org.forgerock.util.LazyMap;
32  
33  /** A {@link Context} containing information relating to the originating HTTP request. */
34  public final class HttpContext extends AbstractContext {
35  
36      // TODO: security parameters such as user name, etc?
37      /**
38       * Attribute in the serialized JSON form that holds the request headers.
39       * @see #HttpContext(JsonValue, ClassLoader)
40       */
41      public static final String ATTR_HEADERS = "headers";
42  
43      /**
44       * Attribute in the serialized JSON form that holds the query and/or form parameters.
45       * @see #HttpContext(JsonValue, ClassLoader)
46       */
47      public static final String ATTR_PARAMETERS = "parameters";
48  
49      /**
50       * Attribute in the serialised JSON form that holds the HTTP method of the request.
51       * @see #HttpContext(JsonValue, ClassLoader)
52       */
53      public static final String ATTR_METHOD = "method";
54  
55      /**
56       * Attribute in the serialised JSON form that holds the full URI of the request, excluding anything beyond the
57       * path component (i.e., no query parameters).
58       * @see #HttpContext(JsonValue, ClassLoader)
59       */
60      public static final String ATTR_PATH = "path";
61  
62      private final Map<String, List<String>> headers;
63      private final Map<String, List<String>> parameters;
64  
65      HttpContext(Context parent, final org.forgerock.http.protocol.Request req) {
66          super(parent, "http");
67          data.put(ATTR_METHOD, HttpUtils.getMethod(req));
68          data.put(ATTR_PATH, getRequestPath(req));
69          this.headers = Collections.unmodifiableMap(new LazyMap<>(
70              new Factory<Map<String, List<String>>>() {
71                  @Override
72                  public Map<String, List<String>> newInstance() {
73                      Map<String, List<String>> result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
74                      for (Map.Entry<String, Header> header : req.getHeaders().asMapOfHeaders().entrySet()) {
75                          String name = header.getKey();
76                          List<String> values = header.getValue().getValues();
77                          result.put(name, values);
78                      }
79                      return result;
80                  }
81              }));
82          data.put(ATTR_HEADERS, headers);
83          this.parameters = Collections.unmodifiableMap(new LazyMap<>(
84              new Factory<Map<String, List<String>>>() {
85                  @Override
86                  public Map<String, List<String>> newInstance() {
87                      Map<String, List<String>> result = new LinkedHashMap<>();
88                      Set<Map.Entry<String, List<String>>> parameters = req.getForm().entrySet();
89                      for (Map.Entry<String, List<String>> parameter : parameters) {
90                          String name = parameter.getKey();
91                          List<String> values = parameter.getValue();
92                          result.put(name, values);
93                      }
94                      return result;
95                  }
96              }));
97          data.put(ATTR_PARAMETERS, parameters);
98      }
99  
100     /**
101      * Restore from JSON representation.
102      *
103      * @param savedContext
104      *            The JSON representation from which this context's attributes
105      *            should be parsed. Must be a JSON Object that contains {@link #ATTR_HEADERS} and
106      *            {@link #ATTR_PARAMETERS} attributes.
107      * @param classLoader
108      *            The ClassLoader which can properly resolve the persisted class-name.
109      */
110     public HttpContext(final JsonValue savedContext, final ClassLoader classLoader) {
111         super(savedContext, classLoader);
112         this.headers = data.get(ATTR_HEADERS).required().asMapOfList(String.class);
113         this.parameters = data.get(ATTR_PARAMETERS).required().asMapOfList(String.class);
114     }
115 
116     private String getRequestPath(org.forgerock.http.protocol.Request req) {
117         return new StringBuilder()
118             .append(req.getUri().getScheme())
119             .append("://")
120             .append(req.getUri().getRawAuthority())
121             .append(req.getUri().getRawPath()).toString();
122     }
123 
124     /**
125      * Returns an unmodifiable list containing the values of the named HTTP
126      * request header.
127      *
128      * @param name
129      *            The name of the HTTP request header.
130      * @return An unmodifiable list containing the values of the named HTTP
131      *         request header, which may be empty if the header is not present
132      *         in the request.
133      */
134     public List<String> getHeader(String name) {
135         List<String> header = headers.get(name);
136         return Collections.unmodifiableList(header != null ? header : Collections.<String> emptyList());
137     }
138 
139     /**
140      * Returns the first value of the named HTTP request header.
141      *
142      * @param name
143      *            The name of the HTTP request header.
144      * @return The first value of the named HTTP request header, or {@code null}
145      *         if the header is not present in the request.
146      */
147     public String getHeaderAsString(String name) {
148         List<String> header = getHeader(name);
149         return header.isEmpty() ? null : header.get(0);
150     }
151 
152     /**
153      * Returns an unmodifiable map of the HTTP request headers.
154      *
155      * @return An unmodifiable map of the HTTP request headers.
156      */
157     public Map<String, List<String>> getHeaders() {
158         return headers;
159     }
160 
161     /**
162      * Returns the effective HTTP method, taking into account presence of the
163      * {@code X-HTTP-Method-Override} header.
164      *
165      * @return The effective HTTP method, taking into account presence of the
166      *         {@code X-HTTP-Method-Override} header.
167      */
168     public String getMethod() {
169         return data.get(ATTR_METHOD).asString();
170     }
171 
172     /**
173      * Returns an unmodifiable list containing the values of the named HTTP
174      * request parameter.
175      *
176      * @param name
177      *            The name of the HTTP request parameter.
178      * @return An unmodifiable list containing the values of the named HTTP
179      *         request parameter, which may be empty if the parameter is not
180      *         present in the request.
181      */
182     public List<String> getParameter(String name) {
183         final List<String> parameter = parameters.get(name);
184         return Collections.unmodifiableList(parameter != null ? parameter : Collections.<String> emptyList());
185     }
186 
187     /**
188      * Returns the first value of the named HTTP request parameter.
189      *
190      * @param name
191      *            The name of the HTTP request parameter.
192      * @return The first value of the named HTTP request parameter, or
193      *         {@code null} if the parameter is not present in the request.
194      */
195     public String getParameterAsString(String name) {
196         final List<String> parameter = getParameter(name);
197         return parameter.isEmpty() ? null : parameter.get(0);
198     }
199 
200     /**
201      * Returns an unmodifiable map of the HTTP request parameters.
202      *
203      * @return An unmodifiable map of the HTTP request parameters.
204      */
205     public Map<String, List<String>> getParameters() {
206         return parameters;
207     }
208 
209     /**
210      * Returns the HTTP request path.
211      *
212      * @return The HTTP request path.
213      */
214     public String getPath() {
215         return data.get(ATTR_PATH).asString();
216     }
217 }