Skip to content

Commit b348d77

Browse files
committed
socket.send now checks how many bytes can in fact be sent without blocking. Fixes #2508
1 parent 7c3c190 commit b348d77

1 file changed

Lines changed: 28 additions & 21 deletions

File tree

Lib/_socket.py

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ def _unmapped_exception(exc):
277277
def java_net_socketexception_handler(exc):
278278
if exc.message.startswith("Address family not supported by protocol family"):
279279
return _add_exception_attrs(
280-
error(errno.EAFNOSUPPORT,
280+
error(errno.EAFNOSUPPORT,
281281
'Address family not supported by protocol family: See http://wiki.python.org/jython/NewSocketModule#IPV6_address_support'))
282282
if exc.message.startswith('Address already in use'):
283283
return error(errno.EADDRINUSE, 'Address already in use')
@@ -296,7 +296,7 @@ def would_block_error(exc=None):
296296
IOException : lambda x: error(errno.ECONNRESET, 'Software caused connection abort'),
297297
InterruptedIOException : lambda x: timeout(errno.ETIMEDOUT, 'timed out'),
298298
IllegalStateException : lambda x: error(errno.EPIPE, 'Illegal state exception'),
299-
299+
300300
java.net.BindException : lambda x: error(errno.EADDRINUSE, 'Address already in use'),
301301
java.net.ConnectException : lambda x: error(errno.ECONNREFUSED, 'Connection refused'),
302302
java.net.NoRouteToHostException : lambda x: error(errno.EHOSTUNREACH, 'No route to host'),
@@ -363,7 +363,7 @@ def raises_java_exception(method_or_function):
363363
"""Maps java socket exceptions to the equivalent python exception.
364364
Also sets _last_error on socket objects so as to support SO_ERROR.
365365
"""
366-
366+
367367
@wraps(method_or_function)
368368
def handle_exception(*args, **kwargs):
369369
is_socket = len(args) > 0 and isinstance(args[0], _realsocket)
@@ -457,7 +457,7 @@ def __call__(self, timeout):
457457

458458

459459
class poll(object):
460-
460+
461461
def __init__(self):
462462
self.queue = LinkedBlockingQueue()
463463
self.registered = dict() # fd -> eventmask
@@ -504,7 +504,7 @@ def _event_test(self, notification):
504504
if notification is None:
505505
return None, 0
506506
mask = self.registered.get(notification.fd, 0) # handle if concurrently removed, by simply ignoring
507-
log.debug("Testing notification=%s mask=%s", notification, mask, extra={"sock": "*"})
507+
log.debug("Testing notification=%s mask=%s", notification, mask, extra={"sock": "*"})
508508
event = 0
509509
if mask & POLLIN and notification.sock._readable():
510510
event |= POLLIN
@@ -516,14 +516,14 @@ def _event_test(self, notification):
516516
event |= POLLHUP
517517
if mask & POLLNVAL and not notification.sock.peer_closed:
518518
event |= POLLNVAL
519-
log.debug("Tested notification=%s event=%s", notification, event, extra={"sock": "*"})
519+
log.debug("Tested notification=%s event=%s", notification, event, extra={"sock": "*"})
520520
return notification.fd, event
521521

522522
def _handle_poll(self, poller):
523523
notification = poller()
524524
if notification is None:
525525
return []
526-
526+
527527
# Pull as many outstanding notifications as possible out
528528
# of the queue
529529
notifications = [notification]
@@ -677,7 +677,7 @@ def wait_for_barrier():
677677
UNKNOWN_SOCKET, CLIENT_SOCKET, SERVER_SOCKET, DATAGRAM_SOCKET = range(4)
678678
_socket_types = {
679679
UNKNOWN_SOCKET: "unknown",
680-
CLIENT_SOCKET: "client",
680+
CLIENT_SOCKET: "client",
681681
SERVER_SOCKET: "server",
682682
DATAGRAM_SOCKET: "datagram"
683683
}
@@ -855,7 +855,7 @@ def bind(self, address):
855855
# in turn use _connect, which uses Bootstrap, not ServerBootstrap
856856

857857
def _init_client_mode(self, channel=None):
858-
# this is client socket specific
858+
# this is client socket specific
859859
self.socket_type = CLIENT_SOCKET
860860
self.incoming = LinkedBlockingQueue() # list of read buffers
861861
self.incoming_head = None # allows msg buffers to be broken up
@@ -906,7 +906,7 @@ def _post_connect(self):
906906
# messages from the peer
907907
if self.connect_handlers:
908908
self.channel.pipeline().addLast(self.python_inbound_handler)
909-
909+
910910
def _peer_closed(x):
911911
log.debug("Peer closed channel %s", x, extra={"sock": self})
912912
self.channel_closed = True
@@ -1016,7 +1016,7 @@ def accept(self):
10161016
return child, peername
10171017

10181018
# DATAGRAM METHODS
1019-
1019+
10201020
def _datagram_connect(self, addr=None):
10211021
# FIXME raise exception if not of the right family
10221022
if addr is not None:
@@ -1077,7 +1077,7 @@ def recv_into(self, buffer, nbytes=0, flags=0):
10771077
return len(data)
10781078

10791079
# GENERAL METHODS
1080-
1080+
10811081
def close(self):
10821082
with self.open_lock:
10831083
self.open_count -= 1
@@ -1087,7 +1087,7 @@ def close(self):
10871087

10881088
if self.channel is None:
10891089
return
1090-
1090+
10911091
close_future = self.channel.close()
10921092
close_future.addListener(self._finish_closing)
10931093

@@ -1179,12 +1179,19 @@ def send(self, data, flags=0):
11791179

11801180
if not self._can_write:
11811181
raise error(errno.ENOTCONN, 'Socket not connected')
1182-
future = self.channel.writeAndFlush(Unpooled.wrappedBuffer(data))
1182+
1183+
bytes_writable = self.channel.bytesBeforeUnwritable()
1184+
if bytes_writable > len(data):
1185+
bytes_writable = len(data)
1186+
1187+
sent_data = data[:bytes_writable]
1188+
1189+
future = self.channel.writeAndFlush(Unpooled.wrappedBuffer(sent_data))
11831190
self._handle_channel_future(future, "send")
1184-
log.debug("Sent data <<<{!r:.20}>>>".format(data), extra={"sock": self})
1185-
# FIXME are we sure we are going to be able to send this much data, especially async?
1186-
return len(data)
1187-
1191+
log.debug("Sent data <<<{!r:.20}>>>".format(sent_data), extra={"sock": self})
1192+
1193+
return len(sent_data)
1194+
11881195
sendall = send # FIXME see note above!
11891196

11901197
def _get_incoming_msg(self, reason):
@@ -1253,7 +1260,7 @@ def recv(self, bufsize, flags=0):
12531260
data, _ = self._get_message(bufsize, "recv")
12541261
log.debug("Received <<<{!r:.20}>>>".format(data), extra={"sock": self})
12551262
return data
1256-
1263+
12571264
def recvfrom(self, bufsize, flags=0):
12581265
self._verify_channel()
12591266
data, sender = self._get_message(bufsize, "recvfrom")
@@ -1438,7 +1445,7 @@ def meth(name,self,*args):
14381445
# FIXME handshake_future - gates all requests. should be cheap (comparable to the old self.active)
14391446

14401447
class ChildSocket(_realsocket):
1441-
1448+
14421449
def __init__(self, parent_socket):
14431450
super(ChildSocket, self).__init__(type=parent_socket.type)
14441451
self.parent_socket = parent_socket
@@ -1621,7 +1628,7 @@ def _get_jsockaddr(address_object, family, sock_type, proto, flags):
16211628
def _get_jsockaddr2(address_object, family, sock_type, proto, flags):
16221629
# Is this an object that was returned from getaddrinfo? If so, it already contains an InetAddress
16231630
if isinstance(address_object, _ip_address_t):
1624-
return java.net.InetSocketAddress(address_object.jaddress, address_object[1])
1631+
return java.net.InetSocketAddress(address_object.jaddress, address_object[1])
16251632
# The user passed an address tuple, not an object returned from getaddrinfo
16261633
# So we must call getaddrinfo, after some translations and checking
16271634
if address_object is None:

0 commit comments

Comments
 (0)