I'm growing a web server in Haskell that interfaces with TCP sockets using network. To read the HTTP header of the client's message, I use the following function:
import Network.Socket hiding ( recv )
import Network.Socket.ByteString ( recv )
import qualified Data.ByteString as S
import Data.ByteString.UTF8 as BSU ( fromString )
readHeader :: Socket -> IO S.ByteString
readHeader sock = go sock mempty
where
go sock prevContent = do
newContent <- recv sock maxNumberOfBytesToReceive
let content = prevContent <> newContent
-- Read the data from the socket unless it's empty which means
-- that the client has closed its socket or we see an empty line
-- which marks the end of the HTTP header
if S.null newContent || emptyLine `S.isSuffixOf` content
then return content
else go sock content
where
-- Only read one byte at a time to avoid reading further than the
-- empty line separating the HTTP header from the HTTP body
maxNumberOfBytesToReceive = 1
emptyLine = BSU.fromString "\r\n\r\n"
The function only reads a byte at a time since I want to be able to read the rest of the client's message from the socket afterwards. But I'm curious whether the function could be more efficient.
My first idea was to read more bytes (say 1024) from the socket, check if those bytes contain an empty line, save that part (the HTTP header) in a ByteString and put the bytes after the empty line back into the socket's buffer. But I'm not sure whether it's possible or wise to put data back into the socket's buffer.
I'm also interested in any other code improvements that come to your mind.
The whole project is here.
Start it with stack ghci
and then run main
. Send it an HTTP request with curl http://localhost:8080/