| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2006-2008 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/http/winhttp_request_throttle.h" | |
| 6 | |
| 7 #include "base/logging.h" | |
| 8 #include "net/http/http_transaction_winhttp.h" | |
| 9 | |
| 10 namespace { | |
| 11 | |
| 12 // The arguments to a WinHttpSendRequest call. | |
| 13 struct SendRequestArgs { | |
| 14 SendRequestArgs() : request_handle(NULL), total_size(0), context(0) {} | |
| 15 | |
| 16 SendRequestArgs(HINTERNET handle, DWORD size, DWORD_PTR context_value) | |
| 17 : request_handle(handle), total_size(size), context(context_value) {} | |
| 18 | |
| 19 HINTERNET request_handle; | |
| 20 DWORD total_size; | |
| 21 DWORD_PTR context; | |
| 22 }; | |
| 23 | |
| 24 } // namespace | |
| 25 | |
| 26 namespace net { | |
| 27 | |
| 28 // Per-server queue for WinHttpSendRequest calls. | |
| 29 class WinHttpRequestThrottle::RequestQueue { | |
| 30 public: | |
| 31 RequestQueue() {} | |
| 32 | |
| 33 // Adds |args| to the end of the queue. | |
| 34 void PushBack(const SendRequestArgs& args) { queue_.push_back(args); } | |
| 35 | |
| 36 // If the queue is not empty, pops the first entry off the queue, saves it | |
| 37 // in |*args|, and returns true. If the queue is empty, returns false. | |
| 38 bool GetFront(SendRequestArgs* args); | |
| 39 | |
| 40 // If the queue has an entry containing |request_handle|, removes it and | |
| 41 // returns true. Otherwise, returns false. | |
| 42 bool Remove(HINTERNET request_handle); | |
| 43 | |
| 44 bool empty() const { return queue_.empty(); } | |
| 45 | |
| 46 private: | |
| 47 std::list<SendRequestArgs> queue_; | |
| 48 | |
| 49 DISALLOW_EVIL_CONSTRUCTORS(RequestQueue); | |
| 50 }; | |
| 51 | |
| 52 bool WinHttpRequestThrottle::RequestQueue::GetFront(SendRequestArgs* args) { | |
| 53 if (queue_.empty()) | |
| 54 return false; | |
| 55 *args = queue_.front(); | |
| 56 queue_.pop_front(); | |
| 57 return true; | |
| 58 } | |
| 59 | |
| 60 bool WinHttpRequestThrottle::RequestQueue::Remove(HINTERNET request_handle) { | |
| 61 std::list<SendRequestArgs>::iterator it; | |
| 62 for (it = queue_.begin(); it != queue_.end(); ++it) { | |
| 63 if (it->request_handle == request_handle) { | |
| 64 queue_.erase(it); | |
| 65 return true; | |
| 66 } | |
| 67 } | |
| 68 return false; | |
| 69 } | |
| 70 | |
| 71 WinHttpRequestThrottle::~WinHttpRequestThrottle() { | |
| 72 #ifndef NDEBUG | |
| 73 ThrottleMap::const_iterator throttle_iter = throttles_.begin(); | |
| 74 for (; throttle_iter != throttles_.end(); ++throttle_iter) { | |
| 75 const PerServerThrottle& throttle = throttle_iter->second; | |
| 76 DCHECK(throttle.num_requests == 0); | |
| 77 DCHECK(!throttle.request_queue.get() || throttle.request_queue->empty()); | |
| 78 } | |
| 79 #endif | |
| 80 } | |
| 81 | |
| 82 BOOL WinHttpRequestThrottle::SubmitRequest(const std::string &server, | |
| 83 HINTERNET request_handle, | |
| 84 DWORD total_size, | |
| 85 DWORD_PTR context) { | |
| 86 PerServerThrottle& throttle = throttles_[server]; | |
| 87 DCHECK(throttle.num_requests >= 0 && | |
| 88 throttle.num_requests <= kMaxConnectionsPerServer); | |
| 89 if (throttle.num_requests >= kMaxConnectionsPerServer) { | |
| 90 if (!throttle.request_queue.get()) | |
| 91 throttle.request_queue.reset(new RequestQueue); | |
| 92 SendRequestArgs args(request_handle, total_size, context); | |
| 93 throttle.request_queue->PushBack(args); | |
| 94 return TRUE; | |
| 95 } | |
| 96 | |
| 97 BOOL ok = SendRequest(request_handle, total_size, context, false); | |
| 98 if (ok) | |
| 99 throttle.num_requests += 1; | |
| 100 return ok; | |
| 101 } | |
| 102 | |
| 103 void WinHttpRequestThrottle::NotifyRequestDone(const std::string& server) { | |
| 104 PerServerThrottle& throttle = throttles_[server]; | |
| 105 DCHECK(throttle.num_requests > 0 && | |
| 106 throttle.num_requests <= kMaxConnectionsPerServer); | |
| 107 throttle.num_requests -= 1; | |
| 108 SendRequestArgs args; | |
| 109 if (throttle.request_queue.get() && | |
| 110 throttle.request_queue->GetFront(&args)) { | |
| 111 throttle.num_requests += 1; | |
| 112 SendRequest(args.request_handle, args.total_size, args.context, true); | |
| 113 } | |
| 114 if (throttles_.size() > static_cast<size_t>(kGarbageCollectionThreshold)) | |
| 115 GarbageCollect(); | |
| 116 } | |
| 117 | |
| 118 void WinHttpRequestThrottle::RemoveRequest(const std::string& server, | |
| 119 HINTERNET request_handle) { | |
| 120 PerServerThrottle& throttle = throttles_[server]; | |
| 121 if (throttle.request_queue.get() && | |
| 122 throttle.request_queue->Remove(request_handle)) | |
| 123 return; | |
| 124 NotifyRequestDone(server); | |
| 125 } | |
| 126 | |
| 127 BOOL WinHttpRequestThrottle::SendRequest(HINTERNET request_handle, | |
| 128 DWORD total_size, | |
| 129 DWORD_PTR context, | |
| 130 bool report_async_error) { | |
| 131 BOOL ok = WinHttpSendRequest(request_handle, | |
| 132 WINHTTP_NO_ADDITIONAL_HEADERS, | |
| 133 0, | |
| 134 WINHTTP_NO_REQUEST_DATA, | |
| 135 0, | |
| 136 total_size, | |
| 137 context); | |
| 138 if (!ok && report_async_error) { | |
| 139 WINHTTP_ASYNC_RESULT async_result = { API_SEND_REQUEST, GetLastError() }; | |
| 140 HttpTransactionWinHttp::StatusCallback( | |
| 141 request_handle, context, | |
| 142 WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, | |
| 143 &async_result, sizeof(async_result)); | |
| 144 } | |
| 145 return ok; | |
| 146 } | |
| 147 | |
| 148 WinHttpRequestThrottle::PerServerThrottle::PerServerThrottle() | |
| 149 : num_requests(0) { | |
| 150 } | |
| 151 | |
| 152 WinHttpRequestThrottle::PerServerThrottle::~PerServerThrottle() { | |
| 153 } | |
| 154 | |
| 155 // static | |
| 156 const int WinHttpRequestThrottle::kMaxConnectionsPerServer = 6; | |
| 157 | |
| 158 // static | |
| 159 const int WinHttpRequestThrottle::kGarbageCollectionThreshold = 64; | |
| 160 | |
| 161 void WinHttpRequestThrottle::GarbageCollect() { | |
| 162 ThrottleMap::iterator throttle_iter = throttles_.begin(); | |
| 163 while (throttle_iter != throttles_.end()) { | |
| 164 PerServerThrottle& throttle = throttle_iter->second; | |
| 165 if (throttle.num_requests == 0 && | |
| 166 (!throttle.request_queue.get() || throttle.request_queue->empty())) { | |
| 167 // Erase the current item but keep the iterator valid. | |
| 168 throttles_.erase(throttle_iter++); | |
| 169 } else { | |
| 170 ++throttle_iter; | |
| 171 } | |
| 172 } | |
| 173 } | |
| 174 | |
| 175 } // namespace net | |
| 176 | |
| OLD | NEW |