Index: net/base/tcp_listen_socket.cc |
=================================================================== |
--- net/base/tcp_listen_socket.cc (revision 135347) |
+++ net/base/tcp_listen_socket.cc (working copy) |
@@ -17,6 +17,7 @@ |
#include "net/base/net_errors.h" |
#endif |
+#include "base/bind.h" |
#include "base/eintr_wrapper.h" |
#include "base/sys_byteorder.h" |
#include "base/threading/platform_thread.h" |
@@ -25,6 +26,7 @@ |
#if defined(OS_WIN) |
typedef int socklen_t; |
+#include "net/base/winsock_init.h" |
#endif // defined(OS_WIN) |
namespace net { |
@@ -32,7 +34,34 @@ |
namespace { |
const int kReadBufSize = 4096; |
+const int kMaxSendBufSize = 1024 * 1024 * 5; // 5MB |
+const net::BackoffEntry::Policy kSendBackoffPolicy = { |
+ // Number of initial errors (in sequence) to ignore before applying |
+ // exponential back-off rules. |
+ 0, |
+ |
+ // Initial delay for exponential back-off in ms. |
+ 25, |
+ |
+ // Factor by which the waiting time will be multiplied. |
+ 2, |
+ |
+ // Fuzzing percentage. ex: 10% will spread requests randomly |
+ // between 90%-100% of the calculated time. |
+ 0, |
+ |
+ // Maximum amount of time we are willing to delay our request in ms. |
+ 100, |
+ |
+ // Time to keep an entry from being discarded even when it |
+ // has no significant state, -1 to never discard. |
+ -1, |
+ |
+ // Don't use initial delay unless the last request was an error. |
+ false, |
+}; |
+ |
} // namespace |
#if defined(OS_WIN) |
@@ -75,7 +104,10 @@ |
: ListenSocket(del), |
socket_(s), |
reads_paused_(false), |
- has_pending_reads_(false) { |
+ has_pending_reads_(false), |
+ send_pending_size_(0), |
+ send_error_(false), |
+ send_backoff_(&kSendBackoffPolicy) { |
#if defined(OS_WIN) |
socket_event_ = WSACreateEvent(); |
// TODO(ibrar): error handling in case of socket_event_ == WSA_INVALID_EVENT |
@@ -96,6 +128,10 @@ |
} |
SOCKET TCPListenSocket::CreateAndBind(const std::string& ip, int port) { |
+#if defined(OS_WIN) |
+ EnsureWinsockInit(); |
+#endif |
+ |
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); |
if (s != kInvalidSocket) { |
#if defined(OS_POSIX) |
@@ -132,33 +168,27 @@ |
} |
void TCPListenSocket::SendInternal(const char* bytes, int len) { |
- char* send_buf = const_cast<char *>(bytes); |
- int len_left = len; |
- while (true) { |
- int sent = HANDLE_EINTR(send(socket_, send_buf, len_left, 0)); |
- if (sent == len_left) { // A shortcut to avoid extraneous checks. |
- break; |
- } |
- if (sent == kSocketError) { |
-#if defined(OS_WIN) |
- if (WSAGetLastError() != WSAEWOULDBLOCK) { |
- LOG(ERROR) << "send failed: WSAGetLastError()==" << WSAGetLastError(); |
-#elif defined(OS_POSIX) |
- if (errno != EWOULDBLOCK && errno != EAGAIN) { |
- LOG(ERROR) << "send failed: errno==" << errno; |
-#endif |
- break; |
- } |
- // Otherwise we would block, and now we have to wait for a retry. |
- // Fall through to PlatformThread::YieldCurrentThread() |
- } else { |
- // sent != len_left according to the shortcut above. |
- // Shift the buffer start and send the remainder after a short while. |
- send_buf += sent; |
- len_left -= sent; |
- } |
- base::PlatformThread::YieldCurrentThread(); |
+ DCHECK(bytes); |
+ DCHECK_GT(len, 0); |
+ |
+ if (send_error_) |
+ return; |
+ |
+ if (send_pending_size_ + len > kMaxSendBufSize) { |
+ LOG(ERROR) << "send failed: buffer overrun"; |
+ send_buffers_.clear(); |
+ send_pending_size_ = 0; |
+ send_error_ = true; |
+ return; |
} |
+ |
+ scoped_refptr<IOBuffer> buffer(new IOBuffer(len)); |
+ memcpy(buffer->data(), bytes, len); |
+ send_buffers_.push_back(new DrainableIOBuffer(buffer, len)); |
+ send_pending_size_ += len; |
+ |
+ if (!send_timer_.IsRunning()) |
+ SendData(); |
} |
void TCPListenSocket::Listen() { |
@@ -319,4 +349,63 @@ |
#endif |
+void TCPListenSocket::SendData() { |
+ DCHECK(!send_buffers_.empty()); |
+ |
+ int total_sent = 0; |
+ |
+ // Send data until all buffers have been sent or a call would block. |
+ while (!send_buffers_.empty()) { |
+ scoped_refptr<DrainableIOBuffer> buffer = send_buffers_.front(); |
+ |
+ int len_left = buffer->BytesRemaining(); |
+ int sent = HANDLE_EINTR(send(socket_, buffer->data(), len_left, 0)); |
+ if (sent > 0) { |
+ if (sent == len_left) |
+ send_buffers_.pop_front(); |
+ else |
+ buffer->DidConsume(sent); |
+ |
+ total_sent += sent; |
+ } else if (sent == kSocketError) { |
+#if defined(OS_WIN) |
+ if (WSAGetLastError() != WSAEWOULDBLOCK) { |
+ LOG(ERROR) << "send failed: WSAGetLastError()==" << WSAGetLastError(); |
+#elif defined(OS_POSIX) |
+ if (errno != EWOULDBLOCK && errno != EAGAIN) { |
+ LOG(ERROR) << "send failed: errno==" << errno; |
+#endif |
+ // Don't try to re-send data after a socket error. |
+ send_buffers_.clear(); |
+ send_pending_size_ = 0; |
+ send_error_ = true; |
+ return; |
+ } |
+ |
+ // The call would block. Don't send any more data at this time. |
+ break; |
+ } else { |
+ NOTREACHED(); |
+ break; |
+ } |
+ } |
+ |
+ if (total_sent > 0) { |
+ send_pending_size_ -= total_sent; |
+ DCHECK_GE(send_pending_size_, 0); |
+ |
+ // Clear the back-off delay. |
+ send_backoff_.Reset(); |
+ } else { |
+ // Increase the back-off delay. |
+ send_backoff_.InformOfRequest(false); |
+ } |
+ |
+ if (!send_buffers_.empty()) { |
+ DCHECK(!send_timer_.IsRunning()); |
+ send_timer_.Start(FROM_HERE, send_backoff_.GetTimeUntilRelease(), |
+ this, &TCPListenSocket::SendData); |
+ } |
+} |
+ |
} // namespace net |