subchannel: enforce separation between half-close and full-close API
This commit is contained in:
parent
1c8c2997c7
commit
1219fd08ca
|
@ -56,6 +56,11 @@ class SingleUseEndpointError(Exception):
|
||||||
class AlreadyClosedError(Exception):
|
class AlreadyClosedError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class NormalCloseUsedOnHalfCloseable(Exception):
|
||||||
|
pass
|
||||||
|
class HalfCloseUsedOnNonHalfCloseable(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
@implementer(IAddress)
|
@implementer(IAddress)
|
||||||
class _WormholeAddress(object):
|
class _WormholeAddress(object):
|
||||||
|
@ -265,7 +270,18 @@ class SubChannel(object):
|
||||||
def writeSequence(self, iovec):
|
def writeSequence(self, iovec):
|
||||||
self.write(b"".join(iovec))
|
self.write(b"".join(iovec))
|
||||||
|
|
||||||
|
def loseWriteConnection(self):
|
||||||
|
if not IHalfCloseableProtocol.providedBy(self._protocol):
|
||||||
|
# this is a clear error
|
||||||
|
raise HalfCloseUsedOnNonHalfCloseable()
|
||||||
|
self.local_close();
|
||||||
|
|
||||||
def loseConnection(self):
|
def loseConnection(self):
|
||||||
|
# TODO: what happens if an IHalfCloseableProtocol calls normal
|
||||||
|
# loseConnection()? I think we need to close the read side too.
|
||||||
|
if IHalfCloseableProtocol.providedBy(self._protocol):
|
||||||
|
# I don't know is correct, so avoid this for now
|
||||||
|
raise NormalCloseUsedOnHalfCloseable()
|
||||||
self.local_close()
|
self.local_close()
|
||||||
|
|
||||||
def getHost(self):
|
def getHost(self):
|
||||||
|
|
|
@ -6,7 +6,8 @@ from twisted.internet.interfaces import ITransport, IHalfCloseableProtocol
|
||||||
from twisted.internet.error import ConnectionDone
|
from twisted.internet.error import ConnectionDone
|
||||||
from ..._dilation.subchannel import (Once, SubChannel,
|
from ..._dilation.subchannel import (Once, SubChannel,
|
||||||
_WormholeAddress, _SubchannelAddress,
|
_WormholeAddress, _SubchannelAddress,
|
||||||
AlreadyClosedError)
|
AlreadyClosedError,
|
||||||
|
NormalCloseUsedOnHalfCloseable)
|
||||||
from .common import mock_manager
|
from .common import mock_manager
|
||||||
|
|
||||||
|
|
||||||
|
@ -174,9 +175,12 @@ class HalfCloseable(unittest.TestCase):
|
||||||
self.assertEqual(p.mock_calls, [mock.call.dataReceived(b"inbound1")])
|
self.assertEqual(p.mock_calls, [mock.call.dataReceived(b"inbound1")])
|
||||||
p.mock_calls[:] = []
|
p.mock_calls[:] = []
|
||||||
|
|
||||||
|
with self.assertRaises(NormalCloseUsedOnHalfCloseable) as e:
|
||||||
|
sc.loseConnection() # TODO: maybe this shouldn't be an error
|
||||||
|
|
||||||
# after a local close, we can't write anymore, but we can still
|
# after a local close, we can't write anymore, but we can still
|
||||||
# receive data
|
# receive data
|
||||||
sc.loseConnection()
|
sc.loseWriteConnection() # TODO or loseConnection?
|
||||||
self.assertEqual(m.mock_calls, [mock.call.send_close(scid)])
|
self.assertEqual(m.mock_calls, [mock.call.send_close(scid)])
|
||||||
m.mock_calls[:] = []
|
m.mock_calls[:] = []
|
||||||
self.assertEqual(p.mock_calls, [mock.call.writeConnectionLost()])
|
self.assertEqual(p.mock_calls, [mock.call.writeConnectionLost()])
|
||||||
|
@ -188,10 +192,13 @@ class HalfCloseable(unittest.TestCase):
|
||||||
"write not allowed on closed subchannel")
|
"write not allowed on closed subchannel")
|
||||||
|
|
||||||
with self.assertRaises(AlreadyClosedError) as e:
|
with self.assertRaises(AlreadyClosedError) as e:
|
||||||
sc.loseConnection()
|
sc.loseWriteConnection()
|
||||||
self.assertEqual(str(e.exception),
|
self.assertEqual(str(e.exception),
|
||||||
"loseConnection not allowed on closed subchannel")
|
"loseConnection not allowed on closed subchannel")
|
||||||
|
|
||||||
|
with self.assertRaises(NormalCloseUsedOnHalfCloseable) as e:
|
||||||
|
sc.loseConnection() # TODO: maybe expect AlreadyClosedError
|
||||||
|
|
||||||
sc.remote_data(b"inbound2")
|
sc.remote_data(b"inbound2")
|
||||||
self.assertEqual(p.mock_calls, [mock.call.dataReceived(b"inbound2")])
|
self.assertEqual(p.mock_calls, [mock.call.dataReceived(b"inbound2")])
|
||||||
p.mock_calls[:] = []
|
p.mock_calls[:] = []
|
||||||
|
@ -223,7 +230,7 @@ class HalfCloseable(unittest.TestCase):
|
||||||
m.mock_calls[:] = []
|
m.mock_calls[:] = []
|
||||||
|
|
||||||
# and a local close will shutdown the connection
|
# and a local close will shutdown the connection
|
||||||
sc.loseConnection()
|
sc.loseWriteConnection()
|
||||||
self.assertEqual(m.mock_calls, [mock.call.send_close(scid),
|
self.assertEqual(m.mock_calls, [mock.call.send_close(scid),
|
||||||
mock.call.subchannel_closed(scid, sc)])
|
mock.call.subchannel_closed(scid, sc)])
|
||||||
self.assertEqual(p.mock_calls, [mock.call.writeConnectionLost()])
|
self.assertEqual(p.mock_calls, [mock.call.writeConnectionLost()])
|
||||||
|
|
Loading…
Reference in New Issue
Block a user