1 // Copyright 2014 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 "url_request_peer.h" 6 7 #include "base/strings/string_number_conversions.h" 8 #include "net/base/load_flags.h" 9 #include "net/http/http_status_code.h" 10 11 namespace cronet { 12 13 static const size_t kBufferSizeIncrement = 8192; 14 15 // Fragment automatically inserted in the User-Agent header to indicate 16 // that the request is coming from this network stack. 17 static const char kUserAgentFragment[] = "; ChromiumJNI/"; 18 19 URLRequestPeer::URLRequestPeer(URLRequestContextPeer* context, 20 URLRequestPeerDelegate* delegate, 21 GURL url, 22 net::RequestPriority priority) 23 : method_("GET"), 24 url_request_(NULL), 25 read_buffer_(new net::GrowableIOBuffer()), 26 bytes_read_(0), 27 total_bytes_read_(0), 28 error_code_(0), 29 http_status_code_(0), 30 canceled_(false), 31 expected_size_(0), 32 streaming_upload_(false) { 33 context_ = context; 34 delegate_ = delegate; 35 url_ = url; 36 priority_ = priority; 37 } 38 39 URLRequestPeer::~URLRequestPeer() { CHECK(url_request_ == NULL); } 40 41 void URLRequestPeer::SetMethod(const std::string& method) { method_ = method; } 42 43 void URLRequestPeer::AddHeader(const std::string& name, 44 const std::string& value) { 45 headers_.SetHeader(name, value); 46 } 47 48 void URLRequestPeer::SetPostContent(const char* bytes, int bytes_len) { 49 if (!upload_data_stream_) { 50 upload_data_stream_.reset( 51 new net::UploadDataStream(net::UploadDataStream::CHUNKED, 0)); 52 } 53 upload_data_stream_->AppendChunk(bytes, bytes_len, true /* is_last_chunk */); 54 } 55 56 void URLRequestPeer::EnableStreamingUpload() { streaming_upload_ = true; } 57 58 void URLRequestPeer::AppendChunk(const char* bytes, 59 int bytes_len, 60 bool is_last_chunk) { 61 VLOG(context_->logging_level()) << "AppendChunk, len: " << bytes_len 62 << ", last: " << is_last_chunk; 63 64 context_->GetNetworkTaskRunner()->PostTask( 65 FROM_HERE, 66 base::Bind(&URLRequestPeer::OnAppendChunk, 67 base::Unretained(this), 68 bytes, 69 bytes_len, 70 is_last_chunk)); 71 } 72 73 74 std::string URLRequestPeer::GetHeader(const std::string &name) const { 75 std::string value; 76 if (url_request_ != NULL) { 77 url_request_->GetResponseHeaderByName(name, &value); 78 } 79 return value; 80 } 81 82 void URLRequestPeer::Start() { 83 context_->GetNetworkTaskRunner()->PostTask( 84 FROM_HERE, 85 base::Bind(&URLRequestPeer::OnInitiateConnection, 86 base::Unretained(this))); 87 } 88 89 void URLRequestPeer::OnAppendChunk(const char* bytes, 90 int bytes_len, 91 bool is_last_chunk) { 92 if (url_request_ != NULL) { 93 url_request_->AppendChunkToUpload(bytes, bytes_len, is_last_chunk); 94 delegate_->OnAppendChunkCompleted(this); 95 } 96 } 97 98 void URLRequestPeer::OnInitiateConnection() { 99 if (canceled_) { 100 return; 101 } 102 103 VLOG(context_->logging_level()) 104 << "Starting chromium request: " << url_.possibly_invalid_spec().c_str() 105 << " priority: " << RequestPriorityToString(priority_); 106 url_request_ = new net::URLRequest( 107 url_, net::DEFAULT_PRIORITY, this, context_->GetURLRequestContext()); 108 url_request_->SetLoadFlags(net::LOAD_DISABLE_CACHE | 109 net::LOAD_DO_NOT_SAVE_COOKIES | 110 net::LOAD_DO_NOT_SEND_COOKIES); 111 url_request_->set_method(method_); 112 url_request_->SetExtraRequestHeaders(headers_); 113 std::string user_agent; 114 if (headers_.HasHeader(net::HttpRequestHeaders::kUserAgent)) { 115 headers_.GetHeader(net::HttpRequestHeaders::kUserAgent, &user_agent); 116 } else { 117 user_agent = context_->GetUserAgent(url_); 118 } 119 size_t pos = user_agent.find(')'); 120 if (pos != std::string::npos) { 121 user_agent.insert(pos, context_->version()); 122 user_agent.insert(pos, kUserAgentFragment); 123 } 124 url_request_->SetExtraRequestHeaderByName( 125 net::HttpRequestHeaders::kUserAgent, user_agent, true /* override */); 126 127 VLOG(context_->logging_level()) << "User agent: " << user_agent; 128 129 if (upload_data_stream_) { 130 url_request_->set_upload(make_scoped_ptr(upload_data_stream_.release())); 131 } else if (streaming_upload_) { 132 url_request_->EnableChunkedUpload(); 133 } 134 135 url_request_->SetPriority(priority_); 136 137 url_request_->Start(); 138 } 139 140 void URLRequestPeer::Cancel() { 141 if (canceled_) { 142 return; 143 } 144 145 canceled_ = true; 146 147 context_->GetNetworkTaskRunner()->PostTask( 148 FROM_HERE, 149 base::Bind(&URLRequestPeer::OnCancelRequest, base::Unretained(this))); 150 } 151 152 void URLRequestPeer::OnCancelRequest() { 153 VLOG(context_->logging_level()) 154 << "Canceling chromium request: " << url_.possibly_invalid_spec(); 155 156 if (url_request_ != NULL) { 157 url_request_->Cancel(); 158 } 159 160 OnRequestCanceled(); 161 } 162 163 void URLRequestPeer::Destroy() { 164 context_->GetNetworkTaskRunner()->PostTask( 165 FROM_HERE, base::Bind(&URLRequestPeer::OnDestroyRequest, this)); 166 } 167 168 // static 169 void URLRequestPeer::OnDestroyRequest(URLRequestPeer* self) { 170 VLOG(self->context_->logging_level()) 171 << "Destroying chromium request: " << self->url_.possibly_invalid_spec(); 172 delete self; 173 } 174 175 void URLRequestPeer::OnResponseStarted(net::URLRequest* request) { 176 if (request->status().status() != net::URLRequestStatus::SUCCESS) { 177 OnRequestFailed(); 178 return; 179 } 180 181 http_status_code_ = request->GetResponseCode(); 182 VLOG(context_->logging_level()) 183 << "Response started with status: " << http_status_code_; 184 185 request->GetResponseHeaderByName("Content-Type", &content_type_); 186 expected_size_ = request->GetExpectedContentSize(); 187 delegate_->OnResponseStarted(this); 188 189 Read(); 190 } 191 192 // Reads all available data or starts an asynchronous read. 193 void URLRequestPeer::Read() { 194 while (true) { 195 if (read_buffer_->RemainingCapacity() == 0) { 196 int new_capacity = read_buffer_->capacity() + kBufferSizeIncrement; 197 read_buffer_->SetCapacity(new_capacity); 198 } 199 200 int bytes_read; 201 if (url_request_->Read( 202 read_buffer_, read_buffer_->RemainingCapacity(), &bytes_read)) { 203 if (bytes_read == 0) { 204 OnRequestSucceeded(); 205 break; 206 } 207 208 VLOG(context_->logging_level()) << "Synchronously read: " << bytes_read 209 << " bytes"; 210 OnBytesRead(bytes_read); 211 } else if (url_request_->status().status() == 212 net::URLRequestStatus::IO_PENDING) { 213 if (bytes_read_ != 0) { 214 VLOG(context_->logging_level()) << "Flushing buffer: " << bytes_read_ 215 << " bytes"; 216 217 delegate_->OnBytesRead(this); 218 read_buffer_->set_offset(0); 219 bytes_read_ = 0; 220 } 221 VLOG(context_->logging_level()) << "Started async read"; 222 break; 223 } else { 224 OnRequestFailed(); 225 break; 226 } 227 } 228 } 229 230 void URLRequestPeer::OnReadCompleted(net::URLRequest* request, int bytes_read) { 231 VLOG(context_->logging_level()) << "Asynchronously read: " << bytes_read 232 << " bytes"; 233 if (bytes_read < 0) { 234 OnRequestFailed(); 235 return; 236 } else if (bytes_read == 0) { 237 OnRequestSucceeded(); 238 return; 239 } 240 241 OnBytesRead(bytes_read); 242 Read(); 243 } 244 245 void URLRequestPeer::OnBytesRead(int bytes_read) { 246 read_buffer_->set_offset(read_buffer_->offset() + bytes_read); 247 bytes_read_ += bytes_read; 248 total_bytes_read_ += bytes_read; 249 } 250 251 void URLRequestPeer::OnRequestSucceeded() { 252 if (canceled_) { 253 return; 254 } 255 256 VLOG(context_->logging_level()) 257 << "Request completed with HTTP status: " << http_status_code_ 258 << ". Total bytes read: " << total_bytes_read_; 259 260 OnRequestCompleted(); 261 } 262 263 void URLRequestPeer::OnRequestFailed() { 264 if (canceled_) { 265 return; 266 } 267 268 error_code_ = url_request_->status().error(); 269 VLOG(context_->logging_level()) 270 << "Request failed with status: " << url_request_->status().status() 271 << " and error: " << net::ErrorToString(error_code_); 272 OnRequestCompleted(); 273 } 274 275 void URLRequestPeer::OnRequestCanceled() { OnRequestCompleted(); } 276 277 void URLRequestPeer::OnRequestCompleted() { 278 VLOG(context_->logging_level()) 279 << "Completed: " << url_.possibly_invalid_spec(); 280 if (url_request_ != NULL) { 281 delete url_request_; 282 url_request_ = NULL; 283 } 284 285 delegate_->OnBytesRead(this); 286 delegate_->OnRequestFinished(this); 287 } 288 289 unsigned char* URLRequestPeer::Data() const { 290 return reinterpret_cast<unsigned char*>(read_buffer_->StartOfBuffer()); 291 } 292 293 } // namespace cronet 294