0 | module Network.HTTP.Message
2 | import Data.String.Parser
3 | import Data.String.Extra
4 | import Derive.Prelude
5 | import Network.HTTP.Header
6 | import Network.HTTP.Status
7 | import Network.HTTP.Method
11 | %language ElabReflection
15 | RawHeaders = List (String, String)
18 | record RawHttpMessage where
19 | constructor MkRawHttpMessage
22 | headers : RawHeaders
25 | record HttpResponse where
26 | constructor MkHttpResponse
37 | status_code : DPair Nat StatusCode
38 | status_name : String
39 | headers : RawHeaders
41 | %runElab derive "RawHttpMessage" [Show]
42 | %runElab derive "HttpResponse" [Show]
45 | serialize_http_message : RawHttpMessage -> String
46 | serialize_http_message message =
48 | $
[ http_method_to_string message.method <+> " " <+> message.path <+> " HTTP/1.1" ]
49 | <+> map (\(k,v) => "\{k}: \{v}") message.headers
53 | serialize_http_response : HttpResponse -> String
54 | serialize_http_response response =
56 | $
[ "HTTP/1.1 " <+> show (response.status_code.fst) <+> response.status_name ]
57 | <+> map (\(k,v) => "\{k}: \{v}") response.headers
60 | eol : Monad m => ParseT m ()
61 | eol = (string "\r\n" <|> string "\n") $> ()
63 | is_eol : Char -> Bool
68 | header : Parser (String, String)
70 | key <- takeUntil ":"
71 | value <- takeWhile1 (not . is_eol)
73 | pure (key, (ltrim value))
76 | http_message_praser : Parser RawHttpMessage
77 | http_message_praser = do
78 | method <- string_to_http_method . pack <$> some (satisfy isUpper)
80 | path <- takeUntil " "
81 | _ <- string "HTTP/1.1"
83 | headers <- many header
85 | pure (MkRawHttpMessage method path headers)
88 | deserialize_http_message : String -> Either String RawHttpMessage
89 | deserialize_http_message = map fst . parse http_message_praser
92 | http_message_response : Parser HttpResponse
93 | http_message_response = do
95 | _ <- string "HTTP/1.1 " <|> string "HTTP/1.0 "
96 | status_code <- natural
98 | status_name <- takeWhile1 (not . is_eol)
100 | headers <- many header
101 | case is_status_code_number status_code of
102 | Yes ok => pure (MkHttpResponse (
status_code ** nat_to_status_code status_code ok)
status_name headers)
103 | No _ => fail $
"status code " <+> show status_code <+> " is outside of bound"
106 | deserialize_http_response : String -> Either String HttpResponse
107 | deserialize_http_response = map fst . parse http_message_response