1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.squareup.okhttp.internal.http;
18
19import java.net.URI;
20import java.util.Date;
21import java.util.List;
22import java.util.Map;
23
24/** Parsed HTTP request headers. */
25final class RequestHeaders {
26  private final URI uri;
27  private final RawHeaders headers;
28
29  /** Don't use a cache to satisfy this request. */
30  private boolean noCache;
31  private int maxAgeSeconds = -1;
32  private int maxStaleSeconds = -1;
33  private int minFreshSeconds = -1;
34
35  /**
36   * This field's name "only-if-cached" is misleading. It actually means "do
37   * not use the network". It is set by a client who only wants to make a
38   * request if it can be fully satisfied by the cache. Cached responses that
39   * would require validation (ie. conditional gets) are not permitted if this
40   * header is set.
41   */
42  private boolean onlyIfCached;
43
44  /**
45   * True if the request contains an authorization field. Although this isn't
46   * necessarily a shared cache, it follows the spec's strict requirements for
47   * shared caches.
48   */
49  private boolean hasAuthorization;
50
51  private int contentLength = -1;
52  private String transferEncoding;
53  private String userAgent;
54  private String host;
55  private String connection;
56  private String acceptEncoding;
57  private String contentType;
58  private String ifModifiedSince;
59  private String ifNoneMatch;
60  private String proxyAuthorization;
61
62  public RequestHeaders(URI uri, RawHeaders headers) {
63    this.uri = uri;
64    this.headers = headers;
65
66    HeaderParser.CacheControlHandler handler = new HeaderParser.CacheControlHandler() {
67      @Override public void handle(String directive, String parameter) {
68        if ("no-cache".equalsIgnoreCase(directive)) {
69          noCache = true;
70        } else if ("max-age".equalsIgnoreCase(directive)) {
71          maxAgeSeconds = HeaderParser.parseSeconds(parameter);
72        } else if ("max-stale".equalsIgnoreCase(directive)) {
73          maxStaleSeconds = HeaderParser.parseSeconds(parameter);
74        } else if ("min-fresh".equalsIgnoreCase(directive)) {
75          minFreshSeconds = HeaderParser.parseSeconds(parameter);
76        } else if ("only-if-cached".equalsIgnoreCase(directive)) {
77          onlyIfCached = true;
78        }
79      }
80    };
81
82    for (int i = 0; i < headers.length(); i++) {
83      String fieldName = headers.getFieldName(i);
84      String value = headers.getValue(i);
85      if ("Cache-Control".equalsIgnoreCase(fieldName)) {
86        HeaderParser.parseCacheControl(value, handler);
87      } else if ("Pragma".equalsIgnoreCase(fieldName)) {
88        if ("no-cache".equalsIgnoreCase(value)) {
89          noCache = true;
90        }
91      } else if ("If-None-Match".equalsIgnoreCase(fieldName)) {
92        ifNoneMatch = value;
93      } else if ("If-Modified-Since".equalsIgnoreCase(fieldName)) {
94        ifModifiedSince = value;
95      } else if ("Authorization".equalsIgnoreCase(fieldName)) {
96        hasAuthorization = true;
97      } else if ("Content-Length".equalsIgnoreCase(fieldName)) {
98        try {
99          contentLength = Integer.parseInt(value);
100        } catch (NumberFormatException ignored) {
101        }
102      } else if ("Transfer-Encoding".equalsIgnoreCase(fieldName)) {
103        transferEncoding = value;
104      } else if ("User-Agent".equalsIgnoreCase(fieldName)) {
105        userAgent = value;
106      } else if ("Host".equalsIgnoreCase(fieldName)) {
107        host = value;
108      } else if ("Connection".equalsIgnoreCase(fieldName)) {
109        connection = value;
110      } else if ("Accept-Encoding".equalsIgnoreCase(fieldName)) {
111        acceptEncoding = value;
112      } else if ("Content-Type".equalsIgnoreCase(fieldName)) {
113        contentType = value;
114      } else if ("Proxy-Authorization".equalsIgnoreCase(fieldName)) {
115        proxyAuthorization = value;
116      }
117    }
118  }
119
120  public boolean isChunked() {
121    return "chunked".equalsIgnoreCase(transferEncoding);
122  }
123
124  public boolean hasConnectionClose() {
125    return "close".equalsIgnoreCase(connection);
126  }
127
128  public URI getUri() {
129    return uri;
130  }
131
132  public RawHeaders getHeaders() {
133    return headers;
134  }
135
136  public boolean isNoCache() {
137    return noCache;
138  }
139
140  public int getMaxAgeSeconds() {
141    return maxAgeSeconds;
142  }
143
144  public int getMaxStaleSeconds() {
145    return maxStaleSeconds;
146  }
147
148  public int getMinFreshSeconds() {
149    return minFreshSeconds;
150  }
151
152  public boolean isOnlyIfCached() {
153    return onlyIfCached;
154  }
155
156  public boolean hasAuthorization() {
157    return hasAuthorization;
158  }
159
160  public int getContentLength() {
161    return contentLength;
162  }
163
164  public String getTransferEncoding() {
165    return transferEncoding;
166  }
167
168  public String getUserAgent() {
169    return userAgent;
170  }
171
172  public String getHost() {
173    return host;
174  }
175
176  public String getConnection() {
177    return connection;
178  }
179
180  public String getAcceptEncoding() {
181    return acceptEncoding;
182  }
183
184  public String getContentType() {
185    return contentType;
186  }
187
188  public String getIfModifiedSince() {
189    return ifModifiedSince;
190  }
191
192  public String getIfNoneMatch() {
193    return ifNoneMatch;
194  }
195
196  public String getProxyAuthorization() {
197    return proxyAuthorization;
198  }
199
200  public void setChunked() {
201    if (this.transferEncoding != null) {
202      headers.removeAll("Transfer-Encoding");
203    }
204    headers.add("Transfer-Encoding", "chunked");
205    this.transferEncoding = "chunked";
206  }
207
208  public void setContentLength(int contentLength) {
209    if (this.contentLength != -1) {
210      headers.removeAll("Content-Length");
211    }
212    headers.add("Content-Length", Integer.toString(contentLength));
213    this.contentLength = contentLength;
214  }
215
216  public void setUserAgent(String userAgent) {
217    if (this.userAgent != null) {
218      headers.removeAll("User-Agent");
219    }
220    headers.add("User-Agent", userAgent);
221    this.userAgent = userAgent;
222  }
223
224  public void setHost(String host) {
225    if (this.host != null) {
226      headers.removeAll("Host");
227    }
228    headers.add("Host", host);
229    this.host = host;
230  }
231
232  public void setConnection(String connection) {
233    if (this.connection != null) {
234      headers.removeAll("Connection");
235    }
236    headers.add("Connection", connection);
237    this.connection = connection;
238  }
239
240  public void setAcceptEncoding(String acceptEncoding) {
241    if (this.acceptEncoding != null) {
242      headers.removeAll("Accept-Encoding");
243    }
244    headers.add("Accept-Encoding", acceptEncoding);
245    this.acceptEncoding = acceptEncoding;
246  }
247
248  public void setContentType(String contentType) {
249    if (this.contentType != null) {
250      headers.removeAll("Content-Type");
251    }
252    headers.add("Content-Type", contentType);
253    this.contentType = contentType;
254  }
255
256  public void setIfModifiedSince(Date date) {
257    if (ifModifiedSince != null) {
258      headers.removeAll("If-Modified-Since");
259    }
260    String formattedDate = HttpDate.format(date);
261    headers.add("If-Modified-Since", formattedDate);
262    ifModifiedSince = formattedDate;
263  }
264
265  public void setIfNoneMatch(String ifNoneMatch) {
266    if (this.ifNoneMatch != null) {
267      headers.removeAll("If-None-Match");
268    }
269    headers.add("If-None-Match", ifNoneMatch);
270    this.ifNoneMatch = ifNoneMatch;
271  }
272
273  /**
274   * Returns true if the request contains conditions that save the server from
275   * sending a response that the client has locally. When the caller adds
276   * conditions, this cache won't participate in the request.
277   */
278  public boolean hasConditions() {
279    return ifModifiedSince != null || ifNoneMatch != null;
280  }
281
282  public void addCookies(Map<String, List<String>> allCookieHeaders) {
283    for (Map.Entry<String, List<String>> entry : allCookieHeaders.entrySet()) {
284      String key = entry.getKey();
285      if ("Cookie".equalsIgnoreCase(key) || "Cookie2".equalsIgnoreCase(key)) {
286        headers.addAll(key, entry.getValue());
287      }
288    }
289  }
290}
291