-- built-in pages like \"404 Not Found\".
, cnfServerHost :: !Strict.ByteString
- -- |A port ID to listen to HTTP clients.
- , cnfServerPort :: !PortID
+ -- |A port number (or service name) to listen to HTTP clients.
+ , cnfServerPort :: !ServiceName
-- |Local IPv4 address to listen to HTTP clients. Set this to
-- @('Just' "0.0.0.0")@ if you want to accept any IPv4
-- |A port ID to listen to HTTPS clients. Local addresses
-- (both for IPv4 and IPv6) will be derived from the parent
-- 'Config'.
- sslServerPort :: !PortID
+ sslServerPort :: !ServiceName
-- |An SSL context for accepting connections.
, sslContext :: !SSLContext
defaultConfig = Config {
cnfServerSoftware = C8.pack "Lucu/1.0"
, cnfServerHost = C8.pack (unsafePerformIO getHostName)
- , cnfServerPort = Service "http"
+ , cnfServerPort = "http"
, cnfServerV4Addr = Just "0.0.0.0"
, cnfServerV6Addr = Just "::"
, cnfSSLConfig = Nothing
import qualified Data.ByteString.Char8 as C8
import qualified Data.ByteString.Lazy.Char8 as L8
import Data.Maybe
-import Network
import Network.HTTP.Lucu.Config
import Network.HTTP.Lucu.Format
import Network.HTTP.Lucu.Headers
sig = C8.unpack (cnfServerSoftware conf)
++ " at "
++ C8.unpack (cnfServerHost conf)
- ++ ( case cnfServerPort conf of
- Service serv -> ", service " ++ serv
- PortNumber num -> ", port " ++ show num
- UnixSocket path -> ", unix socket " ++ show path
- )
in ( eelem "/"
+= ( eelem "html"
+= sattr "xmlns" "http://www.w3.org/1999/xhtml"
where
import Control.Concurrent
-import Network hiding (accept)
+import Control.Exception
+import Control.Monad
+import Network.BSD
+import Network.Socket
import Network.HTTP.Lucu.Config
import Network.HTTP.Lucu.Interaction
import Network.HTTP.Lucu.RequestReader
import Network.HTTP.Lucu.Resource.Tree
import Network.HTTP.Lucu.ResponseWriter
-import Network.HTTP.Lucu.SocketLike
+import Network.HTTP.Lucu.SocketLike as SL
import System.Posix.Signals
-- |This is the entry point of Lucu httpd. It listens to a socket and
= withSocketsDo $
do _ <- installHandler sigPIPE Ignore Nothing
+ -- FIXME: TERRIBLE CODE. NEED MAJOR REWRITE.
case cnfSSLConfig cnf of
Nothing
-> return ()
Just scnf
- -> do so <- listenOn (sslServerPort scnf)
- _loopTID <- forkIO $ httpLoop (sslContext scnf, so)
- return ()
+ -> do case cnfServerV4Addr cnf of
+ Nothing
+ -> return ()
+ Just v4addr
+ -> do so <- listenOn AF_INET v4addr (sslServerPort scnf)
+ p <- socketPort so
+ -- FIXME: Don't throw away the thread
+ -- ID as we can't kill it later
+ -- then. [1]
+ _ <- forkIO $ httpLoop p (sslContext scnf, so)
+ return ()
+
+ case cnfServerV6Addr cnf of
+ Nothing
+ -> return ()
+ Just v6addr
+ -> do so <- listenOn AF_INET6 v6addr (sslServerPort scnf)
+ p <- socketPort so
+ -- FIXME: [1]
+ _ <- forkIO $ httpLoop p (sslContext scnf, so)
+ return ()
- httpLoop =<< listenOn (cnfServerPort cnf)
+ case cnfServerV4Addr cnf of
+ Nothing
+ -> return ()
+ Just v4addr
+ -> do so <- listenOn AF_INET v4addr (cnfServerPort cnf)
+ p <- socketPort so
+ -- FIXME: [1]
+ _ <- forkIO $ httpLoop p so
+ return ()
+
+ case cnfServerV6Addr cnf of
+ Nothing
+ -> return ()
+ Just v6addr
+ -> do so <- listenOn AF_INET6 v6addr (cnfServerPort cnf)
+ p <- socketPort so
+ -- FIXME: [1]
+ _ <- forkIO $ httpLoop p so
+ return ()
+
+ waitForever
where
- httpLoop :: SocketLike s => s -> IO ()
- httpLoop so
- = do (h, addr) <- accept so
+ listenOn :: Family -> HostName -> ServiceName -> IO Socket
+ listenOn fam host srv
+ = do proto <- getProtocolNumber "tcp"
+ let hints = defaultHints {
+ addrFlags = [AI_PASSIVE]
+ , addrFamily = fam
+ , addrSocketType = Stream
+ , addrProtocol = proto
+ }
+ addrs <- getAddrInfo (Just hints) (Just host) (Just srv)
+ let addr = head addrs
+ bracketOnError
+ (socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr))
+ (sClose)
+ (\ sock ->
+ do setSocketOption sock ReuseAddr 1
+ bindSocket sock (addrAddress addr)
+ listen sock maxListenQueue
+ return sock
+ )
+
+ httpLoop :: SocketLike s => PortNumber -> s -> IO ()
+ httpLoop port so
+ = do (h, addr) <- SL.accept so
tQueue <- newInteractionQueue
- readerTID <- forkIO $ requestReader cnf tree fbs h addr tQueue
+ readerTID <- forkIO $ requestReader cnf tree fbs h port addr tQueue
_writerTID <- forkIO $ responseWriter cnf h tQueue readerTID
- httpLoop so
+ httpLoop port so
+
+ waitForever :: IO ()
+ waitForever = forever (threadDelay 1000000)
data Interaction = Interaction {
itrConfig :: !Config
+ , itrLocalPort :: !PortNumber
, itrRemoteAddr :: !SockAddr
, itrRemoteCert :: !(Maybe X509)
, itrResourcePath :: !(Maybe [String])
defaultPageContentType = C8.pack "application/xhtml+xml"
-newInteraction :: Config -> SockAddr -> Maybe X509 -> Maybe Request -> IO Interaction
-newInteraction !conf !addr !cert !req
+newInteraction :: Config -> PortNumber -> SockAddr -> Maybe X509 -> Maybe Request -> IO Interaction
+newInteraction !conf !port !addr !cert !req
= do request <- newTVarIO req
responce <- newTVarIO Response {
resVersion = HttpVersion 1 1
return Interaction {
itrConfig = conf
+ , itrLocalPort = port
, itrRemoteAddr = addr
, itrRemoteCert = cert
, itrResourcePath = Nothing
import Network.HTTP.Lucu.Interaction
import Network.HTTP.Lucu.Request
import Network.HTTP.Lucu.Response
-import Network
import Network.URI
{-
preprocessHeader req
where
setStatus :: StatusCode -> STM ()
- setStatus status
- = status `seq`
- updateItr itr itrResponse
+ setStatus !status
+ = updateItr itr itrResponse
$! \ res -> res {
resStatus = status
}
completeAuthority :: Request -> STM ()
- completeAuthority req
- = req `seq`
- when (uriAuthority (reqURI req) == Nothing)
+ completeAuthority !req
+ = when (uriAuthority (reqURI req) == Nothing)
$ if reqVersion req == HttpVersion 1 0 then
-- HTTP/1.0 なので Config から補完
do let conf = itrConfig itr
host = cnfServerHost conf
- port = case cnfServerPort conf of
- PortNumber n -> Just (fromIntegral n :: Int)
- _ -> Nothing
+ port = itrLocalPort itr
portStr
= case port of
- Just 80 -> Just ""
- Just n -> Just $ ':' : show n
- Nothing -> Nothing
- case portStr of
- Just str -> updateAuthority host (C8.pack str)
- -- FIXME: このエラーの原因は、listen してゐるソ
- -- ケットが INET でない故にポート番號が分からな
- -- い事だが、その事をどうにかして通知した方が良
- -- いと思ふ。stderr?
- Nothing -> setStatus InternalServerError
+ 80 -> ""
+ n -> ':' : show n
+ updateAuthority host (C8.pack portStr)
else
case getHeader (C8.pack "Host") req of
Just str -> let (host, portStr) = parseHost str
import System.IO (stderr)
-requestReader :: HandleLike h => Config -> ResTree -> [FallbackHandler] -> h -> SockAddr -> InteractionQueue -> IO ()
-requestReader !cnf !tree !fbs !h !addr !tQueue
+requestReader :: HandleLike h => Config -> ResTree -> [FallbackHandler] -> h -> PortNumber -> SockAddr -> InteractionQueue -> IO ()
+requestReader !cnf !tree !fbs !h !port !addr !tQueue
= do input <- hGetLBS h
acceptRequest input
`catches`
acceptNonparsableRequest :: StatusCode -> IO ()
acceptNonparsableRequest status
= {-# SCC "acceptNonparsableRequest" #-}
- do itr <- newInteraction cnf addr Nothing Nothing
+ do itr <- newInteraction cnf port addr Nothing Nothing
atomically $ do updateItr itr itrResponse
$ \ res -> res {
resStatus = status
acceptParsableRequest req input
= {-# SCC "acceptParsableRequest" #-}
do cert <- hGetPeerCert h
- itr <- newInteraction cnf addr cert (Just req)
+ itr <- newInteraction cnf port addr cert (Just req)
action
<- atomically $
do preprocess itr
-import Network
import Network.HTTP.Lucu
main :: IO ()
-main = let config = defaultConfig { cnfServerPort = PortNumber 9999 }
+main = let config = defaultConfig { cnfServerPort = "9999" }
resources = mkResTree [ ( []
, helloWorld )
import MiseRafturai
-import Network
import Network.HTTP.Lucu
main :: IO ()
-main = let config = defaultConfig { cnfServerPort = PortNumber 9999 }
+main = let config = defaultConfig { cnfServerPort = "9999" }
resources = mkResTree [ ([], miseRafturai) ]
in
do putStrLn "Access http://localhost:9999/ with your browser."
-import Network
import Network.HTTP.Lucu
import SmallFile
main :: IO ()
-main = let config = defaultConfig { cnfServerPort = PortNumber 9999 }
+main = let config = defaultConfig { cnfServerPort = "9999" }
resources = mkResTree [ ([], smallFile) ]
in
do putStrLn "Access http://localhost:9999/ with your browser."
ImplantedSmall \
Multipart \
SSL \
- StaticDir \
$(NULL)
build: $(TARGETS)
MiseRafturai.hs: mise-rafturai.html
lucu-implant-file -m MiseRafturai -o $@ $<
+ImplantedSmall.hs: SmallFile.hs
+
SmallFile.hs: small-file.txt
lucu-implant-file -m SmallFile -o $@ $<
import qualified Data.ByteString.Lazy.Char8 as L8
import Data.List
import Data.Maybe
-import Network
import Network.HTTP.Lucu
main :: IO ()
-main = let config = defaultConfig { cnfServerPort = PortNumber 9999 }
+main = let config = defaultConfig { cnfServerPort = "9999" }
resources = mkResTree [ ([], resMain) ]
in
do putStrLn "Access http://localhost:9999/ with your browser."
import Control.Monad
import "mtl" Control.Monad.Trans
import Data.Time.Clock
-import Network
import Network.HTTP.Lucu
import OpenSSL
import OpenSSL.EVP.PKey
SSL.contextSetDefaultCiphers ctx
let config = defaultConfig {
- cnfServerPort = PortNumber 9000
+ cnfServerPort = "9000"
, cnfSSLConfig = Just SSLConfig {
- sslServerPort = PortNumber 9001
+ sslServerPort = "9001"
, sslContext = ctx
}
}