1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/http/http_response_info.h"
6
7#include "base/logging.h"
8#include "base/pickle.h"
9#include "base/time.h"
10#include "net/base/auth.h"
11#include "net/base/io_buffer.h"
12#include "net/base/net_errors.h"
13#include "net/base/ssl_cert_request_info.h"
14#include "net/base/x509_certificate.h"
15#include "net/http/http_response_headers.h"
16
17using base::Time;
18
19namespace net {
20
21// These values can be bit-wise combined to form the flags field of the
22// serialized HttpResponseInfo.
23enum {
24  // The version of the response info used when persisting response info.
25  RESPONSE_INFO_VERSION = 2,
26
27  // The minimum version supported for deserializing response info.
28  RESPONSE_INFO_MINIMUM_VERSION = 1,
29
30  // We reserve up to 8 bits for the version number.
31  RESPONSE_INFO_VERSION_MASK = 0xFF,
32
33  // This bit is set if the response info has a cert at the end.
34  RESPONSE_INFO_HAS_CERT = 1 << 8,
35
36  // This bit is set if the response info has a security-bits field (security
37  // strength, in bits, of the SSL connection) at the end.
38  RESPONSE_INFO_HAS_SECURITY_BITS = 1 << 9,
39
40  // This bit is set if the response info has a cert status at the end.
41  RESPONSE_INFO_HAS_CERT_STATUS = 1 << 10,
42
43  // This bit is set if the response info has vary header data.
44  RESPONSE_INFO_HAS_VARY_DATA = 1 << 11,
45
46  // This bit is set if the request was cancelled before completion.
47  RESPONSE_INFO_TRUNCATED = 1 << 12,
48
49  // This bit is set if the response was received via SPDY.
50  RESPONSE_INFO_WAS_SPDY = 1 << 13,
51
52  // This bit is set if the request has NPN negotiated.
53  RESPONSE_INFO_WAS_NPN = 1 << 14,
54
55  // This bit is set if the request was fetched via an explicit proxy.
56  RESPONSE_INFO_WAS_PROXY = 1 << 15,
57
58  // TODO(darin): Add other bits to indicate alternate request methods.
59  // For now, we don't support storing those.
60};
61
62HttpResponseInfo::HttpResponseInfo()
63    : was_cached(false),
64      was_fetched_via_spdy(false),
65      was_npn_negotiated(false),
66      was_fetched_via_proxy(false) {
67}
68
69HttpResponseInfo::HttpResponseInfo(const HttpResponseInfo& rhs)
70    : was_cached(rhs.was_cached),
71      was_fetched_via_spdy(rhs.was_fetched_via_spdy),
72      was_npn_negotiated(rhs.was_npn_negotiated),
73      was_fetched_via_proxy(rhs.was_fetched_via_proxy),
74      socket_address(rhs.socket_address),
75      request_time(rhs.request_time),
76      response_time(rhs.response_time),
77      auth_challenge(rhs.auth_challenge),
78      cert_request_info(rhs.cert_request_info),
79      ssl_info(rhs.ssl_info),
80      headers(rhs.headers),
81      vary_data(rhs.vary_data),
82      metadata(rhs.metadata) {
83}
84
85HttpResponseInfo::~HttpResponseInfo() {
86}
87
88HttpResponseInfo& HttpResponseInfo::operator=(const HttpResponseInfo& rhs) {
89  was_cached = rhs.was_cached;
90  was_fetched_via_spdy = rhs.was_fetched_via_spdy;
91  was_npn_negotiated = rhs.was_npn_negotiated;
92  was_fetched_via_proxy = rhs.was_fetched_via_proxy;
93  socket_address = rhs.socket_address;
94  request_time = rhs.request_time;
95  response_time = rhs.response_time;
96  auth_challenge = rhs.auth_challenge;
97  cert_request_info = rhs.cert_request_info;
98  ssl_info = rhs.ssl_info;
99  headers = rhs.headers;
100  vary_data = rhs.vary_data;
101  metadata = rhs.metadata;
102  return *this;
103}
104
105bool HttpResponseInfo::InitFromPickle(const Pickle& pickle,
106                                      bool* response_truncated) {
107  void* iter = NULL;
108
109  // read flags and verify version
110  int flags;
111  if (!pickle.ReadInt(&iter, &flags))
112    return false;
113  int version = flags & RESPONSE_INFO_VERSION_MASK;
114  if (version < RESPONSE_INFO_MINIMUM_VERSION ||
115      version > RESPONSE_INFO_VERSION) {
116    DLOG(ERROR) << "unexpected response info version: " << version;
117    return false;
118  }
119
120  // read request-time
121  int64 time_val;
122  if (!pickle.ReadInt64(&iter, &time_val))
123    return false;
124  request_time = Time::FromInternalValue(time_val);
125  was_cached = true;  // Set status to show cache resurrection.
126
127  // read response-time
128  if (!pickle.ReadInt64(&iter, &time_val))
129    return false;
130  response_time = Time::FromInternalValue(time_val);
131
132  // read response-headers
133  headers = new HttpResponseHeaders(pickle, &iter);
134  DCHECK_NE(headers->response_code(), -1);
135
136  // read ssl-info
137  if (flags & RESPONSE_INFO_HAS_CERT) {
138    // Version 1 only serialized only the end-entity certificate,
139    // while subsequent versions include the entire chain.
140    X509Certificate::PickleType type = (version == 1) ?
141        X509Certificate::PICKLETYPE_SINGLE_CERTIFICATE :
142        X509Certificate::PICKLETYPE_CERTIFICATE_CHAIN;
143    ssl_info.cert = X509Certificate::CreateFromPickle(pickle, &iter, type);
144  }
145  if (flags & RESPONSE_INFO_HAS_CERT_STATUS) {
146    int cert_status;
147    if (!pickle.ReadInt(&iter, &cert_status))
148      return false;
149    ssl_info.cert_status = cert_status;
150  }
151  if (flags & RESPONSE_INFO_HAS_SECURITY_BITS) {
152    int security_bits;
153    if (!pickle.ReadInt(&iter, &security_bits))
154      return false;
155    ssl_info.security_bits = security_bits;
156  }
157
158  // read vary-data
159  if (flags & RESPONSE_INFO_HAS_VARY_DATA) {
160    if (!vary_data.InitFromPickle(pickle, &iter))
161      return false;
162  }
163
164  // Read socket_address.  This was not always present in the response info,
165  // so we don't fail if it can't be read.  If additional fields are added in
166  // a future version, then they must only be read if this operation succeeds.
167  std::string socket_address_host;
168  if (pickle.ReadString(&iter, &socket_address_host)) {
169    // If the host was written, we always expect the port to follow.
170    uint16 socket_address_port;
171    if (!pickle.ReadUInt16(&iter, &socket_address_port))
172      return false;
173    socket_address = HostPortPair(socket_address_host, socket_address_port);
174  }
175
176  was_fetched_via_spdy = (flags & RESPONSE_INFO_WAS_SPDY) != 0;
177
178  was_npn_negotiated = (flags & RESPONSE_INFO_WAS_NPN) != 0;
179
180  was_fetched_via_proxy = (flags & RESPONSE_INFO_WAS_PROXY) != 0;
181
182  *response_truncated = (flags & RESPONSE_INFO_TRUNCATED) ? true : false;
183
184  return true;
185}
186
187void HttpResponseInfo::Persist(Pickle* pickle,
188                               bool skip_transient_headers,
189                               bool response_truncated) const {
190  int flags = RESPONSE_INFO_VERSION;
191  if (ssl_info.is_valid()) {
192    flags |= RESPONSE_INFO_HAS_CERT;
193    flags |= RESPONSE_INFO_HAS_CERT_STATUS;
194    if (ssl_info.security_bits != -1)
195      flags |= RESPONSE_INFO_HAS_SECURITY_BITS;
196    // TODO(wtc): we should persist ssl_info.connection_status.
197  }
198  if (vary_data.is_valid())
199    flags |= RESPONSE_INFO_HAS_VARY_DATA;
200  if (response_truncated)
201    flags |= RESPONSE_INFO_TRUNCATED;
202  if (was_fetched_via_spdy)
203    flags |= RESPONSE_INFO_WAS_SPDY;
204  if (was_npn_negotiated)
205    flags |= RESPONSE_INFO_WAS_NPN;
206  if (was_fetched_via_proxy)
207    flags |= RESPONSE_INFO_WAS_PROXY;
208
209  pickle->WriteInt(flags);
210  pickle->WriteInt64(request_time.ToInternalValue());
211  pickle->WriteInt64(response_time.ToInternalValue());
212
213  net::HttpResponseHeaders::PersistOptions persist_options =
214      net::HttpResponseHeaders::PERSIST_RAW;
215
216  if (skip_transient_headers) {
217    persist_options =
218        net::HttpResponseHeaders::PERSIST_SANS_COOKIES |
219        net::HttpResponseHeaders::PERSIST_SANS_CHALLENGES |
220        net::HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP |
221        net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE |
222        net::HttpResponseHeaders::PERSIST_SANS_RANGES;
223  }
224
225  headers->Persist(pickle, persist_options);
226
227  if (ssl_info.is_valid()) {
228    ssl_info.cert->Persist(pickle);
229    pickle->WriteInt(ssl_info.cert_status);
230    if (ssl_info.security_bits != -1)
231      pickle->WriteInt(ssl_info.security_bits);
232  }
233
234  if (vary_data.is_valid())
235    vary_data.Persist(pickle);
236
237  pickle->WriteString(socket_address.host());
238  pickle->WriteUInt16(socket_address.port());
239}
240
241}  // namespace net
242