diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Revision history for http-exchange-instantiations
 
+## 0.1.3.0 -- 2023-09-21
+
+* Add missing `interruptibleHandshake`.
+
 ## 0.1.2.1 -- 2023-09-21
 
 * Add missing `exposeInterruptibleContext`.
diff --git a/http-exchange-instantiations.cabal b/http-exchange-instantiations.cabal
--- a/http-exchange-instantiations.cabal
+++ b/http-exchange-instantiations.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.0
 name: http-exchange-instantiations
-version: 0.1.2.1
+version: 0.1.3.0
 synopsis: Instantiations of http-exchange
 -- description:
 license: BSD-3-Clause
diff --git a/src/Http/Exchange/Tls.hs b/src/Http/Exchange/Tls.hs
--- a/src/Http/Exchange/Tls.hs
+++ b/src/Http/Exchange/Tls.hs
@@ -9,6 +9,7 @@
   , exchangeInterruptible
   , exchangeTimeout
   , interruptibleContextNew
+  , interruptibleHandshake
   , exposeInterruptibleContext
     -- * Types
   , InterruptibleContext
@@ -26,7 +27,7 @@
 import Network.TLS (Context)
 import Http.Types (Request,Bodied,Response)
 
-import TlsChannel (NetworkException(..))
+import TlsChannel (NetworkException(..),tryTls)
 import TlsChannel (TransportException(..))
 import Control.Exception (IOException,try,throwIO)
 import TlsExchange (Exception(..),HttpException(..))
@@ -79,6 +80,20 @@
   writeIORef intrRef interruptibleContextError
   pure r
 
+-- | TLS handshake that can be interrupted. Unlike the original handshake
+-- from the @tls@ library, this returns exceptions rather than throwing them.
+-- This function must be called before performing any HTTP exchanges on
+-- the interruptible context.
+interruptibleHandshake ::
+     TVar Bool -- ^ Interrupt
+  -> InterruptibleContext -- ^ TLS Context supporting interruption
+  -> IO (Either TransportException ())
+interruptibleHandshake !intr (InterruptibleContext ctx intrRef) = do
+  writeIORef intrRef intr
+  x <- tryTls (Tls.handshake ctx)
+  writeIORef intrRef interruptibleContextError
+  pure x
+
 -- | Variant of 'exchange' that abandons the exchange if it has not
 -- completed in a given number of microseconds.
 exchangeTimeout :: 
@@ -220,8 +235,9 @@
   pure (InterruptibleContext context intrRef)
 
 -- | Expose the TLS context. Do not call TLS data-exchange functions like
--- @sendData@ or @recvData@ on this context. This context is exposed so
--- that the caller can query it for metadata about the session (certs, etc.).
+-- @sendData@, @recvData@, or @handshake@ on this context. This context is
+-- exposed so that the caller can query it for metadata about the session
+-- (certs, etc.).
 exposeInterruptibleContext :: InterruptibleContext -> Tls.Context
 {-# inline exposeInterruptibleContext #-}
 exposeInterruptibleContext (InterruptibleContext c _) = c
