Подтвердить что ты не робот

Как написать простой 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 года и больше не работает. И я не смог его перенести.

Любые идеи?

4b9b3361

Ответ 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 {..}) в шаблоне и иметь все поля записи в области с правой стороны.