Как написать простой HTTP-сервер в Haskell с помощью Network.HTTP.receiveHTTP
Модуль Network.HTTP
предоставляет функции receiveHTTP
и respondHTTP
, которые я хотел бы использовать для очень простого веб-сервера. Я написал заглушку, которая просто ждет клиентов:
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Network.HTTP
import Network
import Control.Monad
main = withSocketsDo $ do
socket <- listenOn $ PortNumber 8080
forever $ do
(handle, host, port) <- accept socket
print (host, port)
Здесь accpet
дает мне Handle
, и теперь я не могу понять, как использовать Handle
с receiveHTTP
.
Я нашел пример с Google, но с 2008 года и больше не работает. И я не смог его перенести.
Любые идеи?
Ответы
Ответ 1
Возможно, он рассчитывает использовать функцию accept
из Network.Socket
вместо Network
? Это дает вам Socket
вместо Handle
, который вы можете преобразовать в форму, которую может использовать receiveHTTP
.
Обычно Handle
было бы лучше работать напрямую, но здесь HTTP-библиотека позаботится об этом для вас, поэтому вместо этого он ожидает интерфейс более низкого уровня.
РЕДАКТИРОВАТЬ:. Посмотрев на это немного, кажется, что функция socketConnection
в Network.TCP
делает то, что вам нужно. Самое смешное - это сделать сокет внутри Handle
внутри, прежде чем он его прочитает, но, похоже, не дает возможности читать из внешнего Handle
. Строковый параметр для функции должен быть именем удаленного хоста, но похоже, что это просто поддерживается для справки; он фактически не инициирует соединение с этим хостом или что-то еще.
Ответ 2
Вы можете сделать это, но я действительно думаю, что вы не должны. HTTP
может выступать в качестве сервера, но предназначен для использования на стороне клиента. Я немного искал Google, и я не могу найти примеров того, кто на самом деле использует respondHTTP
. Если вы используете HTTP-клиент на стороне клиента в 2016 году, используйте http-conduit
. На стороне сервера warp
или что-то, что от него зависит, вероятно, что вы хотите.
Тем не менее, здесь код.
#!/usr/bin/env stack
-- stack --resolver lts-6.3 --install-ghc runghc --package HTTP
{-# LANGUAGE RecordWildCards #-}
import Control.Monad
import qualified Data.ByteString as B
import Network.HTTP
import Network.Socket
import Network.URI
main = do
lsock <- socket AF_INET Stream defaultProtocol
bind lsock (SockAddrInet 8080 iNADDR_ANY)
listen lsock 1
forever $ do
(csock, _) <- accept lsock
hs <- socketConnection "" 8080 csock
req <- receiveHTTP hs
case req of
Left _ -> error "Receiving request failed"
Right (Request {..}) -> if uriPath rqURI == "/"
then do
respondHTTP hs $
Response (2,0,0) "OK" [] "Hello HTTP"
Network.HTTP.close hs
else do
respondHTTP hs $
Response (4,0,4) "Not found" [] "Nothing here"
Network.HTTP.close hs
В приведенном выше примере используется поддержка стека для скриптов shebang. chmod +x
или запустите его с помощью stack foo.hs
.
Модуль Network
устарел. Всегда используйте Network.Socket
, если вам нужен API сокета. Для чего-то более высокого уровня используйте connection
.
Вы делаете обычную вещь сокета POSIX, затем конвертируете подключенный сокет в HandleStream
с помощью socketConnection
и запускаете на нем respondHTTP
и receiveHTTP
. socketConnection
- это странная функция. Первые два параметра - это имя хоста и порт, которые AFAICT не используются при запуске сервера.
Я использовал расширение RecordWildCards
. Это позволяет мне писать Right (Request {..})
в шаблоне и иметь все поля записи в области с правой стороны.