1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
|
{-# LANGUAGE OverloadedStrings #-}
module Network.HTTP.Semantics.Server (
-- * HTTP server
Server,
-- * Request
Request,
-- ** Accessing request
requestMethod,
requestPath,
requestAuthority,
requestScheme,
requestHeaders,
requestBodySize,
getRequestBodyChunk,
getRequestBodyChunk',
getRequestTrailers,
-- * Aux
Aux,
auxTimeHandle,
auxMySockAddr,
auxPeerSockAddr,
-- * Response
Response,
-- ** Creating response
responseNoBody,
responseFile,
responseStreaming,
responseBuilder,
-- ** Generalized streaming interface
OutBodyIface (..),
responseStreamingIface,
-- ** Accessing response
responseBodySize,
-- ** Trailers maker
TrailersMaker,
NextTrailersMaker (..),
defaultTrailersMaker,
setResponseTrailersMaker,
-- * Push promise
PushPromise (..),
pushPromise,
-- * Types
Path,
Authority,
Scheme,
FileSpec (..),
FileOffset,
ByteCount,
module Network.HTTP.Semantics.ReadN,
module Network.HTTP.Semantics.File,
) where
import Data.ByteString (ByteString)
import Data.ByteString.Builder (Builder)
import qualified Data.ByteString.UTF8 as UTF8
import Data.IORef
import qualified Network.HTTP.Types as H
import Network.HTTP.Semantics
import Network.HTTP.Semantics.File
import Network.HTTP.Semantics.ReadN
import Network.HTTP.Semantics.Server.Internal
import Network.HTTP.Semantics.Status
----------------------------------------------------------------
-- | Server type. Server takes a HTTP request, should
-- generate a HTTP response and push promises, then
-- should give them to the sending function.
-- The sending function would throw exceptions so that
-- they can be logged.
--
-- The sending function must only be called once.
type Server = Request -> Aux -> (Response -> [PushPromise] -> IO ()) -> IO ()
-- | HTTP/2 push promise or sever push.
-- Pseudo REQUEST headers in push promise is automatically generated.
-- Then, a server push is sent according to 'promiseResponse'.
data PushPromise = PushPromise
{ promiseRequestPath :: ByteString
-- ^ Accessor for a URL path in a push promise (a virtual request from a server).
-- E.g. \"\/style\/default.css\".
, promiseResponse :: Response
-- ^ Accessor for response actually pushed from a server.
}
----------------------------------------------------------------
-- | Getting the method from a request.
requestMethod :: Request -> Maybe H.Method
requestMethod (Request req) = getFieldValue tokenMethod vt
where
(_, vt) = inpObjHeaders req
-- | Getting the path from a request.
requestPath :: Request -> Maybe Path
requestPath (Request req) = getFieldValue tokenPath vt
where
(_, vt) = inpObjHeaders req
-- | Getting the authority from a request.
requestAuthority :: Request -> Maybe Authority
requestAuthority (Request req) = UTF8.toString <$> getFieldValue tokenAuthority vt
where
(_, vt) = inpObjHeaders req
-- | Getting the scheme from a request.
requestScheme :: Request -> Maybe Scheme
requestScheme (Request req) = getFieldValue tokenScheme vt
where
(_, vt) = inpObjHeaders req
-- | Getting the headers from a request.
requestHeaders :: Request -> TokenHeaderTable
requestHeaders (Request req) = inpObjHeaders req
-- | Getting the body size from a request.
requestBodySize :: Request -> Maybe Int
requestBodySize (Request req) = inpObjBodySize req
-- | Reading a chunk of the request body.
-- An empty 'ByteString' returned when finished.
getRequestBodyChunk :: Request -> IO ByteString
getRequestBodyChunk = fmap fst . getRequestBodyChunk'
-- | Generalization of 'getRequestBodyChunk' which also returns if the 'ByteString' is the final one
getRequestBodyChunk' :: Request -> IO (ByteString, Bool)
getRequestBodyChunk' (Request req) = inpObjBody req
-- | Reading request trailers.
-- This function must be called after 'getRequestBodyChunk'
-- returns an empty.
getRequestTrailers :: Request -> IO (Maybe TokenHeaderTable)
getRequestTrailers (Request req) = readIORef (inpObjTrailers req)
----------------------------------------------------------------
-- | Creating response without body.
responseNoBody :: H.Status -> H.ResponseHeaders -> Response
responseNoBody st hdr = Response $ OutObj hdr' OutBodyNone defaultTrailersMaker
where
hdr' = setStatus st hdr
-- | Creating response with file.
responseFile :: H.Status -> H.ResponseHeaders -> FileSpec -> Response
responseFile st hdr fileSpec = Response $ OutObj hdr' (OutBodyFile fileSpec) defaultTrailersMaker
where
hdr' = setStatus st hdr
-- | Creating response with builder.
responseBuilder :: H.Status -> H.ResponseHeaders -> Builder -> Response
responseBuilder st hdr builder = Response $ OutObj hdr' (OutBodyBuilder builder) defaultTrailersMaker
where
hdr' = setStatus st hdr
-- | Creating response with streaming.
responseStreaming
:: H.Status
-> H.ResponseHeaders
-> ((Builder -> IO ()) -> IO () -> IO ())
-> Response
responseStreaming st hdr strmbdy = Response $ OutObj hdr' (OutBodyStreaming strmbdy) defaultTrailersMaker
where
hdr' = setStatus st hdr
-- | Generalization of 'responseStreaming'.
responseStreamingIface
:: H.Status
-> H.ResponseHeaders
-> (OutBodyIface -> IO ())
-> Response
responseStreamingIface st hdr strmbdy = Response $ OutObj hdr' (OutBodyStreamingIface strmbdy) defaultTrailersMaker
where
hdr' = setStatus st hdr
----------------------------------------------------------------
-- | Getter for response body size. This value is available for file body.
responseBodySize :: Response -> Maybe Int
responseBodySize (Response (OutObj _ (OutBodyFile (FileSpec _ _ len)) _)) = Just (fromIntegral len)
responseBodySize _ = Nothing
-- | Setting 'TrailersMaker' to 'Response'.
setResponseTrailersMaker :: Response -> TrailersMaker -> Response
setResponseTrailersMaker (Response rsp) tm = Response rsp{outObjTrailers = tm}
----------------------------------------------------------------
-- | Creating push promise.
-- The third argument is traditional, not used.
pushPromise :: ByteString -> Response -> Int -> PushPromise
pushPromise path rsp _ = PushPromise path rsp
|