Index: twisted/internet/abstract.py
===================================================================
--- twisted/internet/abstract.py	(revision 12237)
+++ twisted/internet/abstract.py	(working copy)
@@ -129,7 +129,7 @@
                 return self._postLoseConnection()
             elif self._writeDisconnecting:
                 # I was previously asked to to half-close the connection.
-                self._closeWriteConnection()
+                result = self._closeWriteConnection()
                 self._writeDisconnected = True
                 return result
         return result
Index: twisted/internet/tcp.py
===================================================================
--- twisted/internet/tcp.py	(revision 12237)
+++ twisted/internet/tcp.py	(working copy)
@@ -74,54 +74,48 @@
 class _TLSMixin:
     writeBlockedOnRead = 0
     readBlockedOnWrite = 0
-    sslShutdown = 0
-
+    _userWantRead = _userWantWrite = True
+    
     def getPeerCertificate(self):
         return self.socket.get_peer_certificate()
 
     def doRead(self):
         if self.writeBlockedOnRead:
             self.writeBlockedOnRead = 0
-            self.startWriting()
+            self._resetReadWrite()
         try:
             return Connection.doRead(self)
         except SSL.ZeroReturnError:
-            # close SSL layer, since other side has done so, if we haven't
-            if not self.sslShutdown:
-                try:
-                    self.socket.shutdown()
-                    self.sslShutdown = 1
-                except SSL.Error:
-                    pass
+            #print "ZeroReturnError", self
             return main.CONNECTION_DONE
         except SSL.WantReadError:
             return
         except SSL.WantWriteError:
             self.readBlockedOnWrite = 1
-            self.startWriting()
+            Connection.startWriting(self)
+            Connection.stopReading(self)
             return
+        except SSL.SysCallError, (retval, desc):
+            if ((retval == -1 and desc == 'Unexpected EOF')
+                or retval > 0):
+                return main.CONNECTION_LOST
+            log.err()
+            return main.CONNECTION_LOST
         except SSL.Error:
             log.err()
             return main.CONNECTION_LOST
 
-    def loseConnection(self):
-        Connection.loseConnection(self)
-        if self.connected:
-            self.startReading()
-
-    def halfCloseConnection(self, read=False, write=False):
-        raise RuntimeError, "TLS connections currently do not support half-closing"
-    
     def doWrite(self):
-        if self.writeBlockedOnRead:
-            self.stopWriting()
-            return
+        #print "doWrite", self
+        # Retry disconnecting
+        if self.disconnecting:
+            return self._postLoseConnection()
+        if self._writeDisconnected:
+            return self._closeWriteConnection()
+        
         if self.readBlockedOnWrite:
             self.readBlockedOnWrite = 0
-            # XXX - This is touching internal guts bad bad bad
-            if not self.dataBuffer and not self._tempDataBuffer:
-                self.stopWriting()
-            return self.doRead()
+            self._resetReadWrite()
         return Connection.doWrite(self)
 
     def writeSomeData(self, data):
@@ -131,7 +125,11 @@
             return 0
         except SSL.WantReadError:
             self.writeBlockedOnRead = 1
+            Connection.stopWriting(self)
+            Connection.startReading(self)
             return 0
+        except SSL.ZeroReturnError:
+            return main.CONNECTION_LOST
         except SSL.SysCallError, e:
             if e[0] == -1 and data == "":
                 # errors when writing empty strings are expected
@@ -156,28 +154,122 @@
     def _postLoseConnection(self):
         """Gets called after loseConnection(), after buffered data is sent.
 
-        We close the SSL transport layer, and if the other side hasn't
-        closed it yet we start reading, waiting for a ZeroReturnError
-        which will indicate the SSL shutdown has completed.
+        We try to send an SSL shutdown alert, but if it doesn't work, retry
+        when the socket is writable.
         """
+        #print "_postLoseConnection", self
+        self.socket.set_shutdown(SSL.RECEIVED_SHUTDOWN)
+        return self._sendCloseAlert()
+
+    _first=False
+    def _sendCloseAlert(self):
+        # Okay, *THIS* is a bit complicated.
+        
+        # Basically, the issue is, OpenSSL seems to not actually return
+        # errors from SSL_shutdown. Therefore, the only way to
+        # determine if the close notification has been sent is by 
+        # SSL_shutdown returning "done". However, it will not claim it's
+        # done until it's both sent *and* received a shutdown notification.
+
+        # I don't actually want to wait for a received shutdown
+        # notification, though, so, I have to set RECEIVED_SHUTDOWN
+        # before calling shutdown. Then, it'll return True once it's
+        # *SENT* the shutdown.
+
+        # However, RECEIVED_SHUTDOWN can't be left set, because then
+        # reads will fail, breaking half close.
+
+        # Also, since shutdown doesn't report errors, an empty write call is
+        # done first, to try to detect if the connection has gone away.
+        # (*NOT* an SSL_write call, because that fails once you've called
+        # shutdown)
+        
+        #print "_sendCloseAlert"
+        #import pdb; pdb.set_trace()
         try:
+            os.write(self.socket.fileno(), '')
+        except OSError, se:
+            if se.args[0] in (EINTR, EWOULDBLOCK, ENOBUFS):
+                return 0
+            # Write error, socket gone
+            return main.CONNECTION_LOST
+        
+        try:
+            laststate = self.socket.get_shutdown()
+            self.socket.set_shutdown(laststate | SSL.RECEIVED_SHUTDOWN)
             done = self.socket.shutdown()
-            self.sslShutdown = 1
+            if not (laststate & SSL.RECEIVED_SHUTDOWN):
+                self.socket.set_shutdown(SSL.SENT_SHUTDOWN)
+            #print "SSL_SHUTDOWN:", done
         except SSL.Error:
             log.err()
             return main.CONNECTION_LOST
+
         if done:
+            self.stopWriting()
             return main.CONNECTION_DONE
         else:
-            # we wait for other side to close SSL connection -
-            # this will be signaled by SSL.ZeroReturnError when reading
-            # from the socket
-            self.stopWriting()
-            self.startReading()
+            #print "writeBlockedOnRead:", self.writeBlockedOnRead
+            self.startWriting()
+            #import default
+            #print default.writes
+            return None
 
-            # don't close socket just yet
+    def _closeWriteConnection(self):
+        #print "_closeWriteConnection", self
+        result = self._sendCloseAlert()
+        
+        if result is main.CONNECTION_DONE:
+            self.socket.sock_shutdown(1)
+            p = interfaces.IHalfCloseableProtocol(self.protocol, None)
+            if p:
+                p.writeConnectionLost()
             return None
+        
+        return result
 
+    def _closeReadConnection(self):
+        # Keeps further reads from being received.
+        self.socket.set_shutdown(SSL.RECEIVED_SHUTDOWN)
+        self.socket.sock_shutdown(0)
+        p = interfaces.IHalfCloseableProtocol(self.protocol, None)
+        if p:
+            p.readConnectionLost()
+
+    def startReading(self):
+        self._userWantRead = True
+        if not self.readBlockedOnWrite:
+            return Connection.startReading(self)
+
+    def stopReading(self):
+        self._userWantRead = False
+        if not self.writeBlockedOnRead:
+            return Connection.stopReading(self)
+
+    def startWriting(self):
+        self._userWantWrite = True
+        if not self.writeBlockedOnRead:
+            return Connection.startWriting(self)
+
+    def stopWriting(self):
+        self._userWantWrite = False
+        if not self.readBlockedOnWrite:
+            #print "stopWriting"
+            return Connection.stopWriting(self)
+
+    def _resetReadWrite(self):
+        # After changing readBlockedOnWrite or writeBlockedOnRead,
+        # call this to reset the state to what the user requested.
+        if self._userWantWrite:
+            self.startWriting()
+        else:
+            self.stopWriting()
+        
+        if self._userWantRead:
+            self.startReading()
+        else:
+            self.stopReading()
+    
 class Connection(abstract.FileDescriptor):
     """I am the superclass of all socket-based FileDescriptors.
 
@@ -248,14 +340,6 @@
                 return
             else:
                 return main.CONNECTION_LOST
-        except SSL.SysCallError, (retval, desc):
-            # Yes, SSL might be None, but self.socket.recv() can *only*
-            # raise socket.error, if anything else is raised, it must be an
-            # SSL socket, and so SSL can't be None. (That's my story, I'm
-            # stickin' to it)
-            if retval == -1 and desc == 'Unexpected EOF':
-                return main.CONNECTION_DONE
-            raise
         if not data:
             return main.CONNECTION_DONE
         return self.protocol.dataReceived(data)
