Skip to content

Commit

Permalink
Merge: nitcorn: intro an alternative to answer allowing more contro…
Browse files Browse the repository at this point in the history
…l and revamp the README file

The new `prepare_respond_and_close` is an advanced alternative to `answer`. It allows mainly to delay answering to some requests. I may this to implement simple push notifications. It could also be used to do some parallel or IO intensive work, while processing other requests.

The new README should be more up to date and a better support to get to know the library. I'm not sure about including a code example, but I thought it could help. The file could still benefit from some work, but it's an improvement.

Pull-Request: #1828
Reviewed-by: Jean Privat <jean@pryen.org>
  • Loading branch information
privat committed Nov 11, 2015
2 parents 70240a1 + 75c0efe commit b12bfdd
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 25 deletions.
1 change: 1 addition & 0 deletions lib/libevent.nit
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class Connection
# Close this connection if possible, otherwise mark it to be closed later
redef fun close
do
if closed then return
var success = native_buffer_event.destroy
close_requested = true
closed = success
Expand Down
91 changes: 74 additions & 17 deletions lib/nitcorn/README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,87 @@
The nitcorn Web server framework creates server-side Web apps in Nit
Lightweight framework for Web applications development

# Features

Dynamic content is served by subclassing `Action` and implementing `answer`.
This method receives an `HttpRequest` and must return an `HttpResponse`.
_nitcorn_ provides `FileServer`, a simple `Action` to serve static files.

`HttpRequest` contains the GET and POST arguments as well as session data it one exists.
The produced `HttpResponse` should contain the HTTP status code, the body,
session data to preserve or create a session, and optionally list files to append.

Each `Action` may be associated to many instances of `Route`.
These routes can simply identify the root of a service,
but also define parameters within the URI.

_nitcorn_ instances are configured dynamically in Nit code with the interfaces and routes created as needed.

_nitcorn_ plays well with other Nit services and tools such as `serialization`, `mongodb`, `sqlite` and `nitiwiki`.
It also benefits from the full power of the Nit language:
class refinement can be used to customize default services and merge many applications in a single server,
and the FFI enables calling services in different languages.

# Examples

Want to see `nitcorn` in action? Examples are available at `examples/nitcorn/src/`.
A minimal example follows with a custom `Action` and using `FileServer`.

More general examples are available at `lib/nitcorn/examples/`.
It includes the configuration of `http://xymus.net/` which merges many other _nitcorn_ applications.

Larger projects using _nitcorn_ can be found in the `contrib/` folder:
* _opportunity_ is a meetup planner heavily based on _nitcorn_.
* _tnitter_ is a micro-blogging platform with a simple Web and RESTful interface.
* _benitlux_ uses a custom `Action` to subscribe people to a mailing list and define a RESTful interface.

## Simple hello world server

~~~
import nitcorn
# Simple Action to display the Hello World page and the get arguments
class HelloWorldAction
super Action
redef fun answer(http_request, turi)
do
var title = "Hello World!"
var args = http_request.get_args.join(",", ":")
var response = new HttpResponse(200)
response.body = """
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{{{title}}}</title>
</head>
<body>
<h1>{{{title}}}</h1>
<p>GET args: {{{args}}}</p>
</body>
</html>"""
return response
end
end
# Features and TODO list
# Listen on `localhost:8080`
var vh = new VirtualHost("localhost:8080")
- [x] Virtual hosts and routes
- [x] Configuration change on the fly
- [x] Sessions
- [x] Reading cookies
- [x] Parameterized routes
- [ ] Full cookie support
- [ ] Close interfaces on the fly
- [ ] Better logging
- [ ] Info/status page
- [ ] `ProxyAction` to redirect a request to an external server
- [ ] `ModuleAction` which forwards the request to an independant Nit program
# Serve `http://localhost:8080/hello.html` with our custom action
vh.routes.add new Route("/hello.html", new HelloWorldAction)
## Bugs / Limitations
# Serve everything else under `http://localhost:8080/` using a `FileServer` with a root at "/var/www/"
vh.routes.add new Route(null, new FileServer("/var/www/"))
* The size of requests is limited, so no big uploads
# Launch server
var factory = new HttpFactory.and_libevent
factory.config.virtual_hosts.add vh
factory.run
~~~

# Credits

This nitcorn library is a fork from an independant project originally created in 2013 by
This nitcorn library is a fork from an independent project originally created in 2013 by
Jean-Philippe Caissy, Guillaume Auger, Frederic Sevillano, Justin Michaud-Ouellette,
Stephan Michaud and Maxime Bélanger.

Expand Down
36 changes: 28 additions & 8 deletions lib/nitcorn/reactor.nit
Original file line number Diff line number Diff line change
Expand Up @@ -79,27 +79,47 @@ class HttpServer
if root != null then
turi = ("/" + request.uri.substring_from(root.length)).simplify_path
else turi = request.uri
response = handler.answer(request, turi)

# Delegate the responsibility to respond to the `Action`
handler.prepare_respond_and_close(request, turi, self)
return
else response = new HttpResponse(405)
else response = new HttpResponse(405)

# Send back a response
respond response
close
end

# Send back `response` to the client
fun respond(response: HttpResponse)
do
write response.to_s
for path in response.files do write_file path

close
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
Expand All @@ -111,7 +131,7 @@ 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)
Expand Down

0 comments on commit b12bfdd

Please sign in to comment.