X-Git-Url: http://nitlanguage.org diff --git a/lib/nitcorn/reactor.nit b/lib/nitcorn/reactor.nit index 4b13677..def3699 100644 --- a/lib/nitcorn/reactor.nit +++ b/lib/nitcorn/reactor.nit @@ -20,27 +20,25 @@ module reactor import more_collections -import http_request_parser import vararg_routes import http_request +import http_request_buffer import http_response # A server handling a single connection class HttpServer - super Connection + super HTTPConnection # The associated `HttpFactory` var factory: HttpFactory - # Init the server using `HttpFactory`. - init(buf_ev: NativeBufferEvent, factory: HttpFactory) is old_style_init do - self.factory = factory - end - private var parser = new HttpRequestParser is lazy - redef fun read_callback(str) + # Human readable address of the remote client + var remote_address: String + + redef fun read_http_request(str) do var request_object = parser.parse_http_request(str.to_s) if request_object != null then delegate_answer request_object @@ -74,32 +72,52 @@ class HttpServer request.uri_params = route.parse_params(request.uri) var handler = route.handler - var root = route.path + var root = route.resolve_path(request) var turi if root != null then turi = ("/" + request.uri.substring_from(root.length)).simplify_path else turi = request.uri - response = handler.answer(request, turi) - else response = new HttpResponse(405) - else response = new HttpResponse(405) - # Send back a response - write response.to_s - for path in response.files do write_file path + # Delegate the responsibility to respond to the `Action` + handler.prepare_respond_and_close(request, turi, self) + return + else response = new HttpResponse(404) + else response = new HttpResponse(404) + respond response close end + + # Send back `response` to the client + fun respond(response: HttpResponse) + do + response.render.write_to(self) + for path in response.files do write_file path + end end redef abstract class Action - # Handle a request with the relative URI `truncated_uri` + # Prepare a `HttpResponse` destined to the client in response to the `request` # - # `request` is fully formed request object and has a reference to the session - # if one preexists. + # `request` is fully formed request object with a reference to the session + # if one already exists. # # `truncated_uri` is the ending of the full request URI, truncated from the route # leading to this `Action`. fun answer(request: HttpRequest, truncated_uri: String): HttpResponse is abstract + + # Full to a `request` with sending the response and closing of the `http_server` + # + # Must users only need to implement `answer`, this method is for advanced use only. + # It can be used to delay an answer until an event is raised or work is done on a different thread. + # + # By default this method calls `answer`, relays the response to `http_server.respond` and closes `http_server`. + protected fun prepare_respond_and_close(request: HttpRequest, truncated_uri: String, http_server: HttpServer) + do + var response = answer(request, truncated_uri) + http_server.respond response + http_server.close + end end # Factory to create `HttpServer` instances, and hold the libevent base handler @@ -111,24 +129,32 @@ class HttpFactory # It should be populated after this object has instanciated var config = new ServerConfig.with_factory(self) - # Instanciate a server and libvent + # Instantiate a server and libvent # # You can use this to create the first `HttpFactory`, which is the most common. init and_libevent do init(new NativeEventBase) - redef fun spawn_connection(buf_ev) do return new HttpServer(buf_ev, self) + redef fun spawn_connection(buf_ev, address) do return new HttpServer(buf_ev, self, address) - # Launch the main loop of this server + # Execute the main listening loop to accept connections + # + # After the loop ends, the underlying resources are freed. + # + # When the environment variable `NIT_TESTING` is set to `true`, + # the loop is not executed but the resources are still freed. fun run do - event_base.dispatch - event_base.destroy + if "NIT_TESTING".environ != "true" then + event_base.dispatch + end + + event_base.free end end redef class ServerConfig # Handle to retreive the `HttpFactory` on config change - private var factory: HttpFactory + private var factory: HttpFactory is noinit private init with_factory(factory: HttpFactory) do self.factory = factory end @@ -150,13 +176,14 @@ redef class Sys var listener = listeners[name, port] if listener == null then - listener = factory.bind_to(name, port) + listener = factory.bind_tcp(name, port) if listener != null then sys.listeners[name, port] = listener listeners_count[name, port] = 1 end else - listeners_count[name, port] += 1 + var value = listeners_count[name, port].as(not null) + listeners_count[name, port] = value + 1 end interfac.registered = true @@ -174,7 +201,15 @@ redef class Interfaces redef fun add(e) do super - if vh.server_config != null then sys.listen_on(e, vh.server_config.factory) + var config = virtual_host.server_config + if config != null then register_and_listen(e, config) + end + + # Indirection to `listen_on` and check if this targets all addresses + private fun register_and_listen(e: Interface, config: ServerConfig) + do + listen_on(e, config.factory) + if e.name == "0.0.0.0" or e.name == "::0" then config.default_virtual_host = virtual_host end # TODO remove @@ -184,7 +219,7 @@ redef class VirtualHosts redef fun add(e) do super - for i in e.interfaces do sys.listen_on(i, config.factory) + for i in e.interfaces do e.interfaces.register_and_listen(i, config) end # TODO remove