diff --git a/lib/modules/Protocols.pmod/HTTP.pmod/Query.pike b/lib/modules/Protocols.pmod/HTTP.pmod/Query.pike
index 6e0302eb8b6a1f5772c08b742e90c140401f330c..93f1f79961ce928bc1e5743de47a9198ff53dda1 100644
--- a/lib/modules/Protocols.pmod/HTTP.pmod/Query.pike
+++ b/lib/modules/Protocols.pmod/HTTP.pmod/Query.pike
@@ -194,7 +194,7 @@ void start_tls(int|void blocking, int|void async)
   object write_callback=con->query_write_callback();
   object close_callback=con->query_close_callback();
 
-  SSL.sslfile ssl = SSL.sslfile(con, context);
+  SSL.File ssl = SSL.File(con, context);
   if (blocking) {
     ssl->set_blocking();
   }
diff --git a/lib/modules/Protocols.pmod/HTTP.pmod/Server.pmod/SSLPort.pike b/lib/modules/Protocols.pmod/HTTP.pmod/Server.pmod/SSLPort.pike
index 2bff51b29d6867547547d8efcec41169de7dd02a..cb4209ec18bd0ba8616d76488d9858cb31b17c9b 100644
--- a/lib/modules/Protocols.pmod/HTTP.pmod/Server.pmod/SSLPort.pike
+++ b/lib/modules/Protocols.pmod/HTTP.pmod/Server.pmod/SSLPort.pike
@@ -63,7 +63,7 @@ void destroy() { close(); }
 //! The port accept callback
 protected void new_connection()
 {
-   SSL.sslfile fd=port->accept();
+   SSL.File fd=port->accept();
    Request r=request_program();
    r->attach_fd(fd,this,callback);
 }
@@ -72,7 +72,7 @@ protected void new_connection()
 class MySSLPort
 {
 
-  inherit SSL.sslport;
+  inherit SSL.Port;
 
   //!
   void set_default_keycert()
diff --git a/lib/modules/Protocols.pmod/HTTP.pmod/Session.pike b/lib/modules/Protocols.pmod/HTTP.pmod/Session.pike
index c326f0ee258aab7374e25aa08112c55f6a4941b1..1a8d7dc84a5283370703dea752adea8563cfdaeb 100644
--- a/lib/modules/Protocols.pmod/HTTP.pmod/Session.pike
+++ b/lib/modules/Protocols.pmod/HTTP.pmod/Session.pike
@@ -74,7 +74,7 @@ class Request
 	 url=Standards.URI(url);
       url_requested=url;
 
-#if constant(SSL.sslfile) 	
+#if constant(SSL.File)
       if(url->scheme!="http" && url->scheme!="https")
 	 error("Protocols.HTTP can't handle %O or any other "
 	       "protocols than HTTP or HTTPS\n",
@@ -87,7 +87,7 @@ class Request
 	 error("Protocols.HTTP can't handle %O or any other "
 	       "protocol than HTTP\n",
 	       url->scheme);
-#endif
+#endif /* constant(SSL.File) */
       mapping request_headers = copy_value(default_headers);
       if (url->referer)
 	 request_headers->referer=(string)url->referer;
diff --git a/lib/modules/Protocols.pmod/HTTP.pmod/module.pmod b/lib/modules/Protocols.pmod/HTTP.pmod/module.pmod
index 2a41875c923174a91e8695502737bddd5892ec3a..a540e6bba6378db2a26a70d6bed29bafa4ff3922 100644
--- a/lib/modules/Protocols.pmod/HTTP.pmod/module.pmod
+++ b/lib/modules/Protocols.pmod/HTTP.pmod/module.pmod
@@ -195,7 +195,7 @@ constant response_codes =
     url->port = proxy->port;
     query_variables = url->query = 0;
     url->path = web_url;
-#if constant(SSL.sslfile)
+#if constant(SSL.File)
   } else if (url->scheme == "https") {
 #ifdef HTTP_QUERY_DEBUG
     werror("Proxied SSL request.\n");
@@ -220,7 +220,7 @@ constant response_codes =
       con->start_tls(1);
     }
     proxy_headers = request_headers;
-#endif
+#endif /* constant(SSL.File) */
   } else {
     error("Can't handle proxying of %O.\n", url->scheme);
   }
@@ -267,7 +267,7 @@ constant response_codes =
   if(!con)
     con = .Query();
 
-#if constant(SSL.sslfile) 	
+#if constant(SSL.File)
   if(url->scheme!="http" && url->scheme!="https")
     error("Can't handle %O or any other protocols than HTTP or HTTPS.\n",
 	  url->scheme);
@@ -278,7 +278,7 @@ constant response_codes =
     error("Can't handle %O or any other protocol than HTTP "
 	  "(HTTPS requires Nettle support).\n",
 	  url->scheme);
-#endif
+#endif /* constant(SSL.File) */
 
   mapping default_headers = ([
     "user-agent" : "Mozilla/5.0 (compatible; MSIE 6.0; Pike HTTP client)"
@@ -403,7 +403,7 @@ void do_async_method(string method,
     error("Asynchronous httpu or httpmu not yet supported.\n");
   }
 
-#if constant(SSL.sslfile) 	
+#if constant(SSL.File)
   if(url->scheme!="http" && url->scheme!="https")
     error("Can't handle %O or any other protocols than HTTP or HTTPS.\n",
 	  url->scheme);
@@ -413,7 +413,7 @@ void do_async_method(string method,
   if(url->scheme!="http")
     error("Can't handle %O or any other protocol than HTTP.\n",
 	  url->scheme);
-#endif
+#endif /* constant(SSL.File) */
 
   if(!request_headers)
     request_headers = ([]);
@@ -567,7 +567,7 @@ void do_async_proxied_method(string|Standards.URI proxy,
     url->port = proxy->port;
     query_variables = url->query = 0;
     url->path = web_url;
-#if constant(SSL.sslfile)
+#if constant(SSL.File)
   } else if(url->scheme == "https") {
 #ifdef HTTP_QUERY_DEBUG
     werror("Proxied SSL request.\n");
diff --git a/lib/modules/Protocols.pmod/IRC.pmod/Client.pike b/lib/modules/Protocols.pmod/IRC.pmod/Client.pike
index 09366cced2abc75807f78a57b0e05bbc5d7aa3fd..16fe3d66021168329aea70649442b885883dbb6d 100644
--- a/lib/modules/Protocols.pmod/IRC.pmod/Client.pike
+++ b/lib/modules/Protocols.pmod/IRC.pmod/Client.pike
@@ -15,7 +15,7 @@ mapping channels=([]);
 //! @param server
 //!   The IRC server to connect to.
 //!   If server is an object, it is assumed to be a newly established
-//!   connection to the IRC server to be used. Pass @[SSL.sslfile]
+//!   connection to the IRC server to be used. Pass @[SSL.File]
 //!   connections here to connect to SSL secured IRC networks.
 //! @param options
 //!   An optional mapping with additional IRC client options.
diff --git a/lib/modules/Protocols.pmod/LDAP.pmod/client.pike b/lib/modules/Protocols.pmod/LDAP.pmod/client.pike
index 92f824945f23bf6588a2d4de0f60407787ed35d7..e803dba1a5fb60ab83bbb42c10bfbe9dd686be53 100644
--- a/lib/modules/Protocols.pmod/LDAP.pmod/client.pike
+++ b/lib/modules/Protocols.pmod/LDAP.pmod/client.pike
@@ -705,7 +705,7 @@ typedef mapping(string:ResultAttributeValue) ResultEntry;
 
 #if constant(SSL.Cipher)
     if(lauth->scheme == "ldaps") {
-      SSL.sslfile ssl_fd = SSL.sslfile(low_fd, context);
+      SSL.File ssl_fd = SSL.File(low_fd, context);
       if (!ssl_fd->connect()) {
 	ERROR("Failed to connect to LDAPS server.\n");
       }
@@ -786,7 +786,7 @@ void reset_options()
         context = SSL.Context();
       }
     object _f = ldapfd;
-    ldapfd = SSL.sslfile(_f, context);
+    ldapfd = SSL.File(_f, context);
     return ldapfd->connect();
 #endif
   return 0;    
diff --git a/lib/modules/Protocols.pmod/LDAP.pmod/protocol.pike b/lib/modules/Protocols.pmod/LDAP.pmod/protocol.pike
index 9d2a3b2dc41a08814c179fd9ee651b7f651d3854..ea6c1540625708ce2ecb6d39993eb71c2b6ebbe6 100644
--- a/lib/modules/Protocols.pmod/LDAP.pmod/protocol.pike
+++ b/lib/modules/Protocols.pmod/LDAP.pmod/protocol.pike
@@ -40,7 +40,7 @@ import Protocols.LDAP;
   int connected = 0;
 
 #if constant(SSL.Cipher)
-  Stdio.Stream|SSL.sslfile ldapfd;		// helper fd
+  Stdio.Stream|SSL.File ldapfd;		        // helper fd
 #else
   Stdio.Stream ldapfd;			        // helper fd
 #endif
diff --git a/lib/modules/SSL.pmod/Connection.pike b/lib/modules/SSL.pmod/Connection.pike
index c5ec435bc37ad61ee1e007ac319ad323248f5720..4b6866edb7430147b1a3d0c9dd514963ca89b282 100644
--- a/lib/modules/SSL.pmod/Connection.pike
+++ b/lib/modules/SSL.pmod/Connection.pike
@@ -21,11 +21,11 @@
 //!   classes that inherits it should be used (ie either
 //!   @[ClientConnection] or @[ServerConnection]) depending on whether
 //!   this is to be a client-side or server-side connection. These in
-//!   turn are typically created by @[sslfile()->create()].
+//!   turn are typically created by @[File()->create()].
 //!
 //! @seealso
 //!   @[ClientConnection], @[ServerConnection], @[Context],
-//!   @[Session], @[sslfile], @[state]
+//!   @[Session], @[File], @[State]
 
 //#define SSL3_PROFILING
 
diff --git a/lib/modules/SSL.pmod/Constants.pmod b/lib/modules/SSL.pmod/Constants.pmod
index 1aa7727ad09d6629216c9886b8fd1488b83a5d6c..af956720b9d809fd2b9e93ce761011666389d3a4 100644
--- a/lib/modules/SSL.pmod/Constants.pmod
+++ b/lib/modules/SSL.pmod/Constants.pmod
@@ -70,7 +70,7 @@
 //! Constants for specifying the versions of SSL to use.
 //!
 //! @seealso
-//!   @[SSL.sslfile()->create()], @[SSL.handshake()->create()]
+//!   @[Context]
 enum ProtocolVersion {
   PROTOCOL_SSL_3_0	= 0x300, //! SSL 3.0 - The original SSL3 draft version.
   PROTOCOL_SSL_3_1	= 0x301, //! SSL 3.1 - The RFC 2246 version of SSL.
diff --git a/lib/modules/SSL.pmod/Context.pike b/lib/modules/SSL.pmod/Context.pike
index a8f96be62aff18d725bfcadf28ea5b6057184629..bf882869160552425be505aa59049035b073a922 100644
--- a/lib/modules/SSL.pmod/Context.pike
+++ b/lib/modules/SSL.pmod/Context.pike
@@ -27,11 +27,11 @@
 //!     satisfactory.
 //! @endul
 //!
-//! The initialized @[context] object is then passed to
-//! @[sslfile()->create()] or used as is embedded in @[sslport].
+//! The initialized @[Context] object is then passed to
+//! @[File()->create()] or used as is embedded in @[Port].
 //!
 //! @seealso
-//!   @[sslfile], @[sslport], @[Standards.X509]
+//!   @[File], @[Port], @[Standards.X509]
 
 #ifdef SSL3_DEBUG
 #define SSL3_DEBUG_MSG(X ...)  werror(X)
@@ -216,8 +216,7 @@ array(int) ecc_curves = reverse(sort(indices(ECC_CURVES)));
 array(string(8bit)) advertised_protocols;
 
 //! The maximum amount of data that is sent in each SSL packet by
-//! @[sslfile]. A value between 1 and
-//! @[SSL.Constants.PACKET_MAX_SIZE].
+//! @[File]. A value between 1 and @[Constants.PACKET_MAX_SIZE].
 int packet_max_size = PACKET_MAX_SIZE;
 
 // The signature algorithms to use. According to RFC 5246 7.4.2 all
diff --git a/lib/modules/SSL.pmod/File.pike b/lib/modules/SSL.pmod/File.pike
new file mode 100644
index 0000000000000000000000000000000000000000..eabae5b7903d31276a6a4324a103dbb520cc78fd
--- /dev/null
+++ b/lib/modules/SSL.pmod/File.pike
@@ -0,0 +1,2324 @@
+#pike __REAL_VERSION__
+#require constant(SSL.Cipher)
+
+//! Interface similar to @[Stdio.File].
+//!
+//! @ul
+//! @item
+//!   Handles blocking and nonblocking mode.
+//! @item
+//!   Handles callback mode in an arbitrary backend (also in blocking
+//!   mode).
+//! @item
+//!   Read and write operations might each do both reading and
+//!   writing. In callback mode that means that installing either a
+//!   read or a write callback might install both internally. It also
+//!   means that reading in one thread while writing in another
+//!   doesn't work.
+//! @item
+//!   Callback changing operations like @[set_blocking] and
+//!   @[set_nonblocking] aren't atomic.
+//! @item
+//!   Apart from the above, thread safety/atomicity characteristics
+//!   are retained.
+//! @item
+//!   Blocking characterstics are retained for all functions.
+//! @item
+//!   @[is_open], connection init (@[create]) and close (@[close]) can
+//!   do both reading and writing.
+//! @item
+//!   @[destroy] attempts to close the stream properly by sending the
+//!   close packet, but since it can't do blocking I/O it's not
+//!   certain that it will succeed. The stream should therefore always
+//!   be closed with an explicit @[close] call.
+//! @item
+//!   Abrupt remote close without the proper handshake gets the errno
+//!   @[System.EPIPE].
+//! @item
+//!   Objects do not contain cyclic references, so they are closed and
+//!   destructed timely when dropped.
+//! @endul
+
+// #define SSLFILE_DEBUG
+// #define SSL3_DEBUG
+// #define SSL3_DEBUG_MORE
+// #define SSL3_DEBUG_TRANSPORT
+
+#ifdef SSL3_DEBUG
+protected string stream_descr;
+#define SSL3_DEBUG_MSG(X...)						\
+  werror ("[thr:" + this_thread()->id_number() +			\
+	  "," + (stream ? "" : "ex ") + stream_descr + "] " + X)
+#ifdef SSL3_DEBUG_MORE
+#define SSL3_DEBUG_MORE_MSG(X...) SSL3_DEBUG_MSG (X)
+#endif
+#else
+#define SSL3_DEBUG_MSG(X...) 0
+#endif
+
+#ifndef SSL3_DEBUG_MORE_MSG
+#define SSL3_DEBUG_MORE_MSG(X...) 0
+#endif
+
+protected Stdio.File stream;
+// The stream is closed by shutdown(), which is called directly or
+// indirectly from destroy() or close() but not from anywhere else.
+//
+// Note that a close in nonblocking callback mode might not happen
+// right away. In that case stream remains set after close() returns,
+// suitable callbacks are installed for the close packet exchange, and
+// close_state >= NORMAL_CLOSE. The stream is closed by the callbacks
+// as soon the close packets are done, or if an error occurs.
+
+protected int(-1..65535) linger_time = -1;
+// The linger behaviour set by linger().
+
+protected .Context context;
+// The context to use.
+
+protected .Connection conn;
+// Always set when stream is. Destructed with destroy() at shutdown
+// since it contains cyclic references. Noone else gets to it, though.
+
+protected array(string) write_buffer; // Encrypted data to be written.
+protected String.Buffer read_buffer; // Decrypted data that has been read.
+
+protected mixed callback_id;
+protected function(void|object,void|mixed:int) accept_callback;
+protected function(void|mixed,void|string:int) read_callback;
+protected function(void|mixed:int) write_callback;
+protected function(void|mixed:int) close_callback;
+
+protected Pike.Backend real_backend;
+// The real backend for the stream.
+
+protected Pike.Backend local_backend;
+// Internally all I/O is done using callbacks. When the real backend
+// can't be used, either because we aren't in callback mode (i.e. the
+// user hasn't registered any callbacks), or because we're to do a
+// blocking operation, this local one takes its place. It's
+// created on demand.
+
+protected int nonblocking_mode;
+
+protected int fragment_max_size;
+//! The max amount of data to send in each packet.
+//! Initialized from the context when the object is created.
+
+import .Constants;
+
+protected enum CloseState {
+  ABRUPT_CLOSE = -1,
+  STREAM_OPEN = 0,
+  STREAM_UNINITIALIZED = 1,
+  NORMAL_CLOSE = 2,		// The caller has requested a normal close.
+  CLEAN_CLOSE = 3,		// The caller has requested a clean close.
+}
+protected CloseState close_state = STREAM_UNINITIALIZED;
+// ABRUPT_CLOSE is set if there's a remote close without close packet.
+// The stream is still considered open locally, but reading or writing
+// to it trigs System.EPIPE.
+
+protected int local_errno;
+// If nonzero, override the errno on the stream with this.
+
+protected int cb_errno;
+// Stores the errno from failed I/O in a callback so that the next
+// visible I/O operation can report it properly.
+
+protected int got_extra_read_call_out;
+// 1 when we have a call out to ssl_read_callback. We get this when we
+// need to call read_callback or close_callback but can't do that
+// right away from ssl_read_callback. See comments in that function
+// for more details. -1 if we've switched to non-callback mode and
+// therefore has removed the call out temporarily but need to restore
+// it when switching back. 0 otherwise.
+//
+// -1 is also set before a call to update_internal_state when we want
+// to schedule an extra read call out; update_internal_state will then
+// do the actual call out installation if possible.
+
+protected int alert_cb_called;
+// Need to know if the alert callback has been called in
+// ssl_read_callback since it can't continue in that case. This is
+// only set temporarily while ssl_read_callback runs.
+
+protected constant epipe_errnos = (<
+  System.EPIPE,
+  System.ECONNRESET,
+#if constant(System.WSAECONNRESET)
+  // The following is returned by winsock on windows.
+  // Pike ought to map it to System.ECONNRESET.
+  System.WSAECONNRESET,
+#endif
+>);
+// Multiset containing the errno codes that can occur if the remote
+// end has closed the connection.
+
+// This macro is used in all user called functions that can report I/O
+// errors, both at the beginning and after
+// ssl_(read|write|close)_callback calls.
+#define FIX_ERRNOS(ERROR, NO_ERROR) do {				\
+    if (cb_errno) {							\
+      /* Got a stored error from a previous callback that has failed. */ \
+      local_errno = cb_errno;						\
+      cb_errno = 0;							\
+      {ERROR;}								\
+    }									\
+    else {								\
+      local_errno = 0;							\
+      {NO_ERROR;}							\
+    }									\
+  } while (0)
+
+// Ignore user installed callbacks if a close has been requested locally.
+#define CALLBACK_MODE							\
+  ((read_callback || write_callback || close_callback || accept_callback) && \
+   close_state < NORMAL_CLOSE)
+
+#define SSL_HANDSHAKING (!conn || ((conn->state & CONNECTION_handshaking) && \
+				   close_state != ABRUPT_CLOSE))
+#define SSL_CLOSING_OR_CLOSED						\
+  (conn->state & CONNECTION_local_closing)
+
+// Always wait for input during handshaking and when we expect the
+// remote end to respond to our close packet. We should also check the
+// input buffer for a close packet if there was a failure to write our
+// close packet.
+#define SSL_INTERNAL_READING						\
+  (conn && (SSL_HANDSHAKING ||						\
+	    ((conn->state & CONNECTION_closed) == CONNECTION_local_closed)))
+
+// Try to write when there's data in the write buffer or when we have
+// a close packet to send. The packet is queued separately by
+// ssl_write_callback in the latter case.
+#define SSL_INTERNAL_WRITING (conn &&					\
+			      (sizeof (write_buffer) ||			\
+			       ((conn->state & CONNECTION_local_down) == \
+				CONNECTION_local_closing)))
+
+#ifdef SSLFILE_DEBUG
+
+#if constant (Thread.thread_create)
+
+#define THREAD_T Thread.Thread
+#define THIS_THREAD() this_thread()
+
+
+protected void thread_error (string msg, THREAD_T other_thread)
+{
+#if 0 && constant (_locate_references)
+  werror ("%s\n%O got %d refs", msg, this, _refs (this));
+  _locate_references (this);
+#endif
+  error ("%s"
+	 "%s\n"
+	 "User callbacks: a=%O r=%O w=%O c=%O\n"
+	 "Internal callbacks: r=%O w=%O c=%O\n"
+	 "Backend: %O  This thread: %O  Other thread: %O\n"
+	 "%s",
+	 msg,
+	 !stream ? "Got no stream" :
+	 stream->is_open() ? "Stream is open" :
+	 "Stream is closed",
+	 accept_callback, read_callback, write_callback, close_callback,
+	 stream && stream->query_read_callback(),
+	 stream && stream->query_write_callback(),
+	 stream && stream->query_close_callback(),
+	 stream && stream->query_backend(),
+	 this_thread(), other_thread,
+	 other_thread ? ("Other thread backtrace:\n" +
+			 describe_backtrace (other_thread->backtrace()) +
+			 "----------\n") : "");
+}
+
+#else  // !constant (Thread.thread_create)
+
+#define THREAD_T int
+#define THIS_THREAD() 1
+
+protected void thread_error (string msg, THREAD_T other_thread)
+{
+  error ("%s"
+	 "%s\n"
+	 "User callbacks: a=%O r=%O w=%O c=%O\n"
+	 "Internal callbacks: r=%O w=%O c=%O\n"
+	 "Backend: %O\n",
+	 msg,
+	 !stream ? "Got no stream" :
+	 stream->is_open() ? "Stream is open" :
+	 "Stream is closed",
+	 accept_callback, read_callback, write_callback, close_callback,
+	 stream && stream->query_read_callback(),
+	 stream && stream->query_write_callback(),
+	 stream && stream->query_close_callback(),
+	 stream && stream->query_backend());
+}
+
+#endif	// !constant (Thread.thread_create)
+
+protected THREAD_T op_thread;
+
+// FIXME: Looks like the following check can give false alarms since
+// an fd object can lose all refs even if some callbacks still are
+// registered.
+#define CHECK_CB_MODE(CUR_THREAD) do {					\
+    if (Pike.Backend backend = stream && stream->query_backend()) {	\
+      THREAD_T backend_thread = backend->executing_thread();		\
+      if (backend_thread && backend_thread != CUR_THREAD &&		\
+	  (stream->query_read_callback() ||				\
+	   stream->query_write_callback() ||				\
+	   stream->query_close_callback()))				\
+	/* NB: The other thread backtrace might not be relevant at	\
+	 * all here. */							\
+	thread_error ("In callback mode in a different backend.\n",	\
+		      backend_thread);					\
+    }									\
+  } while (0)
+
+#define LOW_CHECK(OP_THREAD, CUR_THREAD,				\
+		  IN_CALLBACK, CALLED_FROM_REAL_BACKEND) do {		\
+    if (IN_CALLBACK) {							\
+      if (CALLED_FROM_REAL_BACKEND) {					\
+	if (OP_THREAD)							\
+	  thread_error ("Called from real backend while doing an operation.\n",	\
+			OP_THREAD);					\
+      }									\
+      else								\
+	if (!OP_THREAD)							\
+	  error ("Called from local backend outside an operation.\n");	\
+    }									\
+									\
+    if (OP_THREAD && OP_THREAD != CUR_THREAD)				\
+      thread_error ("Doing operation in another thread.\n", OP_THREAD);	\
+									\
+    CHECK_CB_MODE (CUR_THREAD);						\
+  } while (0)
+
+#define CHECK(IN_CALLBACK, CALLED_FROM_REAL_BACKEND) do {		\
+    THREAD_T cur_thread = THIS_THREAD();				\
+    LOW_CHECK (op_thread, cur_thread, IN_CALLBACK, CALLED_FROM_REAL_BACKEND); \
+  } while (0)
+
+#define ENTER(IN_CALLBACK, CALLED_FROM_REAL_BACKEND) do {		\
+    THREAD_T old_op_thread;						\
+    {									\
+      THREAD_T cur_thread = THIS_THREAD();				\
+      old_op_thread = op_thread;					\
+      /* Relying on the interpreter lock here. */			\
+      op_thread = cur_thread;						\
+    }									\
+    LOW_CHECK (old_op_thread, op_thread, IN_CALLBACK, CALLED_FROM_REAL_BACKEND); \
+    mixed _op_err = catch
+
+#define RESTORE do {op_thread = old_op_thread;} while (0)
+
+#define RETURN(RET_VAL) do {RESTORE; return (RET_VAL);} while (0)
+
+#define LEAVE								\
+  ;									\
+  RESTORE;								\
+  if (_op_err) throw (_op_err);						\
+  } while (0)
+
+#else  // !SSLFILE_DEBUG
+
+#define CHECK_CB_MODE(CUR_THREAD) do {} while (0)
+#define CHECK(IN_CALLBACK, CALLED_FROM_REAL_BACKEND) do {} while (0)
+#define ENTER(IN_CALLBACK, CALLED_FROM_REAL_BACKEND) do
+#define RESTORE do {} while (0)
+#define RETURN(RET_VAL) return (RET_VAL)
+#define LEAVE while (0)
+
+#endif	// !SSLFILE_DEBUG
+
+// stream is assumed to be operational on entry but might be zero
+// afterwards. cb_errno is assumed to be 0 on entry.
+#define RUN_MAYBE_BLOCKING(REPEAT_COND, NONWAITING_MODE,		\
+			   ENABLE_READS, ERROR_CODE) do {		\
+  run_local_backend: {							\
+      CHECK_CB_MODE (THIS_THREAD());					\
+      if (!local_backend) local_backend = Pike.SmallBackend();		\
+      stream->set_backend (local_backend);				\
+      stream->set_id (0);						\
+									\
+      while (1) {							\
+	float|int(0..0) action;						\
+									\
+	if (got_extra_read_call_out) {					\
+	  /* Do whatever ssl_read_callback needs to do before we	\
+	   * continue. Since the first arg is zero here it won't call	\
+	   * any user callbacks, so they are superseded as they should	\
+	   * be if we're doing an explicit read (or a write or close,	\
+	   * which legitimately might cause reading to be done). Don't	\
+	   * need to bother with the return value from ssl_read_callback \
+	   * since we'll propagate the error, if any, just below. */	\
+	  if (got_extra_read_call_out > 0)				\
+	    real_backend->remove_call_out (ssl_read_callback);		\
+	  ssl_read_callback (0, 0); /* Will clear got_extra_read_call_out. */ \
+	  action = 0.0;							\
+	}								\
+									\
+	else {								\
+	  stream->set_write_callback (SSL_INTERNAL_WRITING && ssl_write_callback); \
+									\
+	  if (ENABLE_READS) {						\
+	    stream->set_read_callback (ssl_read_callback);		\
+	    stream->set_close_callback (ssl_close_callback);		\
+	  }								\
+	  else {							\
+	    stream->set_read_callback (0);				\
+	    /* Installing a close callback without a read callback	\
+	     * currently doesn't work well in Stdio.File. */		\
+	    stream->set_close_callback (0);				\
+	  }								\
+									\
+	  /* When we fail to write the close packet we should check if	\
+	   * a close packet is in the input buffer before signalling	\
+	   * the error. That means installing the read callbacks	\
+	   * without waiting in the backend. */				\
+	  int zero_timeout = NONWAITING_MODE;				\
+									\
+	  SSL3_DEBUG_MSG ("Running local backend [r:%O w:%O], %s timeout\n", \
+			  !!stream->query_read_callback(),		\
+			  !!stream->query_write_callback(),		\
+			  zero_timeout ? "zero" : "infinite");		\
+									\
+	  action = local_backend (zero_timeout ? 0.0 : 0);		\
+	}								\
+									\
+	if (NONWAITING_MODE && !action) {				\
+	  SSL3_DEBUG_MSG ("Nonwaiting local backend ended - nothing to do\n"); \
+	  break;							\
+	}								\
+									\
+	if (!action && (conn->state & CONNECTION_local_closing)) {	\
+	  SSL3_DEBUG_MSG ("Did not get a remote close - "		\
+			  "signalling delayed error from writing close message\n"); \
+	  cleanup_on_error();						\
+	  cb_errno = System.EPIPE;					\
+	  if (close_state != CLEAN_CLOSE)				\
+	    close_state = ABRUPT_CLOSE;					\
+	}								\
+									\
+	FIX_ERRNOS ({							\
+	    SSL3_DEBUG_MSG ("Local backend ended with error\n");	\
+	    if (stream) {						\
+	      stream->set_id (1);					\
+	      update_internal_state (1);				\
+	      /* Switch backend after updating the installed callbacks. */ \
+	      stream->set_backend (real_backend);			\
+	      CHECK_CB_MODE (THIS_THREAD());				\
+	    }								\
+	    {ERROR_CODE;}						\
+	    break run_local_backend;					\
+	  }, 0);							\
+									\
+	if (!stream) {							\
+	  SSL3_DEBUG_MSG ("Local backend ended after close.\n");	\
+	  break run_local_backend;					\
+	}								\
+									\
+	if (!(REPEAT_COND)) {						\
+	  SSL3_DEBUG_MSG ("Local backend ended - repeat condition false\n"); \
+	  break;							\
+	}								\
+      }									\
+									\
+      stream->set_id (1);						\
+      update_internal_state (1);					\
+      /* Switch backend after updating the installed callbacks. */	\
+      stream->set_backend (real_backend);				\
+      CHECK_CB_MODE (THIS_THREAD());					\
+    }									\
+  } while (0)
+
+protected void create (Stdio.File stream, SSL.Context ctx)
+//! Create an SSL connection over an open @[stream].
+//!
+//! @param stream
+//!   Open socket or pipe to create the connection over.
+//!
+//! @param ctx
+//!   The SSL context.
+//!
+//! The backend used by @[stream] is taken over and restored after the
+//! connection is closed (see @[close] and @[shutdown]). The callbacks
+//! and id in @[stream] are overwritten.
+//!
+//! @note
+//!   The operation mode defaults to nonblocking mode.
+{
+  SSL3_DEBUG_MSG ("SSL.File->create (%O, %O)\n", stream, ctx);
+
+  ENTER (0, 0) {
+    global::stream = stream;
+    global::context = ctx;
+
+#ifdef SSL3_DEBUG
+    if (stream->query_fd)
+      stream_descr = "fd:" + stream->query_fd();
+    else
+      stream_descr = replace (sprintf ("%O", stream), "%", "%%");
+#endif
+    write_buffer = ({});
+    read_buffer = String.Buffer();
+    real_backend = stream->query_backend();
+    close_state = STREAM_OPEN;
+
+    stream->set_read_callback (0);
+    stream->set_write_callback (0);
+    stream->set_close_callback (0);
+    stream->set_id (1);
+
+    fragment_max_size =
+      limit(1, ctx->packet_max_size, PACKET_MAX_SIZE);
+
+    set_nonblocking();
+  } LEAVE;
+}
+
+//! Configure as client and set up the connection.
+//!
+//! @param dest_addr
+//!   Optional name of the server that we are connected to.
+//!
+//! @returns
+//!   Returns @expr{0@} on handshaking failure in blocking mode,
+//!   and otherwise @expr{1@}.
+int(1bit) connect(string|array(string)|void dest_addr)
+{
+  if (conn) error("A connection is already configured!\n");
+
+  ENTER (0, 0) {
+    if (stringp(dest_addr)) {
+      dest_addr = ({ dest_addr });
+    }
+    conn = .ClientConnection(context, dest_addr);
+
+    // Wait for the handshake to finish in blocking mode.
+    if (!nonblocking_mode) {
+      if (!direct_write()) {
+	local_errno = errno();
+	if (stream) {
+	  stream->set_callbacks(0, 0, 0, 0, 0);
+	}
+	conn = UNDEFINED;
+	return 0;
+      }
+    } else {
+      queue_write();
+    }
+  } LEAVE;
+
+  return 1;
+}
+
+//! Configure as server and set up the connection.
+//!
+//! @param pending_data
+//!   Any data that has already been read from the stream.
+//!   This is typically used with protocols that use
+//!   START TLS or similar, where there's a risk that
+//!   "too much" data (ie part of the TLS ClientHello) has
+//!   been read from the stream before deciding that the
+//!   connection is to enter TLS-mode.
+//!
+//! @returns
+//!   Returns @expr{0@} on handshaking failure in blocking mode,
+//!   and otherwise @expr{1@}.
+int(1bit) accept(string|void pending_data)
+{
+  if (conn) error("A connection is already configured!\n");
+
+  ENTER (0, 0) {
+    conn = .ServerConnection(context);
+
+    if (sizeof(pending_data || "")) {
+      if (intp(conn->got_data(pending_data))) {
+	local_errno = errno();
+	if (stream) {
+	  stream->set_callbacks(0, 0, 0, 0, 0);
+	}
+	conn = UNDEFINED;
+	return 0;
+      }
+    }
+
+    // Wait for the handshake to finish in blocking mode.
+    if (!nonblocking_mode) {
+      if (!direct_write()) {
+	local_errno = errno();
+	if (stream) {
+	  stream->set_callbacks(0, 0, 0, 0, 0);
+	}
+	conn = UNDEFINED;
+	return 0;
+      }
+    }
+  } LEAVE;
+
+  return 1;
+}
+
+mixed get_server_names()
+{
+  if (!conn) error("No active conection.\n");
+  return conn->session->server_names;
+}
+
+//! @returns
+//!   Returns peer certificate information, if any.
+mapping get_peer_certificate_info()
+{
+  if (!conn) error("No active conection.\n");
+  return conn->session->cert_data;
+}
+
+//! @returns
+//!   Returns the peer certificate chain, if any.
+array get_peer_certificates()
+{
+  if (!conn) error("No active conection.\n");
+  return conn->session->peer_certificate_chain;
+}
+
+//! Set the linger time on @[close()].
+int(0..1) linger(int(-1..65535)|void seconds)
+{
+  if (!stream) return 0;
+  if (zero_type(seconds)) seconds = -1;
+  if (seconds == linger_time) {
+    // Noop.
+    return 1;
+  }
+  if (stream->linger && !stream->linger(seconds)) return 0;
+  linger_time = seconds;
+  return 1;
+}
+
+int close (void|string how, void|int clean_close, void|int dont_throw)
+//! Close the connection. Both the read and write ends are always
+//! closed
+//!
+//! @param how
+//!   This argument is only for @[Stdio.File] compatibility
+//!   and must be either @expr{"rw"@} or @expr{0@}.
+//!
+//! @param clean_close
+//!   If set then close messages are exchanged to shut down
+//!   the SSL connection but not the underlying stream. It may then
+//!   continue to be used for other communication afterwards. The
+//!   default is to send a close message and then close the stream
+//!   without waiting for a response.
+//!
+//! @param dont_throw
+//!   I/O errors are normally thrown, but that can be turned off with
+//!   @[dont_throw]. In that case @[errno] is set instead and @expr{0@} is
+//!   returned. @expr{1@} is always returned otherwise. It's not an error to
+//!   close an already closed connection.
+//!
+//! @note
+//! If a clean close is requested in nonblocking mode then the stream
+//! is most likely not closed right away, and the backend is then
+//! still needed for a while afterwards to exchange the close packets.
+//! @[is_open] returns 2 in that time window.
+//!
+//! @note
+//! I/O errors from both reading and writing might occur in blocking
+//! mode.
+//!
+//! @note
+//! If a clean close is requested and data following the close message
+//! is received at the same time, then this object will read it and
+//! has no way to undo that. That data can be retrieved with @[read]
+//! afterwards.
+//!
+//! @seealso
+//!   @[shutdown]
+{
+  SSL3_DEBUG_MSG ("SSL.File->close (%O, %O, %O)\n",
+		  how, clean_close, dont_throw);
+
+  if (how && how != "rw")
+    error ("Can only close the connection in both directions simultaneously.\n");
+
+  ENTER (0, 0) {
+    if (!conn || (conn->state & CONNECTION_local_down)) {
+      SSL3_DEBUG_MSG ("SSL.File->close: Already closed (%d)\n", close_state);
+      RETURN (1);
+    }
+    close_state = clean_close ? CLEAN_CLOSE : NORMAL_CLOSE;
+
+    FIX_ERRNOS ({
+	SSL3_DEBUG_MSG ("SSL.File->close: Shutdown after error\n");
+	int err = errno();
+	shutdown();
+	// Get here e.g. if a close callback calls close after an
+	// error, so never throw. (I'm somewhat suspicious to this,
+	// but I guess I did it with good reason.. :P /mast)
+	local_errno = err;
+	RETURN (0);
+      }, 0);
+
+    SSL3_DEBUG_MSG ("ssl_write_callback: Queuing close packet\n");
+    conn->send_close();
+    if (!linger_time) {
+      SSL3_DEBUG_MSG ("ssl_write_callback: Don't care about it being sent.\n");
+      conn->state = [int(0..0)|ConnectionState]
+	(conn->state | CONNECTION_local_closed);
+    }
+
+    // Even in nonblocking mode we call direct_write here to try to
+    // put the close packet in the send buffer before we return. That
+    // way it has a fair chance to get sent even when we're called
+    // from destroy() (in which case it won't work to just install the
+    // write callback as usual and wait for the backend to call it).
+
+    if (!direct_write()) {
+      // Should be shut down after close(), even if an error occurred.
+      int err = errno();
+      shutdown();
+      if (dont_throw) {
+	local_errno = err;
+	RETURN (0);
+      }
+      else if( err != System.EPIPE )
+	// Errors are normally thrown from close().
+        error ("Failed to close SSL connection: %s\n", strerror (err));
+    }
+
+    if (stream && (stream->query_read_callback() || stream->query_write_callback())) {
+      SSL3_DEBUG_MSG ("SSL.File->close: Close underway\n");
+      RESTORE;
+      update_internal_state();
+      return 1;
+    }
+    else {
+      // The local backend run by direct_write has typically already
+      // done this, but it might happen that it doesn't do anything in
+      // case close packets already have been exchanged.
+      shutdown();
+      SSL3_DEBUG_MSG ("SSL.File->close: Close done\n");
+    }
+  } LEAVE;
+  return 1;
+}
+
+protected void cleanup_on_error()
+// Called when any error occurs on the stream. (Doesn't handle errno
+// reporting since it might involve either local_errno and/or
+// cb_errno.)
+{
+  // The session should be purged when an error has occurred.
+  if (conn && conn->session)
+    // Check conn->session since it doesn't exist before the
+    // handshake.
+    conn->context->purge_session (conn->session);
+}
+
+Stdio.File shutdown()
+//! Shut down the SSL connection without sending any more packets.
+//!
+//! If the connection is open then the underlying (still open) stream
+//! is returned.
+//!
+//! If a nonclean (i.e. normal) close has been requested then the
+//! underlying stream is closed now if it wasn't closed already, and
+//! zero is returned.
+//!
+//! If a clean close has been requested (see the second argument to
+//! @[close]) then the behavior depends on the state of the close
+//! packet exchange: The first @[shutdown] call after a successful
+//! exchange returns the (still open) underlying stream, and later
+//! calls return zero and clears @[errno]. If the exchange hasn't
+//! finished then the stream is closed, zero is returned, and @[errno]
+//! will return @[System.EPIPE].
+//!
+//! @seealso
+//!   @[close], @[set_alert_callback]
+{
+  ENTER (0, 0) {
+    if (!stream || !conn) {
+      SSL3_DEBUG_MSG ("SSL.File->shutdown(): Already shut down\n");
+      RETURN (0);
+    }
+
+    if (close_state == STREAM_OPEN || close_state == NORMAL_CLOSE)
+      // If we didn't request a clean close then we pretend to have
+      // received a close message. According to the standard it's ok
+      // anyway as long as the transport isn't used for anything else.
+      conn->state |= CONNECTION_peer_closed;
+
+    SSL3_DEBUG_MSG ("SSL.File->shutdown(): %s %s\n",
+		    close_state != ABRUPT_CLOSE &&
+		    (conn->state &
+		     (CONNECTION_peer_closed | CONNECTION_local_closing)) ==
+		    (CONNECTION_peer_closed | CONNECTION_local_closing) &&
+		    !sizeof (write_buffer) ?
+		    "Proper close" :
+		    close_state == STREAM_OPEN ?
+		    "Not closed" :
+		    "Abrupt close",
+		    conn->describe_state());
+
+    if ((conn->state & CONNECTION_peer_closed) &&
+	sizeof (conn->left_over || "")) {
+#ifdef SSLFILE_DEBUG
+      werror ("Warning: Got buffered data after close in %O: %O%s\n", this,
+	      conn->left_over[..99], sizeof (conn->left_over) > 100 ? "..." : "");
+#endif
+      read_buffer = String.Buffer (sizeof (conn->left_over));
+      read_buffer->add (conn->left_over);
+      close_state = STREAM_OPEN;
+    }
+
+    .Constants.ConnectionState conn_state = conn->state;
+    destruct (conn);		// Necessary to avoid garbage.
+
+    write_buffer = ({});
+
+    if (got_extra_read_call_out > 0)
+      real_backend->remove_call_out (ssl_read_callback);
+    got_extra_read_call_out = 0;
+
+    Stdio.File stream = global::stream;
+    global::stream = 0;
+
+    stream->set_read_callback (0);
+    stream->set_write_callback (0);
+    stream->set_close_callback (0);
+
+    switch (close_state) {
+      case CLEAN_CLOSE:
+	if ((conn_state & CONNECTION_closed) == CONNECTION_closed) {
+	  SSL3_DEBUG_MSG ("SSL.File->shutdown(): Clean close - "
+			  "leaving stream\n");
+	  local_errno = 0;
+	  RETURN (stream);
+	}
+	else {
+	  SSL3_DEBUG_MSG ("SSL.File->shutdown(): Close packets not fully "
+			  "exchanged after clean close (%d) - closing stream\n",
+			  conn_state);
+	  stream->close();
+	  local_errno = System.EPIPE;
+	  RETURN (0);
+	}
+      case STREAM_OPEN:
+	close_state = STREAM_UNINITIALIZED;
+	SSL3_DEBUG_MSG ("SSL.File->shutdown(): Not closed - leaving stream\n");
+	local_errno = 0;
+	RETURN (stream);
+      default:
+	SSL3_DEBUG_MSG ("SSL.File->shutdown(): Nonclean close - closing stream\n");
+	// if (stream->linger) stream->linger(0);
+	stream->close();
+	local_errno = stream->errno() || local_errno;
+	RETURN (0);
+    }
+  } LEAVE;
+}
+
+protected void destroy()
+//! Try to close down the connection properly since it's customary to
+//! close files just by dropping them. No guarantee can be made that
+//! the close packet gets sent successfully though, because we can't
+//! risk blocking I/O here. You should call @[close] explicitly.
+//!
+//! @seealso
+//!   @[close]
+{
+  SSL3_DEBUG_MSG ("SSL.File->destroy()\n");
+
+  // We don't know which thread this will be called in if the refcount
+  // garb or the gc got here. That's not a race problem since it won't
+  // be registered in a backend in that case.
+  if (stream) {
+    // Make sure not to fail in ENTER below due to bad backend thread.
+    // [bug 6958].
+    stream->set_callbacks(0, 0, 0);
+  }
+  ENTER (0, 0) {
+    if (stream) {
+      if (close_state == STREAM_OPEN &&
+	  // Don't bother with closing nicely if there's an error from
+	  // an earlier operation. close() will throw an error for it.
+	  !cb_errno) {
+	// We can't use our set_nonblocking() et al here, since we
+	// might be associated with a backend in a different thread,
+	// and update_internal_state() will install callbacks, which
+	// in turn might trigger the tests in CHECK_CB_MODE().
+	stream->set_nonblocking();	// Make sure not to to block.
+	nonblocking_mode = 0;	// Make sure not to install any callbacks.
+	close (0, 0, 1);
+      }
+      else
+	shutdown();
+    }
+  } LEAVE;
+}
+
+string read (void|int length, void|int(0..1) not_all)
+//! Read some (decrypted) data from the connection. Works like
+//! @[Stdio.File.read].
+//!
+//! @note
+//! I/O errors from both reading and writing might occur in blocking
+//! mode.
+//!
+//! @seealso
+//!   @[write]
+{
+  SSL3_DEBUG_MSG ("SSL.File->read (%d, %d)\n", length, not_all);
+
+  ENTER (0, 0) {
+    if (close_state > STREAM_OPEN) error ("Not open.\n");
+
+    FIX_ERRNOS ({
+	SSL3_DEBUG_MSG ("SSL.File->read: Propagating old callback error: %s\n",
+			strerror (local_errno));
+	RETURN (0);
+      }, 0);
+
+    if (stream)
+      if (not_all) {
+	if (!sizeof (read_buffer))
+	  RUN_MAYBE_BLOCKING (!sizeof (read_buffer) &&
+			      !(conn->state & CONNECTION_peer_closed), 0, 1,
+			      if (sizeof (read_buffer)) {
+				// Got data to return first. Push the
+				// error back so it'll get reported by
+				// the next call.
+				cb_errno = local_errno;
+				local_errno = 0;
+			      }
+			      else RETURN (0););
+      }
+      else {
+	if (sizeof (read_buffer) < length || zero_type (length))
+	  RUN_MAYBE_BLOCKING ((sizeof (read_buffer) < length || zero_type (length)) &&
+			      !(conn->state & CONNECTION_peer_closed),
+			      nonblocking_mode, 1,
+			      if (sizeof (read_buffer)) {
+				// Got data to return first. Push the
+				// error back so it'll get reported by
+				// the next call.
+				cb_errno = local_errno;
+				local_errno = 0;
+			      }
+			      else RETURN (0););
+      }
+
+    string res = read_buffer->get();
+    if (!zero_type (length)) {
+      read_buffer->add (res[length..]);
+      res = res[..length-1];
+    }
+
+    SSL3_DEBUG_MSG ("SSL.File->read: Read done, returning %d bytes "
+		    "(%d still in buffer)\n",
+		    sizeof (res), sizeof (read_buffer));
+    RETURN (res);
+  } LEAVE;
+}
+
+int write (string|array(string) data, mixed... args)
+//! Write some (unencrypted) data to the connection. Works like
+//! @[Stdio.File.write] except that this function often buffers some data
+//! internally, so there's no guarantee that all the consumed data has
+//! been successfully written to the stream in nonblocking mode. It
+//! keeps the internal buffering to a minimum, however.
+//!
+//! @note
+//! This function returns zero if attempts are made to write data
+//! during the handshake phase and the mode is nonblocking.
+//!
+//! @note
+//! I/O errors from both reading and writing might occur in blocking
+//! mode.
+//!
+//! @seealso
+//!   @[read]
+{
+  if (sizeof (args))
+    data = sprintf (arrayp (data) ? data * "" : data, @args);
+
+  SSL3_DEBUG_MSG ("SSL.File->write (%t[%d])\n", data, sizeof (data));
+
+  ENTER (0, 0) {
+    if (close_state > STREAM_OPEN) error ("Not open.\n");
+
+    FIX_ERRNOS ({
+	SSL3_DEBUG_MSG ("SSL.File->write: Propagating old callback error: %s\n",
+			strerror (local_errno));
+	RETURN (-1);
+      }, 0);
+
+    if (nonblocking_mode && SSL_HANDSHAKING) {
+      SSL3_DEBUG_MSG ("SSL.File->write: "
+		      "Still in handshake - cannot accept application data\n");
+      RETURN (0);
+    }
+
+    // Take care of any old data first.
+    if (!direct_write()) RETURN (-1);
+
+    int written = 0;
+
+    if (arrayp (data)) {
+      int idx = 0, pos = 0;
+
+      while (idx < sizeof (data) && !sizeof (write_buffer) &&
+	     // Always stop after writing DATA_CHUNK_SIZE in
+	     // nonblocking mode, so that we don't loop here
+	     // arbitrarily long if the write is very large and the
+	     // bottleneck is in the encryption.
+	     (!nonblocking_mode || written < Stdio.DATA_CHUNK_SIZE)) {
+	int size = sizeof (data[idx]) - pos;
+	if (size > fragment_max_size) {
+	  // send_streaming_data will pick the first fragment_max_size
+	  // bytes of the string, so do that right away in the same
+	  // range operation.
+	  int n = conn->send_streaming_data (
+	    data[idx][pos..pos + fragment_max_size - 1]);
+	  SSL3_DEBUG_MSG ("SSL.File->write: Queued data[%d][%d..%d]\n",
+			  idx, pos, pos + n - 1);
+	  written += n;
+	  pos += n;
+	}
+
+	else {
+	  // Try to fill a packet.
+	  int end;
+	  for (end = idx + 1; end < sizeof (data); end++) {
+	    int newsize = size + sizeof (data[end]);
+	    if (newsize > fragment_max_size) break;
+	    size = newsize;
+	  }
+
+	  if (conn->send_streaming_data (
+		`+ (data[idx][pos..], @data[idx+1..end-1])) < size)
+	    error ("Unexpected fragment_max_size discrepancy wrt send_streaming_data.\n");
+
+	  SSL3_DEBUG_MSG ("SSL.File->write: "
+			  "Queued data[%d][%d..%d] + data[%d..%d]\n",
+			  idx, pos, sizeof (data[idx]) - 1, idx + 1, end - 1);
+	  written += size;
+	  idx = end;
+	  pos = 0;
+	}
+
+	if (!direct_write()) RETURN (written);
+      }
+    }
+
+    else			// data is a string.
+      while (written < sizeof (data) && !sizeof (write_buffer) &&
+	     // Limit the amount written in a single call, for the
+	     // same reason as above.
+	     (!nonblocking_mode || written < Stdio.DATA_CHUNK_SIZE)) {
+	int n = conn->send_streaming_data (
+	  data[written..written + fragment_max_size - 1]);
+	SSL3_DEBUG_MSG ("SSL.File->write: Queued data[%d..%d]\n",
+			written, written + n - 1);
+	written += n;
+	if (!direct_write()) RETURN (written);
+      }
+
+    SSL3_DEBUG_MSG ("SSL.File->write: Write %t done, accepted %d bytes\n",
+		    data, written);
+    RETURN (written);
+  } LEAVE;
+}
+
+int renegotiate()
+//! Renegotiate the connection by starting a new handshake. Note that
+//! the accept callback will be called again when the handshake is
+//! finished.
+//!
+//! Returns zero if there are any I/O errors. @[errno()] will give the
+//! details.
+//!
+//! @note
+//! The read buffer is not cleared - a @expr{read()@} afterwards will
+//! return data from both before and after the renegotiation.
+//!
+//! @bugs
+//! Data in the write queue in nonblocking mode is not properly
+//! written before resetting the connection. Do a blocking
+//! @expr{write("")@} first to avoid problems with that.
+{
+  SSL3_DEBUG_MSG ("SSL.File->renegotiate()\n");
+
+  ENTER (0, 0) {
+    if (close_state > STREAM_OPEN) error ("Not open.\n");
+
+    FIX_ERRNOS ({
+	SSL3_DEBUG_MSG ("SSL.File->renegotiate: "
+			"Propagating old callback error: %s\n",
+			strerror (local_errno));
+	RETURN (0);
+      }, 0);
+
+    if (!stream) {
+      SSL3_DEBUG_MSG ("SSL.File->renegotiate: "
+		      "Connection closed - simulating System.EPIPE\n");
+      cleanup_on_error();
+      local_errno = System.EPIPE;
+      RETURN (0);
+    }
+
+    // FIXME: Change this state with a packet instead so that things
+    // currently in the queue aren't affect by it.
+    conn->expect_change_cipher = 0;
+    conn->certificate_state = 0;
+    conn->state |= CONNECTION_handshaking;
+    update_internal_state();
+
+    conn->send_packet(conn->hello_request());
+
+    RETURN (direct_write());
+  } LEAVE;
+}
+
+void set_callbacks (void|function(mixed, string:int) read,
+		    void|function(mixed:int) write,
+		    void|function(mixed:int) close,
+		    void|function(mixed, string:int) read_oob,
+		    void|function(mixed:int) write_oob,
+		    void|function(void|mixed:int) accept)
+//! Installs all the specified callbacks at once. Use @[UNDEFINED]
+//! to keep the current setting for a callback.
+//!
+//! Like @[set_nonblocking], the callbacks are installed atomically.
+//! As opposed to @[set_nonblocking], this function does not do
+//! anything with the stream, and it doesn't even have to be open.
+//!
+//! @bugs
+//! @[read_oob] and @[write_oob] are currently ignored.
+//!
+//! @seealso
+//! @[set_read_callback], @[set_write_callback],
+//! @[set_close_callback], @[set_accept_callback], @[query_callbacks]
+{
+  SSL3_DEBUG_MSG ("SSL.File->set_callbacks (%O, %O, %O, %O, %O, %O)\n%s",
+		  read, write, close, read_oob, write_oob, accept,
+		  "" || describe_backtrace (backtrace()));
+
+  ENTER(0, 0) {
+
+    // Bypass the ::set_xxx_callback functions; we instead enable all
+    // the event bits at once through the _enable_callbacks call at the end.
+
+    if (!zero_type(read))
+      read_callback = read;
+
+    if (!zero_type(write))
+      write_callback = write;
+
+    if (!zero_type(close))
+      close_callback = close;
+
+    if (!zero_type(accept))
+      accept_callback = accept;
+
+    if (stream) update_internal_state();
+  } LEAVE;
+}
+
+//! @returns
+//!   Returns the currently set callbacks in the same order
+//!   as the arguments to @[set_callbacks].
+//!
+//! @seealso
+//!   @[set_callbacks], @[set_nonblocking]
+array(function(mixed,void|string:int)) query_callbacks()
+{
+  return ({
+    read_callback,
+    write_callback,
+    close_callback,
+    UNDEFINED,
+    UNDEFINED,
+    accept_callback,
+  });
+}
+
+void set_nonblocking (void|function(void|mixed,void|string:int) read,
+		      void|function(void|mixed:int) write,
+		      void|function(void|mixed:int) close,
+		      void|function(void|mixed:int) read_oob,
+		      void|function(void|mixed:int) write_oob,
+		      void|function(void|mixed:int) accept)
+//! Set the stream in nonblocking mode, installing the specified
+//! callbacks. The alert callback isn't touched.
+//!
+//! @note
+//! Prior to version 7.5.12, this function didn't set the accept
+//! callback.
+//!
+//! @bugs
+//! @[read_oob] and @[write_oob] are currently ignored.
+//!
+//! @seealso
+//!   @[set_callbacks], @[query_callbacks], @[set_nonblocking_keep_callbacks],
+//!   @[set_blocking]
+{
+  SSL3_DEBUG_MSG ("SSL.File->set_nonblocking (%O, %O, %O, %O, %O, %O)\n%s",
+		  read, write, close, read_oob, write_oob, accept,
+		  "" || describe_backtrace (backtrace()));
+
+  ENTER (0, 0) {
+    if (close_state > STREAM_OPEN) error ("Not open.\n");
+
+    nonblocking_mode = 1;
+
+    accept_callback = accept;
+    read_callback = read;
+    write_callback = write;
+    close_callback = close;
+
+    if (stream) {
+      stream->set_nonblocking_keep_callbacks();
+      // Has to restore here since a backend waiting in another thread
+      // might be woken immediately when callbacks are registered.
+      RESTORE;
+      update_internal_state();
+      return;
+    }
+  } LEAVE;
+}
+
+void set_nonblocking_keep_callbacks()
+//! Set nonblocking mode like @[set_nonblocking], but don't alter any
+//! callbacks.
+//!
+//! @seealso
+//!   @[set_nonblocking], @[set_blocking], @[set_blocking_keep_callbacks]
+{
+  SSL3_DEBUG_MSG ("SSL.File->set_nonblocking_keep_callbacks()\n");
+
+  ENTER (0, 0) {
+    if (close_state > STREAM_OPEN) error ("Not open.\n");
+
+    nonblocking_mode = 1;
+
+    if (stream) {
+      stream->set_nonblocking_keep_callbacks();
+      // Has to restore here since a backend waiting in another thread
+      // might be woken immediately when callbacks are registered.
+      RESTORE;
+      update_internal_state();
+      return;
+    }
+  } LEAVE;
+}
+
+void set_blocking()
+//! Set the stream in blocking mode. All but the alert callback are
+//! zapped.
+//!
+//! @note
+//! There might be some data still waiting to be written to the
+//! stream. That will be written in the next blocking call, regardless
+//! what it is.
+//!
+//! @note
+//! This function doesn't solve the case when the connection is used
+//! nonblocking in some backend thread and another thread switches it
+//! to blocking and starts using it. To solve that, put a call out in
+//! the backend from the other thread that switches it to blocking,
+//! and then wait until that call out has run.
+//!
+//! @note
+//! Prior to version 7.5.12, this function didn't clear the accept
+//! callback.
+//!
+//! @seealso
+//!   @[set_nonblocking], @[set_blocking_keep_callbacks],
+//!   @[set_nonblocking_keep_callbacks]
+{
+  // Previously this function wrote the remaining write buffer to the
+  // stream directly. But that can only be done safely if we implement
+  // a lock here to wait for any nonblocking operations to complete,
+  // and that could introduce a deadlock since there might be other
+  // lock dependencies between the threads.
+
+  SSL3_DEBUG_MSG ("SSL.File->set_blocking()\n%s",
+		  "" || describe_backtrace (backtrace()));
+
+  ENTER (0, 0) {
+    if (close_state > STREAM_OPEN) error ("Not open.\n");
+
+    nonblocking_mode = 0;
+    accept_callback = read_callback = write_callback = close_callback = 0;
+
+    if (stream) {
+      update_internal_state();
+      stream->set_blocking();
+    }
+  } LEAVE;
+}
+
+void set_blocking_keep_callbacks()
+//! Set blocking mode like @[set_blocking], but don't alter any
+//! callbacks.
+//!
+//! @seealso
+//!   @[set_blocking], @[set_nonblocking]
+{
+  SSL3_DEBUG_MSG ("SSL.File->set_blocking_keep_callbacks()\n");
+
+  ENTER (0, 0) {
+    if (close_state > STREAM_OPEN) error ("Not open.\n");
+
+    nonblocking_mode = 0;
+
+    if (stream) {
+      update_internal_state();
+      stream->set_blocking();
+    }
+  } LEAVE;
+}
+
+int errno()
+//! @returns
+//!   Returns the current error number for the connection.
+//!   Notable values are:
+//!   @int
+//!     @value 0
+//!       No error
+//!     @value System.EPIPE
+//!       Connection closed by other end.
+//!   @endint
+{
+  // We don't check threads for most other query functions, but
+  // looking at errno while doing I/O in another thread can't be done
+  // safely.
+  CHECK (0, 0);
+  return local_errno ? local_errno : stream && stream->errno();
+}
+
+void set_alert_callback (function(object,int|object,string:void) alert)
+//! Install a function that will be called when an alert packet is about
+//! to be sent. It doesn't affect the callback mode - it's called both
+//! from backends and from within normal function calls like @[read]
+//! and @[write].
+//!
+//! This callback can be used to implement fallback to other protocols
+//! when used on the server side together with @[shutdown()].
+//!
+//! @note
+//! This object is part of a cyclic reference whenever this is set,
+//! just like setting any other callback.
+//!
+//! @note
+//!   This callback is not cleared by @[set_blocking], or settable
+//!   by @[set_callbacks] or @[set_nonblocking]. It is also not
+//!   part of the set returned by @[query_callbacks].
+//!
+//! @seealso
+//!   @[query_alert_callback]
+{
+  SSL3_DEBUG_MSG ("SSL.File->set_alert_callback (%O)\n", alert);
+  CHECK (0, 0);
+#ifdef SSLFILE_DEBUG
+  if (close_state == STREAM_UNINITIALIZED || !conn)
+    error ("Doesn't have any connection.\n");
+#endif
+  conn->set_alert_callback (
+    alert &&
+    lambda (object packet, int|object seq_num, string alert_context) {
+      SSL3_DEBUG_MSG ("Calling alert callback %O\n", alert);
+      alert (packet, seq_num, alert_context);
+      alert_cb_called = 1;
+    });
+}
+
+function(object,int|object,string:void) query_alert_callback()
+//! @returns
+//!   Returns the current alert callback.
+//!
+//! @seealso
+//!   @[set_alert_callback]
+{
+  return conn && conn->alert_callback;
+}
+
+void set_accept_callback (function(void|object,void|mixed:int) accept)
+//! Install a function that will be called when the handshake is
+//! finished and the connection is ready for use.
+//!
+//! The callback function will be called with the File object and the
+//! additional id arguments (set with @[set_id]).
+//! 
+//! @note
+//! Like the read, write and close callbacks, installing this callback
+//! implies callback mode, even after the handshake is done.
+//!
+//! @seealso
+//!   @[set_nonblocking], @[set_callbacks],
+//!   @[query_accept_callback], @[query_callbacks]
+{
+  SSL3_DEBUG_MSG ("SSL.File->set_accept_callback (%O)\n", accept);
+  ENTER (0, 0) {
+#ifdef SSLFILE_DEBUG
+    if (close_state == STREAM_UNINITIALIZED)
+      error ("Doesn't have any connection.\n");
+#endif
+    accept_callback = accept;
+    if (stream) update_internal_state();
+  } LEAVE;
+}
+
+function(void|object,void|mixed:int) query_accept_callback()
+//! @returns
+//!   Returns the current accept callback.
+//!
+//! @seealso
+//!   @[set_accept_callback]
+{
+  return accept_callback;
+}
+
+void set_read_callback (function(void|mixed,void|string:int) read)
+//! Install a function to be called when data is available.
+//!
+//! @seealso
+//!   @[query_read_callback], @[set_nonblocking], @[query_callbacks]
+{
+  SSL3_DEBUG_MSG ("SSL.File->set_read_callback (%O)\n", read);
+  ENTER (0, 0) {
+#ifdef SSLFILE_DEBUG
+    if (close_state == STREAM_UNINITIALIZED)
+      error ("Doesn't have any connection.\n");
+#endif
+    read_callback = read;
+    if (stream) update_internal_state();
+  } LEAVE;
+}
+
+function(void|mixed,void|string:int) query_read_callback()
+//! @returns
+//!   Returns the current read callback.
+//!
+//! @seealso
+//!   @[set_read_callback], @[set_nonblocking], @[query_callbacks]
+{
+  return read_callback;
+}
+
+void set_write_callback (function(void|mixed:int) write)
+//! Install a function to be called when data can be written.
+//!
+//! @seealso
+//!   @[query_write_callback], @[set_nonblocking], @[query_callbacks]
+{
+  SSL3_DEBUG_MSG ("SSL.File->set_write_callback (%O)\n", write);
+  ENTER (0, 0) {
+#ifdef SSLFILE_DEBUG
+    if (close_state == STREAM_UNINITIALIZED)
+      error ("Doesn't have any connection.\n");
+#endif
+    write_callback = write;
+    if (stream) update_internal_state();
+  } LEAVE;
+}
+
+function(void|mixed:int) query_write_callback()
+//! @returns
+//!   Returns the current write callback.
+//!
+//! @seealso
+//!   @[set_write_callback], @[set_nonblocking], @[query_callbacks]
+{
+  return write_callback;
+}
+
+void set_close_callback (function(void|mixed:int) close)
+//! Install a function to be called when the connection is closed,
+//! either normally or due to an error (use @[errno] to retrieve it).
+//!
+//! @seealso
+//!   @[query_close_callback], @[set_nonblocking], @[query_callbacks]
+{
+  SSL3_DEBUG_MSG ("SSL.File->set_close_callback (%O)\n", close);
+  ENTER (0, 0) {
+#ifdef SSLFILE_DEBUG
+    if (close_state == STREAM_UNINITIALIZED)
+      error ("Doesn't have any connection.\n");
+#endif
+    close_callback = close;
+    if (stream) update_internal_state();
+  } LEAVE;
+}
+
+function(void|mixed:int) query_close_callback()
+//! @returns
+//!   Returns the current close callback.
+//!
+//! @seealso
+//!   @[set_close_callback], @[set_nonblocking], @[query_callbacks]
+{
+  return close_callback;
+}
+
+void set_id (mixed id)
+//! Set the value to be sent as the first argument to the
+//! callbacks installed by @[set_callbacks].
+//!
+//! @seealso
+//!   @[query_id]
+{
+  SSL3_DEBUG_MSG ("SSL.File->set_id (%O)\n", id);
+  CHECK (0, 0);
+  callback_id = id;
+}
+
+mixed query_id()
+//! @returns
+//!   Returns the currently set id.
+//!
+//! @seealso
+//!   @[set_id]
+{
+  return callback_id;
+}
+
+void set_backend (Pike.Backend backend)
+//! Set the backend used for the file callbacks.
+//!
+//! @seealso
+//!   @[query_backend]
+{
+  ENTER (0, 0) {
+    if (close_state > STREAM_OPEN) error ("Not open.\n");
+
+    if (stream) {
+      if (stream->query_backend() != local_backend)
+	stream->set_backend (backend);
+
+      if (got_extra_read_call_out > 0) {
+	real_backend->remove_call_out (ssl_read_callback);
+	backend->call_out (ssl_read_callback, 0, 1, 0);
+      }
+    }
+
+    real_backend = backend;
+  } LEAVE;
+}
+
+Pike.Backend query_backend()
+//! Return the backend used for the file callbacks.
+//!
+//! @seealso
+//!   @[set_backend]
+{
+  if (close_state > STREAM_OPEN) error ("Not open.\n");
+  return real_backend;
+}
+
+string query_address(int|void arg)
+//! @returns
+//!   Returns the address and port of the connection.
+//!
+//!   See @[Stdio.File.query_address] for details.
+//!
+//! @seealso
+//!   @[Stdio.File.query_address]
+{
+  if (close_state > STREAM_OPEN) error ("Not open.\n");
+  return stream->query_address(arg);
+}
+
+int is_open()
+//! @returns
+//! Returns nonzero if the stream currently is open, zero otherwise.
+//!
+//! This function does nonblocking I/O to check for a close packet in
+//! the input buffer.
+//!
+//! If a clean close has been requested in nonblocking mode, then 2 is
+//! returned until the close packet exchanged has been completed.
+//!
+//! @note
+//! In Pike 7.8 and earlier, this function returned zero in the case
+//! above where it now returns 2.
+{
+  SSL3_DEBUG_MSG ("SSL.File->is_open()\n");
+  ENTER (0, 0) {
+    if ((close_state == STREAM_OPEN || close_state == CLEAN_CLOSE) &&
+	stream && stream->is_open()) {
+      // When close_state == STREAM_OPEN, we have to check if there's
+      // a close packet waiting to be read. This is common in
+      // keep-alive situations since the remote end might have sent a
+      // close packet and closed the connection a long time ago, and
+      // in a typical blocking client no action has been taken causing
+      // the close packet to be read.
+      //
+      // If close_state == CLEAN_CLOSE, we should return true as long
+      // as close packets haven't been exchanged in both directions.
+      //
+      // Could avoid the whole local backend hoopla here by
+      // essentially doing a peek and call ssl_read_callback directly,
+      // but that'd lead to subtle code duplication. (Also, peek is
+      // currently not implemented on NT.)
+      ConnectionState closed = conn->state & CONNECTION_closed;
+      if ((close_state == CLEAN_CLOSE ?
+	   closed != CONNECTION_closed : !closed))
+	RUN_MAYBE_BLOCKING (
+	  action && (close_state == CLEAN_CLOSE ?
+		     (conn->state & CONNECTION_closed) != CONNECTION_closed :
+		     !(conn->state & CONNECTION_closed)),
+	  1, 1,
+	  RETURN (!epipe_errnos[local_errno]));
+      closed = conn->state & CONNECTION_closed;
+      RETURN (close_state == CLEAN_CLOSE ?
+	      ((closed != CONNECTION_closed) && 2) : !closed);
+    }
+  } LEAVE;
+  return 0;
+}
+
+Stdio.File query_stream()
+//! Return the underlying stream.
+//!
+//! @note
+//! Avoid any temptation to do
+//! @expr{destruct(file_obj->query_stream())@}. That almost certainly
+//! creates more problems than it solves.
+//!
+//! You probably want to use @[shutdown].
+//!
+//! @seealso
+//!   @[shutdown]
+{
+  SSL3_DEBUG_MSG ("SSL.File->query_stream(): Called from %s:%d\n",
+		  backtrace()[-2][0], backtrace()[-2][1]);
+  return stream;
+}
+
+.Connection query_connection()
+//! Return the SSL connection object.
+//!
+//! This returns the low-level @[SSL.connection] object.
+{
+  SSL3_DEBUG_MSG ("SSL.File->query_connection(): Called from %s:%d\n",
+		  backtrace()[-2][0], backtrace()[-2][1]);
+  return conn;
+}
+
+SSL.Context query_context()
+//! Return the SSL context object.
+{
+  return conn && conn->context;
+}
+
+string _sprintf(int t) {
+  return t=='O' && sprintf("SSL.File(%O)", stream && stream->_fd);
+}
+
+
+protected void update_internal_state (void|int assume_real_backend)
+// Update the internal callbacks according to the current state. Does
+// nothing if the local backend is active, unless assume_real_backend
+// is set, in which case we're installing callbacks for the real
+// backend anyway (necessary to avoid races when we're about to switch
+// from the local to the real backend).
+{
+  // When the local backend is used, callbacks are set explicitly
+  // before it's started.
+  if (assume_real_backend || stream->query_backend() != local_backend) {
+    mixed install_read_cbs, install_write_cb;
+
+    if (nonblocking_mode &&
+	(SSL_HANDSHAKING || close_state >= NORMAL_CLOSE)) {
+      // Normally we never install our own callbacks if we aren't in
+      // callback mode, but handling a nonblocking close is an
+      // exception.
+      install_read_cbs = SSL_INTERNAL_READING;
+      install_write_cb = SSL_INTERNAL_WRITING;
+
+      SSL3_DEBUG_MORE_MSG ("update_internal_state: "
+			   "%s [r:%O w:%O rcb:%O]\n",
+			   SSL_HANDSHAKING ? "Handshaking" : "After close",
+			   !!install_read_cbs, !!install_write_cb,
+			   got_extra_read_call_out);
+    }
+
+    // CALLBACK_MODE but slightly optimized below.
+    else if (read_callback || write_callback || close_callback || accept_callback) {
+      install_read_cbs = (read_callback || close_callback || accept_callback ||
+			  SSL_INTERNAL_READING);
+      install_write_cb = (write_callback || SSL_INTERNAL_WRITING);
+
+      SSL3_DEBUG_MORE_MSG ("update_internal_state: "
+			   "Callback mode [r:%O w:%O rcb:%O]\n",
+			   !!install_read_cbs, !!install_write_cb,
+			   got_extra_read_call_out);
+    }
+
+    else {
+      // Not in callback mode. Can't install callbacks even though we'd
+      // "need" to - have to cope with the local backend in each
+      // operation instead.
+      SSL3_DEBUG_MORE_MSG ("update_internal_state: "
+			   "Not in callback mode [rcb:%O]\n",
+			   got_extra_read_call_out);
+    }
+
+    if (!got_extra_read_call_out && sizeof (read_buffer) && read_callback)
+      // Got buffered read data and there's someone ready to receive it,
+      // so schedule a call to ssl_read_callback to handle it.
+      got_extra_read_call_out = -1;
+
+    // If got_extra_read_call_out is set here then we wait with all
+    // other callbacks so that the extra ssl_read_callback call is
+    // carried out before anything else.
+
+    if (install_read_cbs) {
+      if (got_extra_read_call_out < 0) {
+	real_backend->call_out (ssl_read_callback, 0, 1, 0);
+	got_extra_read_call_out = 1;
+      }
+      else {
+	stream->set_read_callback (ssl_read_callback);
+	stream->set_close_callback (ssl_close_callback);
+      }
+    }
+    else {
+      stream->set_read_callback (0);
+      // Installing a close callback without a read callback
+      // currently doesn't work well in Stdio.File.
+      stream->set_close_callback (0);
+      if (got_extra_read_call_out > 0) {
+	real_backend->remove_call_out (ssl_read_callback);
+	got_extra_read_call_out = -1;
+      }
+    }
+
+    stream->set_write_callback (install_write_cb && !got_extra_read_call_out &&
+				ssl_write_callback);
+
+#ifdef SSLFILE_DEBUG
+    if (!assume_real_backend && op_thread)
+      // Check that we haven't installed callbacks that might start
+      // executing in parallell in another thread. That's legitimate
+      // in some cases (e.g. set_nonblocking) but in those cases we're
+      // called after RESTORE or LEAVE, which has reset op_thread, so
+      // we skip this check if op_thread is zero.
+      CHECK_CB_MODE (THIS_THREAD());
+#endif
+  }
+
+  else
+    SSL3_DEBUG_MORE_MSG ("update_internal_state: "
+			 "In local backend - nothing done [rcb:%O]\n",
+			 got_extra_read_call_out);
+}
+
+protected int queue_write()
+// Return 0 if the connection is still alive, 1 if it was closed
+// politely, and -1 if it died unexpectedly (specifically, our side
+// has sent a fatal alert packet (not close notify) for some reason
+// and therefore nullified the connection).
+{
+  if (!conn) return -1;
+
+  // Estimate how much data there is in the write_buffer.
+  int got = sizeof(write_buffer) &&
+    (sizeof(write_buffer[-1]) * sizeof(write_buffer));
+
+  int buffer_limit = 16384;
+  if (conn->state & CONNECTION_closing) buffer_limit = 1;
+
+  while (got < buffer_limit) {
+    int|string res = conn->to_write();
+
+#ifdef SSL3_DEBUG_TRANSPORT
+    werror ("queue_write: To write: %O\n", res);
+#endif
+
+    if (!stringp(res)) {
+      SSL3_DEBUG_MSG ("queue_write: Connection closed %s\n",
+		      res == 1 ? "normally" : "abruptly");
+      return res;
+    }
+
+    if (res == "") {
+      SSL3_DEBUG_MSG ("queue_write: Got nothing to write (%d strings buffered)\n",
+		      sizeof (write_buffer));
+      break;
+    }
+
+    int was_empty = !sizeof (write_buffer);
+    write_buffer += ({ res });
+    got += sizeof(res);
+
+    SSL3_DEBUG_MSG ("queue_write: Got %d bytes to write (%d strings buffered)\n",
+		    sizeof (res), sizeof (write_buffer));
+    if (was_empty && stream)
+      update_internal_state();
+  }
+
+  SSL3_DEBUG_MSG ("queue_write: Returning 0 (%d strings buffered)\n",
+		  sizeof(write_buffer));
+
+  return 0;
+}
+
+protected int direct_write()
+// Do a write directly (and maybe also read if there's internal
+// reading to be done). Something to write is assumed to exist (either
+// in write_buffer or in the packet queue). Returns zero on error (as
+// opposed to queue_write).
+{
+  if (!stream) {
+    SSL3_DEBUG_MSG ("direct_write: "
+		    "Connection already closed - simulating System.EPIPE\n");
+    // If it was closed explicitly locally then close_state would be
+    // set and we'd never get here, so we can report it as a remote
+    // close.
+    cleanup_on_error();
+    local_errno = System.EPIPE;
+    return 0;
+  }
+
+  else {
+    if (queue_write() < 0 || close_state == ABRUPT_CLOSE) {
+      SSL3_DEBUG_MSG ("direct_write: "
+		      "Connection closed abruptly - simulating System.EPIPE\n");
+      cleanup_on_error();
+      local_errno = System.EPIPE;
+      // Don't set close_state = ABRUPT_CLOSE here. It's either set
+      // already, or there's an abrupt close due to an ALERT_fatal,
+      // which we shouldn't mix up with ABRUPT_CLOSE.
+      return 0;
+    }
+
+    if (SSL_INTERNAL_WRITING || SSL_INTERNAL_READING)
+      RUN_MAYBE_BLOCKING (SSL_INTERNAL_WRITING || SSL_INTERNAL_READING,
+			  nonblocking_mode, SSL_INTERNAL_READING,
+			  SSL3_DEBUG_MORE_MSG ("direct_write: Got error\n");
+			  return 0;);
+  }
+
+  SSL3_DEBUG_MORE_MSG ("direct_write: Ok\n");
+  return 1;
+}
+
+protected int ssl_read_callback (int called_from_real_backend, string input)
+{
+  SSL3_DEBUG_MSG ("ssl_read_callback (%O, %s): "
+		  "nonblocking mode=%d, callback mode=%d %s%s\n",
+		  called_from_real_backend,
+		  input ? "string[" + sizeof (input) + "]" : "0 (queued extra call)",
+		  nonblocking_mode, !!(CALLBACK_MODE),
+		  conn ? conn->describe_state() : "not connected",
+		  conn && SSL_CLOSING_OR_CLOSED ?
+		  ", closing (" + close_state + ")" : "");
+
+  ENTER (1, called_from_real_backend) {
+    int call_accept_cb;
+
+    if (input) {
+      int handshake_already_finished = !(conn->state & CONNECTION_handshaking);
+      string|int data =
+	close_state == ABRUPT_CLOSE ? -1 : conn->got_data (input);
+
+#ifdef SSLFILE_DEBUG
+      if (got_extra_read_call_out)
+	error ("Got to real read callback with queued extra read call out.\n");
+#endif
+
+#ifdef SSL3_DEBUG_TRANSPORT
+      werror ("ssl_read_callback: Got data: %O\n", data);
+#endif
+
+      if (alert_cb_called)
+	SSL3_DEBUG_MSG ("ssl_read_callback: After alert cb call\n");
+
+      else {
+	int write_res;
+	if (stringp(data) || (data > 0) ||
+	    ((data < 0) && (conn->state & CONNECTION_handshaking))) {
+#ifdef SSLFILE_DEBUG
+	  if (!stream)
+	    error ("Got zapped stream in callback.\n");
+#endif
+	  // got_data might have put more packets in the write queue if
+	  // we're handshaking.
+	  write_res = queue_write();
+	}
+
+	cb_errno = 0;
+
+	if (stringp (data)) {
+	  if (!handshake_already_finished &&
+	      !(conn->state & CONNECTION_handshaking)) {
+	    SSL3_DEBUG_MSG ("ssl_read_callback: Handshake finished\n");
+	    update_internal_state();
+	    if (called_from_real_backend && accept_callback) {
+#ifdef SSLFILE_DEBUG
+	      if (close_state >= NORMAL_CLOSE)
+		error ("Didn't expect the connection to be "
+		       "explicitly closed already.\n");
+#endif
+	      call_accept_cb = 1;
+	    }
+	  }
+
+	  SSL3_DEBUG_MSG ("ssl_read_callback: "
+			  "Got %d bytes of application data\n", sizeof (data));
+	  read_buffer->add (data);
+	}
+
+	else if (data < 0 || write_res < 0) {
+	  SSL3_DEBUG_MSG ("ssl_read_callback: "
+			  "Got abrupt remote close - simulating System.EPIPE\n");
+	  cleanup_on_error();
+	  cb_errno = System.EPIPE;
+	}
+
+	// Don't use data > 0 here since we might have processed some
+	// application data and a close in the same got_data call.
+	if (conn->state & CONNECTION_peer_closed) {
+	  SSL3_DEBUG_MSG ("ssl_read_callback: Got close packet\n");
+	}
+      }
+    }
+
+    else {
+      // This is another call that has been queued below through a
+      // call out. That is necessary whenever we need to call several
+      // user callbacks from the same invocation: We can't do anything
+      // after calling a user callback, since the user is free to e.g.
+      // remove the callbacks and "hand over" us to another thread and
+      // do more I/O there while this one returns. We solve this
+      // problem by queuing a call out to ourselves (with zero as
+      // input) to continue.
+      got_extra_read_call_out = 0;
+      update_internal_state();
+    }
+
+    // Figure out what we need to do. call_accept_cb is already set
+    // from above.
+    int(0..1) call_read_cb;
+    int(0..1) do_close_stuff;
+    if (alert_cb_called) {
+      if (!conn) {
+	SSL3_DEBUG_MSG ("ssl_read_callback: Shut down from alert callback\n");
+	RESTORE;
+	return 0;
+      }
+      // Make sure the alert is queued for writing.
+      queue_write();
+    }
+    else {
+      call_read_cb =
+	called_from_real_backend && read_callback && sizeof (read_buffer) &&
+	// Shouldn't get here when close_state == ABRUPT_CLOSE.
+	close_state < NORMAL_CLOSE;
+      do_close_stuff =
+	!!((conn->state & CONNECTION_peer_closed) || cb_errno);
+    }
+
+    if (alert_cb_called || call_accept_cb + call_read_cb + do_close_stuff > 1) {
+      // Need to do a call out to ourselves; see comment above.
+#ifdef SSLFILE_DEBUG
+      if (!alert_cb_called && !called_from_real_backend)
+	error ("Internal confusion.\n");
+      if (called_from_real_backend && got_extra_read_call_out < 0)
+	error ("Ended up in ssl_read_callback from real backend "
+	       "when no callbacks are supposed to be installed.\n");
+#endif
+      if (!got_extra_read_call_out) {
+	got_extra_read_call_out = -1;
+	SSL3_DEBUG_MSG ("ssl_read_callback: Too much to do (%O, %O, %O, %O) - "
+			"scheduled another call\n", alert_cb_called, call_accept_cb,
+			call_read_cb, do_close_stuff);
+	update_internal_state();
+      }
+      else
+	SSL3_DEBUG_MSG ("ssl_read_callback: Too much to do (%O, %O, %O, %O) - "
+			"another call already queued\n",
+			alert_cb_called, call_accept_cb, call_read_cb, do_close_stuff);
+      alert_cb_called = 0;
+    }
+
+    else
+      if (got_extra_read_call_out) {
+	// Don't know if this actually can happen, but it's symmetric.
+	if (got_extra_read_call_out > 0)
+	  real_backend->remove_call_out (ssl_read_callback);
+	got_extra_read_call_out = 0;
+	update_internal_state();
+      }
+
+    // Now actually do (a bit of) what should be done.
+
+    if (call_accept_cb) {
+      SSL3_DEBUG_MSG ("ssl_read_callback: Calling accept callback %O\n",
+		      accept_callback);
+      RESTORE;
+      return accept_callback (this, callback_id);
+    }
+
+    else if (call_read_cb) {
+      string received = read_buffer->get();
+      SSL3_DEBUG_MSG ("call_read_callback: Calling read callback %O "
+		      "with %d bytes\n", read_callback, sizeof (received));
+      // Never called if there's an error - no need to propagate cb_errno.
+      RESTORE;
+      return read_callback (callback_id, received);
+    }
+
+    else if (do_close_stuff) {
+#ifdef SSLFILE_DEBUG
+      if (got_extra_read_call_out)
+	error ("Shouldn't have more to do after close stuff.\n");
+#endif
+
+      if ((conn->state & (CONNECTION_peer_closed | CONNECTION_local_closing)) ==
+	  CONNECTION_peer_closed) {
+	// Deinstall read side cbs to avoid reading more and install
+	// the write cb to send the close packet. Can't use
+	// update_internal_state here since we should force a
+	// deinstall of the read side cbs even when we're still in
+	// callback mode, to correctly imitate the behavior in
+	// Stdio.File (it deinstalls read side cbs whenever the close
+	// cb is called, but it's possible to reinstall the close cb
+	// later and get another call to it).
+	if (stream->query_backend() != local_backend) {
+	  stream->set_read_callback (0);
+	  stream->set_close_callback (0);
+	  stream->set_write_callback (ssl_write_callback);
+	  SSL3_DEBUG_MORE_MSG ("ssl_read_callback: Setting cbs for close [r:0 w:1]\n");
+	}
+      }
+
+      if (called_from_real_backend && close_callback) {
+	// errno() should return the error in the close callback - need to
+	// propagate it here.
+	FIX_ERRNOS (
+	  SSL3_DEBUG_MSG ("ssl_read_callback: Calling close callback %O (error %s)\n",
+			  close_callback, strerror (local_errno)),
+	  SSL3_DEBUG_MSG ("ssl_read_callback: Calling close callback %O (read eof)\n",
+			  close_callback)
+	);
+	RESTORE;
+	return close_callback (callback_id);
+      }
+
+      if (close_state >= NORMAL_CLOSE) {
+	SSL3_DEBUG_MSG ("ssl_read_callback: "
+			"In or after local close - shutting down\n");
+	shutdown();
+      }
+
+      if (cb_errno) {
+	SSL3_DEBUG_MSG ("ssl_read_callback: Returning with error\n");
+	// Make sure the local backend exits after this, so that the
+	// error isn't clobbered by later I/O.
+	RESTORE;
+	return -1;
+      }
+    }
+
+  } LEAVE;
+  return 0;
+}
+
+protected int ssl_write_callback (int called_from_real_backend)
+{
+  SSL3_DEBUG_MSG ("ssl_write_callback (%O): "
+		  "nonblocking mode=%d, callback mode=%d %s%s\n",
+		  called_from_real_backend,
+		  nonblocking_mode, !!(CALLBACK_MODE),
+		  conn ? conn->describe_state() : "",
+		  conn && SSL_CLOSING_OR_CLOSED ?
+		  ", closing (" + close_state + ")" : "");
+
+  int ret = 0;
+
+  ENTER (1, called_from_real_backend) {
+#ifdef SSLFILE_DEBUG
+    if (!stream)
+      error ("Got zapped stream in callback.\n");
+    if (got_extra_read_call_out)
+      error ("Got to write callback with queued extra read call out.\n");
+#endif
+
+  write_to_stream:
+    do {
+      if (sizeof (write_buffer)) {
+	int written;
+#ifdef SIMULATE_CLOSE_PACKET_WRITE_FAILURE
+	if (conn->state & CONNECTION_local_closing)
+	  written = -1;
+	else
+#endif
+	  written = stream->write (write_buffer);
+
+	if (written < 0 ) {
+#ifdef SIMULATE_CLOSE_PACKET_WRITE_FAILURE
+	  if (conn->state & CONNECTION_local_closing)
+	    cb_errno = System.EPIPE;
+	  else
+#endif
+	    cb_errno = stream->errno();
+	  cleanup_on_error();
+	  SSL3_DEBUG_MSG ("ssl_write_callback: Write failed: %s\n",
+			  strerror (cb_errno));
+
+	  // Make sure the local backend exits after this, so that the
+	  // error isn't clobbered by later I/O.
+	  ret = -1;
+
+	  if (conn->state & CONNECTION_local_closing &&
+	      epipe_errnos[cb_errno]) {
+	    // See if it's an error from writing a close packet that
+	    // should be ignored.
+	    write_buffer = ({}); // No use trying to write the close again.
+
+	    if (close_state == CLEAN_CLOSE) {
+	      // Never accept a failure if a clean close is requested.
+	      if (!(conn->state & CONNECTION_failing)) {
+		// Make sure that the connection is failing.
+		conn->send_packet(conn->alert(ALERT_fatal, ALERT_close_notify));
+	      }
+	      update_internal_state();
+	    }
+
+	    else {
+	      cb_errno = 0;
+
+	      if (conn->state & CONNECTION_peer_closed)
+		SSL3_DEBUG_MSG ("ssl_write_callback: Stream closed properly "
+				"remotely - ignoring failure to send close packet\n");
+
+	      else {
+		SSL3_DEBUG_MSG ("ssl_write_callback: Stream closed remotely - "
+				"checking input buffer for proper remote close\n");
+		update_internal_state();
+		if (called_from_real_backend) {
+		  // Shouldn't wait for a close packet that might
+		  // arrive later on, so we start a nonblocking local
+		  // backend to check for it. If we're already in a
+		  // local backend, this is handled by special cases
+		  // in RUN_MAYBE_BLOCKING.
+		  RUN_MAYBE_BLOCKING (
+		    (!(conn->state & CONNECTION_peer_closed)),
+		    1, 1, {});
+		}
+		else {
+		  // Can't start a nested local backend - skip out to
+		  // the one we're called from.
+		  RESTORE;
+		  return ret;
+		}
+	      }
+	    }
+	  }
+
+	  // Still try to call the write (or close) callback to
+	  // propagate the error to it.
+	  break write_to_stream;
+	}
+
+	for (int bytes = written; bytes > 0;) {
+	  if (bytes >= sizeof(write_buffer[0])) {
+	    bytes -= sizeof(write_buffer[0]);
+	    write_buffer = write_buffer[1..];
+	  } else {
+	    write_buffer[0] = write_buffer[0][bytes..];
+	    bytes = 0;
+	    break;
+	  }
+	}
+
+	SSL3_DEBUG_MSG ("ssl_write_callback: Wrote %d bytes (%d strings left)\n",
+			written, sizeof (write_buffer));
+	if (sizeof (write_buffer)) {
+	  RESTORE;
+	  return ret;
+	}
+	update_internal_state();
+      }
+
+      if (int err = queue_write()) {
+	if (err > 0) {
+#ifdef SSLFILE_DEBUG
+	  if (!(conn->state & CONNECTION_closed))
+	    error ("Expected a close to be sent or received\n");
+#endif
+
+	  if (sizeof (write_buffer))
+	    SSL3_DEBUG_MSG ("ssl_write_callback: "
+			    "Close packet queued but not yet sent\n");
+	  else {
+#ifdef SSLFILE_DEBUG
+	    if (!(conn->state & CONNECTION_local_closed))
+	      error ("Expected a close packet to be queued or sent.\n");
+#endif
+	    if (conn->state & CONNECTION_peer_closed ||
+		close_state != CLEAN_CLOSE) {
+	      SSL3_DEBUG_MSG ("ssl_write_callback: %s\n",
+			      conn->state & CONNECTION_peer_closed ?
+			      "Close packets exchanged" : "Close packet sent");
+	      break write_to_stream;
+	    }
+	    else {
+	      SSL3_DEBUG_MSG ("ssl_write_callback: "
+			      "Close packet sent - expecting response\n");
+	      // Not SSL_INTERNAL_WRITING anymore.
+	      update_internal_state();
+	    }
+	  }
+
+	  RESTORE;
+	  return ret;
+	}
+
+	else {
+	  SSL3_DEBUG_MSG ("ssl_write_callback: "
+			  "Connection closed abruptly remotely - "
+			  "simulating System.EPIPE\n");
+	  cleanup_on_error();
+	  cb_errno = System.EPIPE;
+	  ret = -1;
+	  break write_to_stream;
+	}
+      }
+    } while (sizeof (write_buffer));
+
+    if (called_from_real_backend) {
+      if (conn->state & CONNECTION_local_closed) {
+	if (close_callback && cb_errno && close_state < NORMAL_CLOSE) {
+	  // Better signal errors writing the close packet to the
+	  // close callback.
+	  FIX_ERRNOS (
+	    SSL3_DEBUG_MSG ("ssl_write_callback: Calling close callback %O "
+			    "(error %s)\n", close_callback, strerror (local_errno)),
+	    0
+	  );
+	  RESTORE;
+	  return close_callback (callback_id);
+	}
+      }
+
+      if (write_callback && !SSL_HANDSHAKING
+	  && (close_state < NORMAL_CLOSE || cb_errno)) {
+	// errno() should return the error in the write callback - need
+	// to propagate it here.
+	FIX_ERRNOS (
+	  SSL3_DEBUG_MSG ("ssl_write_callback: Calling write callback %O "
+			  "(error %s)\n", write_callback, strerror (local_errno)),
+	  SSL3_DEBUG_MSG ("ssl_write_callback: Calling write callback %O\n",
+			  write_callback)
+	);
+	RESTORE;
+	return write_callback (callback_id);
+      }
+    }
+
+    if ((close_state >= NORMAL_CLOSE &&
+	 (conn->state & CONNECTION_local_closing)) || cb_errno) {
+      SSL3_DEBUG_MSG ("ssl_write_callback: "
+		      "In or after local close - shutting down\n");
+      shutdown();
+    }
+  } LEAVE;
+  return ret;
+}
+
+protected int ssl_close_callback (int called_from_real_backend)
+{
+  SSL3_DEBUG_MSG ("ssl_close_callback (%O): "
+		  "nonblocking mode=%d, callback mode=%d %s%s\n",
+		  called_from_real_backend,
+		  nonblocking_mode, !!(CALLBACK_MODE),
+		  conn ? conn->describe_state() : "",
+		  conn && SSL_CLOSING_OR_CLOSED ?
+		  ", closing (" + close_state + ")" : "");
+
+  ENTER (1, called_from_real_backend) {
+#ifdef SSLFILE_DEBUG
+    if (!stream)
+      error ("Got zapped stream in callback.\n");
+    if (got_extra_read_call_out)
+      error ("Got to close callback with queued extra read call out.\n");
+#endif
+
+    // If we've arrived here due to an error, let it override any
+    // older errno from an earlier callback.
+    if (int new_errno = stream->errno()) {
+      SSL3_DEBUG_MSG ("ssl_close_callback: Got error %s\n", strerror (new_errno));
+      cleanup_on_error();
+      cb_errno = new_errno;
+    }
+#ifdef SSL3_DEBUG
+    else if (cb_errno)
+      SSL3_DEBUG_MSG ("ssl_close_callback: Propagating errno from another callback\n");
+#endif
+
+    if (!cb_errno) {
+      if (!conn || conn->state & CONNECTION_peer_closed)
+	SSL3_DEBUG_MSG ("ssl_close_callback: After clean close\n");
+
+      else {
+	// The remote end has closed the connection without sending a
+	// close packet.
+	//
+	// This became legal by popular demand in TLS 1.1.
+	SSL3_DEBUG_MSG ("ssl_close_callback: Did not get a remote close.\n");
+	conn->state = [int(0..0)|ConnectionState]
+	  (conn->state | CONNECTION_peer_closed);
+	SSL3_DEBUG_MSG ("ssl_close_callback: Abrupt close - "
+			"simulating System.EPIPE\n");
+	cleanup_on_error();
+	cb_errno = System.EPIPE;
+	close_state = ABRUPT_CLOSE;
+      }
+    }
+
+    if (called_from_real_backend && close_callback) {
+      // errno() should return the error in the close callback - need to
+      // propagate it here.
+      FIX_ERRNOS (
+	SSL3_DEBUG_MSG ("ssl_close_callback: Calling close callback %O (error %s)\n",
+			close_callback, strerror (local_errno)),
+	SSL3_DEBUG_MSG ("ssl_close_callback: Calling close callback %O (read eof)\n",
+			close_callback)
+      );
+      RESTORE;
+      // Note that the callback should call close() (or free things
+      // so that we get destructed) - there's no need for us to
+      // schedule a shutdown after it.
+      return close_callback (callback_id);
+    }
+
+    if (close_state >= NORMAL_CLOSE) {
+      SSL3_DEBUG_MSG ("ssl_close_callback: "
+		      "In or after local close - shutting down\n");
+      shutdown();
+    }
+  } LEAVE;
+
+  // Make sure the local backend exits after this, so that the error
+  // isn't clobbered by later I/O.
+  return -1;
+}
+
+//! The next protocol chosen by the client during application layer
+//! protocol negotiation (ALPN) or next protocol negotiation (NPN).
+string `->next_protocol() {
+    return conn->next_protocol;
+}
+
+//! Return the currently active cipher suite.
+int query_suite()
+{
+  return conn?->session?->cipher_suite;
+}
diff --git a/lib/modules/SSL.pmod/Port.pike b/lib/modules/SSL.pmod/Port.pike
new file mode 100644
index 0000000000000000000000000000000000000000..f51b2c60d83f2c784e1966cf246cc09f24300904
--- /dev/null
+++ b/lib/modules/SSL.pmod/Port.pike
@@ -0,0 +1,172 @@
+#pike __REAL_VERSION__
+#require constant(SSL.Cipher)
+
+#define Context .Context
+
+//! Interface similar to @[Stdio.Port].
+
+//!
+inherit Stdio.Port : socket;
+
+//! Context to use for the connections.
+Context ctx;
+
+protected ADT.Queue accept_queue = ADT.Queue();
+
+//!
+function(object, mixed|void:void) accept_callback;
+
+//! @decl void finished_callback(SSL.File f, mixed|void id)
+//!
+//! SSL connection accept callback.
+//!
+//! @param f
+//!   The @[File] that just finished negotiation.
+//!
+//! This function is installed as the @[File] accept callback by
+//! @[ssl_callback()], and enqueues the newly negotiated @[File] on
+//! the accept queue.
+//!
+//! If there has been an @[accept_callback] installed by @[bind()] or
+//! @[listen_fd()], it will be called with all pending @[File]s on the
+//! accept queue.
+//!
+//! If there's no @[accept_callback], then the @[File] will have to be
+//! retrieved from the queue by calling @[accept()].
+void finished_callback(object f, mixed|void id)
+{
+  accept_queue->put(f);
+  while (accept_callback && sizeof(accept_queue))
+  {
+    accept_callback(f, id);
+  }
+}
+
+//! Connection accept callback.
+//!
+//! This function is installed as the @[Stdio.Port] callback, and
+//! accepts the connection and creates a corresponding @[File] with
+//! @[finished_callback()] as the accept callback.
+//!
+//! @seealso
+//!   @[bind()], @[finished_callback()]
+void ssl_callback(mixed id)
+{
+  object f = socket_accept();
+  if (f)
+  {
+    object ssl_fd = .File(f, ctx);
+    ssl_fd->accept();
+    ssl_fd->set_accept_callback(finished_callback);
+  }
+}
+
+#if 0
+void set_id(mixed id)
+{
+  error( "Not supported\n" );
+}
+
+mixed query_id()
+{
+  error( "Not supported\n" );
+}
+#endif
+
+//! @decl int bind(int port, @
+//!                function(SSL.File|void, mixed|void: int) callback, @
+//!                string|void ip)
+//!
+//! Bind an SSL port.
+//!
+//! @param port
+//!   Port number to bind.
+//!
+//! @param callback
+//!   Callback to call when the SSL connection has been negotiated.
+//!
+//!   The callback is called with an @[File] as the first argument,
+//!   and the id for the @[File] as the second.
+//!
+//!   If the @[callback] is @expr{0@} (zero), then negotiated @[File]s
+//!   will be enqueued for later retrieval with @[accept()].
+//!
+//! @param ip
+//!   Optional IP-number to bind.
+//!
+//! @returns
+//!   Returns @expr{1@} if binding of the port succeeded,
+//!   and @expr{0@} (zero) on failure.
+//!
+//! @seealso
+//!   @[Stdio.Port()->bind()], @[File()->set_accept_callback()],
+//!   @[listen_fd()]
+int bind(int port, function callback, string|void ip)
+{
+  accept_callback = callback;
+  return socket::bind(port, ssl_callback, ip);
+}
+
+//! @decl int listen_fd(int fd, @
+//!                     function(File|void, mixed|void: int) callback)
+//!
+//! Set up listening for SSL connections on an already opened fd.
+//!
+//! @param fd
+//!   File descriptor to listen on.
+//!
+//! @param callback
+//!   Callback to call when the SSL connection has been negotiated.
+//!
+//!   The callback is called with an @[File] as the first argument,
+//!   and the id for the @[File] as the second.
+//!
+//!   If the @[callback] is @expr{0@} (zero), then negotiated @[File]s
+//!   will be enqueued for later retrieval with @[accept()].
+//!
+//! @returns
+//!   Returns @expr{1@} if listening on the fd succeeded,
+//!   and @expr{0@} (zero) on failure.
+//!
+//! @seealso
+//!   @[Stdio.Port()->listen_fd()], @[File()->set_accept_callback()],
+//!   @[bind()]
+int listen_fd(int fd, function callback)
+{
+  accept_callback = callback;
+  return socket::listen_fd(fd, ssl_callback);
+}
+
+//! Low-level accept.
+//!
+//! @seealso
+//!   @[Stdio.Port()->accept()]
+Stdio.File socket_accept()
+{
+  return socket::accept();
+}
+
+//! @decl File accept()
+//!
+//! Get the next pending @[File] from the @[accept_queue].
+//!
+//! @returns
+//!   Returns the next pending @[File] if any, and @expr{0@} (zero) if
+//!   there are none.
+object accept()
+{
+  return accept_queue->get();
+}
+
+//! Create a new port for accepting SSL connections.
+//!
+//! @seealso
+//!   @[bind()], @[listen_fd()]
+void create(Context|void ctx)
+{
+#ifdef SSL3_DEBUG
+  werror("SSL.Port->create\n");
+#endif
+  if (!ctx) ctx = Context();
+  this_program::ctx = ctx;
+}
diff --git a/lib/modules/SSL.pmod/https.pike b/lib/modules/SSL.pmod/https.pike
index 14181a719c2ce84481657a08b61ce0fb3e7b0c5d..73f71f49cdd3d27e9ca7a2a5d8f5864cded629df 100644
--- a/lib/modules/SSL.pmod/https.pike
+++ b/lib/modules/SSL.pmod/https.pike
@@ -52,7 +52,7 @@ class MyContext
 }
 
 #ifndef HTTPS_CLIENT
-SSL.sslport port;
+SSL.Port port;
 
 void my_accept_callback(object f)
 {
@@ -111,7 +111,7 @@ class Client
     "Host: " HOST ":" + PORT + "\r\n"
     "\r\n";
 
-  SSL.sslfile ssl;
+  SSL.File ssl;
   int sent;
 
   void write_cb()
@@ -143,7 +143,7 @@ class Client
     // Make sure all cipher suites are available.
     ctx->preferred_suites = ctx->get_suites(-1, 2);
     werror("Starting\n");
-    ssl = SSL.sslfile(con, ctx);
+    ssl = SSL.File(con, ctx);
     ssl->connect();
     ssl->set_nonblocking(got_data, write_cb, con_closed);
   }
@@ -220,7 +220,7 @@ int main()
 
   SSL3_DEBUG_MSG("Certs:\n%O\n", ctx->cert_pairs);
 
-  port = SSL.sslport(ctx);
+  port = SSL.Port(ctx);
 
   werror("Starting\n");
   if (!port->bind(PORT, my_accept_callback))
diff --git a/lib/modules/SSL.pmod/module.pmod b/lib/modules/SSL.pmod/module.pmod
index cd3576251f9f7b4c316b7836a1ca41e23b167888..85679964a9cd9bd53b01f902719e435c587cfb4b 100644
--- a/lib/modules/SSL.pmod/module.pmod
+++ b/lib/modules/SSL.pmod/module.pmod
@@ -9,17 +9,17 @@
 //!
 //! The classes that typical users need to use are
 //! @dl
-//!   @item @[sslfile]
+//!   @item @[File]
 //!     This is an object that attempts to behave as a @[Stdio.File]
 //!     as much as possible.
 //!
-//!   @item @[sslport]
+//!   @item @[Port]
 //!     This is an object that attempts to behave as a @[Stdio.Port]
-//!     as much as possible, with @[sslport()->accept()] returning
-//!     @[sslfile] objects.
+//!     as much as possible, with @[Port()->accept()] returning
+//!     @[File] objects.
 //!
-//!   @item @[context]
-//!     The configurated context for the @[sslfile].
+//!   @item @[Context]
+//!     The configurated context for the @[File].
 //!
 //!   @item @[Constants.CertificatePair]
 //!     A class for keeping track of certificate chains and their
@@ -31,5 +31,5 @@
 //! the constants for output.
 //!
 //! @seealso
-//!   @[sslfile], @[sslport], @[context], @[Constants.CertificatePair],
+//!   @[File], @[Port], @[Context], @[Constants.CertificatePair],
 //!   @[Constants]
diff --git a/lib/modules/SSL.pmod/sslfile.pike b/lib/modules/SSL.pmod/sslfile.pike
index d6c08f705e187ece3dc85144d12ed6ea2813a0f5..40b40315054f3a06fc89201a1b01b7664afc602c 100644
--- a/lib/modules/SSL.pmod/sslfile.pike
+++ b/lib/modules/SSL.pmod/sslfile.pike
@@ -1,2324 +1,4 @@
-#pike __REAL_VERSION__
+#pike 7.8
 #require constant(SSL.Cipher)
 
-//! Interface similar to @[Stdio.File].
-//!
-//! @ul
-//! @item
-//!   Handles blocking and nonblocking mode.
-//! @item
-//!   Handles callback mode in an arbitrary backend (also in blocking
-//!   mode).
-//! @item
-//!   Read and write operations might each do both reading and
-//!   writing. In callback mode that means that installing either a
-//!   read or a write callback might install both internally. It also
-//!   means that reading in one thread while writing in another
-//!   doesn't work.
-//! @item
-//!   Callback changing operations like @[set_blocking] and
-//!   @[set_nonblocking] aren't atomic.
-//! @item
-//!   Apart from the above, thread safety/atomicity characteristics
-//!   are retained.
-//! @item
-//!   Blocking characterstics are retained for all functions.
-//! @item
-//!   @[is_open], connection init (@[create]) and close (@[close]) can
-//!   do both reading and writing.
-//! @item
-//!   @[destroy] attempts to close the stream properly by sending the
-//!   close packet, but since it can't do blocking I/O it's not
-//!   certain that it will succeed. The stream should therefore always
-//!   be closed with an explicit @[close] call.
-//! @item
-//!   Abrupt remote close without the proper handshake gets the errno
-//!   @[System.EPIPE].
-//! @item
-//!   Objects do not contain cyclic references, so they are closed and
-//!   destructed timely when dropped.
-//! @endul
-
-// #define SSLFILE_DEBUG
-// #define SSL3_DEBUG
-// #define SSL3_DEBUG_MORE
-// #define SSL3_DEBUG_TRANSPORT
-
-#ifdef SSL3_DEBUG
-protected string stream_descr;
-#define SSL3_DEBUG_MSG(X...)						\
-  werror ("[thr:" + this_thread()->id_number() +			\
-	  "," + (stream ? "" : "ex ") + stream_descr + "] " + X)
-#ifdef SSL3_DEBUG_MORE
-#define SSL3_DEBUG_MORE_MSG(X...) SSL3_DEBUG_MSG (X)
-#endif
-#else
-#define SSL3_DEBUG_MSG(X...) 0
-#endif
-
-#ifndef SSL3_DEBUG_MORE_MSG
-#define SSL3_DEBUG_MORE_MSG(X...) 0
-#endif
-
-protected Stdio.File stream;
-// The stream is closed by shutdown(), which is called directly or
-// indirectly from destroy() or close() but not from anywhere else.
-//
-// Note that a close in nonblocking callback mode might not happen
-// right away. In that case stream remains set after close() returns,
-// suitable callbacks are installed for the close packet exchange, and
-// close_state >= NORMAL_CLOSE. The stream is closed by the callbacks
-// as soon the close packets are done, or if an error occurs.
-
-protected int(-1..65535) linger_time = -1;
-// The linger behaviour set by linger().
-
-protected .Context context;
-// The context to use.
-
-protected .Connection conn;
-// Always set when stream is. Destructed with destroy() at shutdown
-// since it contains cyclic references. Noone else gets to it, though.
-
-protected array(string) write_buffer; // Encrypted data to be written.
-protected String.Buffer read_buffer; // Decrypted data that has been read.
-
-protected mixed callback_id;
-protected function(void|object,void|mixed:int) accept_callback;
-protected function(void|mixed,void|string:int) read_callback;
-protected function(void|mixed:int) write_callback;
-protected function(void|mixed:int) close_callback;
-
-protected Pike.Backend real_backend;
-// The real backend for the stream.
-
-protected Pike.Backend local_backend;
-// Internally all I/O is done using callbacks. When the real backend
-// can't be used, either because we aren't in callback mode (i.e. the
-// user hasn't registered any callbacks), or because we're to do a
-// blocking operation, this local one takes its place. It's
-// created on demand.
-
-protected int nonblocking_mode;
-
-protected int fragment_max_size;
-//! The max amount of data to send in each packet.
-//! Initialized from the context when the object is created.
-
-import .Constants;
-
-protected enum CloseState {
-  ABRUPT_CLOSE = -1,
-  STREAM_OPEN = 0,
-  STREAM_UNINITIALIZED = 1,
-  NORMAL_CLOSE = 2,		// The caller has requested a normal close.
-  CLEAN_CLOSE = 3,		// The caller has requested a clean close.
-}
-protected CloseState close_state = STREAM_UNINITIALIZED;
-// ABRUPT_CLOSE is set if there's a remote close without close packet.
-// The stream is still considered open locally, but reading or writing
-// to it trigs System.EPIPE.
-
-protected int local_errno;
-// If nonzero, override the errno on the stream with this.
-
-protected int cb_errno;
-// Stores the errno from failed I/O in a callback so that the next
-// visible I/O operation can report it properly.
-
-protected int got_extra_read_call_out;
-// 1 when we have a call out to ssl_read_callback. We get this when we
-// need to call read_callback or close_callback but can't do that
-// right away from ssl_read_callback. See comments in that function
-// for more details. -1 if we've switched to non-callback mode and
-// therefore has removed the call out temporarily but need to restore
-// it when switching back. 0 otherwise.
-//
-// -1 is also set before a call to update_internal_state when we want
-// to schedule an extra read call out; update_internal_state will then
-// do the actual call out installation if possible.
-
-protected int alert_cb_called;
-// Need to know if the alert callback has been called in
-// ssl_read_callback since it can't continue in that case. This is
-// only set temporarily while ssl_read_callback runs.
-
-protected constant epipe_errnos = (<
-  System.EPIPE,
-  System.ECONNRESET,
-#if constant(System.WSAECONNRESET)
-  // The following is returned by winsock on windows.
-  // Pike ought to map it to System.ECONNRESET.
-  System.WSAECONNRESET,
-#endif
->);
-// Multiset containing the errno codes that can occur if the remote
-// end has closed the connection.
-
-// This macro is used in all user called functions that can report I/O
-// errors, both at the beginning and after
-// ssl_(read|write|close)_callback calls.
-#define FIX_ERRNOS(ERROR, NO_ERROR) do {				\
-    if (cb_errno) {							\
-      /* Got a stored error from a previous callback that has failed. */ \
-      local_errno = cb_errno;						\
-      cb_errno = 0;							\
-      {ERROR;}								\
-    }									\
-    else {								\
-      local_errno = 0;							\
-      {NO_ERROR;}							\
-    }									\
-  } while (0)
-
-// Ignore user installed callbacks if a close has been requested locally.
-#define CALLBACK_MODE							\
-  ((read_callback || write_callback || close_callback || accept_callback) && \
-   close_state < NORMAL_CLOSE)
-
-#define SSL_HANDSHAKING (!conn || ((conn->state & CONNECTION_handshaking) && \
-				   close_state != ABRUPT_CLOSE))
-#define SSL_CLOSING_OR_CLOSED						\
-  (conn->state & CONNECTION_local_closing)
-
-// Always wait for input during handshaking and when we expect the
-// remote end to respond to our close packet. We should also check the
-// input buffer for a close packet if there was a failure to write our
-// close packet.
-#define SSL_INTERNAL_READING						\
-  (conn && (SSL_HANDSHAKING ||						\
-	    ((conn->state & CONNECTION_closed) == CONNECTION_local_closed)))
-
-// Try to write when there's data in the write buffer or when we have
-// a close packet to send. The packet is queued separately by
-// ssl_write_callback in the latter case.
-#define SSL_INTERNAL_WRITING (conn &&					\
-			      (sizeof (write_buffer) ||			\
-			       ((conn->state & CONNECTION_local_down) == \
-				CONNECTION_local_closing)))
-
-#ifdef SSLFILE_DEBUG
-
-#if constant (Thread.thread_create)
-
-#define THREAD_T Thread.Thread
-#define THIS_THREAD() this_thread()
-
-
-protected void thread_error (string msg, THREAD_T other_thread)
-{
-#if 0 && constant (_locate_references)
-  werror ("%s\n%O got %d refs", msg, this, _refs (this));
-  _locate_references (this);
-#endif
-  error ("%s"
-	 "%s\n"
-	 "User callbacks: a=%O r=%O w=%O c=%O\n"
-	 "Internal callbacks: r=%O w=%O c=%O\n"
-	 "Backend: %O  This thread: %O  Other thread: %O\n"
-	 "%s",
-	 msg,
-	 !stream ? "Got no stream" :
-	 stream->is_open() ? "Stream is open" :
-	 "Stream is closed",
-	 accept_callback, read_callback, write_callback, close_callback,
-	 stream && stream->query_read_callback(),
-	 stream && stream->query_write_callback(),
-	 stream && stream->query_close_callback(),
-	 stream && stream->query_backend(),
-	 this_thread(), other_thread,
-	 other_thread ? ("Other thread backtrace:\n" +
-			 describe_backtrace (other_thread->backtrace()) +
-			 "----------\n") : "");
-}
-
-#else  // !constant (Thread.thread_create)
-
-#define THREAD_T int
-#define THIS_THREAD() 1
-
-protected void thread_error (string msg, THREAD_T other_thread)
-{
-  error ("%s"
-	 "%s\n"
-	 "User callbacks: a=%O r=%O w=%O c=%O\n"
-	 "Internal callbacks: r=%O w=%O c=%O\n"
-	 "Backend: %O\n",
-	 msg,
-	 !stream ? "Got no stream" :
-	 stream->is_open() ? "Stream is open" :
-	 "Stream is closed",
-	 accept_callback, read_callback, write_callback, close_callback,
-	 stream && stream->query_read_callback(),
-	 stream && stream->query_write_callback(),
-	 stream && stream->query_close_callback(),
-	 stream && stream->query_backend());
-}
-
-#endif	// !constant (Thread.thread_create)
-
-protected THREAD_T op_thread;
-
-// FIXME: Looks like the following check can give false alarms since
-// an fd object can lose all refs even if some callbacks still are
-// registered.
-#define CHECK_CB_MODE(CUR_THREAD) do {					\
-    if (Pike.Backend backend = stream && stream->query_backend()) {	\
-      THREAD_T backend_thread = backend->executing_thread();		\
-      if (backend_thread && backend_thread != CUR_THREAD &&		\
-	  (stream->query_read_callback() ||				\
-	   stream->query_write_callback() ||				\
-	   stream->query_close_callback()))				\
-	/* NB: The other thread backtrace might not be relevant at	\
-	 * all here. */							\
-	thread_error ("In callback mode in a different backend.\n",	\
-		      backend_thread);					\
-    }									\
-  } while (0)
-
-#define LOW_CHECK(OP_THREAD, CUR_THREAD,				\
-		  IN_CALLBACK, CALLED_FROM_REAL_BACKEND) do {		\
-    if (IN_CALLBACK) {							\
-      if (CALLED_FROM_REAL_BACKEND) {					\
-	if (OP_THREAD)							\
-	  thread_error ("Called from real backend while doing an operation.\n",	\
-			OP_THREAD);					\
-      }									\
-      else								\
-	if (!OP_THREAD)							\
-	  error ("Called from local backend outside an operation.\n");	\
-    }									\
-									\
-    if (OP_THREAD && OP_THREAD != CUR_THREAD)				\
-      thread_error ("Doing operation in another thread.\n", OP_THREAD);	\
-									\
-    CHECK_CB_MODE (CUR_THREAD);						\
-  } while (0)
-
-#define CHECK(IN_CALLBACK, CALLED_FROM_REAL_BACKEND) do {		\
-    THREAD_T cur_thread = THIS_THREAD();				\
-    LOW_CHECK (op_thread, cur_thread, IN_CALLBACK, CALLED_FROM_REAL_BACKEND); \
-  } while (0)
-
-#define ENTER(IN_CALLBACK, CALLED_FROM_REAL_BACKEND) do {		\
-    THREAD_T old_op_thread;						\
-    {									\
-      THREAD_T cur_thread = THIS_THREAD();				\
-      old_op_thread = op_thread;					\
-      /* Relying on the interpreter lock here. */			\
-      op_thread = cur_thread;						\
-    }									\
-    LOW_CHECK (old_op_thread, op_thread, IN_CALLBACK, CALLED_FROM_REAL_BACKEND); \
-    mixed _op_err = catch
-
-#define RESTORE do {op_thread = old_op_thread;} while (0)
-
-#define RETURN(RET_VAL) do {RESTORE; return (RET_VAL);} while (0)
-
-#define LEAVE								\
-  ;									\
-  RESTORE;								\
-  if (_op_err) throw (_op_err);						\
-  } while (0)
-
-#else  // !SSLFILE_DEBUG
-
-#define CHECK_CB_MODE(CUR_THREAD) do {} while (0)
-#define CHECK(IN_CALLBACK, CALLED_FROM_REAL_BACKEND) do {} while (0)
-#define ENTER(IN_CALLBACK, CALLED_FROM_REAL_BACKEND) do
-#define RESTORE do {} while (0)
-#define RETURN(RET_VAL) return (RET_VAL)
-#define LEAVE while (0)
-
-#endif	// !SSLFILE_DEBUG
-
-// stream is assumed to be operational on entry but might be zero
-// afterwards. cb_errno is assumed to be 0 on entry.
-#define RUN_MAYBE_BLOCKING(REPEAT_COND, NONWAITING_MODE,		\
-			   ENABLE_READS, ERROR_CODE) do {		\
-  run_local_backend: {							\
-      CHECK_CB_MODE (THIS_THREAD());					\
-      if (!local_backend) local_backend = Pike.SmallBackend();		\
-      stream->set_backend (local_backend);				\
-      stream->set_id (0);						\
-									\
-      while (1) {							\
-	float|int(0..0) action;						\
-									\
-	if (got_extra_read_call_out) {					\
-	  /* Do whatever ssl_read_callback needs to do before we	\
-	   * continue. Since the first arg is zero here it won't call	\
-	   * any user callbacks, so they are superseded as they should	\
-	   * be if we're doing an explicit read (or a write or close,	\
-	   * which legitimately might cause reading to be done). Don't	\
-	   * need to bother with the return value from ssl_read_callback \
-	   * since we'll propagate the error, if any, just below. */	\
-	  if (got_extra_read_call_out > 0)				\
-	    real_backend->remove_call_out (ssl_read_callback);		\
-	  ssl_read_callback (0, 0); /* Will clear got_extra_read_call_out. */ \
-	  action = 0.0;							\
-	}								\
-									\
-	else {								\
-	  stream->set_write_callback (SSL_INTERNAL_WRITING && ssl_write_callback); \
-									\
-	  if (ENABLE_READS) {						\
-	    stream->set_read_callback (ssl_read_callback);		\
-	    stream->set_close_callback (ssl_close_callback);		\
-	  }								\
-	  else {							\
-	    stream->set_read_callback (0);				\
-	    /* Installing a close callback without a read callback	\
-	     * currently doesn't work well in Stdio.File. */		\
-	    stream->set_close_callback (0);				\
-	  }								\
-									\
-	  /* When we fail to write the close packet we should check if	\
-	   * a close packet is in the input buffer before signalling	\
-	   * the error. That means installing the read callbacks	\
-	   * without waiting in the backend. */				\
-	  int zero_timeout = NONWAITING_MODE;				\
-									\
-	  SSL3_DEBUG_MSG ("Running local backend [r:%O w:%O], %s timeout\n", \
-			  !!stream->query_read_callback(),		\
-			  !!stream->query_write_callback(),		\
-			  zero_timeout ? "zero" : "infinite");		\
-									\
-	  action = local_backend (zero_timeout ? 0.0 : 0);		\
-	}								\
-									\
-	if (NONWAITING_MODE && !action) {				\
-	  SSL3_DEBUG_MSG ("Nonwaiting local backend ended - nothing to do\n"); \
-	  break;							\
-	}								\
-									\
-	if (!action && (conn->state & CONNECTION_local_closing)) {	\
-	  SSL3_DEBUG_MSG ("Did not get a remote close - "		\
-			  "signalling delayed error from writing close message\n"); \
-	  cleanup_on_error();						\
-	  cb_errno = System.EPIPE;					\
-	  if (close_state != CLEAN_CLOSE)				\
-	    close_state = ABRUPT_CLOSE;					\
-	}								\
-									\
-	FIX_ERRNOS ({							\
-	    SSL3_DEBUG_MSG ("Local backend ended with error\n");	\
-	    if (stream) {						\
-	      stream->set_id (1);					\
-	      update_internal_state (1);				\
-	      /* Switch backend after updating the installed callbacks. */ \
-	      stream->set_backend (real_backend);			\
-	      CHECK_CB_MODE (THIS_THREAD());				\
-	    }								\
-	    {ERROR_CODE;}						\
-	    break run_local_backend;					\
-	  }, 0);							\
-									\
-	if (!stream) {							\
-	  SSL3_DEBUG_MSG ("Local backend ended after close.\n");	\
-	  break run_local_backend;					\
-	}								\
-									\
-	if (!(REPEAT_COND)) {						\
-	  SSL3_DEBUG_MSG ("Local backend ended - repeat condition false\n"); \
-	  break;							\
-	}								\
-      }									\
-									\
-      stream->set_id (1);						\
-      update_internal_state (1);					\
-      /* Switch backend after updating the installed callbacks. */	\
-      stream->set_backend (real_backend);				\
-      CHECK_CB_MODE (THIS_THREAD());					\
-    }									\
-  } while (0)
-
-protected void create (Stdio.File stream, SSL.Context ctx)
-//! Create an SSL connection over an open @[stream].
-//!
-//! @param stream
-//!   Open socket or pipe to create the connection over.
-//!
-//! @param ctx
-//!   The SSL context.
-//!
-//! The backend used by @[stream] is taken over and restored after the
-//! connection is closed (see @[close] and @[shutdown]). The callbacks
-//! and id in @[stream] are overwritten.
-//!
-//! @note
-//!   The operation mode defaults to nonblocking mode.
-{
-  SSL3_DEBUG_MSG ("SSL.sslfile->create (%O, %O)\n", stream, ctx);
-
-  ENTER (0, 0) {
-    global::stream = stream;
-    global::context = ctx;
-
-#ifdef SSL3_DEBUG
-    if (stream->query_fd)
-      stream_descr = "fd:" + stream->query_fd();
-    else
-      stream_descr = replace (sprintf ("%O", stream), "%", "%%");
-#endif
-    write_buffer = ({});
-    read_buffer = String.Buffer();
-    real_backend = stream->query_backend();
-    close_state = STREAM_OPEN;
-
-    stream->set_read_callback (0);
-    stream->set_write_callback (0);
-    stream->set_close_callback (0);
-    stream->set_id (1);
-
-    fragment_max_size =
-      limit(1, ctx->packet_max_size, PACKET_MAX_SIZE);
-
-    set_nonblocking();
-  } LEAVE;
-}
-
-//! Configure as client and set up the connection.
-//!
-//! @param dest_addr
-//!   Optional name of the server that we are connected to.
-//!
-//! @returns
-//!   Returns @expr{0@} on handshaking failure in blocking mode,
-//!   and otherwise @expr{1@}.
-int(1bit) connect(string|array(string)|void dest_addr)
-{
-  if (conn) error("A connection is already configured!\n");
-
-  ENTER (0, 0) {
-    if (stringp(dest_addr)) {
-      dest_addr = ({ dest_addr });
-    }
-    conn = .ClientConnection(context, dest_addr);
-
-    // Wait for the handshake to finish in blocking mode.
-    if (!nonblocking_mode) {
-      if (!direct_write()) {
-	local_errno = errno();
-	if (stream) {
-	  stream->set_callbacks(0, 0, 0, 0, 0);
-	}
-	conn = UNDEFINED;
-	return 0;
-      }
-    } else {
-      queue_write();
-    }
-  } LEAVE;
-
-  return 1;
-}
-
-//! Configure as server and set up the connection.
-//!
-//! @param pending_data
-//!   Any data that has already been read from the stream.
-//!   This is typically used with protocols that use
-//!   START TLS or similar, where there's a risk that
-//!   "too much" data (ie part of the TLS ClientHello) has
-//!   been read from the stream before deciding that the
-//!   connection is to enter TLS-mode.
-//!
-//! @returns
-//!   Returns @expr{0@} on handshaking failure in blocking mode,
-//!   and otherwise @expr{1@}.
-int(1bit) accept(string|void pending_data)
-{
-  if (conn) error("A connection is already configured!\n");
-
-  ENTER (0, 0) {
-    conn = .ServerConnection(context);
-
-    if (sizeof(pending_data || "")) {
-      if (intp(conn->got_data(pending_data))) {
-	local_errno = errno();
-	if (stream) {
-	  stream->set_callbacks(0, 0, 0, 0, 0);
-	}
-	conn = UNDEFINED;
-	return 0;
-      }
-    }
-
-    // Wait for the handshake to finish in blocking mode.
-    if (!nonblocking_mode) {
-      if (!direct_write()) {
-	local_errno = errno();
-	if (stream) {
-	  stream->set_callbacks(0, 0, 0, 0, 0);
-	}
-	conn = UNDEFINED;
-	return 0;
-      }
-    }
-  } LEAVE;
-
-  return 1;
-}
-
-mixed get_server_names()
-{
-  if (!conn) error("No active conection.\n");
-  return conn->session->server_names;
-}
-
-//! @returns
-//!   Returns peer certificate information, if any.
-mapping get_peer_certificate_info()
-{
-  if (!conn) error("No active conection.\n");
-  return conn->session->cert_data;
-}
-
-//! @returns
-//!   Returns the peer certificate chain, if any.
-array get_peer_certificates()
-{
-  if (!conn) error("No active conection.\n");
-  return conn->session->peer_certificate_chain;
-}
-
-//! Set the linger time on @[close()].
-int(0..1) linger(int(-1..65535)|void seconds)
-{
-  if (!stream) return 0;
-  if (zero_type(seconds)) seconds = -1;
-  if (seconds == linger_time) {
-    // Noop.
-    return 1;
-  }
-  if (stream->linger && !stream->linger(seconds)) return 0;
-  linger_time = seconds;
-  return 1;
-}
-
-int close (void|string how, void|int clean_close, void|int dont_throw)
-//! Close the connection. Both the read and write ends are always
-//! closed
-//!
-//! @param how
-//!   This argument is only for @[Stdio.File] compatibility
-//!   and must be either @expr{"rw"@} or @expr{0@}.
-//!
-//! @param clean_close
-//!   If set then close messages are exchanged to shut down
-//!   the SSL connection but not the underlying stream. It may then
-//!   continue to be used for other communication afterwards. The
-//!   default is to send a close message and then close the stream
-//!   without waiting for a response.
-//!
-//! @param dont_throw
-//!   I/O errors are normally thrown, but that can be turned off with
-//!   @[dont_throw]. In that case @[errno] is set instead and @expr{0@} is
-//!   returned. @expr{1@} is always returned otherwise. It's not an error to
-//!   close an already closed connection.
-//!
-//! @note
-//! If a clean close is requested in nonblocking mode then the stream
-//! is most likely not closed right away, and the backend is then
-//! still needed for a while afterwards to exchange the close packets.
-//! @[is_open] returns 2 in that time window.
-//!
-//! @note
-//! I/O errors from both reading and writing might occur in blocking
-//! mode.
-//!
-//! @note
-//! If a clean close is requested and data following the close message
-//! is received at the same time, then this object will read it and
-//! has no way to undo that. That data can be retrieved with @[read]
-//! afterwards.
-//!
-//! @seealso
-//!   @[shutdown]
-{
-  SSL3_DEBUG_MSG ("SSL.sslfile->close (%O, %O, %O)\n",
-		  how, clean_close, dont_throw);
-
-  if (how && how != "rw")
-    error ("Can only close the connection in both directions simultaneously.\n");
-
-  ENTER (0, 0) {
-    if (!conn || (conn->state & CONNECTION_local_down)) {
-      SSL3_DEBUG_MSG ("SSL.sslfile->close: Already closed (%d)\n", close_state);
-      RETURN (1);
-    }
-    close_state = clean_close ? CLEAN_CLOSE : NORMAL_CLOSE;
-
-    FIX_ERRNOS ({
-	SSL3_DEBUG_MSG ("SSL.sslfile->close: Shutdown after error\n");
-	int err = errno();
-	shutdown();
-	// Get here e.g. if a close callback calls close after an
-	// error, so never throw. (I'm somewhat suspicious to this,
-	// but I guess I did it with good reason.. :P /mast)
-	local_errno = err;
-	RETURN (0);
-      }, 0);
-
-    SSL3_DEBUG_MSG ("ssl_write_callback: Queuing close packet\n");
-    conn->send_close();
-    if (!linger_time) {
-      SSL3_DEBUG_MSG ("ssl_write_callback: Don't care about it being sent.\n");
-      conn->state = [int(0..0)|ConnectionState]
-	(conn->state | CONNECTION_local_closed);
-    }
-
-    // Even in nonblocking mode we call direct_write here to try to
-    // put the close packet in the send buffer before we return. That
-    // way it has a fair chance to get sent even when we're called
-    // from destroy() (in which case it won't work to just install the
-    // write callback as usual and wait for the backend to call it).
-
-    if (!direct_write()) {
-      // Should be shut down after close(), even if an error occurred.
-      int err = errno();
-      shutdown();
-      if (dont_throw) {
-	local_errno = err;
-	RETURN (0);
-      }
-      else if( err != System.EPIPE )
-	// Errors are normally thrown from close().
-        error ("Failed to close SSL connection: %s\n", strerror (err));
-    }
-
-    if (stream && (stream->query_read_callback() || stream->query_write_callback())) {
-      SSL3_DEBUG_MSG ("SSL.sslfile->close: Close underway\n");
-      RESTORE;
-      update_internal_state();
-      return 1;
-    }
-    else {
-      // The local backend run by direct_write has typically already
-      // done this, but it might happen that it doesn't do anything in
-      // case close packets already have been exchanged.
-      shutdown();
-      SSL3_DEBUG_MSG ("SSL.sslfile->close: Close done\n");
-    }
-  } LEAVE;
-  return 1;
-}
-
-protected void cleanup_on_error()
-// Called when any error occurs on the stream. (Doesn't handle errno
-// reporting since it might involve either local_errno and/or
-// cb_errno.)
-{
-  // The session should be purged when an error has occurred.
-  if (conn && conn->session)
-    // Check conn->session since it doesn't exist before the
-    // handshake.
-    conn->context->purge_session (conn->session);
-}
-
-Stdio.File shutdown()
-//! Shut down the SSL connection without sending any more packets.
-//!
-//! If the connection is open then the underlying (still open) stream
-//! is returned.
-//!
-//! If a nonclean (i.e. normal) close has been requested then the
-//! underlying stream is closed now if it wasn't closed already, and
-//! zero is returned.
-//!
-//! If a clean close has been requested (see the second argument to
-//! @[close]) then the behavior depends on the state of the close
-//! packet exchange: The first @[shutdown] call after a successful
-//! exchange returns the (still open) underlying stream, and later
-//! calls return zero and clears @[errno]. If the exchange hasn't
-//! finished then the stream is closed, zero is returned, and @[errno]
-//! will return @[System.EPIPE].
-//!
-//! @seealso
-//!   @[close], @[set_alert_callback]
-{
-  ENTER (0, 0) {
-    if (!stream || !conn) {
-      SSL3_DEBUG_MSG ("SSL.sslfile->shutdown(): Already shut down\n");
-      RETURN (0);
-    }
-
-    if (close_state == STREAM_OPEN || close_state == NORMAL_CLOSE)
-      // If we didn't request a clean close then we pretend to have
-      // received a close message. According to the standard it's ok
-      // anyway as long as the transport isn't used for anything else.
-      conn->state |= CONNECTION_peer_closed;
-
-    SSL3_DEBUG_MSG ("SSL.sslfile->shutdown(): %s %s\n",
-		    close_state != ABRUPT_CLOSE &&
-		    (conn->state &
-		     (CONNECTION_peer_closed | CONNECTION_local_closing)) ==
-		    (CONNECTION_peer_closed | CONNECTION_local_closing) &&
-		    !sizeof (write_buffer) ?
-		    "Proper close" :
-		    close_state == STREAM_OPEN ?
-		    "Not closed" :
-		    "Abrupt close",
-		    conn->describe_state());
-
-    if ((conn->state & CONNECTION_peer_closed) &&
-	sizeof (conn->left_over || "")) {
-#ifdef SSLFILE_DEBUG
-      werror ("Warning: Got buffered data after close in %O: %O%s\n", this,
-	      conn->left_over[..99], sizeof (conn->left_over) > 100 ? "..." : "");
-#endif
-      read_buffer = String.Buffer (sizeof (conn->left_over));
-      read_buffer->add (conn->left_over);
-      close_state = STREAM_OPEN;
-    }
-
-    .Constants.ConnectionState conn_state = conn->state;
-    destruct (conn);		// Necessary to avoid garbage.
-
-    write_buffer = ({});
-
-    if (got_extra_read_call_out > 0)
-      real_backend->remove_call_out (ssl_read_callback);
-    got_extra_read_call_out = 0;
-
-    Stdio.File stream = global::stream;
-    global::stream = 0;
-
-    stream->set_read_callback (0);
-    stream->set_write_callback (0);
-    stream->set_close_callback (0);
-
-    switch (close_state) {
-      case CLEAN_CLOSE:
-	if ((conn_state & CONNECTION_closed) == CONNECTION_closed) {
-	  SSL3_DEBUG_MSG ("SSL.sslfile->shutdown(): Clean close - "
-			  "leaving stream\n");
-	  local_errno = 0;
-	  RETURN (stream);
-	}
-	else {
-	  SSL3_DEBUG_MSG ("SSL.sslfile->shutdown(): Close packets not fully "
-			  "exchanged after clean close (%d) - closing stream\n",
-			  conn_state);
-	  stream->close();
-	  local_errno = System.EPIPE;
-	  RETURN (0);
-	}
-      case STREAM_OPEN:
-	close_state = STREAM_UNINITIALIZED;
-	SSL3_DEBUG_MSG ("SSL.sslfile->shutdown(): Not closed - leaving stream\n");
-	local_errno = 0;
-	RETURN (stream);
-      default:
-	SSL3_DEBUG_MSG ("SSL.sslfile->shutdown(): Nonclean close - closing stream\n");
-	// if (stream->linger) stream->linger(0);
-	stream->close();
-	local_errno = stream->errno() || local_errno;
-	RETURN (0);
-    }
-  } LEAVE;
-}
-
-protected void destroy()
-//! Try to close down the connection properly since it's customary to
-//! close files just by dropping them. No guarantee can be made that
-//! the close packet gets sent successfully though, because we can't
-//! risk blocking I/O here. You should call @[close] explicitly.
-//!
-//! @seealso
-//!   @[close]
-{
-  SSL3_DEBUG_MSG ("SSL.sslfile->destroy()\n");
-
-  // We don't know which thread this will be called in if the refcount
-  // garb or the gc got here. That's not a race problem since it won't
-  // be registered in a backend in that case.
-  if (stream) {
-    // Make sure not to fail in ENTER below due to bad backend thread.
-    // [bug 6958].
-    stream->set_callbacks(0, 0, 0);
-  }
-  ENTER (0, 0) {
-    if (stream) {
-      if (close_state == STREAM_OPEN &&
-	  // Don't bother with closing nicely if there's an error from
-	  // an earlier operation. close() will throw an error for it.
-	  !cb_errno) {
-	// We can't use our set_nonblocking() et al here, since we
-	// might be associated with a backend in a different thread,
-	// and update_internal_state() will install callbacks, which
-	// in turn might trigger the tests in CHECK_CB_MODE().
-	stream->set_nonblocking();	// Make sure not to to block.
-	nonblocking_mode = 0;	// Make sure not to install any callbacks.
-	close (0, 0, 1);
-      }
-      else
-	shutdown();
-    }
-  } LEAVE;
-}
-
-string read (void|int length, void|int(0..1) not_all)
-//! Read some (decrypted) data from the connection. Works like
-//! @[Stdio.File.read].
-//!
-//! @note
-//! I/O errors from both reading and writing might occur in blocking
-//! mode.
-//!
-//! @seealso
-//!   @[write]
-{
-  SSL3_DEBUG_MSG ("SSL.sslfile->read (%d, %d)\n", length, not_all);
-
-  ENTER (0, 0) {
-    if (close_state > STREAM_OPEN) error ("Not open.\n");
-
-    FIX_ERRNOS ({
-	SSL3_DEBUG_MSG ("SSL.sslfile->read: Propagating old callback error: %s\n",
-			strerror (local_errno));
-	RETURN (0);
-      }, 0);
-
-    if (stream)
-      if (not_all) {
-	if (!sizeof (read_buffer))
-	  RUN_MAYBE_BLOCKING (!sizeof (read_buffer) &&
-			      !(conn->state & CONNECTION_peer_closed), 0, 1,
-			      if (sizeof (read_buffer)) {
-				// Got data to return first. Push the
-				// error back so it'll get reported by
-				// the next call.
-				cb_errno = local_errno;
-				local_errno = 0;
-			      }
-			      else RETURN (0););
-      }
-      else {
-	if (sizeof (read_buffer) < length || zero_type (length))
-	  RUN_MAYBE_BLOCKING ((sizeof (read_buffer) < length || zero_type (length)) &&
-			      !(conn->state & CONNECTION_peer_closed),
-			      nonblocking_mode, 1,
-			      if (sizeof (read_buffer)) {
-				// Got data to return first. Push the
-				// error back so it'll get reported by
-				// the next call.
-				cb_errno = local_errno;
-				local_errno = 0;
-			      }
-			      else RETURN (0););
-      }
-
-    string res = read_buffer->get();
-    if (!zero_type (length)) {
-      read_buffer->add (res[length..]);
-      res = res[..length-1];
-    }
-
-    SSL3_DEBUG_MSG ("SSL.sslfile->read: Read done, returning %d bytes "
-		    "(%d still in buffer)\n",
-		    sizeof (res), sizeof (read_buffer));
-    RETURN (res);
-  } LEAVE;
-}
-
-int write (string|array(string) data, mixed... args)
-//! Write some (unencrypted) data to the connection. Works like
-//! @[Stdio.File.write] except that this function often buffers some data
-//! internally, so there's no guarantee that all the consumed data has
-//! been successfully written to the stream in nonblocking mode. It
-//! keeps the internal buffering to a minimum, however.
-//!
-//! @note
-//! This function returns zero if attempts are made to write data
-//! during the handshake phase and the mode is nonblocking.
-//!
-//! @note
-//! I/O errors from both reading and writing might occur in blocking
-//! mode.
-//!
-//! @seealso
-//!   @[read]
-{
-  if (sizeof (args))
-    data = sprintf (arrayp (data) ? data * "" : data, @args);
-
-  SSL3_DEBUG_MSG ("SSL.sslfile->write (%t[%d])\n", data, sizeof (data));
-
-  ENTER (0, 0) {
-    if (close_state > STREAM_OPEN) error ("Not open.\n");
-
-    FIX_ERRNOS ({
-	SSL3_DEBUG_MSG ("SSL.sslfile->write: Propagating old callback error: %s\n",
-			strerror (local_errno));
-	RETURN (-1);
-      }, 0);
-
-    if (nonblocking_mode && SSL_HANDSHAKING) {
-      SSL3_DEBUG_MSG ("SSL.sslfile->write: "
-		      "Still in handshake - cannot accept application data\n");
-      RETURN (0);
-    }
-
-    // Take care of any old data first.
-    if (!direct_write()) RETURN (-1);
-
-    int written = 0;
-
-    if (arrayp (data)) {
-      int idx = 0, pos = 0;
-
-      while (idx < sizeof (data) && !sizeof (write_buffer) &&
-	     // Always stop after writing DATA_CHUNK_SIZE in
-	     // nonblocking mode, so that we don't loop here
-	     // arbitrarily long if the write is very large and the
-	     // bottleneck is in the encryption.
-	     (!nonblocking_mode || written < Stdio.DATA_CHUNK_SIZE)) {
-	int size = sizeof (data[idx]) - pos;
-	if (size > fragment_max_size) {
-	  // send_streaming_data will pick the first fragment_max_size
-	  // bytes of the string, so do that right away in the same
-	  // range operation.
-	  int n = conn->send_streaming_data (
-	    data[idx][pos..pos + fragment_max_size - 1]);
-	  SSL3_DEBUG_MSG ("SSL.sslfile->write: Queued data[%d][%d..%d]\n",
-			  idx, pos, pos + n - 1);
-	  written += n;
-	  pos += n;
-	}
-
-	else {
-	  // Try to fill a packet.
-	  int end;
-	  for (end = idx + 1; end < sizeof (data); end++) {
-	    int newsize = size + sizeof (data[end]);
-	    if (newsize > fragment_max_size) break;
-	    size = newsize;
-	  }
-
-	  if (conn->send_streaming_data (
-		`+ (data[idx][pos..], @data[idx+1..end-1])) < size)
-	    error ("Unexpected fragment_max_size discrepancy wrt send_streaming_data.\n");
-
-	  SSL3_DEBUG_MSG ("SSL.sslfile->write: "
-			  "Queued data[%d][%d..%d] + data[%d..%d]\n",
-			  idx, pos, sizeof (data[idx]) - 1, idx + 1, end - 1);
-	  written += size;
-	  idx = end;
-	  pos = 0;
-	}
-
-	if (!direct_write()) RETURN (written);
-      }
-    }
-
-    else			// data is a string.
-      while (written < sizeof (data) && !sizeof (write_buffer) &&
-	     // Limit the amount written in a single call, for the
-	     // same reason as above.
-	     (!nonblocking_mode || written < Stdio.DATA_CHUNK_SIZE)) {
-	int n = conn->send_streaming_data (
-	  data[written..written + fragment_max_size - 1]);
-	SSL3_DEBUG_MSG ("SSL.sslfile->write: Queued data[%d..%d]\n",
-			written, written + n - 1);
-	written += n;
-	if (!direct_write()) RETURN (written);
-      }
-
-    SSL3_DEBUG_MSG ("SSL.sslfile->write: Write %t done, accepted %d bytes\n",
-		    data, written);
-    RETURN (written);
-  } LEAVE;
-}
-
-int renegotiate()
-//! Renegotiate the connection by starting a new handshake. Note that
-//! the accept callback will be called again when the handshake is
-//! finished.
-//!
-//! Returns zero if there are any I/O errors. @[errno()] will give the
-//! details.
-//!
-//! @note
-//! The read buffer is not cleared - a @expr{read()@} afterwards will
-//! return data from both before and after the renegotiation.
-//!
-//! @bugs
-//! Data in the write queue in nonblocking mode is not properly
-//! written before resetting the connection. Do a blocking
-//! @expr{write("")@} first to avoid problems with that.
-{
-  SSL3_DEBUG_MSG ("SSL.sslfile->renegotiate()\n");
-
-  ENTER (0, 0) {
-    if (close_state > STREAM_OPEN) error ("Not open.\n");
-
-    FIX_ERRNOS ({
-	SSL3_DEBUG_MSG ("SSL.sslfile->renegotiate: "
-			"Propagating old callback error: %s\n",
-			strerror (local_errno));
-	RETURN (0);
-      }, 0);
-
-    if (!stream) {
-      SSL3_DEBUG_MSG ("SSL.sslfile->renegotiate: "
-		      "Connection closed - simulating System.EPIPE\n");
-      cleanup_on_error();
-      local_errno = System.EPIPE;
-      RETURN (0);
-    }
-
-    // FIXME: Change this state with a packet instead so that things
-    // currently in the queue aren't affect by it.
-    conn->expect_change_cipher = 0;
-    conn->certificate_state = 0;
-    conn->state |= CONNECTION_handshaking;
-    update_internal_state();
-
-    conn->send_packet(conn->hello_request());
-
-    RETURN (direct_write());
-  } LEAVE;
-}
-
-void set_callbacks (void|function(mixed, string:int) read,
-		    void|function(mixed:int) write,
-		    void|function(mixed:int) close,
-		    void|function(mixed, string:int) read_oob,
-		    void|function(mixed:int) write_oob,
-		    void|function(void|mixed:int) accept)
-//! Installs all the specified callbacks at once. Use @[UNDEFINED]
-//! to keep the current setting for a callback.
-//!
-//! Like @[set_nonblocking], the callbacks are installed atomically.
-//! As opposed to @[set_nonblocking], this function does not do
-//! anything with the stream, and it doesn't even have to be open.
-//!
-//! @bugs
-//! @[read_oob] and @[write_oob] are currently ignored.
-//!
-//! @seealso
-//! @[set_read_callback], @[set_write_callback],
-//! @[set_close_callback], @[set_accept_callback], @[query_callbacks]
-{
-  SSL3_DEBUG_MSG ("SSL.sslfile->set_callbacks (%O, %O, %O, %O, %O, %O)\n%s",
-		  read, write, close, read_oob, write_oob, accept,
-		  "" || describe_backtrace (backtrace()));
-
-  ENTER(0, 0) {
-
-    // Bypass the ::set_xxx_callback functions; we instead enable all
-    // the event bits at once through the _enable_callbacks call at the end.
-
-    if (!zero_type(read))
-      read_callback = read;
-
-    if (!zero_type(write))
-      write_callback = write;
-
-    if (!zero_type(close))
-      close_callback = close;
-
-    if (!zero_type(accept))
-      accept_callback = accept;
-
-    if (stream) update_internal_state();
-  } LEAVE;
-}
-
-//! @returns
-//!   Returns the currently set callbacks in the same order
-//!   as the arguments to @[set_callbacks].
-//!
-//! @seealso
-//!   @[set_callbacks], @[set_nonblocking]
-array(function(mixed,void|string:int)) query_callbacks()
-{
-  return ({
-    read_callback,
-    write_callback,
-    close_callback,
-    UNDEFINED,
-    UNDEFINED,
-    accept_callback,
-  });
-}
-
-void set_nonblocking (void|function(void|mixed,void|string:int) read,
-		      void|function(void|mixed:int) write,
-		      void|function(void|mixed:int) close,
-		      void|function(void|mixed:int) read_oob,
-		      void|function(void|mixed:int) write_oob,
-		      void|function(void|mixed:int) accept)
-//! Set the stream in nonblocking mode, installing the specified
-//! callbacks. The alert callback isn't touched.
-//!
-//! @note
-//! Prior to version 7.5.12, this function didn't set the accept
-//! callback.
-//!
-//! @bugs
-//! @[read_oob] and @[write_oob] are currently ignored.
-//!
-//! @seealso
-//!   @[set_callbacks], @[query_callbacks], @[set_nonblocking_keep_callbacks],
-//!   @[set_blocking]
-{
-  SSL3_DEBUG_MSG ("SSL.sslfile->set_nonblocking (%O, %O, %O, %O, %O, %O)\n%s",
-		  read, write, close, read_oob, write_oob, accept,
-		  "" || describe_backtrace (backtrace()));
-
-  ENTER (0, 0) {
-    if (close_state > STREAM_OPEN) error ("Not open.\n");
-
-    nonblocking_mode = 1;
-
-    accept_callback = accept;
-    read_callback = read;
-    write_callback = write;
-    close_callback = close;
-
-    if (stream) {
-      stream->set_nonblocking_keep_callbacks();
-      // Has to restore here since a backend waiting in another thread
-      // might be woken immediately when callbacks are registered.
-      RESTORE;
-      update_internal_state();
-      return;
-    }
-  } LEAVE;
-}
-
-void set_nonblocking_keep_callbacks()
-//! Set nonblocking mode like @[set_nonblocking], but don't alter any
-//! callbacks.
-//!
-//! @seealso
-//!   @[set_nonblocking], @[set_blocking], @[set_blocking_keep_callbacks]
-{
-  SSL3_DEBUG_MSG ("SSL.sslfile->set_nonblocking_keep_callbacks()\n");
-
-  ENTER (0, 0) {
-    if (close_state > STREAM_OPEN) error ("Not open.\n");
-
-    nonblocking_mode = 1;
-
-    if (stream) {
-      stream->set_nonblocking_keep_callbacks();
-      // Has to restore here since a backend waiting in another thread
-      // might be woken immediately when callbacks are registered.
-      RESTORE;
-      update_internal_state();
-      return;
-    }
-  } LEAVE;
-}
-
-void set_blocking()
-//! Set the stream in blocking mode. All but the alert callback are
-//! zapped.
-//!
-//! @note
-//! There might be some data still waiting to be written to the
-//! stream. That will be written in the next blocking call, regardless
-//! what it is.
-//!
-//! @note
-//! This function doesn't solve the case when the connection is used
-//! nonblocking in some backend thread and another thread switches it
-//! to blocking and starts using it. To solve that, put a call out in
-//! the backend from the other thread that switches it to blocking,
-//! and then wait until that call out has run.
-//!
-//! @note
-//! Prior to version 7.5.12, this function didn't clear the accept
-//! callback.
-//!
-//! @seealso
-//!   @[set_nonblocking], @[set_blocking_keep_callbacks],
-//!   @[set_nonblocking_keep_callbacks]
-{
-  // Previously this function wrote the remaining write buffer to the
-  // stream directly. But that can only be done safely if we implement
-  // a lock here to wait for any nonblocking operations to complete,
-  // and that could introduce a deadlock since there might be other
-  // lock dependencies between the threads.
-
-  SSL3_DEBUG_MSG ("SSL.sslfile->set_blocking()\n%s",
-		  "" || describe_backtrace (backtrace()));
-
-  ENTER (0, 0) {
-    if (close_state > STREAM_OPEN) error ("Not open.\n");
-
-    nonblocking_mode = 0;
-    accept_callback = read_callback = write_callback = close_callback = 0;
-
-    if (stream) {
-      update_internal_state();
-      stream->set_blocking();
-    }
-  } LEAVE;
-}
-
-void set_blocking_keep_callbacks()
-//! Set blocking mode like @[set_blocking], but don't alter any
-//! callbacks.
-//!
-//! @seealso
-//!   @[set_blocking], @[set_nonblocking]
-{
-  SSL3_DEBUG_MSG ("SSL.sslfile->set_blocking_keep_callbacks()\n");
-
-  ENTER (0, 0) {
-    if (close_state > STREAM_OPEN) error ("Not open.\n");
-
-    nonblocking_mode = 0;
-
-    if (stream) {
-      update_internal_state();
-      stream->set_blocking();
-    }
-  } LEAVE;
-}
-
-int errno()
-//! @returns
-//!   Returns the current error number for the connection.
-//!   Notable values are:
-//!   @int
-//!     @value 0
-//!       No error
-//!     @value System.EPIPE
-//!       Connection closed by other end.
-//!   @endint
-{
-  // We don't check threads for most other query functions, but
-  // looking at errno while doing I/O in another thread can't be done
-  // safely.
-  CHECK (0, 0);
-  return local_errno ? local_errno : stream && stream->errno();
-}
-
-void set_alert_callback (function(object,int|object,string:void) alert)
-//! Install a function that will be called when an alert packet is about
-//! to be sent. It doesn't affect the callback mode - it's called both
-//! from backends and from within normal function calls like @[read]
-//! and @[write].
-//!
-//! This callback can be used to implement fallback to other protocols
-//! when used on the server side together with @[shutdown()].
-//!
-//! @note
-//! This object is part of a cyclic reference whenever this is set,
-//! just like setting any other callback.
-//!
-//! @note
-//!   This callback is not cleared by @[set_blocking], or settable
-//!   by @[set_callbacks] or @[set_nonblocking]. It is also not
-//!   part of the set returned by @[query_callbacks].
-//!
-//! @seealso
-//!   @[query_alert_callback]
-{
-  SSL3_DEBUG_MSG ("SSL.sslfile->set_alert_callback (%O)\n", alert);
-  CHECK (0, 0);
-#ifdef SSLFILE_DEBUG
-  if (close_state == STREAM_UNINITIALIZED || !conn)
-    error ("Doesn't have any connection.\n");
-#endif
-  conn->set_alert_callback (
-    alert &&
-    lambda (object packet, int|object seq_num, string alert_context) {
-      SSL3_DEBUG_MSG ("Calling alert callback %O\n", alert);
-      alert (packet, seq_num, alert_context);
-      alert_cb_called = 1;
-    });
-}
-
-function(object,int|object,string:void) query_alert_callback()
-//! @returns
-//!   Returns the current alert callback.
-//!
-//! @seealso
-//!   @[set_alert_callback]
-{
-  return conn && conn->alert_callback;
-}
-
-void set_accept_callback (function(void|object,void|mixed:int) accept)
-//! Install a function that will be called when the handshake is
-//! finished and the connection is ready for use.
-//!
-//! The callback function will be called with the sslfile object
-//! and the additional id arguments (set with @[set_id]).
-//! 
-//! @note
-//! Like the read, write and close callbacks, installing this callback
-//! implies callback mode, even after the handshake is done.
-//!
-//! @seealso
-//!   @[set_nonblocking], @[set_callbacks],
-//!   @[query_accept_callback], @[query_callbacks]
-{
-  SSL3_DEBUG_MSG ("SSL.sslfile->set_accept_callback (%O)\n", accept);
-  ENTER (0, 0) {
-#ifdef SSLFILE_DEBUG
-    if (close_state == STREAM_UNINITIALIZED)
-      error ("Doesn't have any connection.\n");
-#endif
-    accept_callback = accept;
-    if (stream) update_internal_state();
-  } LEAVE;
-}
-
-function(void|object,void|mixed:int) query_accept_callback()
-//! @returns
-//!   Returns the current accept callback.
-//!
-//! @seealso
-//!   @[set_accept_callback]
-{
-  return accept_callback;
-}
-
-void set_read_callback (function(void|mixed,void|string:int) read)
-//! Install a function to be called when data is available.
-//!
-//! @seealso
-//!   @[query_read_callback], @[set_nonblocking], @[query_callbacks]
-{
-  SSL3_DEBUG_MSG ("SSL.sslfile->set_read_callback (%O)\n", read);
-  ENTER (0, 0) {
-#ifdef SSLFILE_DEBUG
-    if (close_state == STREAM_UNINITIALIZED)
-      error ("Doesn't have any connection.\n");
-#endif
-    read_callback = read;
-    if (stream) update_internal_state();
-  } LEAVE;
-}
-
-function(void|mixed,void|string:int) query_read_callback()
-//! @returns
-//!   Returns the current read callback.
-//!
-//! @seealso
-//!   @[set_read_callback], @[set_nonblocking], @[query_callbacks]
-{
-  return read_callback;
-}
-
-void set_write_callback (function(void|mixed:int) write)
-//! Install a function to be called when data can be written.
-//!
-//! @seealso
-//!   @[query_write_callback], @[set_nonblocking], @[query_callbacks]
-{
-  SSL3_DEBUG_MSG ("SSL.sslfile->set_write_callback (%O)\n", write);
-  ENTER (0, 0) {
-#ifdef SSLFILE_DEBUG
-    if (close_state == STREAM_UNINITIALIZED)
-      error ("Doesn't have any connection.\n");
-#endif
-    write_callback = write;
-    if (stream) update_internal_state();
-  } LEAVE;
-}
-
-function(void|mixed:int) query_write_callback()
-//! @returns
-//!   Returns the current write callback.
-//!
-//! @seealso
-//!   @[set_write_callback], @[set_nonblocking], @[query_callbacks]
-{
-  return write_callback;
-}
-
-void set_close_callback (function(void|mixed:int) close)
-//! Install a function to be called when the connection is closed,
-//! either normally or due to an error (use @[errno] to retrieve it).
-//!
-//! @seealso
-//!   @[query_close_callback], @[set_nonblocking], @[query_callbacks]
-{
-  SSL3_DEBUG_MSG ("SSL.sslfile->set_close_callback (%O)\n", close);
-  ENTER (0, 0) {
-#ifdef SSLFILE_DEBUG
-    if (close_state == STREAM_UNINITIALIZED)
-      error ("Doesn't have any connection.\n");
-#endif
-    close_callback = close;
-    if (stream) update_internal_state();
-  } LEAVE;
-}
-
-function(void|mixed:int) query_close_callback()
-//! @returns
-//!   Returns the current close callback.
-//!
-//! @seealso
-//!   @[set_close_callback], @[set_nonblocking], @[query_callbacks]
-{
-  return close_callback;
-}
-
-void set_id (mixed id)
-//! Set the value to be sent as the first argument to the
-//! callbacks installed by @[set_callbacks].
-//!
-//! @seealso
-//!   @[query_id]
-{
-  SSL3_DEBUG_MSG ("SSL.sslfile->set_id (%O)\n", id);
-  CHECK (0, 0);
-  callback_id = id;
-}
-
-mixed query_id()
-//! @returns
-//!   Returns the currently set id.
-//!
-//! @seealso
-//!   @[set_id]
-{
-  return callback_id;
-}
-
-void set_backend (Pike.Backend backend)
-//! Set the backend used for the file callbacks.
-//!
-//! @seealso
-//!   @[query_backend]
-{
-  ENTER (0, 0) {
-    if (close_state > STREAM_OPEN) error ("Not open.\n");
-
-    if (stream) {
-      if (stream->query_backend() != local_backend)
-	stream->set_backend (backend);
-
-      if (got_extra_read_call_out > 0) {
-	real_backend->remove_call_out (ssl_read_callback);
-	backend->call_out (ssl_read_callback, 0, 1, 0);
-      }
-    }
-
-    real_backend = backend;
-  } LEAVE;
-}
-
-Pike.Backend query_backend()
-//! Return the backend used for the file callbacks.
-//!
-//! @seealso
-//!   @[set_backend]
-{
-  if (close_state > STREAM_OPEN) error ("Not open.\n");
-  return real_backend;
-}
-
-string query_address(int|void arg)
-//! @returns
-//!   Returns the address and port of the connection.
-//!
-//!   See @[Stdio.File.query_address] for details.
-//!
-//! @seealso
-//!   @[Stdio.File.query_address]
-{
-  if (close_state > STREAM_OPEN) error ("Not open.\n");
-  return stream->query_address(arg);
-}
-
-int is_open()
-//! @returns
-//! Returns nonzero if the stream currently is open, zero otherwise.
-//!
-//! This function does nonblocking I/O to check for a close packet in
-//! the input buffer.
-//!
-//! If a clean close has been requested in nonblocking mode, then 2 is
-//! returned until the close packet exchanged has been completed.
-//!
-//! @note
-//! In Pike 7.8 and earlier, this function returned zero in the case
-//! above where it now returns 2.
-{
-  SSL3_DEBUG_MSG ("SSL.sslfile->is_open()\n");
-  ENTER (0, 0) {
-    if ((close_state == STREAM_OPEN || close_state == CLEAN_CLOSE) &&
-	stream && stream->is_open()) {
-      // When close_state == STREAM_OPEN, we have to check if there's
-      // a close packet waiting to be read. This is common in
-      // keep-alive situations since the remote end might have sent a
-      // close packet and closed the connection a long time ago, and
-      // in a typical blocking client no action has been taken causing
-      // the close packet to be read.
-      //
-      // If close_state == CLEAN_CLOSE, we should return true as long
-      // as close packets haven't been exchanged in both directions.
-      //
-      // Could avoid the whole local backend hoopla here by
-      // essentially doing a peek and call ssl_read_callback directly,
-      // but that'd lead to subtle code duplication. (Also, peek is
-      // currently not implemented on NT.)
-      ConnectionState closed = conn->state & CONNECTION_closed;
-      if ((close_state == CLEAN_CLOSE ?
-	   closed != CONNECTION_closed : !closed))
-	RUN_MAYBE_BLOCKING (
-	  action && (close_state == CLEAN_CLOSE ?
-		     (conn->state & CONNECTION_closed) != CONNECTION_closed :
-		     !(conn->state & CONNECTION_closed)),
-	  1, 1,
-	  RETURN (!epipe_errnos[local_errno]));
-      closed = conn->state & CONNECTION_closed;
-      RETURN (close_state == CLEAN_CLOSE ?
-	      ((closed != CONNECTION_closed) && 2) : !closed);
-    }
-  } LEAVE;
-  return 0;
-}
-
-Stdio.File query_stream()
-//! Return the underlying stream.
-//!
-//! @note
-//! Avoid any temptation to do
-//! @expr{destruct(sslfile_obj->query_stream())@}. That almost
-//! certainly creates more problems than it solves.
-//!
-//! You probably want to use @[shutdown].
-//!
-//! @seealso
-//!   @[shutdown]
-{
-  SSL3_DEBUG_MSG ("SSL.sslfile->query_stream(): Called from %s:%d\n",
-		  backtrace()[-2][0], backtrace()[-2][1]);
-  return stream;
-}
-
-.Connection query_connection()
-//! Return the SSL connection object.
-//!
-//! This returns the low-level @[SSL.connection] object.
-{
-  SSL3_DEBUG_MSG ("SSL.sslfile->query_connection(): Called from %s:%d\n",
-		  backtrace()[-2][0], backtrace()[-2][1]);
-  return conn;
-}
-
-SSL.Context query_context()
-//! Return the SSL context object.
-{
-  return conn && conn->context;
-}
-
-string _sprintf(int t) {
-  return t=='O' && sprintf("SSL.sslfile(%O)", stream && stream->_fd);
-}
-
-
-protected void update_internal_state (void|int assume_real_backend)
-// Update the internal callbacks according to the current state. Does
-// nothing if the local backend is active, unless assume_real_backend
-// is set, in which case we're installing callbacks for the real
-// backend anyway (necessary to avoid races when we're about to switch
-// from the local to the real backend).
-{
-  // When the local backend is used, callbacks are set explicitly
-  // before it's started.
-  if (assume_real_backend || stream->query_backend() != local_backend) {
-    mixed install_read_cbs, install_write_cb;
-
-    if (nonblocking_mode &&
-	(SSL_HANDSHAKING || close_state >= NORMAL_CLOSE)) {
-      // Normally we never install our own callbacks if we aren't in
-      // callback mode, but handling a nonblocking close is an
-      // exception.
-      install_read_cbs = SSL_INTERNAL_READING;
-      install_write_cb = SSL_INTERNAL_WRITING;
-
-      SSL3_DEBUG_MORE_MSG ("update_internal_state: "
-			   "%s [r:%O w:%O rcb:%O]\n",
-			   SSL_HANDSHAKING ? "Handshaking" : "After close",
-			   !!install_read_cbs, !!install_write_cb,
-			   got_extra_read_call_out);
-    }
-
-    // CALLBACK_MODE but slightly optimized below.
-    else if (read_callback || write_callback || close_callback || accept_callback) {
-      install_read_cbs = (read_callback || close_callback || accept_callback ||
-			  SSL_INTERNAL_READING);
-      install_write_cb = (write_callback || SSL_INTERNAL_WRITING);
-
-      SSL3_DEBUG_MORE_MSG ("update_internal_state: "
-			   "Callback mode [r:%O w:%O rcb:%O]\n",
-			   !!install_read_cbs, !!install_write_cb,
-			   got_extra_read_call_out);
-    }
-
-    else {
-      // Not in callback mode. Can't install callbacks even though we'd
-      // "need" to - have to cope with the local backend in each
-      // operation instead.
-      SSL3_DEBUG_MORE_MSG ("update_internal_state: "
-			   "Not in callback mode [rcb:%O]\n",
-			   got_extra_read_call_out);
-    }
-
-    if (!got_extra_read_call_out && sizeof (read_buffer) && read_callback)
-      // Got buffered read data and there's someone ready to receive it,
-      // so schedule a call to ssl_read_callback to handle it.
-      got_extra_read_call_out = -1;
-
-    // If got_extra_read_call_out is set here then we wait with all
-    // other callbacks so that the extra ssl_read_callback call is
-    // carried out before anything else.
-
-    if (install_read_cbs) {
-      if (got_extra_read_call_out < 0) {
-	real_backend->call_out (ssl_read_callback, 0, 1, 0);
-	got_extra_read_call_out = 1;
-      }
-      else {
-	stream->set_read_callback (ssl_read_callback);
-	stream->set_close_callback (ssl_close_callback);
-      }
-    }
-    else {
-      stream->set_read_callback (0);
-      // Installing a close callback without a read callback
-      // currently doesn't work well in Stdio.File.
-      stream->set_close_callback (0);
-      if (got_extra_read_call_out > 0) {
-	real_backend->remove_call_out (ssl_read_callback);
-	got_extra_read_call_out = -1;
-      }
-    }
-
-    stream->set_write_callback (install_write_cb && !got_extra_read_call_out &&
-				ssl_write_callback);
-
-#ifdef SSLFILE_DEBUG
-    if (!assume_real_backend && op_thread)
-      // Check that we haven't installed callbacks that might start
-      // executing in parallell in another thread. That's legitimate
-      // in some cases (e.g. set_nonblocking) but in those cases we're
-      // called after RESTORE or LEAVE, which has reset op_thread, so
-      // we skip this check if op_thread is zero.
-      CHECK_CB_MODE (THIS_THREAD());
-#endif
-  }
-
-  else
-    SSL3_DEBUG_MORE_MSG ("update_internal_state: "
-			 "In local backend - nothing done [rcb:%O]\n",
-			 got_extra_read_call_out);
-}
-
-protected int queue_write()
-// Return 0 if the connection is still alive, 1 if it was closed
-// politely, and -1 if it died unexpectedly (specifically, our side
-// has sent a fatal alert packet (not close notify) for some reason
-// and therefore nullified the connection).
-{
-  if (!conn) return -1;
-
-  // Estimate how much data there is in the write_buffer.
-  int got = sizeof(write_buffer) &&
-    (sizeof(write_buffer[-1]) * sizeof(write_buffer));
-
-  int buffer_limit = 16384;
-  if (conn->state & CONNECTION_closing) buffer_limit = 1;
-
-  while (got < buffer_limit) {
-    int|string res = conn->to_write();
-
-#ifdef SSL3_DEBUG_TRANSPORT
-    werror ("queue_write: To write: %O\n", res);
-#endif
-
-    if (!stringp(res)) {
-      SSL3_DEBUG_MSG ("queue_write: Connection closed %s\n",
-		      res == 1 ? "normally" : "abruptly");
-      return res;
-    }
-
-    if (res == "") {
-      SSL3_DEBUG_MSG ("queue_write: Got nothing to write (%d strings buffered)\n",
-		      sizeof (write_buffer));
-      break;
-    }
-
-    int was_empty = !sizeof (write_buffer);
-    write_buffer += ({ res });
-    got += sizeof(res);
-
-    SSL3_DEBUG_MSG ("queue_write: Got %d bytes to write (%d strings buffered)\n",
-		    sizeof (res), sizeof (write_buffer));
-    if (was_empty && stream)
-      update_internal_state();
-  }
-
-  SSL3_DEBUG_MSG ("queue_write: Returning 0 (%d strings buffered)\n",
-		  sizeof(write_buffer));
-
-  return 0;
-}
-
-protected int direct_write()
-// Do a write directly (and maybe also read if there's internal
-// reading to be done). Something to write is assumed to exist (either
-// in write_buffer or in the packet queue). Returns zero on error (as
-// opposed to queue_write).
-{
-  if (!stream) {
-    SSL3_DEBUG_MSG ("direct_write: "
-		    "Connection already closed - simulating System.EPIPE\n");
-    // If it was closed explicitly locally then close_state would be
-    // set and we'd never get here, so we can report it as a remote
-    // close.
-    cleanup_on_error();
-    local_errno = System.EPIPE;
-    return 0;
-  }
-
-  else {
-    if (queue_write() < 0 || close_state == ABRUPT_CLOSE) {
-      SSL3_DEBUG_MSG ("direct_write: "
-		      "Connection closed abruptly - simulating System.EPIPE\n");
-      cleanup_on_error();
-      local_errno = System.EPIPE;
-      // Don't set close_state = ABRUPT_CLOSE here. It's either set
-      // already, or there's an abrupt close due to an ALERT_fatal,
-      // which we shouldn't mix up with ABRUPT_CLOSE.
-      return 0;
-    }
-
-    if (SSL_INTERNAL_WRITING || SSL_INTERNAL_READING)
-      RUN_MAYBE_BLOCKING (SSL_INTERNAL_WRITING || SSL_INTERNAL_READING,
-			  nonblocking_mode, SSL_INTERNAL_READING,
-			  SSL3_DEBUG_MORE_MSG ("direct_write: Got error\n");
-			  return 0;);
-  }
-
-  SSL3_DEBUG_MORE_MSG ("direct_write: Ok\n");
-  return 1;
-}
-
-protected int ssl_read_callback (int called_from_real_backend, string input)
-{
-  SSL3_DEBUG_MSG ("ssl_read_callback (%O, %s): "
-		  "nonblocking mode=%d, callback mode=%d %s%s\n",
-		  called_from_real_backend,
-		  input ? "string[" + sizeof (input) + "]" : "0 (queued extra call)",
-		  nonblocking_mode, !!(CALLBACK_MODE),
-		  conn ? conn->describe_state() : "not connected",
-		  conn && SSL_CLOSING_OR_CLOSED ?
-		  ", closing (" + close_state + ")" : "");
-
-  ENTER (1, called_from_real_backend) {
-    int call_accept_cb;
-
-    if (input) {
-      int handshake_already_finished = !(conn->state & CONNECTION_handshaking);
-      string|int data =
-	close_state == ABRUPT_CLOSE ? -1 : conn->got_data (input);
-
-#ifdef SSLFILE_DEBUG
-      if (got_extra_read_call_out)
-	error ("Got to real read callback with queued extra read call out.\n");
-#endif
-
-#ifdef SSL3_DEBUG_TRANSPORT
-      werror ("ssl_read_callback: Got data: %O\n", data);
-#endif
-
-      if (alert_cb_called)
-	SSL3_DEBUG_MSG ("ssl_read_callback: After alert cb call\n");
-
-      else {
-	int write_res;
-	if (stringp(data) || (data > 0) ||
-	    ((data < 0) && (conn->state & CONNECTION_handshaking))) {
-#ifdef SSLFILE_DEBUG
-	  if (!stream)
-	    error ("Got zapped stream in callback.\n");
-#endif
-	  // got_data might have put more packets in the write queue if
-	  // we're handshaking.
-	  write_res = queue_write();
-	}
-
-	cb_errno = 0;
-
-	if (stringp (data)) {
-	  if (!handshake_already_finished &&
-	      !(conn->state & CONNECTION_handshaking)) {
-	    SSL3_DEBUG_MSG ("ssl_read_callback: Handshake finished\n");
-	    update_internal_state();
-	    if (called_from_real_backend && accept_callback) {
-#ifdef SSLFILE_DEBUG
-	      if (close_state >= NORMAL_CLOSE)
-		error ("Didn't expect the connection to be "
-		       "explicitly closed already.\n");
-#endif
-	      call_accept_cb = 1;
-	    }
-	  }
-
-	  SSL3_DEBUG_MSG ("ssl_read_callback: "
-			  "Got %d bytes of application data\n", sizeof (data));
-	  read_buffer->add (data);
-	}
-
-	else if (data < 0 || write_res < 0) {
-	  SSL3_DEBUG_MSG ("ssl_read_callback: "
-			  "Got abrupt remote close - simulating System.EPIPE\n");
-	  cleanup_on_error();
-	  cb_errno = System.EPIPE;
-	}
-
-	// Don't use data > 0 here since we might have processed some
-	// application data and a close in the same got_data call.
-	if (conn->state & CONNECTION_peer_closed) {
-	  SSL3_DEBUG_MSG ("ssl_read_callback: Got close packet\n");
-	}
-      }
-    }
-
-    else {
-      // This is another call that has been queued below through a
-      // call out. That is necessary whenever we need to call several
-      // user callbacks from the same invocation: We can't do anything
-      // after calling a user callback, since the user is free to e.g.
-      // remove the callbacks and "hand over" us to another thread and
-      // do more I/O there while this one returns. We solve this
-      // problem by queuing a call out to ourselves (with zero as
-      // input) to continue.
-      got_extra_read_call_out = 0;
-      update_internal_state();
-    }
-
-    // Figure out what we need to do. call_accept_cb is already set
-    // from above.
-    int(0..1) call_read_cb;
-    int(0..1) do_close_stuff;
-    if (alert_cb_called) {
-      if (!conn) {
-	SSL3_DEBUG_MSG ("ssl_read_callback: Shut down from alert callback\n");
-	RESTORE;
-	return 0;
-      }
-      // Make sure the alert is queued for writing.
-      queue_write();
-    }
-    else {
-      call_read_cb =
-	called_from_real_backend && read_callback && sizeof (read_buffer) &&
-	// Shouldn't get here when close_state == ABRUPT_CLOSE.
-	close_state < NORMAL_CLOSE;
-      do_close_stuff =
-	!!((conn->state & CONNECTION_peer_closed) || cb_errno);
-    }
-
-    if (alert_cb_called || call_accept_cb + call_read_cb + do_close_stuff > 1) {
-      // Need to do a call out to ourselves; see comment above.
-#ifdef SSLFILE_DEBUG
-      if (!alert_cb_called && !called_from_real_backend)
-	error ("Internal confusion.\n");
-      if (called_from_real_backend && got_extra_read_call_out < 0)
-	error ("Ended up in ssl_read_callback from real backend "
-	       "when no callbacks are supposed to be installed.\n");
-#endif
-      if (!got_extra_read_call_out) {
-	got_extra_read_call_out = -1;
-	SSL3_DEBUG_MSG ("ssl_read_callback: Too much to do (%O, %O, %O, %O) - "
-			"scheduled another call\n", alert_cb_called, call_accept_cb,
-			call_read_cb, do_close_stuff);
-	update_internal_state();
-      }
-      else
-	SSL3_DEBUG_MSG ("ssl_read_callback: Too much to do (%O, %O, %O, %O) - "
-			"another call already queued\n",
-			alert_cb_called, call_accept_cb, call_read_cb, do_close_stuff);
-      alert_cb_called = 0;
-    }
-
-    else
-      if (got_extra_read_call_out) {
-	// Don't know if this actually can happen, but it's symmetric.
-	if (got_extra_read_call_out > 0)
-	  real_backend->remove_call_out (ssl_read_callback);
-	got_extra_read_call_out = 0;
-	update_internal_state();
-      }
-
-    // Now actually do (a bit of) what should be done.
-
-    if (call_accept_cb) {
-      SSL3_DEBUG_MSG ("ssl_read_callback: Calling accept callback %O\n",
-		      accept_callback);
-      RESTORE;
-      return accept_callback (this, callback_id);
-    }
-
-    else if (call_read_cb) {
-      string received = read_buffer->get();
-      SSL3_DEBUG_MSG ("call_read_callback: Calling read callback %O "
-		      "with %d bytes\n", read_callback, sizeof (received));
-      // Never called if there's an error - no need to propagate cb_errno.
-      RESTORE;
-      return read_callback (callback_id, received);
-    }
-
-    else if (do_close_stuff) {
-#ifdef SSLFILE_DEBUG
-      if (got_extra_read_call_out)
-	error ("Shouldn't have more to do after close stuff.\n");
-#endif
-
-      if ((conn->state & (CONNECTION_peer_closed | CONNECTION_local_closing)) ==
-	  CONNECTION_peer_closed) {
-	// Deinstall read side cbs to avoid reading more and install
-	// the write cb to send the close packet. Can't use
-	// update_internal_state here since we should force a
-	// deinstall of the read side cbs even when we're still in
-	// callback mode, to correctly imitate the behavior in
-	// Stdio.File (it deinstalls read side cbs whenever the close
-	// cb is called, but it's possible to reinstall the close cb
-	// later and get another call to it).
-	if (stream->query_backend() != local_backend) {
-	  stream->set_read_callback (0);
-	  stream->set_close_callback (0);
-	  stream->set_write_callback (ssl_write_callback);
-	  SSL3_DEBUG_MORE_MSG ("ssl_read_callback: Setting cbs for close [r:0 w:1]\n");
-	}
-      }
-
-      if (called_from_real_backend && close_callback) {
-	// errno() should return the error in the close callback - need to
-	// propagate it here.
-	FIX_ERRNOS (
-	  SSL3_DEBUG_MSG ("ssl_read_callback: Calling close callback %O (error %s)\n",
-			  close_callback, strerror (local_errno)),
-	  SSL3_DEBUG_MSG ("ssl_read_callback: Calling close callback %O (read eof)\n",
-			  close_callback)
-	);
-	RESTORE;
-	return close_callback (callback_id);
-      }
-
-      if (close_state >= NORMAL_CLOSE) {
-	SSL3_DEBUG_MSG ("ssl_read_callback: "
-			"In or after local close - shutting down\n");
-	shutdown();
-      }
-
-      if (cb_errno) {
-	SSL3_DEBUG_MSG ("ssl_read_callback: Returning with error\n");
-	// Make sure the local backend exits after this, so that the
-	// error isn't clobbered by later I/O.
-	RESTORE;
-	return -1;
-      }
-    }
-
-  } LEAVE;
-  return 0;
-}
-
-protected int ssl_write_callback (int called_from_real_backend)
-{
-  SSL3_DEBUG_MSG ("ssl_write_callback (%O): "
-		  "nonblocking mode=%d, callback mode=%d %s%s\n",
-		  called_from_real_backend,
-		  nonblocking_mode, !!(CALLBACK_MODE),
-		  conn ? conn->describe_state() : "",
-		  conn && SSL_CLOSING_OR_CLOSED ?
-		  ", closing (" + close_state + ")" : "");
-
-  int ret = 0;
-
-  ENTER (1, called_from_real_backend) {
-#ifdef SSLFILE_DEBUG
-    if (!stream)
-      error ("Got zapped stream in callback.\n");
-    if (got_extra_read_call_out)
-      error ("Got to write callback with queued extra read call out.\n");
-#endif
-
-  write_to_stream:
-    do {
-      if (sizeof (write_buffer)) {
-	int written;
-#ifdef SIMULATE_CLOSE_PACKET_WRITE_FAILURE
-	if (conn->state & CONNECTION_local_closing)
-	  written = -1;
-	else
-#endif
-	  written = stream->write (write_buffer);
-
-	if (written < 0 ) {
-#ifdef SIMULATE_CLOSE_PACKET_WRITE_FAILURE
-	  if (conn->state & CONNECTION_local_closing)
-	    cb_errno = System.EPIPE;
-	  else
-#endif
-	    cb_errno = stream->errno();
-	  cleanup_on_error();
-	  SSL3_DEBUG_MSG ("ssl_write_callback: Write failed: %s\n",
-			  strerror (cb_errno));
-
-	  // Make sure the local backend exits after this, so that the
-	  // error isn't clobbered by later I/O.
-	  ret = -1;
-
-	  if (conn->state & CONNECTION_local_closing &&
-	      epipe_errnos[cb_errno]) {
-	    // See if it's an error from writing a close packet that
-	    // should be ignored.
-	    write_buffer = ({}); // No use trying to write the close again.
-
-	    if (close_state == CLEAN_CLOSE) {
-	      // Never accept a failure if a clean close is requested.
-	      if (!(conn->state & CONNECTION_failing)) {
-		// Make sure that the connection is failing.
-		conn->send_packet(conn->alert(ALERT_fatal, ALERT_close_notify));
-	      }
-	      update_internal_state();
-	    }
-
-	    else {
-	      cb_errno = 0;
-
-	      if (conn->state & CONNECTION_peer_closed)
-		SSL3_DEBUG_MSG ("ssl_write_callback: Stream closed properly "
-				"remotely - ignoring failure to send close packet\n");
-
-	      else {
-		SSL3_DEBUG_MSG ("ssl_write_callback: Stream closed remotely - "
-				"checking input buffer for proper remote close\n");
-		update_internal_state();
-		if (called_from_real_backend) {
-		  // Shouldn't wait for a close packet that might
-		  // arrive later on, so we start a nonblocking local
-		  // backend to check for it. If we're already in a
-		  // local backend, this is handled by special cases
-		  // in RUN_MAYBE_BLOCKING.
-		  RUN_MAYBE_BLOCKING (
-		    (!(conn->state & CONNECTION_peer_closed)),
-		    1, 1, {});
-		}
-		else {
-		  // Can't start a nested local backend - skip out to
-		  // the one we're called from.
-		  RESTORE;
-		  return ret;
-		}
-	      }
-	    }
-	  }
-
-	  // Still try to call the write (or close) callback to
-	  // propagate the error to it.
-	  break write_to_stream;
-	}
-
-	for (int bytes = written; bytes > 0;) {
-	  if (bytes >= sizeof(write_buffer[0])) {
-	    bytes -= sizeof(write_buffer[0]);
-	    write_buffer = write_buffer[1..];
-	  } else {
-	    write_buffer[0] = write_buffer[0][bytes..];
-	    bytes = 0;
-	    break;
-	  }
-	}
-
-	SSL3_DEBUG_MSG ("ssl_write_callback: Wrote %d bytes (%d strings left)\n",
-			written, sizeof (write_buffer));
-	if (sizeof (write_buffer)) {
-	  RESTORE;
-	  return ret;
-	}
-	update_internal_state();
-      }
-
-      if (int err = queue_write()) {
-	if (err > 0) {
-#ifdef SSLFILE_DEBUG
-	  if (!(conn->state & CONNECTION_closed))
-	    error ("Expected a close to be sent or received\n");
-#endif
-
-	  if (sizeof (write_buffer))
-	    SSL3_DEBUG_MSG ("ssl_write_callback: "
-			    "Close packet queued but not yet sent\n");
-	  else {
-#ifdef SSLFILE_DEBUG
-	    if (!(conn->state & CONNECTION_local_closed))
-	      error ("Expected a close packet to be queued or sent.\n");
-#endif
-	    if (conn->state & CONNECTION_peer_closed ||
-		close_state != CLEAN_CLOSE) {
-	      SSL3_DEBUG_MSG ("ssl_write_callback: %s\n",
-			      conn->state & CONNECTION_peer_closed ?
-			      "Close packets exchanged" : "Close packet sent");
-	      break write_to_stream;
-	    }
-	    else {
-	      SSL3_DEBUG_MSG ("ssl_write_callback: "
-			      "Close packet sent - expecting response\n");
-	      // Not SSL_INTERNAL_WRITING anymore.
-	      update_internal_state();
-	    }
-	  }
-
-	  RESTORE;
-	  return ret;
-	}
-
-	else {
-	  SSL3_DEBUG_MSG ("ssl_write_callback: "
-			  "Connection closed abruptly remotely - "
-			  "simulating System.EPIPE\n");
-	  cleanup_on_error();
-	  cb_errno = System.EPIPE;
-	  ret = -1;
-	  break write_to_stream;
-	}
-      }
-    } while (sizeof (write_buffer));
-
-    if (called_from_real_backend) {
-      if (conn->state & CONNECTION_local_closed) {
-	if (close_callback && cb_errno && close_state < NORMAL_CLOSE) {
-	  // Better signal errors writing the close packet to the
-	  // close callback.
-	  FIX_ERRNOS (
-	    SSL3_DEBUG_MSG ("ssl_write_callback: Calling close callback %O "
-			    "(error %s)\n", close_callback, strerror (local_errno)),
-	    0
-	  );
-	  RESTORE;
-	  return close_callback (callback_id);
-	}
-      }
-
-      if (write_callback && !SSL_HANDSHAKING
-	  && (close_state < NORMAL_CLOSE || cb_errno)) {
-	// errno() should return the error in the write callback - need
-	// to propagate it here.
-	FIX_ERRNOS (
-	  SSL3_DEBUG_MSG ("ssl_write_callback: Calling write callback %O "
-			  "(error %s)\n", write_callback, strerror (local_errno)),
-	  SSL3_DEBUG_MSG ("ssl_write_callback: Calling write callback %O\n",
-			  write_callback)
-	);
-	RESTORE;
-	return write_callback (callback_id);
-      }
-    }
-
-    if ((close_state >= NORMAL_CLOSE &&
-	 (conn->state & CONNECTION_local_closing)) || cb_errno) {
-      SSL3_DEBUG_MSG ("ssl_write_callback: "
-		      "In or after local close - shutting down\n");
-      shutdown();
-    }
-  } LEAVE;
-  return ret;
-}
-
-protected int ssl_close_callback (int called_from_real_backend)
-{
-  SSL3_DEBUG_MSG ("ssl_close_callback (%O): "
-		  "nonblocking mode=%d, callback mode=%d %s%s\n",
-		  called_from_real_backend,
-		  nonblocking_mode, !!(CALLBACK_MODE),
-		  conn ? conn->describe_state() : "",
-		  conn && SSL_CLOSING_OR_CLOSED ?
-		  ", closing (" + close_state + ")" : "");
-
-  ENTER (1, called_from_real_backend) {
-#ifdef SSLFILE_DEBUG
-    if (!stream)
-      error ("Got zapped stream in callback.\n");
-    if (got_extra_read_call_out)
-      error ("Got to close callback with queued extra read call out.\n");
-#endif
-
-    // If we've arrived here due to an error, let it override any
-    // older errno from an earlier callback.
-    if (int new_errno = stream->errno()) {
-      SSL3_DEBUG_MSG ("ssl_close_callback: Got error %s\n", strerror (new_errno));
-      cleanup_on_error();
-      cb_errno = new_errno;
-    }
-#ifdef SSL3_DEBUG
-    else if (cb_errno)
-      SSL3_DEBUG_MSG ("ssl_close_callback: Propagating errno from another callback\n");
-#endif
-
-    if (!cb_errno) {
-      if (!conn || conn->state & CONNECTION_peer_closed)
-	SSL3_DEBUG_MSG ("ssl_close_callback: After clean close\n");
-
-      else {
-	// The remote end has closed the connection without sending a
-	// close packet.
-	//
-	// This became legal by popular demand in TLS 1.1.
-	SSL3_DEBUG_MSG ("ssl_close_callback: Did not get a remote close.\n");
-	conn->state = [int(0..0)|ConnectionState]
-	  (conn->state | CONNECTION_peer_closed);
-	SSL3_DEBUG_MSG ("ssl_close_callback: Abrupt close - "
-			"simulating System.EPIPE\n");
-	cleanup_on_error();
-	cb_errno = System.EPIPE;
-	close_state = ABRUPT_CLOSE;
-      }
-    }
-
-    if (called_from_real_backend && close_callback) {
-      // errno() should return the error in the close callback - need to
-      // propagate it here.
-      FIX_ERRNOS (
-	SSL3_DEBUG_MSG ("ssl_close_callback: Calling close callback %O (error %s)\n",
-			close_callback, strerror (local_errno)),
-	SSL3_DEBUG_MSG ("ssl_close_callback: Calling close callback %O (read eof)\n",
-			close_callback)
-      );
-      RESTORE;
-      // Note that the callback should call close() (or free things
-      // so that we get destructed) - there's no need for us to
-      // schedule a shutdown after it.
-      return close_callback (callback_id);
-    }
-
-    if (close_state >= NORMAL_CLOSE) {
-      SSL3_DEBUG_MSG ("ssl_close_callback: "
-		      "In or after local close - shutting down\n");
-      shutdown();
-    }
-  } LEAVE;
-
-  // Make sure the local backend exits after this, so that the error
-  // isn't clobbered by later I/O.
-  return -1;
-}
-
-//! The next protocol chosen by the client during application layer
-//! protocol negotiation (ALPN) or next protocol negotiation (NPN).
-string `->next_protocol() {
-    return conn->next_protocol;
-}
-
-//! Return the currently active cipher suite.
-int query_suite()
-{
-  return conn?->session?->cipher_suite;
-}
+inherit .File;
diff --git a/lib/modules/SSL.pmod/sslport.pike b/lib/modules/SSL.pmod/sslport.pike
index 2322f460105934851190052e21507255d8a7d20d..36c1369dcb49d43130681fdbf29e81c195445c53 100644
--- a/lib/modules/SSL.pmod/sslport.pike
+++ b/lib/modules/SSL.pmod/sslport.pike
@@ -1,175 +1,4 @@
-#pike __REAL_VERSION__
+#pike 7.8
 #require constant(SSL.Cipher)
 
-#define Context .Context
-
-//! Interface similar to @[Stdio.Port].
-
-//!
-inherit Stdio.Port : socket;
-
-//! Context to use for the connections.
-Context ctx;
-
-protected ADT.Queue accept_queue = ADT.Queue();
-
-//!
-function(object, mixed|void:void) accept_callback;
-
-//! @decl void finished_callback(SSL.sslfile f, mixed|void id)
-//!
-//! SSL connection accept callback.
-//!
-//! @param f
-//!   The @[SSL.sslfile] that just finished negotiation.
-//!
-//! This function is installed as the @[SSL.sslfile] accept
-//! callback by @[ssl_callback()], and enqueues the newly
-//! negotiated @[SSL.sslfile] on the accept queue.
-//!
-//! If there has been an @[accept_callback] installed by
-//! @[bind()] or @[listen_fd()], it will be called with
-//! all pending @[SSL.sslfile]s on the accept queue.
-//!
-//! If there's no @[accept_callback], then the @[SSL.sslfile]
-//! will have to be retrieved from the queue by calling @[accept()].
-void finished_callback(object f, mixed|void id)
-{
-  accept_queue->put(f);
-  while (accept_callback && sizeof(accept_queue))
-  {
-    accept_callback(f, id);
-  }
-}
-
-//! Connection accept callback.
-//!
-//! This function is installed as the @[Stdio.Port] callback,
-//! and accepts the connection and creates a corresponding
-//! @[SSL.sslfile] with @[finished_callback()] as the accept
-//! callback.
-//!
-//! @seealso
-//!   @[bind()], @[finished_callback()]
-void ssl_callback(mixed id)
-{
-  object f = socket_accept();
-  if (f)
-  {
-    object ssl_fd = .sslfile(f, ctx);
-    ssl_fd->accept();
-    ssl_fd->set_accept_callback(finished_callback);
-  }
-}
-
-#if 0
-void set_id(mixed id)
-{
-  error( "Not supported\n" );
-}
-
-mixed query_id()
-{
-  error( "Not supported\n" );
-}
-#endif
-
-//! @decl int bind(int port, @
-//!                function(SSL.sslfile|void, mixed|void: int) callback, @
-//!                string|void ip)
-//!
-//! Bind an SSL port.
-//!
-//! @param port
-//!   Port number to bind.
-//!
-//! @param callback
-//!   Callback to call when the SSL connection has been negotiated.
-//!
-//!   The callback is called with an @[SSL.sslfile] as the first argument,
-//!   and the id for the @[SSL.sslfile] as the second.
-//!
-//!   If the @[callback] is @expr{0@} (zero), then negotiated
-//!   @[SSL.sslfile]s will be enqueued for later retrieval with
-//!   @[accept()].
-//!
-//! @param ip
-//!   Optional IP-number to bind.
-//!
-//! @returns
-//!   Returns @expr{1@} if binding of the port succeeded,
-//!   and @expr{0@} (zero) on failure.
-//!
-//! @seealso
-//!   @[Stdio.Port()->bind()], @[SSL.sslfile()->set_accept_callback()],
-//!   @[listen_fd()]
-int bind(int port, function callback, string|void ip)
-{
-  accept_callback = callback;
-  return socket::bind(port, ssl_callback, ip);
-}
-
-//! @decl int listen_fd(int fd, @
-//!                     function(SSL.sslfile|void, mixed|void: int) callback)
-//!
-//! Set up listening for SSL connections on an already opened fd.
-//!
-//! @param fd
-//!   File descriptor to listen on.
-//!
-//! @param callback
-//!   Callback to call when the SSL connection has been negotiated.
-//!
-//!   The callback is called with an @[SSL.sslfile] as the first argument,
-//!   and the id for the @[SSL.sslfile] as the second.
-//!
-//!   If the @[callback] is @expr{0@} (zero), then negotiated
-//!   @[SSL.sslfile]s will be enqueued for later retrieval with
-//!   @[accept()].
-//!
-//! @returns
-//!   Returns @expr{1@} if listening on the fd succeeded,
-//!   and @expr{0@} (zero) on failure.
-//!
-//! @seealso
-//!   @[Stdio.Port()->listen_fd()], @[SSL.sslfile()->set_accept_callback()],
-//!   @[bind()]
-int listen_fd(int fd, function callback)
-{
-  accept_callback = callback;
-  return socket::listen_fd(fd, ssl_callback);
-}
-
-//! Low-level accept.
-//!
-//! @seealso
-//!   @[Stdio.Port()->accept()]
-Stdio.File socket_accept()
-{
-  return socket::accept();
-}
-
-//! @decl SSL.sslfile accept()
-//!
-//! Get the next pending @[SSL.sslfile] from the @[accept_queue].
-//!
-//! @returns
-//!   Returns the next pending @[SSL.sslfile] if any,
-//!   and @expr{0@} (zero) if there are none.
-object accept()
-{
-  return accept_queue->get();
-}
-
-//! Create a new port for accepting SSL connections.
-//!
-//! @seealso
-//!   @[bind()], @[listen_fd()]
-void create(Context|void ctx)
-{
-#ifdef SSL3_DEBUG
-  werror("SSL.sslport->create\n");
-#endif
-  if (!ctx) ctx = Context();
-  this_program::ctx = ctx;
-}
+inherit .Port;
diff --git a/lib/modules/SSL.pmod/testsuite.in b/lib/modules/SSL.pmod/testsuite.in
index f83184e1d040d44574dc25563ddd64eb47dde195..b23b6d6a638cc0e170f302d2f4935f0f7ae43a55 100644
--- a/lib/modules/SSL.pmod/testsuite.in
+++ b/lib/modules/SSL.pmod/testsuite.in
@@ -238,7 +238,7 @@ define(run_sub_test, [[
   ]])
 ]])
 
-dnl Displaced tests for SSL.sslfile.
+dnl Displaced tests for SSL.File.
 run_sub_test(({"SRCDIR../../../src/modules/_Stdio/async_tls_close_test.pike", "0", "0"}))
 run_sub_test(({"SRCDIR../../../src/modules/_Stdio/async_tls_close_test.pike", "0", "1"}))
 run_sub_test(({"SRCDIR../../../src/modules/_Stdio/async_tls_close_test.pike", "1", "0"}))
@@ -440,9 +440,9 @@ test_do([[
     Stdio.File server_con =
       client_con->pipe(Stdio.PROP_NONBLOCK | Stdio.PROP_BIDIRECTIONAL);
 
-    SSL.sslfile server = SSL.sslfile(server_con, server_ctx);
+    SSL.File server = SSL.File(server_con, server_ctx);
 
-    SSL.sslfile client = SSL.sslfile(client_con, client_ctx);
+    SSL.File client = SSL.File(client_con, client_ctx);
 
     if (!client->connect()) return 0;
     if (!server->accept()) return 0;
diff --git a/lib/modules/Sql.pmod/pgsql.pike b/lib/modules/Sql.pmod/pgsql.pike
index db0850eb8bb63539883af7dc192a70ed66e6b751..276a9ac5e0fe612fe63ad11e12052431e29e0c60 100644
--- a/lib/modules/Sql.pmod/pgsql.pike
+++ b/lib/modules/Sql.pmod/pgsql.pike
@@ -333,7 +333,7 @@ final private object getsocket(void|int nossl)
     return UNDEFINED;
 
   object fcon;
-#if constant(SSL.sslfile)
+#if constant(SSL.File)
   if(!nossl && (options->use_ssl || options->force_ssl))
   { PD("SSLRequest\n");
     { object c=.pgsql_util.PGassist();
diff --git a/lib/modules/Sql.pmod/pgsql_util.pmod b/lib/modules/Sql.pmod/pgsql_util.pmod
index dcbba5fd0a4c886c9114c747878678e51bad0769..a338346bda88c34848669fa61e0009ae436227c8 100644
--- a/lib/modules/Sql.pmod/pgsql_util.pmod
+++ b/lib/modules/Sql.pmod/pgsql_util.pmod
@@ -189,16 +189,16 @@ class PGconn
   }
 }
 
-#if constant(SSL.sslfile)
+#if constant(SSL.File)
 class PGconnS
-{ inherit SSL.sslfile:std;
+{ inherit SSL.File:std;
   inherit PGassist:pg;
 
   Stdio.File rawstream;
 
   inline int(-1..1) peek(int timeout)
   { return rawstream.peek(timeout);			    // This is a kludge
-  }			 // Actually SSL.sslfile should provide a peek() method
+  }			 // Actually SSL.File should provide a peek() method
 
   inline string read(int len,void|int(0..1) not_all)
   { return std::read(len,not_all);
diff --git a/src/modules/_Stdio/async_tls_close_test.pike b/src/modules/_Stdio/async_tls_close_test.pike
index 04702a0285b2083cab77eb739ab29095ce03b22b..1f4b5c7021e9fc9d44de2f628ab50da1e087aa7d 100644
--- a/src/modules/_Stdio/async_tls_close_test.pike
+++ b/src/modules/_Stdio/async_tls_close_test.pike
@@ -21,12 +21,12 @@ int main (int argc, array(string) argv)
 			       });
 
   Stdio.File con = Stdio.File();
-  SSL.sslfile tlscon;
+  SSL.File tlscon;
   con->async_connect (
     "127.0.0.1", 36565,
     lambda (int success) {
       if (success) {
-	tlscon = SSL.sslfile (con, SSL.Context());
+	tlscon = SSL.File (con, SSL.Context());
 	tlscon->connect("localhost");
 	tlscon->set_write_callback (lambda () {
 				      fail = "Handshake should not succeed.\n";