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