-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathHeader.hs
152 lines (125 loc) · 4.65 KB
/
Header.hs
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
module Header where
import Data.Monoid
import qualified Data.ByteString.Lazy as BL hiding (pack)
import qualified Data.ByteString.Lazy.Char8 as BL
import Data.Int
import Types
import Network.URI
import Data.Char
-- Outgoing headers:
--
messageLength = 10
isItZipped = True
buildHeader :: Int -> Bool -> Int64 -> String -> BL.ByteString
buildHeader code isItZipped messageLength lastModified = header code isItZipped messageLength lastModified
-- Build a header with correct content length
header :: Int -> Bool -> Int64 -> String -> BL.ByteString
header code isItZipped messageLength lastModified
| isItZipped = mconcat [ status, plainText, contentLength, mLength, modified, gzipped, endHeader ]
| otherwise = mconcat [ status, plainText, contentLength, mLength, modified, endHeader ]
where
status = statusCode code
contentLength = BL.pack "\r\nContent-Length: "
mLength = BL.pack . show $ messageLength
gzipped = BL.pack "\r\nContent-Encoding: gzip"
plainText = BL.pack "\r\nContent-Type: text/plain; charset=\"Shift_JIS\""
modified = if lastModified == ""
then BL.pack ""
else BL.pack ("\r\nLast-Modified: " ++ lastModified)
endHeader = BL.pack "\r\n\r\n"
statusCode :: Int -> BL.ByteString
statusCode code = BL.pack $ check code
where
check code
| code == 200 = "HTTP/1.0 200 OK"
| code == 401 = "HTTP/1.0 401 Unathorized"
| code == 400 = "HTTP/1.0 400 Bad Request"
| code == 404 = "HTTP/1.0 404 Not Found"
| otherwise = "HTTP/1.0 403 Forbidden"
-- Incoming headers:
--
-- Parses the header sent by the user, looks for the GET data
parseHeader :: String -> Header
parseHeader = (parseHead 1) . lines
where
parseHead :: Int -> [String] -> Header
parseHead i [] = Header "meow"
parseHead i (h:hs)
| i > 100 = parseHead (i + 1) []
| take 3 h == "GET" = checkForEnd 1 h hs
| otherwise = parseHead (i + 1) hs
checkForEnd i headerGet [] = Header "asdf"
checkForEnd i headerGet (h:hs)
| i > 100 = Header "lulz"
| h == ("\r\n\r\n") = Header headerGet
| h == ("\r\n") = Header headerGet
| h == ("\r") = Header headerGet
| h == ("\n") = Header headerGet
| otherwise = checkForEnd (i + 1) headerGet hs
parseHeaderStream headerIn = HeaderNew getRequestQuery getGzipFlag getUserAgent getDoesItEnd
where
stream :: [String]
stream = headerStream headerIn
-- Escape the Url Encoding also!
getRequestQuery :: (String, String)
getRequestQuery = parseRequestQuery stream
getGzipFlag :: Bool
getGzipFlag = parseGzipFlag stream
getUserAgent :: Maybe String
getUserAgent = parseUserAgent stream
getDoesItEnd :: Bool
getDoesItEnd = parseDoesItEnd stream
parseRequestQuery :: [String] -> (String, String)
parseRequestQuery [] = ("asdf", "asdf")
parseRequestQuery (x:xs)
| length (words x) < 1 = parseRequestQuery xs
| "GET" == s = (s, unEscapeString x)
| "HEAD" == s = (s, unEscapeString x)
| "POST" == s = (s, unEscapeString x)
| "PUT" == s = (s, unEscapeString x)
| "DELETE" == s = (s, unEscapeString x)
| "TRACE" == s = (s, unEscapeString x)
| "CONNECT" == s = (s, unEscapeString x)
| otherwise = parseRequestQuery xs
where
s = head $ words x
methods :: [String]
methods = ["GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT"]
parseGzipFlag :: [String] -> Bool
parseGzipFlag [] = False
parseGzipFlag (x:xs)
| ("accept-encoding" `elem` s) && ("gzip" `elem` s) = True
| otherwise = parseGzipFlag xs
where
-- remove commas, then make everything lowercase, and finally split by word
s = words $ removePunctuation trimmed ""
-- take only 100 chars to be parsed
trimmed = take 100 x
-- recursive function to remove punctuation and replace them with a space
removePunctuation [] s = reverse s
removePunctuation (z:zs) s
| z == ',' = removePunctuation zs (' ' : s)
| z == ';' = removePunctuation zs (' ' : s)
| z == ':' = removePunctuation zs (' ' : s)
| otherwise = removePunctuation zs ((toLower z):s)
parseUserAgent :: [String] -> Maybe String
parseUserAgent [] = Nothing
parseUserAgent (x:xs)
| "User-Agent:" `elem` s = Just x
| otherwise = parseUserAgent xs
where
s = words x
parseDoesItEnd :: [String] -> Bool
parseDoesItEnd [] = False
parseDoesItEnd (x:xs)
| x == "\r" = True
| otherwise = parseDoesItEnd xs
-- Gets a header without waiting to be blocked. It should work with a stream of header info
headerStream :: String -> [String]
headerStream headerIn = getHeaderStream (lines $ headerIn) [] 0
where
getHeaderStream [] m _ = m
getHeaderStream (h:hs) m i
| i > 50 = m
| h == "\r" = (h:m)
| otherwise = getHeaderStream hs (h:m) i