-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Second Transfer HTTP/2 web server -- @package second-transfer @version 0.5.0.0 module SecondTransfer.Sessions.Config -- | Get/set a numeric Id from a SessionCoordinates. For example, to -- get the session id with this, import `Control.Lens.(^.)` and then do -- --
-- session_id = session_coordinates ^. sessionId --sessionId :: Functor f => (Int -> f Int) -> SessionCoordinates -> f SessionCoordinates -- | Creates a default sessions context. Modify as needed using the lenses -- interfaces defaultSessionsConfig :: SessionsConfig -- | Lens to access sessionsCallbacks in the SessionsConfig object. sessionsCallbacks :: Lens' SessionsConfig SessionsCallbacks reportErrorCallback :: Iso' SessionsCallbacks (Maybe ErrorCallback) -- | Components at an individual session. Used to report where in the -- session an error was produced. This interface is likely to change in -- the future, as we add more metadata to exceptions data SessionComponent SessionInputThread_HTTP2SessionComponent :: SessionComponent SessionHeadersOutputThread_HTTP2SessionComponent :: SessionComponent SessionDataOutputThread_HTTP2SessionComponent :: SessionComponent Framer_HTTP2SessionComponent :: SessionComponent Session_HTTP11 :: SessionComponent -- | Information used to identify a particular session. newtype SessionCoordinates SessionCoordinates :: Int -> SessionCoordinates -- | Callbacks that you can provide your sessions to notify you of -- interesting things happening in the server. data SessionsCallbacks SessionsCallbacks :: Maybe ErrorCallback -> SessionsCallbacks _reportErrorCallback :: SessionsCallbacks -> Maybe ErrorCallback -- | Configuration information you can provide to the session maker. data SessionsConfig SessionsConfig :: SessionsCallbacks -> SessionsConfig -- | Session callbacks _sessionsCallbacks :: SessionsConfig -> SessionsCallbacks -- | Used by this session engine to report an error at some component, in a -- particular session. type ErrorCallback = (SessionComponent, SessionCoordinates, SomeException) -> IO () instance Show SessionCoordinates instance Show SessionComponent instance Eq SessionCoordinates module SecondTransfer.Sessions makeSessionsContext :: SessionsConfig -> IO SessionsContext -- | Contains information that applies to all sessions created in the -- program. Use the lenses interface to access members of this struct. data SessionsContext module SecondTransfer.Exception -- | Abstract exception. All HTTP/2 exceptions derive from here data HTTP2SessionException HTTP2SessionException :: e -> HTTP2SessionException -- | Abstract exception. Thrown when encoding/decoding of a frame fails data FramerException FramerException :: e -> FramerException -- | Thrown when the HTTP/2 connection prefix doesn't match the expected -- prefix. data BadPrefaceException BadPrefaceException :: BadPrefaceException -- | Abstract exception. All HTTP/1.1 related exceptions derive from here. -- Notice that this includes a lot of logical errors and they can be -- raised when handling HTTP/2 sessions also data HTTP11Exception HTTP11Exception :: e -> HTTP11Exception data HTTP11SyntaxException HTTP11SyntaxException :: String -> HTTP11SyntaxException -- | Thrown with HTTP1.1 over HTTP1.1 sessions when the response -- body or the request body doesn't include a Content-Length header -- field, even if it should have included it data ContentLengthMissingException ContentLengthMissingException :: ContentLengthMissingException -- | Throw exceptions derived from this (e.g, GenericIOProblem -- below) to have the HTTP/2 session to terminate gracefully. data IOProblem IOProblem :: e -> IOProblem -- | A concrete case of the above exception. Throw one of this if you don't -- want to implement your own type. Use IOProblem in catch -- signatures. data GenericIOProblem GenericIOProblem :: GenericIOProblem -- | This exception will be raised inside a CoherentWorker when -- the underlying stream is cancelled (STREAM_RESET in HTTP/2). Do any -- necessary cleanup in a handler, or simply use the fact that the -- exception is asynchronously delivered to your CoherentWorker Haskell -- thread, giving you an opportunity to interrupt any blocked operations. data StreamCancelledException StreamCancelledException :: StreamCancelledException instance Typeable HTTP2SessionException instance Typeable FramerException instance Typeable BadPrefaceException instance Typeable HTTP11Exception instance Typeable ContentLengthMissingException instance Typeable HTTP11SyntaxException instance Typeable IOProblem instance Typeable GenericIOProblem instance Typeable StreamCancelledException instance Show BadPrefaceException instance Show ContentLengthMissingException instance Show HTTP11SyntaxException instance Show GenericIOProblem instance Show StreamCancelledException instance Exception StreamCancelledException instance Exception GenericIOProblem instance Exception IOProblem instance Show IOProblem instance Exception HTTP11SyntaxException instance Exception ContentLengthMissingException instance Exception HTTP11Exception instance Show HTTP11Exception instance Exception BadPrefaceException instance Exception FramerException instance Show FramerException instance Exception HTTP2SessionException instance Show HTTP2SessionException module SecondTransfer.Types -- | Callback type to push data to a channel. Part of this interface is the -- abstract exception type IOProblem. Throw an instance of it from here -- to notify the session that the connection has been broken. There is no -- way to signal "normal termination", since HTTP/2's normal termination -- can be observed at a higher level when a GO_AWAY frame is seen. type PushAction = ByteString -> IO () -- | Callback type to pull data from a channel. The same as to PushAction -- applies to exceptions thrown from there. type PullAction = IO ByteString -- | A function which takes three arguments: the first one says how to send -- data (on a socket or similar transport), and the second one how to -- receive data on the transport. The third argument encapsulates the -- sequence of steps needed for a clean shutdown. -- -- You can implement one of these to let somebody else supply the push, -- pull and close callbacks. For example, tlsServeWithALPN will -- supply these arguments to an Attendant. -- -- Attendants encapsulate all the session book-keeping functionality, -- which for HTTP/2 is quite complicated. You use the functions -- http**Attendant to create one of these from a CoherentWorker. -- -- This library supplies two of such Attendant factories, -- http11Attendant for HTTP 1.1 sessions, and -- http2Attendant for HTTP/2 sessions. type Attendant = PushAction -> PullAction -> CloseAction -> IO () -- | Callback that the session calls to realease resources associated with -- the channels. Take into account that your callback should be able to -- deal with non-clean shutdowns also, for example, if the connection to -- the remote peer is severed suddenly. type CloseAction = IO () -- | Gets a single header from the list getHeaderFromFlatList :: Headers -> ByteString -> Maybe ByteString -- | The name part of a header type HeaderName = ByteString -- | The value part of a header type HeaderValue = ByteString -- | The complete header type Header = (HeaderName, HeaderValue) -- | List of headers. The first part of each tuple is the header name (be -- sure to conform to the HTTP/2 convention of using lowercase) and the -- second part is the headers contents. This list needs to include the -- special :method, :scheme, :authority and :path pseudo-headers for -- requests; and :status (with a plain numeric value represented in ascii -- digits) for responses. type Headers = [Header] -- | Finalization headers. If you don't know what they are, chances are -- that you don't need to worry about them for now. The support in this -- library for those are at best sketchy. type FinalizationHeaders = Headers -- | A request is a set of headers and a request body.... which will -- normally be empty, except for POST and PUT requests. But this library -- enforces none of that. type Request = (Headers, Maybe InputDataStream) -- | Finalization headers type Footers = FinalizationHeaders -- | Main type of this library. You implement one of these for your server. -- Basically this is a callback that the library calls as soon as it has -- all the headers of a request. For GET requests that's the entire -- request basically, but for POST and PUT requests this is just before -- the data starts arriving to the server. type CoherentWorker = Request -> IO PrincipalStream -- | You use this type to answer a request. The Headers are thus -- response headers and they should contain the :status pseudo-header. -- The PushedStreams is a list of pushed streams...(I don't thaink -- that I'm handling those yet) type PrincipalStream = (Headers, PushedStreams, DataAndConclusion) -- | A list of pushed streams. Notice that a list of IO computations is -- required here. These computations only happen when and if the streams -- are pushed to the client. The lazy nature of Haskell helps to avoid -- unneeded computations if the streams are not going to be sent to the -- client. type PushedStreams = [IO PushedStream] -- | A pushed stream, represented by a list of request headers, a list of -- response headers, and the usual response body (which may include final -- footers (not implemented yet)). type PushedStream = (Headers, Headers, DataAndConclusion) -- | A source-like conduit with the data returned in the response. The -- return value of the conduit is a list of footers. For now that list -- can be anything (even bottom), I'm not handling it just yet. type DataAndConclusion = ConduitM () ByteString IO Footers -- | This is a Source conduit (see Haskell Data.Conduit library from -- Michael Snoyman) that you can use to retrieve the data sent by the -- client piece-wise. type InputDataStream = Source IO ByteString -- | Utilities for working with headers. module SecondTransfer.Utils.HTTPHeaders -- | HTTP headers are case-insensitive, so we can use lowercase versions -- everywhere lowercaseHeaders :: Headers -> Headers -- | Checks that headers are lowercase headersAreValidHTTP2 :: Headers -> Bool -- | Looks for a given header fetchHeader :: Headers -> ByteString -> Maybe ByteString -- | Abstract data-type. Use fromList to get one of these from -- Headers. The underlying representation admits better -- asymptotics. data HeaderEditor -- | O(n*log n) Builds the editor from a list. fromList :: Headers -> HeaderEditor -- | O(n) Takes the HeaderEditor back to Headers toList :: HeaderEditor -> Headers -- | headerLens header_name represents a lens into the headers, and you can -- use it then to add, alter and remove headers. It uses the same -- semantics than replaceHeaderValue headerLens :: ByteString -> Lens' HeaderEditor (Maybe ByteString) -- | replaceHeaderValue headers header_name maybe_header_value looks for -- header_name. If header_name is found and maybe_header_value is -- nothing, it returns a new headers list with the header deleted. If -- header_name is found and header_value is Just new_value, it returns a -- new list with the header containing the new value. If header_name is -- not in headers and maybe_header_value is Nothing, it returns the -- original headers list. If header_name is not in headers and -- maybe_header_value is Just new_value, it returns a new list where the -- last element is (header_name, new_value) replaceHeaderValue :: HeaderEditor -> ByteString -> Maybe ByteString -> HeaderEditor -- | Replaces a "host" HTTP/1.1 header by an ":authority" HTTP/2 header. -- The list is expected to be already in lowercase, so nothing will -- happen if there the header name portion is "Host" instead of "host". -- -- Notice that having a Host header in an HTTP/2 message is -- perfectly valid in certain circumstances, check Section 8.1.2.3 -- of the spec for details. replaceHostByAuthority :: HeaderEditor -> HeaderEditor instance Eq Autosorted instance Ord Autosorted module SecondTransfer.MainLoop -- | Simple function to open tlsServeWithALPN :: FilePath -> FilePath -> String -> [(String, Attendant)] -> Int -> IO () -- | Interruptible version of tlsServeWithALPN. Use the extra -- argument to ask the server to finish: you pass an empty MVar and when -- you want to finish you just populate it. tlsServeWithALPNAndFinishOnRequest :: FilePath -> FilePath -> String -> [(String, Attendant)] -> Int -> MVar FinishRequest -> IO () -- | Activates logging to terminal enableConsoleLogging :: IO () -- | Exceptions inheriting from IOProblem. This is thrown by the -- OpenSSL subsystem to signal that the connection was broken or that -- otherwise there was a problem at the SSL layer. data TLSLayerGenericProblem TLSLayerGenericProblem :: String -> TLSLayerGenericProblem -- | Singleton type. Used in conjunction with an MVar. If the MVar -- is full, the fuction tlsServeWithALPNAndFinishOnRequest knows -- that it should finish at its earliest convenience and call the -- CloseAction for any open sessions. data FinishRequest FinishRequest :: FinishRequest module SecondTransfer.Http2 -- | The type of this function is equivalent to: -- --
-- http2Attendant :: CoherentWorker -> PushAction -> PullAction -> CloseAction -> IO () ---- -- Given a CoherentWorker, this function wraps it with flow -- control, multiplexing, and state maintenance needed to run an HTTP/2 -- session. -- -- Notice that this function is using HTTP/2 over TLS. We haven't -- implemented yet a session handling mechanism for HTTP/1.1 . http2Attendant :: SessionsContext -> CoherentWorker -> Attendant module SecondTransfer.Http1 -- | Session attendant that speaks HTTP/1.1 http11Attendant :: SessionsContext -> CoherentWorker -> Attendant -- | This library implements enough of the HTTP/2 to build compliant HTTP/2 -- servers. It also implements enough of HTTP/1.1 so you can actually use -- it to build polyglot web-servers. -- -- For HTTP/2, frame encoding and decoding is done with Kazu Yamamoto's -- http2 package. This library just takes care of making sense of -- sent and received frames. -- -- You can find more detailed information about this library at the page -- https://www.httptwo.com/second-transfer/. -- -- The library -- --
-- import SecondTransfer(
-- CoherentWorker
-- , Footers
-- , DataAndConclusion
-- , tlsServeWithALPN
-- , http2Attendant
-- , http11Attendant
-- )
-- import SecondTransfer.Sessions(
-- makeSessionsContext
-- , defaultSessionsConfig
-- )
--
-- import Data.Conduit
--
--
-- saysHello :: DataAndConclusion
-- saysHello = do
-- -- The data in each yield will be automatically split across multiple
-- -- data frames if needed, so you can yield a large block of contents here
-- -- if you wish.
-- -- If you do multiple yields, no data will be left buffered between them,
-- -- so that you can for example implement a chat client in a single HTTP/2 stream.
-- -- Pity browsers hardly support that.
-- yield "Hello world!"
-- -- No footers
-- return []
--
--
-- helloWorldWorker :: CoherentWorker
-- helloWorldWorker request = return (
-- [
-- (":status", "200")
-- ],
-- [], -- No pushed streams
-- saysHello
-- )
--
--
-- -- For this program to work, it should be run from the top of
-- -- the developement directory.
-- main = do
-- sessions_context <- makeSessionsContext defaultSessionsConfig
-- let
-- http2_attendant = http2Attendant sessions_context helloWorldWorker
-- http11_attendant = http11Attendant sessions_context helloWorldWorker
-- tlsServeWithALPN
-- "tests/support/servercert.pem" -- Server certificate
-- "tests/support/privkey.pem" -- Certificate private key
-- "127.0.0.1" -- On which interface to bind
-- [
-- ("h2-14", http2_attendant), -- Protocols present in the ALPN negotiation
-- ("h2", http2_attendant), -- they may be slightly different, but for this
-- -- test it doesn't matter.
--
-- ("http1.1", http11_attendant) -- Let's talk HTTP1.1 if everything else fails.
-- ]
-- 8000
--
--
-- CoherentWorker is the type of the basic callback function that
-- you need to implement. The callback is used to handle all requests to
-- the server on a given negotiated ALPN protocol. If you need routing
-- functionality (and you most certainly will need it), you need to build
-- that functionality inside the callback.
--
-- The above program uses a test certificate by a fake certificate
-- authority. The certificate is valid for the server name ("authority",
-- in HTTP/2 lingo) www.httpdos.com. So, in order for the above program
-- to run, you probably need to add an alias to your /etc/hosts file. You
-- also need very up-to-date versions of OpenSSL (I'm using OpenSSL
-- 1.0.2) to be compliant with the cipher suites demanded by HTTP/2. The
-- easiest way to test the above program is using a fairly recent version
-- of curl. If everything is allright, you should be able to do:
--
-- -- $ curl -k --http2 https://www.httpdos.com:8000/ -- Hello world! --module SecondTransfer -- | List of headers. The first part of each tuple is the header name (be -- sure to conform to the HTTP/2 convention of using lowercase) and the -- second part is the headers contents. This list needs to include the -- special :method, :scheme, :authority and :path pseudo-headers for -- requests; and :status (with a plain numeric value represented in ascii -- digits) for responses. type Headers = [Header] -- | The name part of a header type HeaderName = ByteString -- | The value part of a header type HeaderValue = ByteString -- | The complete header type Header = (HeaderName, HeaderValue) -- | A request is a set of headers and a request body.... which will -- normally be empty, except for POST and PUT requests. But this library -- enforces none of that. type Request = (Headers, Maybe InputDataStream) -- | Finalization headers type Footers = FinalizationHeaders -- | Main type of this library. You implement one of these for your server. -- Basically this is a callback that the library calls as soon as it has -- all the headers of a request. For GET requests that's the entire -- request basically, but for POST and PUT requests this is just before -- the data starts arriving to the server. type CoherentWorker = Request -> IO PrincipalStream -- | You use this type to answer a request. The Headers are thus -- response headers and they should contain the :status pseudo-header. -- The PushedStreams is a list of pushed streams...(I don't thaink -- that I'm handling those yet) type PrincipalStream = (Headers, PushedStreams, DataAndConclusion) -- | A list of pushed streams. Notice that a list of IO computations is -- required here. These computations only happen when and if the streams -- are pushed to the client. The lazy nature of Haskell helps to avoid -- unneeded computations if the streams are not going to be sent to the -- client. type PushedStreams = [IO PushedStream] -- | A pushed stream, represented by a list of request headers, a list of -- response headers, and the usual response body (which may include final -- footers (not implemented yet)). type PushedStream = (Headers, Headers, DataAndConclusion) -- | A source-like conduit with the data returned in the response. The -- return value of the conduit is a list of footers. For now that list -- can be anything (even bottom), I'm not handling it just yet. type DataAndConclusion = ConduitM () ByteString IO Footers -- | This is a Source conduit (see Haskell Data.Conduit library from -- Michael Snoyman) that you can use to retrieve the data sent by the -- client piece-wise. type InputDataStream = Source IO ByteString -- | Finalization headers. If you don't know what they are, chances are -- that you don't need to worry about them for now. The support in this -- library for those are at best sketchy. type FinalizationHeaders = Headers -- | A function which takes three arguments: the first one says how to send -- data (on a socket or similar transport), and the second one how to -- receive data on the transport. The third argument encapsulates the -- sequence of steps needed for a clean shutdown. -- -- You can implement one of these to let somebody else supply the push, -- pull and close callbacks. For example, tlsServeWithALPN will -- supply these arguments to an Attendant. -- -- Attendants encapsulate all the session book-keeping functionality, -- which for HTTP/2 is quite complicated. You use the functions -- http**Attendant to create one of these from a CoherentWorker. -- -- This library supplies two of such Attendant factories, -- http11Attendant for HTTP 1.1 sessions, and -- http2Attendant for HTTP/2 sessions. type Attendant = PushAction -> PullAction -> CloseAction -> IO () -- | Session attendant that speaks HTTP/1.1 http11Attendant :: SessionsContext -> CoherentWorker -> Attendant -- | The type of this function is equivalent to: -- --
-- http2Attendant :: CoherentWorker -> PushAction -> PullAction -> CloseAction -> IO () ---- -- Given a CoherentWorker, this function wraps it with flow -- control, multiplexing, and state maintenance needed to run an HTTP/2 -- session. -- -- Notice that this function is using HTTP/2 over TLS. We haven't -- implemented yet a session handling mechanism for HTTP/1.1 . http2Attendant :: SessionsContext -> CoherentWorker -> Attendant -- | Simple function to open tlsServeWithALPN :: FilePath -> FilePath -> String -> [(String, Attendant)] -> Int -> IO () -- | Interruptible version of tlsServeWithALPN. Use the extra -- argument to ask the server to finish: you pass an empty MVar and when -- you want to finish you just populate it. tlsServeWithALPNAndFinishOnRequest :: FilePath -> FilePath -> String -> [(String, Attendant)] -> Int -> MVar FinishRequest -> IO () -- | Exceptions inheriting from IOProblem. This is thrown by the -- OpenSSL subsystem to signal that the connection was broken or that -- otherwise there was a problem at the SSL layer. data TLSLayerGenericProblem TLSLayerGenericProblem :: String -> TLSLayerGenericProblem -- | Singleton type. Used in conjunction with an MVar. If the MVar -- is full, the fuction tlsServeWithALPNAndFinishOnRequest knows -- that it should finish at its earliest convenience and call the -- CloseAction for any open sessions. data FinishRequest FinishRequest :: FinishRequest -- | Activates logging to terminal enableConsoleLogging :: IO ()