1 module http_serv;
2 
3 import heaploop.looping;
4 import heaploop.networking.http;
5 import std.string;
6 import std.stdio;
7 import std.typecons;
8 
9 
10 alias Tuple!(int, string[string], string[]) RackResponse;
11 
12 interface RackApp {
13   RackResponse call(string[string] env);
14 }
15 
16 class RackAdapter {
17   private:
18     RackApp _app;
19 
20   public:
21       this(RackApp app) {
22           _app = app;
23       }
24 
25       void handleRequest(HttpRequest req, HttpResponse res) {
26           string[string] env = requestToEnv(req);
27           RackResponse response = _app.call(env);
28           sendResponse(res, response);
29       }
30 
31   private:
32       string[string] requestToEnv(HttpRequest request) {
33           string[string] environment;
34 
35           environment["HTTP_VERSION"] = request.protocolVersion.toString;
36           environment["REQUEST_METHOD"] = request.method;
37           environment["REQUEST_URI"] = request.rawUri;
38           environment["REQUEST_PATH"] = request.uri.path;
39           environment["SCRIPT_NAME"] = "";
40           environment["PATH_INFO"] = request.uri.path;
41           environment["QUERY_STRING"] = request.uri.query;
42           environment["FRAGMENT"] = request.uri.fragment;
43 
44           string headerKey;
45           auto transTable = makeTrans("-", "_");
46           foreach(h; request.headers) {
47               headerKey = "HTTP_%s".format(h.name.toUpper.translate(transTable));
48               environment[headerKey] = h.value;
49           }
50           return environment;
51       }
52 
53       void sendResponse(HttpResponse response, RackResponse rackResponse) {
54           response.statusCode(rackResponse[0]);
55           foreach(string header, string value; rackResponse[1]) {
56               response.addHeader(header, value);
57           }
58           foreach(string chunk; rackResponse[2]) {
59               response.write(chunk);
60           }
61           response.end;
62       }
63 }
64 
65 class HelloApp : RackApp {
66     RackResponse call(string[string] env) {
67         string[string] headers;
68         string[] resBody;
69 
70         headers["Content-Type"] = "text/plain";
71         resBody ~= "Hello, World!";
72 
73         return RackResponse(200, headers, resBody);
74     }
75 }
76 
77 void main() {
78     loop ^^= {
79         auto server = new HttpListener;
80         server.bind4("0.0.0.0", 3000);
81         writeln("listening on http://localhost:3000");
82         server.listen ^^= (connection) {
83             try {
84                 auto app = new HelloApp;
85                 auto adapter = new RackAdapter(app);
86                 connection.process ^^= (&adapter.handleRequest);
87             } catch(Exception ex) {
88                 writeln("something went wrong processing http in this connection");
89             }
90         };
91     };
92 }