Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(138)

Side by Side Diff: ios/net/crn_http_protocol_handler.mm

Issue 2968453002: Eliminate copying of data in iOS protocol handler to improve performance (Closed)
Patch Set: Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2012 The Chromium Authors. All rights reserved. 1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #import "ios/net/crn_http_protocol_handler.h" 5 #import "ios/net/crn_http_protocol_handler.h"
6 6
7 #include <stdint.h> 7 #include <stdint.h>
8 8
9 #include <memory> 9 #include <memory>
10 #include <utility> 10 #include <utility>
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
43 #if !defined(__has_feature) || !__has_feature(objc_arc) 43 #if !defined(__has_feature) || !__has_feature(objc_arc)
44 #error "This file requires ARC support." 44 #error "This file requires ARC support."
45 #endif 45 #endif
46 46
47 namespace net { 47 namespace net {
48 class HttpProtocolHandlerCore; 48 class HttpProtocolHandlerCore;
49 } 49 }
50 50
51 namespace { 51 namespace {
52 52
53 // Size of the buffer used to read the net::URLRequest. 53 // Minimum size of the buffer used to read the net::URLRequest.
54 const int kIOBufferSize = 64 * 1024; 54 const int kIOBufferMinSize = 64 * 1024;
55 55
56 // The maximum size of NSData that can be passed to the client 'didReceiveData' 56 // Maximum size of the buffer used to read the net::URLRequest.
57 // callback. This value must always be greater or equal to |kIOBufferSize|. 57 const int kIOBufferMaxSize = 16 * kIOBufferMinSize; // 1MB
58 const int kClientMaxBufferSize = 4 * kIOBufferSize;
59 58
60 // Global instance of the HTTPProtocolHandlerDelegate. 59 // Global instance of the HTTPProtocolHandlerDelegate.
61 net::HTTPProtocolHandlerDelegate* g_protocol_handler_delegate = nullptr; 60 net::HTTPProtocolHandlerDelegate* g_protocol_handler_delegate = nullptr;
62 61
63 // Empty callback. 62 // Empty callback.
64 void DoNothing(bool flag) {} 63 void DoNothing(bool flag) {}
65 64
66 } // namespace 65 } // namespace
67 66
68 // Bridge class to forward NSStream events to the HttpProtocolHandlerCore. 67 // Bridge class to forward NSStream events to the HttpProtocolHandlerCore.
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
182 // credentials if |auth_ok| is true. 181 // credentials if |auth_ok| is true.
183 void CompleteAuthentication(bool auth_ok, 182 void CompleteAuthentication(bool auth_ok,
184 const base::string16& username, 183 const base::string16& username,
185 const base::string16& password); 184 const base::string16& password);
186 void StripPostSpecificHeaders(NSMutableURLRequest* request); 185 void StripPostSpecificHeaders(NSMutableURLRequest* request);
187 void CancelAfterSSLError(); 186 void CancelAfterSSLError();
188 void ContinueAfterSSLError(); 187 void ContinueAfterSSLError();
189 void SSLErrorCallback(bool carryOn); 188 void SSLErrorCallback(bool carryOn);
190 void HostStateCallback(bool carryOn); 189 void HostStateCallback(bool carryOn);
191 void StartReading(); 190 void StartReading();
191 void AllocateReadBuffer(int last_read_data_size);
192 192
193 base::ThreadChecker thread_checker_; 193 base::ThreadChecker thread_checker_;
194 194
195 // The NSURLProtocol client. 195 // The NSURLProtocol client.
196 id<CRNNetworkClientProtocol> client_; 196 id<CRNNetworkClientProtocol> client_;
197 scoped_refptr<IOBuffer> buffer_; 197 std::unique_ptr<char[]> read_buffer_;
198 int read_buffer_size_;
199 scoped_refptr<WrappedIOBuffer> read_buffer_wrapper_;
198 base::scoped_nsobject<NSMutableURLRequest> request_; 200 base::scoped_nsobject<NSMutableURLRequest> request_;
199 // Stream delegate to read the HTTPBodyStream. 201 // Stream delegate to read the HTTPBodyStream.
200 base::scoped_nsobject<CRWHTTPStreamDelegate> stream_delegate_; 202 base::scoped_nsobject<CRWHTTPStreamDelegate> stream_delegate_;
201 // Vector of readers used to accumulate a POST data stream. 203 // Vector of readers used to accumulate a POST data stream.
202 std::vector<std::unique_ptr<UploadElementReader>> post_data_readers_; 204 std::vector<std::unique_ptr<UploadElementReader>> post_data_readers_;
203 205
204 // This cannot be a scoped pointer because it must be deleted on the IO 206 // This cannot be a scoped pointer because it must be deleted on the IO
205 // thread. 207 // thread.
206 URLRequest* net_request_; 208 URLRequest* net_request_;
207 209
208 base::WeakPtr<RequestTracker> tracker_; 210 base::WeakPtr<RequestTracker> tracker_;
209 211
210 DISALLOW_COPY_AND_ASSIGN(HttpProtocolHandlerCore); 212 DISALLOW_COPY_AND_ASSIGN(HttpProtocolHandlerCore);
211 }; 213 };
212 214
213 HttpProtocolHandlerCore::HttpProtocolHandlerCore(NSURLRequest* request) 215 HttpProtocolHandlerCore::HttpProtocolHandlerCore(NSURLRequest* request)
214 : client_(nil), 216 : client_(nil),
215 buffer_(new IOBuffer(kIOBufferSize)), 217 read_buffer_size_(kIOBufferMinSize),
218 read_buffer_wrapper_(nullptr),
216 net_request_(nullptr) { 219 net_request_(nullptr) {
217 // The request will be accessed from another thread. It is safer to make a 220 // The request will be accessed from another thread. It is safer to make a
218 // copy to avoid conflicts. 221 // copy to avoid conflicts.
219 // The copy is mutable, because that request will be given to the client in 222 // The copy is mutable, because that request will be given to the client in
220 // case of a redirect, but with a different URL. The URL must be created 223 // case of a redirect, but with a different URL. The URL must be created
221 // from the absoluteString of the original URL, because mutableCopy only 224 // from the absoluteString of the original URL, because mutableCopy only
222 // shallowly copies the request, and just retains the non-threadsafe NSURL. 225 // shallowly copies the request, and just retains the non-threadsafe NSURL.
223 thread_checker_.DetachFromThread(); 226 thread_checker_.DetachFromThread();
224 request_.reset([request mutableCopy]); 227 request_.reset([request mutableCopy]);
228 read_buffer_.reset(new char[kIOBufferMinSize]);
225 [request_ setURL:[NSURL URLWithString:[[request URL] absoluteString]]]; 229 [request_ setURL:[NSURL URLWithString:[[request URL] absoluteString]]];
226 } 230 }
227 231
228 void HttpProtocolHandlerCore::HandleStreamEvent(NSStream* stream, 232 void HttpProtocolHandlerCore::HandleStreamEvent(NSStream* stream,
229 NSStreamEvent event) { 233 NSStreamEvent event) {
230 DCHECK(thread_checker_.CalledOnValidThread()); 234 DCHECK(thread_checker_.CalledOnValidThread());
231 DCHECK(stream_delegate_); 235 DCHECK(stream_delegate_);
232 switch (event) { 236 switch (event) {
233 case NSStreamEventErrorOccurred: 237 case NSStreamEventErrorOccurred:
234 DLOG(ERROR) 238 DLOG(ERROR)
235 << "Failed to read POST data: " 239 << "Failed to read POST data: "
236 << base::SysNSStringToUTF8([[stream streamError] description]); 240 << base::SysNSStringToUTF8([[stream streamError] description]);
237 StopListeningStream(stream); 241 StopListeningStream(stream);
238 StopRequestWithError(NSURLErrorUnknown, ERR_UNEXPECTED); 242 StopRequestWithError(NSURLErrorUnknown, ERR_UNEXPECTED);
239 break; 243 break;
240 case NSStreamEventEndEncountered: 244 case NSStreamEventEndEncountered:
241 StopListeningStream(stream); 245 StopListeningStream(stream);
242 if (!post_data_readers_.empty()) { 246 if (!post_data_readers_.empty()) {
243 // NOTE: This call will result in |post_data_readers_| being cleared, 247 // NOTE: This call will result in |post_data_readers_| being cleared,
244 // which is the desired behavior. 248 // which is the desired behavior.
245 net_request_->set_upload(base::MakeUnique<ElementsUploadDataStream>( 249 net_request_->set_upload(base::MakeUnique<ElementsUploadDataStream>(
246 std::move(post_data_readers_), 0)); 250 std::move(post_data_readers_), 0));
247 DCHECK(post_data_readers_.empty()); 251 DCHECK(post_data_readers_.empty());
248 } 252 }
249 net_request_->Start(); 253 net_request_->Start();
250 if (tracker_) 254 if (tracker_)
251 tracker_->StartRequest(net_request_); 255 tracker_->StartRequest(net_request_);
252 break; 256 break;
253 case NSStreamEventHasBytesAvailable: { 257 case NSStreamEventHasBytesAvailable: {
254 NSUInteger length; 258 NSInteger length;
255 DCHECK([stream isKindOfClass:[NSInputStream class]]); 259 DCHECK([stream isKindOfClass:[NSInputStream class]]);
256 length = [(NSInputStream*)stream read:(unsigned char*)buffer_->data() 260 // TODO(crbug.com/738025): Dynamically change the size of the read buffer
257 maxLength:kIOBufferSize]; 261 // to improve the read (POST) performance, see AllocateReadBuffer() &
262 // avoid unnecessary data copy.
263 length = [(NSInputStream*)stream
sdefresne 2017/06/29 16:32:53 drive-by: style guide bans casts using C style (ht
kapishnikov 2017/06/29 19:40:11 Done. It is good to know. Thanks!
264 read:(unsigned char*)(read_buffer_.get())maxLength:read_buffer_size_];
mef 2017/06/29 16:18:38 nit: weird formatting. Maybe try git cl fomat ios/
kapishnikov 2017/06/29 19:40:11 That was the output of "git cl format". Agree that
258 if (length) { 265 if (length) {
mef 2017/06/29 16:18:38 Length could be -1 if error occurred. We should ha
kapishnikov 2017/06/29 19:40:11 Nice catch. Fixed it.
259 std::vector<char> owned_data(buffer_->data(), buffer_->data() + length); 266 std::vector<char> owned_data(read_buffer_.get(),
267 read_buffer_.get() + length);
260 post_data_readers_.push_back( 268 post_data_readers_.push_back(
261 base::MakeUnique<UploadOwnedBytesElementReader>(&owned_data)); 269 base::MakeUnique<UploadOwnedBytesElementReader>(&owned_data));
262 } 270 }
263 break; 271 break;
264 } 272 }
265 case NSStreamEventNone: 273 case NSStreamEventNone:
266 case NSStreamEventOpenCompleted: 274 case NSStreamEventOpenCompleted:
267 case NSStreamEventHasSpaceAvailable: 275 case NSStreamEventHasSpaceAvailable:
268 break; 276 break;
269 default: 277 default:
(...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after
491 tracker_->CaptureHeaders(net_request_); 499 tracker_->CaptureHeaders(net_request_);
492 long long expectedContentLength = [response expectedContentLength]; 500 long long expectedContentLength = [response expectedContentLength];
493 if (expectedContentLength > 0) 501 if (expectedContentLength > 0)
494 tracker_->CaptureExpectedLength(net_request_, expectedContentLength); 502 tracker_->CaptureExpectedLength(net_request_, expectedContentLength);
495 } 503 }
496 504
497 // Don't call any function on the response from now on, as the client may be 505 // Don't call any function on the response from now on, as the client may be
498 // using it and the object is not re-entrant. 506 // using it and the object is not re-entrant.
499 [client_ didReceiveResponse:response]; 507 [client_ didReceiveResponse:response];
500 508
501 int bytes_read = net_request_->Read(buffer_.get(), kIOBufferSize); 509 read_buffer_wrapper_ = new WrappedIOBuffer((const char*)(read_buffer_.get()));
510 int bytes_read =
511 net_request_->Read(read_buffer_wrapper_.get(), read_buffer_size_);
502 if (bytes_read == net::ERR_IO_PENDING) 512 if (bytes_read == net::ERR_IO_PENDING)
503 return; 513 return;
504 514
505 if (bytes_read >= 0) { 515 if (bytes_read >= 0) {
506 OnReadCompleted(net_request_, bytes_read); 516 OnReadCompleted(net_request_, bytes_read);
507 } else { 517 } else {
508 int error = bytes_read; 518 int error = bytes_read;
509 StopRequestWithError(IOSErrorCode(error), error); 519 StopRequestWithError(IOSErrorCode(error), error);
510 } 520 }
511 } 521 }
512 522
513 void HttpProtocolHandlerCore::OnReadCompleted(URLRequest* request, 523 void HttpProtocolHandlerCore::OnReadCompleted(URLRequest* request,
514 int bytes_read) { 524 int bytes_read) {
515 DCHECK_NE(net::ERR_IO_PENDING, bytes_read); 525 DCHECK_NE(net::ERR_IO_PENDING, bytes_read);
516 DCHECK(thread_checker_.CalledOnValidThread()); 526 DCHECK(thread_checker_.CalledOnValidThread());
517 527
518 if (net_request_ == nullptr) 528 if (net_request_ == nullptr)
519 return; 529 return;
520 530
521 DCHECK_EQ(net_request_, request); 531 DCHECK_EQ(net_request_, request);
522 DCHECK_GE(kClientMaxBufferSize, kIOBufferSize);
523 532
524 // Read all we can from the socket and put it into data. 533 // Read data from the socket until no bytes left to read.
525 // TODO(droger): It may be possible to avoid some of the copies (using
526 // WrappedIOBuffer for example).
527 uint64_t total_bytes_read = 0; 534 uint64_t total_bytes_read = 0;
528 while (bytes_read > 0) { 535 while (bytes_read > 0) {
529 base::scoped_nsobject<NSMutableData> data( 536 total_bytes_read += bytes_read;
530 [[NSMutableData alloc] initWithCapacity:bytes_read]); 537 // The NSData will take the ownership of |buffer_|.
531 // |bytes_read| should always be less or equal to |kClientMaxBufferSize|. 538 NSData* data =
532 // This is ensured by the fact that the max read buffer size (i.e. 539 [NSData dataWithBytesNoCopy:read_buffer_.release() length:bytes_read];
533 // |kIOBufferSize|) is always smaller or equal to |kClientMaxBufferSize|. 540 // If the data is not encoded in UTF8, the NSString is nil.
534 while (bytes_read > 0 && 541 DVLOG(3) << "To client:" << std::endl
535 [data length] + bytes_read <= kClientMaxBufferSize) { 542 << base::SysNSStringToUTF8([[NSString alloc]
536 total_bytes_read += bytes_read; 543 initWithData:data
537 [data appendBytes:buffer_->data() length:bytes_read]; 544 encoding:NSUTF8StringEncoding]);
538 bytes_read = request->Read(buffer_.get(), kIOBufferSize); 545 // Pass the read data to the client.
539 } 546 [client_ didLoadData:data];
540 547
541 if ([data length] > 0) { 548 // Allocate a new buffer and continue reading from the socket.
542 // If the data is not encoded in UTF8, the NSString is nil. 549 AllocateReadBuffer(bytes_read);
543 DVLOG(3) << "To client:" << std::endl 550 read_buffer_wrapper_ =
544 << base::SysNSStringToUTF8([[NSString alloc] 551 new WrappedIOBuffer((const char*)(read_buffer_.get()));
545 initWithData:data 552 bytes_read = request->Read(read_buffer_wrapper_.get(), read_buffer_size_);
546 encoding:NSUTF8StringEncoding]);
547 [client_ didLoadData:data];
548 }
549 } 553 }
550 554
551 if (tracker_) 555 if (tracker_)
552 tracker_->CaptureReceivedBytes(request, total_bytes_read); 556 tracker_->CaptureReceivedBytes(request, total_bytes_read);
553 557
554 if (bytes_read == net::OK) { 558 if (bytes_read == net::OK) {
555 // If there is nothing more to read. 559 // If there is nothing more to read.
556 StopNetRequest(); 560 StopNetRequest();
557 [client_ didFinishLoading]; 561 [client_ didFinishLoading];
558 } else if (bytes_read != net::ERR_IO_PENDING) { 562 } else if (bytes_read != net::ERR_IO_PENDING) {
559 // If there was an error (not canceled). 563 // If there was an error (not canceled).
560 int error = bytes_read; 564 int error = bytes_read;
561 StopRequestWithError(IOSErrorCode(error), error); 565 StopRequestWithError(IOSErrorCode(error), error);
562 } 566 }
563 } 567 }
564 568
569 void HttpProtocolHandlerCore::AllocateReadBuffer(int last_read_data_size) {
570 if (last_read_data_size == read_buffer_size_) {
571 // If the whole buffer was filled with data then increase the buffer size
572 // for the next read but don't exceed |kIOBufferMaxSize|.
573 read_buffer_size_ = std::min(read_buffer_size_ * 2, kIOBufferMaxSize);
574 } else if (read_buffer_size_ / 2 >= last_read_data_size) {
575 // If only a half or less of the buffer was filled with data then reduce
576 // the buffer size for the next read but not make it smaller than
577 // |kIOBufferMinSize|.
578 read_buffer_size_ = std::max(read_buffer_size_ / 2, kIOBufferMinSize);
579 }
580 read_buffer_.reset(new char[read_buffer_size_]);
581 }
582
565 HttpProtocolHandlerCore::~HttpProtocolHandlerCore() { 583 HttpProtocolHandlerCore::~HttpProtocolHandlerCore() {
566 DCHECK(thread_checker_.CalledOnValidThread()); 584 DCHECK(thread_checker_.CalledOnValidThread());
567 [client_ cancelAuthRequest]; 585 [client_ cancelAuthRequest];
568 DCHECK(!net_request_); 586 DCHECK(!net_request_);
569 DCHECK(!stream_delegate_); 587 DCHECK(!stream_delegate_);
570 } 588 }
571 589
572 // static 590 // static
573 void HttpProtocolHandlerCore::Destruct(const HttpProtocolHandlerCore* x) { 591 void HttpProtocolHandlerCore::Destruct(const HttpProtocolHandlerCore* x) {
574 scoped_refptr<base::SingleThreadTaskRunner> task_runner = 592 scoped_refptr<base::SingleThreadTaskRunner> task_runner =
(...skipping 389 matching lines...) Expand 10 before | Expand all | Expand 10 after
964 base::Bind(&net::HttpProtocolHandlerCore::Cancel, _core)); 982 base::Bind(&net::HttpProtocolHandlerCore::Cancel, _core));
965 [_protocolProxy invalidate]; 983 [_protocolProxy invalidate];
966 } 984 }
967 985
968 - (void)stopLoading { 986 - (void)stopLoading {
969 [self cancelRequest]; 987 [self cancelRequest];
970 _protocolProxy.reset(); 988 _protocolProxy.reset();
971 } 989 }
972 990
973 @end 991 @end
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698