url_request_redirect_job.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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/url_request/url_request_redirect_job.h"
6
7#include <string>
8
9#include "base/bind.h"
10#include "base/compiler_specific.h"
11#include "base/logging.h"
12#include "base/message_loop/message_loop.h"
13#include "base/strings/stringprintf.h"
14#include "net/base/load_timing_info.h"
15#include "net/base/net_errors.h"
16#include "net/base/net_log.h"
17#include "net/http/http_response_headers.h"
18#include "net/http/http_util.h"
19#include "net/url_request/url_request.h"
20
21namespace net {
22
23URLRequestRedirectJob::URLRequestRedirectJob(URLRequest* request,
24                                             NetworkDelegate* network_delegate,
25                                             const GURL& redirect_destination,
26                                             ResponseCode response_code,
27                                             const std::string& redirect_reason)
28    : URLRequestJob(request, network_delegate),
29      redirect_destination_(redirect_destination),
30      response_code_(response_code),
31      redirect_reason_(redirect_reason),
32      weak_factory_(this) {
33  DCHECK(!redirect_reason_.empty());
34}
35
36void URLRequestRedirectJob::GetResponseInfo(HttpResponseInfo* info) {
37  // Should only be called after the URLRequest has been notified there's header
38  // information.
39  DCHECK(fake_headers_.get());
40
41  // This assumes |info| is a freshly constructed HttpResponseInfo.
42  info->headers = fake_headers_;
43  info->request_time = response_time_;
44  info->response_time = response_time_;
45}
46
47void URLRequestRedirectJob::GetLoadTimingInfo(
48    LoadTimingInfo* load_timing_info) const {
49  // Set send_start and send_end to receive_headers_end_ to be consistent
50  // with network cache behavior.
51  load_timing_info->send_start = receive_headers_end_;
52  load_timing_info->send_end = receive_headers_end_;
53  load_timing_info->receive_headers_end = receive_headers_end_;
54}
55
56void URLRequestRedirectJob::Start() {
57  request()->net_log().AddEvent(
58      NetLog::TYPE_URL_REQUEST_REDIRECT_JOB,
59      NetLog::StringCallback("reason", &redirect_reason_));
60  base::MessageLoop::current()->PostTask(
61      FROM_HERE,
62      base::Bind(&URLRequestRedirectJob::StartAsync,
63                 weak_factory_.GetWeakPtr()));
64}
65
66bool URLRequestRedirectJob::CopyFragmentOnRedirect(const GURL& location) const {
67  // The instantiators have full control over the desired redirection target,
68  // including the reference fragment part of the URL.
69  return false;
70}
71
72int URLRequestRedirectJob::GetResponseCode() const {
73  // Should only be called after the URLRequest has been notified there's header
74  // information.
75  DCHECK(fake_headers_.get());
76  return response_code_;
77}
78
79URLRequestRedirectJob::~URLRequestRedirectJob() {}
80
81void URLRequestRedirectJob::StartAsync() {
82  receive_headers_end_ = base::TimeTicks::Now();
83  response_time_ = base::Time::Now();
84
85  std::string header_string =
86      base::StringPrintf("HTTP/1.1 %i Internal Redirect\n"
87                             "Location: %s\n"
88                             "Non-Authoritative-Reason: %s",
89                         response_code_,
90                         redirect_destination_.spec().c_str(),
91                         redirect_reason_.c_str());
92
93  std::string http_origin;
94  const net::HttpRequestHeaders& request_headers =
95      request_->extra_request_headers();
96  if (request_headers.GetHeader("Origin", &http_origin)) {
97    // If this redirect is used in a cross-origin request, add CORS headers to
98    // make sure that the redirect gets through. Note that the destination URL
99    // is still subject to the usual CORS policy, i.e. the resource will only
100    // be available to web pages if the server serves the response with the
101    // required CORS response headers.
102    header_string += base::StringPrintf(
103        "\n"
104        "Access-Control-Allow-Origin: %s\n"
105        "Access-Control-Allow-Credentials: true",
106        http_origin.c_str());
107  }
108
109  fake_headers_ = new HttpResponseHeaders(
110      HttpUtil::AssembleRawHeaders(header_string.c_str(),
111                                   header_string.length()));
112  DCHECK(fake_headers_->IsRedirect(NULL));
113
114  request()->net_log().AddEvent(
115      NetLog::TYPE_URL_REQUEST_FAKE_RESPONSE_HEADERS_CREATED,
116      base::Bind(
117          &HttpResponseHeaders::NetLogCallback,
118          base::Unretained(fake_headers_.get())));
119
120  // TODO(mmenke):  Consider calling the NetworkDelegate with the headers here.
121  // There's some weirdness about how to handle the case in which the delegate
122  // tries to modify the redirect location, in terms of how IsSafeRedirect
123  // should behave, and whether the fragment should be copied.
124  URLRequestJob::NotifyHeadersComplete();
125}
126
127}  // namespace net
128