| OLD | NEW |
| (Empty) |
| 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 // This job type handles Chrome plugin network interception. When a plugin | |
| 6 // wants to intercept a request, a job of this type is created. The intercept | |
| 7 // job communicates with the plugin to retrieve the response headers and data. | |
| 8 | |
| 9 #include "chrome/common/net/url_request_intercept_job.h" | |
| 10 | |
| 11 #include <vector> | |
| 12 | |
| 13 #include "base/compiler_specific.h" | |
| 14 #include "base/message_loop.h" | |
| 15 #include "base/string_util.h" | |
| 16 #include "chrome/common/chrome_plugin_lib.h" | |
| 17 #include "chrome/common/notification_source.h" | |
| 18 #include "net/base/io_buffer.h" | |
| 19 #include "net/base/net_errors.h" | |
| 20 #include "net/base/x509_certificate.h" | |
| 21 #include "net/http/http_response_headers.h" | |
| 22 #include "net/url_request/url_request.h" | |
| 23 | |
| 24 using base::Time; | |
| 25 using base::TimeDelta; | |
| 26 | |
| 27 // | |
| 28 // URLRequestInterceptJob | |
| 29 // | |
| 30 | |
| 31 URLRequestInterceptJob::URLRequestInterceptJob(net::URLRequest* request, | |
| 32 ChromePluginLib* plugin, | |
| 33 ScopableCPRequest* cprequest) | |
| 34 : net::URLRequestJob(request), | |
| 35 cprequest_(cprequest), | |
| 36 plugin_(plugin), | |
| 37 read_buffer_(NULL), | |
| 38 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { | |
| 39 cprequest_->data = this; // see FromCPRequest(). | |
| 40 | |
| 41 registrar_.Add(this, NotificationType::CHROME_PLUGIN_UNLOADED, | |
| 42 Source<ChromePluginLib>(plugin_)); | |
| 43 } | |
| 44 | |
| 45 URLRequestInterceptJob::~URLRequestInterceptJob() { | |
| 46 if (plugin_) { | |
| 47 plugin_->functions().request_funcs->end_request(cprequest_.get(), | |
| 48 CPERR_SUCCESS); | |
| 49 } | |
| 50 } | |
| 51 | |
| 52 void URLRequestInterceptJob::DetachPlugin() { | |
| 53 registrar_.RemoveAll(); | |
| 54 plugin_ = NULL; | |
| 55 } | |
| 56 | |
| 57 void URLRequestInterceptJob::Start() { | |
| 58 // Start reading asynchronously so that all error reporting and data | |
| 59 // callbacks happen as they would for network requests. | |
| 60 MessageLoop::current()->PostTask( | |
| 61 FROM_HERE, | |
| 62 method_factory_.NewRunnableMethod( | |
| 63 &URLRequestInterceptJob::StartAsync)); | |
| 64 } | |
| 65 | |
| 66 void URLRequestInterceptJob::Kill() { | |
| 67 if (plugin_) { | |
| 68 plugin_->functions().request_funcs->end_request(cprequest_.get(), | |
| 69 CPERR_CANCELLED); | |
| 70 DetachPlugin(); | |
| 71 } | |
| 72 net::URLRequestJob::Kill(); | |
| 73 method_factory_.RevokeAll(); | |
| 74 } | |
| 75 | |
| 76 bool URLRequestInterceptJob::ReadRawData(net::IOBuffer* dest, int dest_size, | |
| 77 int* bytes_read) { | |
| 78 DCHECK_NE(dest_size, 0); | |
| 79 DCHECK(bytes_read); | |
| 80 | |
| 81 if (!plugin_) | |
| 82 return false; | |
| 83 | |
| 84 int rv = plugin_->functions().request_funcs->read(cprequest_.get(), | |
| 85 dest->data(), dest_size); | |
| 86 if (rv >= 0) { | |
| 87 *bytes_read = rv; | |
| 88 return true; | |
| 89 } | |
| 90 | |
| 91 if (rv == CPERR_IO_PENDING) { | |
| 92 read_buffer_ = dest; | |
| 93 read_buffer_size_ = dest_size; | |
| 94 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0)); | |
| 95 } else { | |
| 96 // TODO(mpcomplete): better error code | |
| 97 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, | |
| 98 net::ERR_FAILED)); | |
| 99 } | |
| 100 | |
| 101 return false; | |
| 102 } | |
| 103 | |
| 104 bool URLRequestInterceptJob::GetMimeType(std::string* mime_type) const { | |
| 105 return request_->response_headers()->GetMimeType(mime_type); | |
| 106 } | |
| 107 | |
| 108 bool URLRequestInterceptJob::GetCharset(std::string* charset) { | |
| 109 return request_->response_headers()->GetCharset(charset); | |
| 110 } | |
| 111 | |
| 112 bool URLRequestInterceptJob::GetContentEncodings( | |
| 113 std::vector<Filter::FilterType>* encoding_types) { | |
| 114 DCHECK(encoding_types->empty()); | |
| 115 if (!request_->response_headers()) | |
| 116 return false; | |
| 117 | |
| 118 std::string encoding_type; | |
| 119 void* iter = NULL; | |
| 120 while (request_->response_headers()->EnumerateHeader( | |
| 121 &iter, "Content-Encoding", &encoding_type)) { | |
| 122 encoding_types->push_back(Filter::ConvertEncodingToType(encoding_type)); | |
| 123 } | |
| 124 | |
| 125 // Even if encoding types are empty, there is a chance that we need to add | |
| 126 // some decoding, as some proxies strip encoding completely. In such cases, | |
| 127 // we may need to add (for example) SDCH filtering (when the context suggests | |
| 128 // it is appropriate). | |
| 129 Filter::FixupEncodingTypes(*this, encoding_types); | |
| 130 return !encoding_types->empty(); | |
| 131 } | |
| 132 | |
| 133 void URLRequestInterceptJob::GetResponseInfo(net::HttpResponseInfo* info) { | |
| 134 if (!plugin_) | |
| 135 return; | |
| 136 | |
| 137 std::string raw_headers; | |
| 138 int size = plugin_->functions().request_funcs->get_response_info( | |
| 139 cprequest_.get(), CPRESPONSEINFO_HTTP_RAW_HEADERS, NULL, 0); | |
| 140 int rv = size < 0 ? size : | |
| 141 plugin_->functions().request_funcs->get_response_info( | |
| 142 cprequest_.get(), CPRESPONSEINFO_HTTP_RAW_HEADERS, | |
| 143 WriteInto(&raw_headers, size+1), size); | |
| 144 if (rv != CPERR_SUCCESS) { | |
| 145 // TODO(mpcomplete): what should we do here? | |
| 146 raw_headers = "HTTP/1.1 404 Not Found"; | |
| 147 raw_headers.push_back('\0'); | |
| 148 } | |
| 149 | |
| 150 info->headers = new net::HttpResponseHeaders(raw_headers); | |
| 151 | |
| 152 if (request_->url().SchemeIsSecure()) { | |
| 153 // Make up a fake certificate for intercepted data since we don't have | |
| 154 // access to the real SSL info. | |
| 155 // TODO(mpcomplete): we should probably have the interception API transmit | |
| 156 // the SSL info, but the only consumer of this API (Gears) doesn't keep that | |
| 157 // around. We should change that. | |
| 158 const char* kCertIssuer = "Chrome Internal"; | |
| 159 const int kLifetimeDays = 100; | |
| 160 | |
| 161 DLOG(WARNING) << "Issuing a fake SSL certificate for interception of URL " | |
| 162 << request_->url(); | |
| 163 | |
| 164 info->ssl_info.cert = | |
| 165 new net::X509Certificate(request_->url().GetWithEmptyPath().spec(), | |
| 166 kCertIssuer, | |
| 167 Time::Now(), | |
| 168 Time::Now() + | |
| 169 TimeDelta::FromDays(kLifetimeDays)); | |
| 170 info->ssl_info.cert_status = 0; | |
| 171 info->ssl_info.security_bits = -1; | |
| 172 } | |
| 173 } | |
| 174 | |
| 175 int URLRequestInterceptJob::GetResponseCode() const { | |
| 176 if (!plugin_) | |
| 177 return -1; | |
| 178 | |
| 179 int status = 200; | |
| 180 plugin_->functions().request_funcs->get_response_info( | |
| 181 cprequest_.get(), CPRESPONSEINFO_HTTP_STATUS, &status, sizeof(status)); | |
| 182 | |
| 183 return status; | |
| 184 } | |
| 185 | |
| 186 bool URLRequestInterceptJob::IsRedirectResponse(GURL* location, | |
| 187 int* http_status_code) { | |
| 188 if (!request_->response_headers()) | |
| 189 return false; | |
| 190 | |
| 191 std::string value; | |
| 192 if (!request_->response_headers()->IsRedirect(&value)) | |
| 193 return false; | |
| 194 | |
| 195 *location = request_->url().Resolve(value); | |
| 196 *http_status_code = request_->response_headers()->response_code(); | |
| 197 return true; | |
| 198 } | |
| 199 | |
| 200 void URLRequestInterceptJob::StartAsync() { | |
| 201 // We may have been orphaned... | |
| 202 if (!request_ || !plugin_) | |
| 203 return; | |
| 204 | |
| 205 int rv = plugin_->functions().request_funcs->start_request(cprequest_.get()); | |
| 206 if (rv != CPERR_IO_PENDING) | |
| 207 OnStartCompleted(rv); | |
| 208 } | |
| 209 | |
| 210 void URLRequestInterceptJob::OnStartCompleted(int result) { | |
| 211 if (result != CPERR_SUCCESS) { | |
| 212 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, | |
| 213 net::ERR_CONNECTION_FAILED)); | |
| 214 return; | |
| 215 } | |
| 216 | |
| 217 NotifyHeadersComplete(); | |
| 218 } | |
| 219 | |
| 220 void URLRequestInterceptJob::OnReadCompleted(int bytes_read) { | |
| 221 if (bytes_read < 0) { | |
| 222 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, | |
| 223 net::ERR_FAILED)); | |
| 224 return; | |
| 225 } | |
| 226 | |
| 227 SetStatus(net::URLRequestStatus()); // clear the async flag | |
| 228 NotifyReadComplete(bytes_read); | |
| 229 } | |
| 230 | |
| 231 void URLRequestInterceptJob::Observe(NotificationType type, | |
| 232 const NotificationSource& source, | |
| 233 const NotificationDetails& details) { | |
| 234 DCHECK(type == NotificationType::CHROME_PLUGIN_UNLOADED); | |
| 235 DCHECK(plugin_ == Source<ChromePluginLib>(source).ptr()); | |
| 236 DetachPlugin(); | |
| 237 } | |
| OLD | NEW |