lib/nitcorn: http_request_buffer subclass Connection instead of refining it
[nit.git] / lib / nitcorn / http_request_buffer.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Http request parsing for bufferized inputs.
16 module http_request_buffer
17
18 intrude import libevent
19
20 # Connection rebuilding HTTP requests
21 #
22 # Subclass should refine `read_full_request` and avoid `read_callback`.
23 class HTTPConnection
24 super Connection
25
26 private var in_request = false
27 private var in_header = false
28 private var in_body = false
29 private var current_header = new Array[Writable]
30 private var current_body = new Array[Writable]
31 private var content_length = 0
32 private var current_length = 0
33
34 # FIXME will not work if the header/body delimiter fall between two watermarks windows.
35 redef fun read_callback_native(cstr, len)
36 do
37 # is this the start of a request?
38 if not in_request then
39 parse_start
40 end
41 var str = cstr.to_s_with_length(len)
42 var body: String
43 # parsing header
44 if in_header then
45 body = parse_header(str)
46 else
47 body = str
48 end
49 # parsing body
50 if in_body then
51 parse_body(body)
52 end
53 end
54
55
56 # We have a new request entering
57 private fun parse_start do
58 in_request = true
59 # reset values
60 current_header.clear
61 current_body.clear
62 current_length = 0
63 content_length = 0
64 # next step is to find the header part
65 in_header = true
66 in_body = false
67 end
68
69 # We are receiving the header of a request
70 #
71 # Return parsed body foud in header window
72 private fun parse_header(str: String): String do
73 # split in CRLF
74 var parts = str.split("\r\n\r\n")
75 # first part go in the header
76 current_header.add parts.shift
77
78 # if there is more part we are done with headers
79 if not parts.is_empty then
80 # get content-length
81 parse_content_length current_header.join
82 # next step if to parse body
83 in_header = false
84 in_body = true
85 # return rest of the body
86 return parts.join("\r\n")
87 end
88 return ""
89 end
90
91 # Extract and set `content_length` from header.
92 private fun parse_content_length(head: String) do
93 var hlines = head.split("\r\n")
94 for hline in hlines do
95 var hparts = hline.split(": ")
96 if hparts.first == "Content-Length" then
97 content_length = hparts[1].to_i
98 end
99 end
100 end
101
102 # We are receiving body parts.
103 private fun parse_body(str: String) do
104 current_length += str.length
105 current_body.add str
106 if current_length >= content_length then
107 parse_end
108 end
109 end
110
111 # We have reached the end of the body
112 private fun parse_end do
113 var res = new FlatBuffer.with_capacity(content_length)
114 for ch in current_header do res.append ch.write_to_string
115 res.append "\r\n\r\n"
116 for cb in current_body do res.append cb.write_to_string
117 read_callback(res.write_to_string)
118 in_request = false
119 end
120 end