1// Copyright (c) 2012 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/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/cert/signed_certificate_timestamp.h"
14#include "net/cert/x509_certificate.h"
15#include "net/http/http_response_headers.h"
16#include "net/ssl/ssl_cert_request_info.h"
17
18using base::Time;
19
20namespace net {
21
22namespace {
23
24X509Certificate::PickleType GetPickleTypeForVersion(int version) {
25  switch (version) {
26    case 1:
27      return X509Certificate::PICKLETYPE_SINGLE_CERTIFICATE;
28    case 2:
29      return X509Certificate::PICKLETYPE_CERTIFICATE_CHAIN_V2;
30    case 3:
31    default:
32      return X509Certificate::PICKLETYPE_CERTIFICATE_CHAIN_V3;
33  }
34}
35
36}  // namespace
37
38// These values can be bit-wise combined to form the flags field of the
39// serialized HttpResponseInfo.
40enum {
41  // The version of the response info used when persisting response info.
42  RESPONSE_INFO_VERSION = 3,
43
44  // The minimum version supported for deserializing response info.
45  RESPONSE_INFO_MINIMUM_VERSION = 1,
46
47  // We reserve up to 8 bits for the version number.
48  RESPONSE_INFO_VERSION_MASK = 0xFF,
49
50  // This bit is set if the response info has a cert at the end.
51  // Version 1 serialized only the end-entity certificate, while subsequent
52  // versions include the available certificate chain.
53  RESPONSE_INFO_HAS_CERT = 1 << 8,
54
55  // This bit is set if the response info has a security-bits field (security
56  // strength, in bits, of the SSL connection) at the end.
57  RESPONSE_INFO_HAS_SECURITY_BITS = 1 << 9,
58
59  // This bit is set if the response info has a cert status at the end.
60  RESPONSE_INFO_HAS_CERT_STATUS = 1 << 10,
61
62  // This bit is set if the response info has vary header data.
63  RESPONSE_INFO_HAS_VARY_DATA = 1 << 11,
64
65  // This bit is set if the request was cancelled before completion.
66  RESPONSE_INFO_TRUNCATED = 1 << 12,
67
68  // This bit is set if the response was received via SPDY.
69  RESPONSE_INFO_WAS_SPDY = 1 << 13,
70
71  // This bit is set if the request has NPN negotiated.
72  RESPONSE_INFO_WAS_NPN = 1 << 14,
73
74  // This bit is set if the request was fetched via an explicit proxy.
75  RESPONSE_INFO_WAS_PROXY = 1 << 15,
76
77  // This bit is set if the response info has an SSL connection status field.
78  // This contains the ciphersuite used to fetch the resource as well as the
79  // protocol version, compression method and whether SSLv3 fallback was used.
80  RESPONSE_INFO_HAS_SSL_CONNECTION_STATUS = 1 << 16,
81
82  // This bit is set if the response info has protocol version.
83  RESPONSE_INFO_HAS_NPN_NEGOTIATED_PROTOCOL = 1 << 17,
84
85  // This bit is set if the response info has connection info.
86  RESPONSE_INFO_HAS_CONNECTION_INFO = 1 << 18,
87
88  // This bit is set if the request has http authentication.
89  RESPONSE_INFO_USE_HTTP_AUTHENTICATION = 1 << 19,
90
91  // This bit is set if ssl_info has SCTs.
92  RESPONSE_INFO_HAS_SIGNED_CERTIFICATE_TIMESTAMPS = 1 << 20,
93
94  // TODO(darin): Add other bits to indicate alternate request methods.
95  // For now, we don't support storing those.
96};
97
98HttpResponseInfo::HttpResponseInfo()
99    : was_cached(false),
100      server_data_unavailable(false),
101      network_accessed(false),
102      was_fetched_via_spdy(false),
103      was_npn_negotiated(false),
104      was_fetched_via_proxy(false),
105      did_use_http_auth(false),
106      connection_info(CONNECTION_INFO_UNKNOWN) {
107}
108
109HttpResponseInfo::HttpResponseInfo(const HttpResponseInfo& rhs)
110    : was_cached(rhs.was_cached),
111      server_data_unavailable(rhs.server_data_unavailable),
112      network_accessed(rhs.network_accessed),
113      was_fetched_via_spdy(rhs.was_fetched_via_spdy),
114      was_npn_negotiated(rhs.was_npn_negotiated),
115      was_fetched_via_proxy(rhs.was_fetched_via_proxy),
116      proxy_server(rhs.proxy_server),
117      did_use_http_auth(rhs.did_use_http_auth),
118      socket_address(rhs.socket_address),
119      npn_negotiated_protocol(rhs.npn_negotiated_protocol),
120      connection_info(rhs.connection_info),
121      request_time(rhs.request_time),
122      response_time(rhs.response_time),
123      auth_challenge(rhs.auth_challenge),
124      cert_request_info(rhs.cert_request_info),
125      ssl_info(rhs.ssl_info),
126      headers(rhs.headers),
127      vary_data(rhs.vary_data),
128      metadata(rhs.metadata) {
129}
130
131HttpResponseInfo::~HttpResponseInfo() {
132}
133
134HttpResponseInfo& HttpResponseInfo::operator=(const HttpResponseInfo& rhs) {
135  was_cached = rhs.was_cached;
136  server_data_unavailable = rhs.server_data_unavailable;
137  network_accessed = rhs.network_accessed;
138  was_fetched_via_spdy = rhs.was_fetched_via_spdy;
139  proxy_server = rhs.proxy_server;
140  was_npn_negotiated = rhs.was_npn_negotiated;
141  was_fetched_via_proxy = rhs.was_fetched_via_proxy;
142  did_use_http_auth = rhs.did_use_http_auth;
143  socket_address = rhs.socket_address;
144  npn_negotiated_protocol = rhs.npn_negotiated_protocol;
145  connection_info = rhs.connection_info;
146  request_time = rhs.request_time;
147  response_time = rhs.response_time;
148  auth_challenge = rhs.auth_challenge;
149  cert_request_info = rhs.cert_request_info;
150  ssl_info = rhs.ssl_info;
151  headers = rhs.headers;
152  vary_data = rhs.vary_data;
153  metadata = rhs.metadata;
154  return *this;
155}
156
157bool HttpResponseInfo::InitFromPickle(const Pickle& pickle,
158                                      bool* response_truncated) {
159  PickleIterator iter(pickle);
160
161  // Read flags and verify version
162  int flags;
163  if (!pickle.ReadInt(&iter, &flags))
164    return false;
165  int version = flags & RESPONSE_INFO_VERSION_MASK;
166  if (version < RESPONSE_INFO_MINIMUM_VERSION ||
167      version > RESPONSE_INFO_VERSION) {
168    DLOG(ERROR) << "unexpected response info version: " << version;
169    return false;
170  }
171
172  // Read request-time
173  int64 time_val;
174  if (!pickle.ReadInt64(&iter, &time_val))
175    return false;
176  request_time = Time::FromInternalValue(time_val);
177  was_cached = true;  // Set status to show cache resurrection.
178
179  // Read response-time
180  if (!pickle.ReadInt64(&iter, &time_val))
181    return false;
182  response_time = Time::FromInternalValue(time_val);
183
184  // Read response-headers
185  headers = new HttpResponseHeaders(pickle, &iter);
186  if (headers->response_code() == -1)
187    return false;
188
189  // Read ssl-info
190  if (flags & RESPONSE_INFO_HAS_CERT) {
191    X509Certificate::PickleType type = GetPickleTypeForVersion(version);
192    ssl_info.cert = X509Certificate::CreateFromPickle(pickle, &iter, type);
193    if (!ssl_info.cert.get())
194      return false;
195  }
196  if (flags & RESPONSE_INFO_HAS_CERT_STATUS) {
197    CertStatus cert_status;
198    if (!pickle.ReadUInt32(&iter, &cert_status))
199      return false;
200    ssl_info.cert_status = cert_status;
201  }
202  if (flags & RESPONSE_INFO_HAS_SECURITY_BITS) {
203    int security_bits;
204    if (!pickle.ReadInt(&iter, &security_bits))
205      return false;
206    ssl_info.security_bits = security_bits;
207  }
208
209  if (flags & RESPONSE_INFO_HAS_SSL_CONNECTION_STATUS) {
210    int connection_status;
211    if (!pickle.ReadInt(&iter, &connection_status))
212      return false;
213    ssl_info.connection_status = connection_status;
214  }
215
216  if (flags & RESPONSE_INFO_HAS_SIGNED_CERTIFICATE_TIMESTAMPS) {
217    int num_scts;
218    if (!pickle.ReadInt(&iter, &num_scts))
219      return false;
220    for (int i = 0; i < num_scts; ++i) {
221      scoped_refptr<ct::SignedCertificateTimestamp> sct(
222          ct::SignedCertificateTimestamp::CreateFromPickle(&iter));
223      uint16 status;
224      if (!sct.get() || !pickle.ReadUInt16(&iter, &status))
225        return false;
226      ssl_info.signed_certificate_timestamps.push_back(
227          SignedCertificateTimestampAndStatus(
228              sct, static_cast<ct::SCTVerifyStatus>(status)));
229    }
230  }
231
232  // Read vary-data
233  if (flags & RESPONSE_INFO_HAS_VARY_DATA) {
234    if (!vary_data.InitFromPickle(pickle, &iter))
235      return false;
236  }
237
238  // Read socket_address.
239  std::string socket_address_host;
240  if (pickle.ReadString(&iter, &socket_address_host)) {
241    // If the host was written, we always expect the port to follow.
242    uint16 socket_address_port;
243    if (!pickle.ReadUInt16(&iter, &socket_address_port))
244      return false;
245    socket_address = HostPortPair(socket_address_host, socket_address_port);
246  } else if (version > 1) {
247    // socket_address was not always present in version 1 of the response
248    // info, so we don't fail if it can't be read.
249    return false;
250  }
251
252  // Read protocol-version.
253  if (flags & RESPONSE_INFO_HAS_NPN_NEGOTIATED_PROTOCOL) {
254    if (!pickle.ReadString(&iter, &npn_negotiated_protocol))
255      return false;
256  }
257
258  // Read connection info.
259  if (flags & RESPONSE_INFO_HAS_CONNECTION_INFO) {
260    int value;
261    if (!pickle.ReadInt(&iter, &value))
262      return false;
263
264    if (value > static_cast<int>(CONNECTION_INFO_UNKNOWN) &&
265        value < static_cast<int>(NUM_OF_CONNECTION_INFOS)) {
266      connection_info = static_cast<ConnectionInfo>(value);
267    }
268  }
269
270  was_fetched_via_spdy = (flags & RESPONSE_INFO_WAS_SPDY) != 0;
271
272  was_npn_negotiated = (flags & RESPONSE_INFO_WAS_NPN) != 0;
273
274  was_fetched_via_proxy = (flags & RESPONSE_INFO_WAS_PROXY) != 0;
275
276  *response_truncated = (flags & RESPONSE_INFO_TRUNCATED) != 0;
277
278  did_use_http_auth = (flags & RESPONSE_INFO_USE_HTTP_AUTHENTICATION) != 0;
279
280  return true;
281}
282
283void HttpResponseInfo::Persist(Pickle* pickle,
284                               bool skip_transient_headers,
285                               bool response_truncated) const {
286  int flags = RESPONSE_INFO_VERSION;
287  if (ssl_info.is_valid()) {
288    flags |= RESPONSE_INFO_HAS_CERT;
289    flags |= RESPONSE_INFO_HAS_CERT_STATUS;
290    if (ssl_info.security_bits != -1)
291      flags |= RESPONSE_INFO_HAS_SECURITY_BITS;
292    if (ssl_info.connection_status != 0)
293      flags |= RESPONSE_INFO_HAS_SSL_CONNECTION_STATUS;
294  }
295  if (vary_data.is_valid())
296    flags |= RESPONSE_INFO_HAS_VARY_DATA;
297  if (response_truncated)
298    flags |= RESPONSE_INFO_TRUNCATED;
299  if (was_fetched_via_spdy)
300    flags |= RESPONSE_INFO_WAS_SPDY;
301  if (was_npn_negotiated) {
302    flags |= RESPONSE_INFO_WAS_NPN;
303    flags |= RESPONSE_INFO_HAS_NPN_NEGOTIATED_PROTOCOL;
304  }
305  if (was_fetched_via_proxy)
306    flags |= RESPONSE_INFO_WAS_PROXY;
307  if (connection_info != CONNECTION_INFO_UNKNOWN)
308    flags |= RESPONSE_INFO_HAS_CONNECTION_INFO;
309  if (did_use_http_auth)
310    flags |= RESPONSE_INFO_USE_HTTP_AUTHENTICATION;
311  if (!ssl_info.signed_certificate_timestamps.empty())
312    flags |= RESPONSE_INFO_HAS_SIGNED_CERTIFICATE_TIMESTAMPS;
313
314  pickle->WriteInt(flags);
315  pickle->WriteInt64(request_time.ToInternalValue());
316  pickle->WriteInt64(response_time.ToInternalValue());
317
318  net::HttpResponseHeaders::PersistOptions persist_options =
319      net::HttpResponseHeaders::PERSIST_RAW;
320
321  if (skip_transient_headers) {
322    persist_options =
323        net::HttpResponseHeaders::PERSIST_SANS_COOKIES |
324        net::HttpResponseHeaders::PERSIST_SANS_CHALLENGES |
325        net::HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP |
326        net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE |
327        net::HttpResponseHeaders::PERSIST_SANS_RANGES |
328        net::HttpResponseHeaders::PERSIST_SANS_SECURITY_STATE;
329  }
330
331  headers->Persist(pickle, persist_options);
332
333  if (ssl_info.is_valid()) {
334    ssl_info.cert->Persist(pickle);
335    pickle->WriteUInt32(ssl_info.cert_status);
336    if (ssl_info.security_bits != -1)
337      pickle->WriteInt(ssl_info.security_bits);
338    if (ssl_info.connection_status != 0)
339      pickle->WriteInt(ssl_info.connection_status);
340    if (!ssl_info.signed_certificate_timestamps.empty()) {
341      pickle->WriteInt(ssl_info.signed_certificate_timestamps.size());
342      for (SignedCertificateTimestampAndStatusList::const_iterator it =
343           ssl_info.signed_certificate_timestamps.begin(); it !=
344           ssl_info.signed_certificate_timestamps.end(); ++it) {
345        it->sct->Persist(pickle);
346        pickle->WriteUInt16(it->status);
347      }
348    }
349  }
350
351  if (vary_data.is_valid())
352    vary_data.Persist(pickle);
353
354  pickle->WriteString(socket_address.host());
355  pickle->WriteUInt16(socket_address.port());
356
357  if (was_npn_negotiated)
358    pickle->WriteString(npn_negotiated_protocol);
359
360  if (connection_info != CONNECTION_INFO_UNKNOWN)
361    pickle->WriteInt(static_cast<int>(connection_info));
362}
363
364HttpResponseInfo::ConnectionInfo HttpResponseInfo::ConnectionInfoFromNextProto(
365    NextProto next_proto) {
366  switch (next_proto) {
367    case kProtoDeprecatedSPDY2:
368      return CONNECTION_INFO_DEPRECATED_SPDY2;
369    case kProtoSPDY3:
370    case kProtoSPDY31:
371      return CONNECTION_INFO_SPDY3;
372    case kProtoSPDY4:
373      return CONNECTION_INFO_SPDY4;
374    case kProtoQUIC1SPDY3:
375      return CONNECTION_INFO_QUIC1_SPDY3;
376
377    case kProtoUnknown:
378    case kProtoHTTP11:
379      break;
380  }
381
382  NOTREACHED();
383  return CONNECTION_INFO_UNKNOWN;
384}
385
386// static
387std::string HttpResponseInfo::ConnectionInfoToString(
388    ConnectionInfo connection_info) {
389  switch (connection_info) {
390    case CONNECTION_INFO_UNKNOWN:
391      return "unknown";
392    case CONNECTION_INFO_HTTP1:
393      return "http/1";
394    case CONNECTION_INFO_DEPRECATED_SPDY2:
395      return "spdy/2";
396    case CONNECTION_INFO_SPDY3:
397      return "spdy/3";
398    case CONNECTION_INFO_SPDY4:
399      // This is the HTTP/2 draft 14 identifier. For internal
400      // consistency, HTTP/2 is named SPDY4 within Chromium.
401      return "h2-14";
402    case CONNECTION_INFO_QUIC1_SPDY3:
403      return "quic/1+spdy/3";
404    case NUM_OF_CONNECTION_INFOS:
405      break;
406  }
407  NOTREACHED();
408  return "";
409}
410
411}  // namespace net
412