我正在尝试在Haskell中编写一个非常基本的Web服务器。这是我的代码:
{-# LANGUAGE OverloadedStrings #-}
import Network (withSocketsDo, listenOn, PortID(..))
import Network.Socket (Socket, accept, close, setSocketOption, SocketOption(..))
import Network.Socket.ByteString (send, sendAll, recv)
import Control.Concurrent.Async (async)
import Control.Monad (forever)
import Data.ByteString.Char8 (unpack)
import Request
main = withSocketsDo $ do
sock <- listenOn $ PortNumber 3000
putStrLn "Listening on port 3000..."
forever $ do
(conn, _) <- accept sock
async $ handleAccept conn
handleAccept :: Socket -> IO ()
handleAccept sock = do
putStrLn $ "Connected!"
rawReq <- recv sock 4096
let req = parseRawRequest $ unpack rawReq -- returns Maybe Request
putStrLn $ show req
handleRequest sock req
handleRequest :: Socket -> Maybe Request -> IO ()
handleRequest sock Nothing = do
putStrLn "Closing..."
handleRequest sock req = do
sendAll sock "In handleRequest!" -- Doesn't appear until server is killed.
这是我期望发生的事情:
"Listening on port 3000..."
打印在服务器端。curl localhost:3000
"Connected!"
在服务器端打印。"In handleRequest!"
已打印。实际上发生了什么:
"Listening on port 3000..."
打印在服务器端。curl localhost:3000
"Connected!"
在服务器端打印。CTRL+C
杀死了服务器"In handleRequest!"
打印客户端。我怀疑这与recv
中可能存在的延迟有关,尽管我此后立即使用了该值(我将原始请求解析为Request
类型),因此从理论上讲应该对其进行评估。
如果我将sendAll sock "Yadda yadda
放在handleAccept
的末尾,则一切正常。当我将此行为移至新功能handleRequest
时,事情就变得不妙了。
有什么想法吗?我是Haskell的新手,因此,对于此问题或我的代码,我将不胜感激。
干杯。
编辑:
这太奇怪了!我“修复”了它,但我不知道为什么会这样。
这是我杀死服务器后才出现的行:
handleRequest sock req = do
sendAll sock "In handleRequest!" -- Doesn't appear until server is killed.
如果在发送后我有意关闭了套接字 ,则它可以正常工作:
handleRequest sock req = do
sendAll sock "In handleRequest!" -- Now appears without killing the server
close sock
因此它在连接关闭时发送。这与以前的行为一致,因为服务器被杀死后,连接会自动关闭。
现在让您感到困惑。如果我将其替换为:
handleRequest sock req = do
sendAll sock "In handleRequest!\n" -- Works perfect
这在不关闭连接的情况下有效!它达到了我的预期,仅需添加换行符。为什么会这样?
到底是什么?是我的终端的打印问题,不是代码? (OSX iTerm2)
编辑2:
被要求提供我的Request
模块的代码:
import Data.List (isInfixOf)
import Data.List.Split (splitOn)
data RequestType = GET | PUT
deriving Show
data Request =
Request {
reqType :: RequestType,
path :: String,
options :: [(String, String)]
} deriving Show
-- Turn a raw HTTP request into a request
-- object.
parseRawRequest :: String -> Maybe Request
parseRawRequest rawReq =
Request <$> parseRawRequestType rawReq
<*> parseRawRequestPath rawReq
<*> parseRawRequestOps rawReq
-- Turn an (entire) raw HTTP request into just
-- the request type.
parseRawRequestType :: String -> Maybe RequestType
parseRawRequestType rawReq =
case typ of
"GET" -> Just GET
"PUT" -> Just PUT
_ -> Nothing
where typ = (head . words . head . lines) rawReq
-- Turn an (entire) raw HTTP request into just
-- the path.
parseRawRequestPath :: String -> Maybe String
parseRawRequestPath = Just . (!! 1) . words . head . lines
-- Turn an (entire) raw HTTP request into just
-- a lookup table of their options.
parseRawRequestOps :: String -> Maybe [(String, String)]
parseRawRequestOps rawReq = Just [("One", "Two")] -- Test impl
答案 0 :(得分:3)
我有一个答案和一个建议。
建议您在接受后关闭naggle算法:
setSocketOption conn NoDelay 1
答案是您的sendAll
正在发送数据,但是curl没有打印数据。例如,您可以使用netcat进行确认。我注释掉了您的Nothing
案,因此无论我在netcat中键入什么内容,我都一定会收到"In handleRequest!"
消息:
服务器:
% ghc so.hs && ./so
Listening on port 3000...
Connected!
Nothing
客户端:
% nc localhost 3000
test ; My input, with a newline
In handleRequest! ; Printed out, no newline
或者,您可以使用curl的-N
选项禁用缓冲。
% curl -N localhost:3000
In handleRequest!