1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2013 Frederic Sevillano
4 # Copyright 2013 Jean-Philippe Caissy <jpcaissy@piji.ca>
5 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
7 # Licensed under the Apache License, Version 2.0 (the "License");
8 # you may not use this file except in compliance with the License.
9 # You may obtain a copy of the License at
11 # http://www.apache.org/licenses/LICENSE-2.0
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS,
15 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
19 # Provides the `HttpRequest` class and services to create it
24 # A request received over HTTP, is build by `HttpRequestParser`
28 # HTTP protocol version
29 var http_version
: String
31 # Method of this request (GET or POST)
34 # The host targetter by this request (usually the server)
37 # The full URL requested by the client (including the `query_string`)
40 # The resource requested by the client (only the page, not the `query_string`)
43 # The string following `?` in the requested URL
46 # The header of this request
47 var header
= new HashMap[String, String]
49 # The content of the cookie of this request
50 var cookie
= new HashMap[String, String]
52 # The arguments passed with the GET method,
53 var get_args
= new HashMap[String, String]
55 # The arguments passed with the POST method
56 var post_args
= new HashMap[String, String]
59 # Utility class to parse a request string and build a `HttpRequest`
61 # The main method is `parse_http_request`.
62 class HttpRequestParser
63 # The current `HttpRequest` under construction
64 private var http_request
: HttpRequest
70 private var header_fields
= new Array[String]
72 # Words of the first line
73 private var first_line
= new Array[String]
77 fun parse_http_request
(full_request
: String): nullable HttpRequest
81 var http_request
= new HttpRequest
82 self.http_request
= http_request
84 segment_http_request
(full_request
)
86 # Parse first line, looks like "GET dir/index.html?user=xymus HTTP/1.0"
87 http_request
.method
= first_line
[0]
88 http_request
.url
= first_line
[1]
89 http_request
.http_version
= first_line
[2]
92 if http_request
.url
.has
('?') then
93 http_request
.uri
= first_line
[1].substring
(0, first_line
[1].index_of
('?'))
94 http_request
.query_string
= first_line
[1].substring_from
(first_line
[1].index_of
('?')+1)
95 http_request
.get_args
= parse_url
97 http_request
.uri
= first_line
[1]
101 if http_request
.method
== "POST" then
102 var lines
= body
.split_with
('&')
104 var parts
= line
.split_once_on
('=')
105 if parts
.length
> 1 then
106 var decoded
= parts
[1].replace
('+', " ").from_percent_encoding
107 if decoded
== null then
111 http_request
.post_args
[parts
[0]] = decoded
113 print
"POST Error: {line} format error on {line}"
119 for i
in header_fields
do
120 var temp_field
= i
.split_with
(": ")
122 if temp_field
.length
== 2 then
123 http_request
.header
[temp_field
[0]] = temp_field
[1]
128 if http_request
.header
.keys
.has
("Cookie") then
129 var cookie
= http_request
.header
["Cookie"]
130 for couple
in cookie
.split_with
(';') do
131 var words
= couple
.trim
.split_with
('=')
132 if words
.length
!= 2 then continue
133 http_request
.cookie
[words
[0]] = words
[1]
140 private fun clear_data
146 private fun segment_http_request
(http_request
: String): Bool
148 var header_end
= http_request
.search
("\r\n\r\n")
150 if header_end
== null then
151 header_fields
= http_request
.split_with
("\r\n")
153 header_fields
= http_request
.substring
(0, header_end
.from
).split_with
("\r\n")
154 body
= http_request
.substring
(header_end
.after
, http_request
.length-1
)
157 # If a line of the http_request is long it may change line, it has " " at the
158 # end to indicate this. This section turns them into 1 line.
159 if header_fields
.length
> 1 and header_fields
[0].has_suffix
(" ") then
160 var temp_req
= header_fields
[0].substring
(0, header_fields
[0].length-1
) + header_fields
[1]
162 first_line
= temp_req
.split_with
(' ')
166 if first_line
.length
!= 3 then return false
168 first_line
= header_fields
[0].split_with
(' ')
171 if first_line
.length
!= 3 then return false
174 # Cut off the header in lines
176 while pos
< header_fields
.length
do
177 if pos
< header_fields
.length-1
and header_fields
[pos
].has_suffix
(" ") then
178 header_fields
[pos
] = header_fields
[pos
].substring
(0, header_fields
[pos
].length-1
) + header_fields
[pos
+1]
179 header_fields
.remove_at
(pos
+1)
188 # Extract args from the URL
189 private fun parse_url
: HashMap[String, String]
191 var query_strings
= new HashMap[String, String]
193 if http_request
.url
.has
('?') then
194 var get_args
= http_request
.query_string
.split_with
("&")
195 for param
in get_args
do
196 var key_value
= param
.split_with
("=")
197 if key_value
.length
< 2 then continue
198 query_strings
[key_value
[0]] = key_value
[1]