1/*
2 *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include <time.h>
12#include <algorithm>
13#include "webrtc/base/asyncsocket.h"
14#include "webrtc/base/common.h"
15#include "webrtc/base/diskcache.h"
16#include "webrtc/base/httpclient.h"
17#include "webrtc/base/httpcommon-inl.h"
18#include "webrtc/base/logging.h"
19#include "webrtc/base/pathutils.h"
20#include "webrtc/base/scoped_ptr.h"
21#include "webrtc/base/socketstream.h"
22#include "webrtc/base/stringencode.h"
23#include "webrtc/base/stringutils.h"
24#include "webrtc/base/thread.h"
25
26namespace rtc {
27
28//////////////////////////////////////////////////////////////////////
29// Helpers
30//////////////////////////////////////////////////////////////////////
31
32namespace {
33
34const size_t kCacheHeader = 0;
35const size_t kCacheBody = 1;
36
37// Convert decimal string to integer
38bool HttpStringToUInt(const std::string& str, size_t* val) {
39  ASSERT(NULL != val);
40  char* eos = NULL;
41  *val = strtoul(str.c_str(), &eos, 10);
42  return (*eos == '\0');
43}
44
45bool HttpShouldCache(const HttpTransaction& t) {
46  bool verb_allows_cache = (t.request.verb == HV_GET)
47                           || (t.request.verb == HV_HEAD);
48  bool is_range_response = t.response.hasHeader(HH_CONTENT_RANGE, NULL);
49  bool has_expires = t.response.hasHeader(HH_EXPIRES, NULL);
50  bool request_allows_cache =
51    has_expires || (std::string::npos != t.request.path.find('?'));
52  bool response_allows_cache =
53    has_expires || HttpCodeIsCacheable(t.response.scode);
54
55  bool may_cache = verb_allows_cache
56                   && request_allows_cache
57                   && response_allows_cache
58                   && !is_range_response;
59
60  std::string value;
61  if (t.response.hasHeader(HH_CACHE_CONTROL, &value)) {
62    HttpAttributeList directives;
63    HttpParseAttributes(value.data(), value.size(), directives);
64    // Response Directives Summary:
65    // public - always cacheable
66    // private - do not cache in a shared cache
67    // no-cache - may cache, but must revalidate whether fresh or stale
68    // no-store - sensitive information, do not cache or store in any way
69    // max-age - supplants Expires for staleness
70    // s-maxage - use as max-age for shared caches, ignore otherwise
71    // must-revalidate - may cache, but must revalidate after stale
72    // proxy-revalidate - shared cache must revalidate
73    if (HttpHasAttribute(directives, "no-store", NULL)) {
74      may_cache = false;
75    } else if (HttpHasAttribute(directives, "public", NULL)) {
76      may_cache = true;
77    }
78  }
79  return may_cache;
80}
81
82enum HttpCacheState {
83  HCS_FRESH,  // In cache, may use
84  HCS_STALE,  // In cache, must revalidate
85  HCS_NONE    // Not in cache
86};
87
88HttpCacheState HttpGetCacheState(const HttpTransaction& t) {
89  // Temporaries
90  std::string s_temp;
91  time_t u_temp;
92
93  // Current time
94  time_t now = time(0);
95
96  HttpAttributeList cache_control;
97  if (t.response.hasHeader(HH_CACHE_CONTROL, &s_temp)) {
98    HttpParseAttributes(s_temp.data(), s_temp.size(), cache_control);
99  }
100
101  // Compute age of cache document
102  time_t date;
103  if (!t.response.hasHeader(HH_DATE, &s_temp)
104      || !HttpDateToSeconds(s_temp, &date))
105    return HCS_NONE;
106
107  // TODO: Timestamp when cache request sent and response received?
108  time_t request_time = date;
109  time_t response_time = date;
110
111  time_t apparent_age = 0;
112  if (response_time > date) {
113    apparent_age = response_time - date;
114  }
115
116  time_t corrected_received_age = apparent_age;
117  size_t i_temp;
118  if (t.response.hasHeader(HH_AGE, &s_temp)
119      && HttpStringToUInt(s_temp, (&i_temp))) {
120    u_temp = static_cast<time_t>(i_temp);
121    corrected_received_age = std::max(apparent_age, u_temp);
122  }
123
124  time_t response_delay = response_time - request_time;
125  time_t corrected_initial_age = corrected_received_age + response_delay;
126  time_t resident_time = now - response_time;
127  time_t current_age = corrected_initial_age + resident_time;
128
129  // Compute lifetime of document
130  time_t lifetime;
131  if (HttpHasAttribute(cache_control, "max-age", &s_temp)) {
132    lifetime = atoi(s_temp.c_str());
133  } else if (t.response.hasHeader(HH_EXPIRES, &s_temp)
134             && HttpDateToSeconds(s_temp, &u_temp)) {
135    lifetime = u_temp - date;
136  } else if (t.response.hasHeader(HH_LAST_MODIFIED, &s_temp)
137             && HttpDateToSeconds(s_temp, &u_temp)) {
138    // TODO: Issue warning 113 if age > 24 hours
139    lifetime = static_cast<size_t>(now - u_temp) / 10;
140  } else {
141    return HCS_STALE;
142  }
143
144  return (lifetime > current_age) ? HCS_FRESH : HCS_STALE;
145}
146
147enum HttpValidatorStrength {
148  HVS_NONE,
149  HVS_WEAK,
150  HVS_STRONG
151};
152
153HttpValidatorStrength
154HttpRequestValidatorLevel(const HttpRequestData& request) {
155  if (HV_GET != request.verb)
156    return HVS_STRONG;
157  return request.hasHeader(HH_RANGE, NULL) ? HVS_STRONG : HVS_WEAK;
158}
159
160HttpValidatorStrength
161HttpResponseValidatorLevel(const HttpResponseData& response) {
162  std::string value;
163  if (response.hasHeader(HH_ETAG, &value)) {
164    bool is_weak = (strnicmp(value.c_str(), "W/", 2) == 0);
165    return is_weak ? HVS_WEAK : HVS_STRONG;
166  }
167  if (response.hasHeader(HH_LAST_MODIFIED, &value)) {
168    time_t last_modified, date;
169    if (HttpDateToSeconds(value, &last_modified)
170        && response.hasHeader(HH_DATE, &value)
171        && HttpDateToSeconds(value, &date)
172        && (last_modified + 60 < date)) {
173      return HVS_STRONG;
174    }
175    return HVS_WEAK;
176  }
177  return HVS_NONE;
178}
179
180std::string GetCacheID(const HttpRequestData& request) {
181  std::string id, url;
182  id.append(ToString(request.verb));
183  id.append("_");
184  request.getAbsoluteUri(&url);
185  id.append(url);
186  return id;
187}
188
189}  // anonymous namespace
190
191//////////////////////////////////////////////////////////////////////
192// Public Helpers
193//////////////////////////////////////////////////////////////////////
194
195bool HttpWriteCacheHeaders(const HttpResponseData* response,
196                           StreamInterface* output, size_t* size) {
197  size_t length = 0;
198  // Write all unknown and end-to-end headers to a cache file
199  for (HttpData::const_iterator it = response->begin();
200       it != response->end(); ++it) {
201    HttpHeader header;
202    if (FromString(header, it->first) && !HttpHeaderIsEndToEnd(header))
203      continue;
204    length += it->first.length() + 2 + it->second.length() + 2;
205    if (!output)
206      continue;
207    std::string formatted_header(it->first);
208    formatted_header.append(": ");
209    formatted_header.append(it->second);
210    formatted_header.append("\r\n");
211    StreamResult result = output->WriteAll(formatted_header.data(),
212                                           formatted_header.length(),
213                                           NULL, NULL);
214    if (SR_SUCCESS != result) {
215      return false;
216    }
217  }
218  if (output && (SR_SUCCESS != output->WriteAll("\r\n", 2, NULL, NULL))) {
219    return false;
220  }
221  length += 2;
222  if (size)
223    *size = length;
224  return true;
225}
226
227bool HttpReadCacheHeaders(StreamInterface* input, HttpResponseData* response,
228                          HttpData::HeaderCombine combine) {
229  while (true) {
230    std::string formatted_header;
231    StreamResult result = input->ReadLine(&formatted_header);
232    if ((SR_EOS == result) || (1 == formatted_header.size())) {
233      break;
234    }
235    if (SR_SUCCESS != result) {
236      return false;
237    }
238    size_t end_of_name = formatted_header.find(':');
239    if (std::string::npos == end_of_name) {
240      LOG_F(LS_WARNING) << "Malformed cache header";
241      continue;
242    }
243    size_t start_of_value = end_of_name + 1;
244    size_t end_of_value = formatted_header.length();
245    while ((start_of_value < end_of_value)
246           && isspace(formatted_header[start_of_value]))
247      ++start_of_value;
248    while ((start_of_value < end_of_value)
249           && isspace(formatted_header[end_of_value-1]))
250     --end_of_value;
251    size_t value_length = end_of_value - start_of_value;
252
253    std::string name(formatted_header.substr(0, end_of_name));
254    std::string value(formatted_header.substr(start_of_value, value_length));
255    response->changeHeader(name, value, combine);
256  }
257  return true;
258}
259
260//////////////////////////////////////////////////////////////////////
261// HttpClient
262//////////////////////////////////////////////////////////////////////
263
264const size_t kDefaultRetries = 1;
265const size_t kMaxRedirects = 5;
266
267HttpClient::HttpClient(const std::string& agent, StreamPool* pool,
268                       HttpTransaction* transaction)
269    : agent_(agent), pool_(pool),
270      transaction_(transaction), free_transaction_(false),
271      retries_(kDefaultRetries), attempt_(0), redirects_(0),
272      redirect_action_(REDIRECT_DEFAULT),
273      uri_form_(URI_DEFAULT), cache_(NULL), cache_state_(CS_READY),
274      resolver_(NULL) {
275  base_.notify(this);
276  if (NULL == transaction_) {
277    free_transaction_ = true;
278    transaction_ = new HttpTransaction;
279  }
280}
281
282HttpClient::~HttpClient() {
283  base_.notify(NULL);
284  base_.abort(HE_SHUTDOWN);
285  if (resolver_) {
286    resolver_->Destroy(false);
287  }
288  release();
289  if (free_transaction_)
290    delete transaction_;
291}
292
293void HttpClient::reset() {
294  server_.Clear();
295  request().clear(true);
296  response().clear(true);
297  context_.reset();
298  redirects_ = 0;
299  base_.abort(HE_OPERATION_CANCELLED);
300}
301
302void HttpClient::OnResolveResult(AsyncResolverInterface* resolver) {
303  if (resolver != resolver_) {
304    return;
305  }
306  int error = resolver_->GetError();
307  server_ = resolver_->address();
308  resolver_->Destroy(false);
309  resolver_ = NULL;
310  if (error != 0) {
311    LOG(LS_ERROR) << "Error " << error << " resolving name: "
312                  << server_;
313    onHttpComplete(HM_CONNECT, HE_CONNECT_FAILED);
314  } else {
315    connect();
316  }
317}
318
319void HttpClient::StartDNSLookup() {
320  resolver_ = new AsyncResolver();
321  resolver_->SignalDone.connect(this, &HttpClient::OnResolveResult);
322  resolver_->Start(server_);
323}
324
325void HttpClient::set_server(const SocketAddress& address) {
326  server_ = address;
327  // Setting 'Host' here allows it to be overridden before starting the request,
328  // if necessary.
329  request().setHeader(HH_HOST, HttpAddress(server_, false), true);
330}
331
332StreamInterface* HttpClient::GetDocumentStream() {
333  return base_.GetDocumentStream();
334}
335
336void HttpClient::start() {
337  if (base_.mode() != HM_NONE) {
338    // call reset() to abort an in-progress request
339    ASSERT(false);
340    return;
341  }
342
343  ASSERT(!IsCacheActive());
344
345  if (request().hasHeader(HH_TRANSFER_ENCODING, NULL)) {
346    // Exact size must be known on the client.  Instead of using chunked
347    // encoding, wrap data with auto-caching file or memory stream.
348    ASSERT(false);
349    return;
350  }
351
352  attempt_ = 0;
353
354  // If no content has been specified, using length of 0.
355  request().setHeader(HH_CONTENT_LENGTH, "0", false);
356
357  if (!agent_.empty()) {
358    request().setHeader(HH_USER_AGENT, agent_, false);
359  }
360
361  UriForm uri_form = uri_form_;
362  if (PROXY_HTTPS == proxy_.type) {
363    // Proxies require absolute form
364    uri_form = URI_ABSOLUTE;
365    request().version = HVER_1_0;
366    request().setHeader(HH_PROXY_CONNECTION, "Keep-Alive", false);
367  } else {
368    request().setHeader(HH_CONNECTION, "Keep-Alive", false);
369  }
370
371  if (URI_ABSOLUTE == uri_form) {
372    // Convert to absolute uri form
373    std::string url;
374    if (request().getAbsoluteUri(&url)) {
375      request().path = url;
376    } else {
377      LOG(LS_WARNING) << "Couldn't obtain absolute uri";
378    }
379  } else if (URI_RELATIVE == uri_form) {
380    // Convert to relative uri form
381    std::string host, path;
382    if (request().getRelativeUri(&host, &path)) {
383      request().setHeader(HH_HOST, host);
384      request().path = path;
385    } else {
386      LOG(LS_WARNING) << "Couldn't obtain relative uri";
387    }
388  }
389
390  if ((NULL != cache_) && CheckCache()) {
391    return;
392  }
393
394  connect();
395}
396
397void HttpClient::connect() {
398  int stream_err;
399  if (server_.IsUnresolvedIP()) {
400    StartDNSLookup();
401    return;
402  }
403  StreamInterface* stream = pool_->RequestConnectedStream(server_, &stream_err);
404  if (stream == NULL) {
405    ASSERT(0 != stream_err);
406    LOG(LS_ERROR) << "RequestConnectedStream error: " << stream_err;
407    onHttpComplete(HM_CONNECT, HE_CONNECT_FAILED);
408  } else {
409    base_.attach(stream);
410    if (stream->GetState() == SS_OPEN) {
411      base_.send(&transaction_->request);
412    }
413  }
414}
415
416void HttpClient::prepare_get(const std::string& url) {
417  reset();
418  Url<char> purl(url);
419  set_server(SocketAddress(purl.host(), purl.port()));
420  request().verb = HV_GET;
421  request().path = purl.full_path();
422}
423
424void HttpClient::prepare_post(const std::string& url,
425                              const std::string& content_type,
426                              StreamInterface* request_doc) {
427  reset();
428  Url<char> purl(url);
429  set_server(SocketAddress(purl.host(), purl.port()));
430  request().verb = HV_POST;
431  request().path = purl.full_path();
432  request().setContent(content_type, request_doc);
433}
434
435void HttpClient::release() {
436  if (StreamInterface* stream = base_.detach()) {
437    pool_->ReturnConnectedStream(stream);
438  }
439}
440
441bool HttpClient::ShouldRedirect(std::string* location) const {
442  // TODO: Unittest redirection.
443  if ((REDIRECT_NEVER == redirect_action_)
444      || !HttpCodeIsRedirection(response().scode)
445      || !response().hasHeader(HH_LOCATION, location)
446      || (redirects_ >= kMaxRedirects))
447    return false;
448  return (REDIRECT_ALWAYS == redirect_action_)
449         || (HC_SEE_OTHER == response().scode)
450         || (HV_HEAD == request().verb)
451         || (HV_GET == request().verb);
452}
453
454bool HttpClient::BeginCacheFile() {
455  ASSERT(NULL != cache_);
456  ASSERT(CS_READY == cache_state_);
457
458  std::string id = GetCacheID(request());
459  CacheLock lock(cache_, id, true);
460  if (!lock.IsLocked()) {
461    LOG_F(LS_WARNING) << "Couldn't lock cache";
462    return false;
463  }
464
465  if (HE_NONE != WriteCacheHeaders(id)) {
466    return false;
467  }
468
469  scoped_ptr<StreamInterface> stream(cache_->WriteResource(id, kCacheBody));
470  if (!stream) {
471    LOG_F(LS_ERROR) << "Couldn't open body cache";
472    return false;
473  }
474  lock.Commit();
475
476  // Let's secretly replace the response document with Folgers Crystals,
477  // er, StreamTap, so that we can mirror the data to our cache.
478  StreamInterface* output = response().document.release();
479  if (!output) {
480    output = new NullStream;
481  }
482  StreamTap* tap = new StreamTap(output, stream.release());
483  response().document.reset(tap);
484  return true;
485}
486
487HttpError HttpClient::WriteCacheHeaders(const std::string& id) {
488  scoped_ptr<StreamInterface> stream(cache_->WriteResource(id, kCacheHeader));
489  if (!stream) {
490    LOG_F(LS_ERROR) << "Couldn't open header cache";
491    return HE_CACHE;
492  }
493
494  if (!HttpWriteCacheHeaders(&transaction_->response, stream.get(), NULL)) {
495    LOG_F(LS_ERROR) << "Couldn't write header cache";
496    return HE_CACHE;
497  }
498
499  return HE_NONE;
500}
501
502void HttpClient::CompleteCacheFile() {
503  // Restore previous response document
504  StreamTap* tap = static_cast<StreamTap*>(response().document.release());
505  response().document.reset(tap->Detach());
506
507  int error;
508  StreamResult result = tap->GetTapResult(&error);
509
510  // Delete the tap and cache stream (which completes cache unlock)
511  delete tap;
512
513  if (SR_SUCCESS != result) {
514    LOG(LS_ERROR) << "Cache file error: " << error;
515    cache_->DeleteResource(GetCacheID(request()));
516  }
517}
518
519bool HttpClient::CheckCache() {
520  ASSERT(NULL != cache_);
521  ASSERT(CS_READY == cache_state_);
522
523  std::string id = GetCacheID(request());
524  if (!cache_->HasResource(id)) {
525    // No cache file available
526    return false;
527  }
528
529  HttpError error = ReadCacheHeaders(id, true);
530
531  if (HE_NONE == error) {
532    switch (HttpGetCacheState(*transaction_)) {
533    case HCS_FRESH:
534      // Cache content is good, read from cache
535      break;
536    case HCS_STALE:
537      // Cache content may be acceptable.  Issue a validation request.
538      if (PrepareValidate()) {
539        return false;
540      }
541      // Couldn't validate, fall through.
542      FALLTHROUGH();
543    case HCS_NONE:
544      // Cache content is not useable.  Issue a regular request.
545      response().clear(false);
546      return false;
547    }
548  }
549
550  if (HE_NONE == error) {
551    error = ReadCacheBody(id);
552    cache_state_ = CS_READY;
553  }
554
555  if (HE_CACHE == error) {
556    LOG_F(LS_WARNING) << "Cache failure, continuing with normal request";
557    response().clear(false);
558    return false;
559  }
560
561  SignalHttpClientComplete(this, error);
562  return true;
563}
564
565HttpError HttpClient::ReadCacheHeaders(const std::string& id, bool override) {
566  scoped_ptr<StreamInterface> stream(cache_->ReadResource(id, kCacheHeader));
567  if (!stream) {
568    return HE_CACHE;
569  }
570
571  HttpData::HeaderCombine combine =
572    override ? HttpData::HC_REPLACE : HttpData::HC_AUTO;
573
574  if (!HttpReadCacheHeaders(stream.get(), &transaction_->response, combine)) {
575    LOG_F(LS_ERROR) << "Error reading cache headers";
576    return HE_CACHE;
577  }
578
579  response().scode = HC_OK;
580  return HE_NONE;
581}
582
583HttpError HttpClient::ReadCacheBody(const std::string& id) {
584  cache_state_ = CS_READING;
585
586  HttpError error = HE_NONE;
587
588  size_t data_size;
589  scoped_ptr<StreamInterface> stream(cache_->ReadResource(id, kCacheBody));
590  if (!stream || !stream->GetAvailable(&data_size)) {
591    LOG_F(LS_ERROR) << "Unavailable cache body";
592    error = HE_CACHE;
593  } else {
594    error = OnHeaderAvailable(false, false, data_size);
595  }
596
597  if ((HE_NONE == error)
598      && (HV_HEAD != request().verb)
599      && response().document) {
600    // Allocate on heap to not explode the stack.
601    const int array_size = 1024 * 64;
602    scoped_ptr<char[]> buffer(new char[array_size]);
603    StreamResult result = Flow(stream.get(), buffer.get(), array_size,
604                               response().document.get());
605    if (SR_SUCCESS != result) {
606      error = HE_STREAM;
607    }
608  }
609
610  return error;
611}
612
613bool HttpClient::PrepareValidate() {
614  ASSERT(CS_READY == cache_state_);
615  // At this point, request() contains the pending request, and response()
616  // contains the cached response headers.  Reformat the request to validate
617  // the cached content.
618  HttpValidatorStrength vs_required = HttpRequestValidatorLevel(request());
619  HttpValidatorStrength vs_available = HttpResponseValidatorLevel(response());
620  if (vs_available < vs_required) {
621    return false;
622  }
623  std::string value;
624  if (response().hasHeader(HH_ETAG, &value)) {
625    request().addHeader(HH_IF_NONE_MATCH, value);
626  }
627  if (response().hasHeader(HH_LAST_MODIFIED, &value)) {
628    request().addHeader(HH_IF_MODIFIED_SINCE, value);
629  }
630  response().clear(false);
631  cache_state_ = CS_VALIDATING;
632  return true;
633}
634
635HttpError HttpClient::CompleteValidate() {
636  ASSERT(CS_VALIDATING == cache_state_);
637
638  std::string id = GetCacheID(request());
639
640  // Merge cached headers with new headers
641  HttpError error = ReadCacheHeaders(id, false);
642  if (HE_NONE != error) {
643    // Rewrite merged headers to cache
644    CacheLock lock(cache_, id);
645    error = WriteCacheHeaders(id);
646  }
647  if (HE_NONE != error) {
648    error = ReadCacheBody(id);
649  }
650  return error;
651}
652
653HttpError HttpClient::OnHeaderAvailable(bool ignore_data, bool chunked,
654                                        size_t data_size) {
655  // If we are ignoring the data, this is an intermediate header.
656  // TODO: don't signal intermediate headers.  Instead, do all header-dependent
657  // processing now, and either set up the next request, or fail outright.
658  // TODO: by default, only write response documents with a success code.
659  SignalHeaderAvailable(this, !ignore_data, ignore_data ? 0 : data_size);
660  if (!ignore_data && !chunked && (data_size != SIZE_UNKNOWN)
661      && response().document) {
662    // Attempt to pre-allocate space for the downloaded data.
663    if (!response().document->ReserveSize(data_size)) {
664      return HE_OVERFLOW;
665    }
666  }
667  return HE_NONE;
668}
669
670//
671// HttpBase Implementation
672//
673
674HttpError HttpClient::onHttpHeaderComplete(bool chunked, size_t& data_size) {
675  if (CS_VALIDATING == cache_state_) {
676    if (HC_NOT_MODIFIED == response().scode) {
677      return CompleteValidate();
678    }
679    // Should we remove conditional headers from request?
680    cache_state_ = CS_READY;
681    cache_->DeleteResource(GetCacheID(request()));
682    // Continue processing response as normal
683  }
684
685  ASSERT(!IsCacheActive());
686  if ((request().verb == HV_HEAD) || !HttpCodeHasBody(response().scode)) {
687    // HEAD requests and certain response codes contain no body
688    data_size = 0;
689  }
690  if (ShouldRedirect(NULL)
691      || ((HC_PROXY_AUTHENTICATION_REQUIRED == response().scode)
692          && (PROXY_HTTPS == proxy_.type))) {
693    // We're going to issue another request, so ignore the incoming data.
694    base_.set_ignore_data(true);
695  }
696
697  HttpError error = OnHeaderAvailable(base_.ignore_data(), chunked, data_size);
698  if (HE_NONE != error) {
699    return error;
700  }
701
702  if ((NULL != cache_)
703      && !base_.ignore_data()
704      && HttpShouldCache(*transaction_)) {
705    if (BeginCacheFile()) {
706      cache_state_ = CS_WRITING;
707    }
708  }
709  return HE_NONE;
710}
711
712void HttpClient::onHttpComplete(HttpMode mode, HttpError err) {
713  if (((HE_DISCONNECTED == err) || (HE_CONNECT_FAILED == err)
714       || (HE_SOCKET_ERROR == err))
715      && (HC_INTERNAL_SERVER_ERROR == response().scode)
716      && (attempt_ < retries_)) {
717    // If the response code has not changed from the default, then we haven't
718    // received anything meaningful from the server, so we are eligible for a
719    // retry.
720    ++attempt_;
721    if (request().document && !request().document->Rewind()) {
722      // Unable to replay the request document.
723      err = HE_STREAM;
724    } else {
725      release();
726      connect();
727      return;
728    }
729  } else if (err != HE_NONE) {
730    // fall through
731  } else if (mode == HM_CONNECT) {
732    base_.send(&transaction_->request);
733    return;
734  } else if ((mode == HM_SEND) || HttpCodeIsInformational(response().scode)) {
735    // If you're interested in informational headers, catch
736    // SignalHeaderAvailable.
737    base_.recv(&transaction_->response);
738    return;
739  } else {
740    if (!HttpShouldKeepAlive(response())) {
741      LOG(LS_VERBOSE) << "HttpClient: closing socket";
742      base_.stream()->Close();
743    }
744    std::string location;
745    if (ShouldRedirect(&location)) {
746      Url<char> purl(location);
747      set_server(SocketAddress(purl.host(), purl.port()));
748      request().path = purl.full_path();
749      if (response().scode == HC_SEE_OTHER) {
750        request().verb = HV_GET;
751        request().clearHeader(HH_CONTENT_TYPE);
752        request().clearHeader(HH_CONTENT_LENGTH);
753        request().document.reset();
754      } else if (request().document && !request().document->Rewind()) {
755        // Unable to replay the request document.
756        ASSERT(REDIRECT_ALWAYS == redirect_action_);
757        err = HE_STREAM;
758      }
759      if (err == HE_NONE) {
760        ++redirects_;
761        context_.reset();
762        response().clear(false);
763        release();
764        start();
765        return;
766      }
767    } else if ((HC_PROXY_AUTHENTICATION_REQUIRED == response().scode)
768               && (PROXY_HTTPS == proxy_.type)) {
769      std::string authorization, auth_method;
770      HttpData::const_iterator begin = response().begin(HH_PROXY_AUTHENTICATE);
771      HttpData::const_iterator end = response().end(HH_PROXY_AUTHENTICATE);
772      for (HttpData::const_iterator it = begin; it != end; ++it) {
773        HttpAuthContext *context = context_.get();
774        HttpAuthResult res = HttpAuthenticate(
775          it->second.data(), it->second.size(),
776          proxy_.address,
777          ToString(request().verb), request().path,
778          proxy_.username, proxy_.password,
779          context, authorization, auth_method);
780        context_.reset(context);
781        if (res == HAR_RESPONSE) {
782          request().setHeader(HH_PROXY_AUTHORIZATION, authorization);
783          if (request().document && !request().document->Rewind()) {
784            err = HE_STREAM;
785          } else {
786            // Explicitly do not reset the HttpAuthContext
787            response().clear(false);
788            // TODO: Reuse socket when authenticating?
789            release();
790            start();
791            return;
792          }
793        } else if (res == HAR_IGNORE) {
794          LOG(INFO) << "Ignoring Proxy-Authenticate: " << auth_method;
795          continue;
796        } else {
797          break;
798        }
799      }
800    }
801  }
802  if (CS_WRITING == cache_state_) {
803    CompleteCacheFile();
804    cache_state_ = CS_READY;
805  } else if (CS_READING == cache_state_) {
806    cache_state_ = CS_READY;
807  }
808  release();
809  SignalHttpClientComplete(this, err);
810}
811
812void HttpClient::onHttpClosed(HttpError err) {
813  // This shouldn't occur, since we return the stream to the pool upon command
814  // completion.
815  ASSERT(false);
816}
817
818//////////////////////////////////////////////////////////////////////
819// HttpClientDefault
820//////////////////////////////////////////////////////////////////////
821
822HttpClientDefault::HttpClientDefault(SocketFactory* factory,
823                                     const std::string& agent,
824                                     HttpTransaction* transaction)
825    : ReuseSocketPool(factory ? factory : Thread::Current()->socketserver()),
826      HttpClient(agent, NULL, transaction) {
827  set_pool(this);
828}
829
830//////////////////////////////////////////////////////////////////////
831
832}  // namespace rtc
833