Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Warp: Binding to Unix Domain Sockets

The example code listed here shows how to make warp listen only on specific hosts.

Furtheremore, this post shows some basics on how to use unix domain sockets in Haskell.

How can I combine those two approaches in order to make warp listen on (i.e. bind to) a specific unix domain socket (say, warp.sock)?

Note: This question intentionally shows no research effort as it was answered Q&A-Style.

like image 395
Uli Köhler Avatar asked Mar 24 '14 22:03

Uli Köhler


People also ask

Are UNIX domain sockets faster?

Unix domain sockets are often twice as fast as a TCP socket when both peers are on the same host. The Unix domain protocols are not an actual protocol suite, but a way of performing client/server communication on a single host using the same API that is used for clients and servers on different hosts.

Does Windows support Unix domain sockets?

Unix sockets allow inter-process communication (IPC) between processes on the same machine. In practice that means that all recent builds of Windows 10 support Unix sockets, plus Windows 11, of course.

How do I use a domain socket in UNIX?

To create a UNIX domain socket, use the socket function and specify AF_UNIX as the domain for the socket. The z/TPF system supports a maximum number of 16,383 active UNIX domain sockets at any time. After a UNIX domain socket is created, you must bind the socket to a unique file path by using the bind function.

Are UNIX domain sockets reliable?

Valid socket types in the UNIX domain are: SOCK_STREAM, for a stream-oriented socket; SOCK_DGRAM, for a datagram-oriented socket that preserves message boundaries (as on most UNIX implementations, UNIX domain datagram sockets are always reliable and don't reorder datagrams); and (since Linux 2.6.


2 Answers

You can use runSettingsSocket with a AF_UNIX socket:

{-# LANGUAGE OverloadedStrings #-}

import Network.Wai (responseLBS)
import Network.Wai.Handler.Warp
import Network.Socket
import Network.HTTP.Types (status200)
import Network.HTTP.Types.Header (hContentType)

main = do
    let port = 3000
    -- Open the socket
    sock <- socket AF_UNIX Stream 0
    bind sock $ SockAddrUnix "warp.sock"
    listen sock maxListenQueue
    -- Run the server
    let settings = defaultSettings { settingsPort = port }
    runSettingsSocket settings sock app
    -- Cleanup: Close socket
    close sock

app req f = f $
    responseLBS status200 [(hContentType, "text/plain")] "Hello world!"

Note that this will obviously only work on unixoid platforms.

like image 127
Uli Köhler Avatar answered Oct 21 '22 06:10

Uli Köhler


FWIW: If one wants to use http-client to use that UNIX socket:

{-# LANGUAGE OverloadedStrings #-}

import Network.HTTP.Client
import Network.HTTP.Client.Internal (Connection, openSocketConnection, makeConnection)
import Network.Socket.ByteString (sendAll, recv)

import qualified Control.Exception as E
import qualified Network.Socket as NS

main :: IO ()
main = do
    mgr <- newManager defaultManagerSettings {
        managerRawConnection = createUnixConnection
    }
    -- This changes in http-client-0.5, use parseUrlThrow
    req <- parseUrl "http://localhost/whatever"
    res <- httpLbs req mgr
    print (responseBody res)

createUnixConnection :: IO (Maybe NS.HostAddress -> String -> Int -> IO Connection)
createUnixConnection = return $ \_ _ _ -> openUnixConnection "warp.sock"

openUnixConnection :: String -> IO Connection
openUnixConnection addr = E.bracketOnError
    (NS.socket NS.AF_UNIX NS.Stream NS.defaultProtocol)
    (NS.close)
    $ \sock -> do
        NS.connect sock sockAddr
        socketConnection sock chunksize
  where
    sockAddr = NS.SockAddrUnix addr
    chunksize = 8192

-------------------------------------------------------------------------------
-- Copied from http-client
-------------------------------------------------------------------------------

socketConnection :: NS.Socket -> Int -> IO Connection
socketConnection socket chunksize = makeConnection
    (recv socket chunksize)
    (sendAll socket)
    (NS.close socket)
like image 40
phadej Avatar answered Oct 21 '22 04:10

phadej