1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2013 Jean-Philippe Caissy <jpcaissy@piji.ca>
4 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
10 # http://www.apache.org/licenses/LICENSE-2.0
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
18 # Core of the `nitcorn` project, provides `HttpFactory` and `Action`
21 import more_collections
28 # A server handling a single connection
32 # The associated `HttpFactory`
33 var factory
: HttpFactory
35 init(buf_ev
: NativeBufferEvent, factory
: HttpFactory) do self.factory
= factory
37 private var parser
= new HttpRequestParser is lazy
39 redef fun read_callback
(str
)
41 # TODO support bigger inputs (such as big forms and file upload)
43 var request_object
= parser
.parse_http_request
(str
.to_s
)
45 if request_object
!= null then delegate_answer request_object
49 fun delegate_answer
(request
: HttpRequest)
51 # Find target virtual host
52 var virtual_host
= null
53 if request
.header
.keys
.has
("Host") then
54 var host
= request
.header
["Host"]
55 if host
.index_of
(':') == -1 then host
+= ":80"
56 for vh
in factory
.config
.virtual_hosts
do
57 for i
in vh
.interfaces
do if i
.to_s
== host
then
64 # Use default virtual host if none already responded
65 if virtual_host
== null then virtual_host
= factory
.config
.default_virtual_host
67 # Get a response from the virtual host
69 if virtual_host
!= null then
70 var route
= virtual_host
.routes
[request
.uri
]
72 var handler
= route
.handler
76 turi
= ("/" + request
.uri
.substring_from
(root
.length
)).simplify_path
77 else turi
= request
.uri
78 response
= handler
.answer
(request
, turi
)
79 else response
= new HttpResponse(405)
80 else response
= new HttpResponse(405)
82 # Send back a response
88 redef abstract class Action
89 # Handle a request with the relative URI `truncated_uri`
91 # `request` is fully formed request object and has a reference to the session
94 # `truncated_uri` is the ending of the full request URI, truncated from the route
95 # leading to this `Action`.
96 fun answer
(request
: HttpRequest, truncated_uri
: String): HttpResponse is abstract
99 # Factory to create `HttpServer` instances, and hold the libevent base handler
101 super ConnectionFactory
103 # Configuration of this server
105 # It should be populated after this object has instanciated
106 var config
= new ServerConfig.with_factory
(self)
108 # Instanciate a server and libvent
110 # You can use this to create the first `HttpFactory`, which is the most common.
111 init and_libevent
do init(new NativeEventBase)
113 redef fun spawn_connection
(buf_ev
) do return new HttpServer(buf_ev
, self)
115 # Launch the main loop of this server
123 redef class ServerConfig
124 # Handle to retreive the `HttpFactory` on config change
125 private var factory
: HttpFactory
127 private init with_factory
(factory
: HttpFactory) do self.factory
= factory
132 private var listeners
= new HashMap2[String, Int, ConnectionListener]
134 # Hosts needong each listener
135 private var listeners_count
= new HashMap2[String, Int, Int]
137 # Activate a listener on `interfac` if there's not already one
138 private fun listen_on
(interfac
: Interface, factory
: HttpFactory)
140 if interfac
.registered
then return
142 var name
= interfac
.name
143 var port
= interfac
.port
145 var listener
= listeners
[name
, port
]
146 if listener
== null then
147 listener
= factory
.bind_to
(name
, port
)
148 if listener
!= null then
149 sys
.listeners
[name
, port
] = listener
150 listeners_count
[name
, port
] = 1
153 listeners_count
[name
, port
] += 1
156 interfac
.registered
= true
159 # TODO close listener
162 redef class Interface
163 # Has `self` been registered by `listen_on`?
164 private var registered
= false
167 redef class Interfaces
171 if vh
.server_config
!= null then sys
.listen_on
(e
, vh
.server_config
.factory
)
177 redef class VirtualHosts
181 for i
in e
.interfaces
do sys
.listen_on
(i
, config
.factory
)