Billy Barrow 1 giorno fa
parent
commit
dee35c7120
9 ha cambiato i file con 128 aggiunte e 53 eliminazioni
  1. 3 2
      examples/SimpleApi.vala
  2. 2 0
      meson.build
  3. 18 38
      src/Context.vala
  4. 1 1
      src/RequestHandler.vala
  5. 50 0
      src/Response.vala
  6. 7 4
      src/Router.vala
  7. 38 6
      src/Server.vala
  8. 3 2
      src/meson.build
  9. 6 0
      vapi/libmicrohttpd.vapi

+ 3 - 2
examples/SimpleApi.vala

@@ -1,4 +1,5 @@
 using Astralis;
+using Invercargill;
 
 void main() {
     var router = new Router();
@@ -6,12 +7,12 @@ void main() {
     
     router.map_get("/hello", (context) => {
         print("Handling /hello\n");
-        context.respond_string(StatusCode.OK, "Hello from Astralis!");
+        return new BufferedHttpResult.from_string("Hello from Astralis!");
     });
 
     router.map_get("/json", (context) => {
         print("Handling /json\n");
-        context.respond_string(StatusCode.OK, "{ \"message\": \"Hello JSON\" }", "application/json");
+        return new BufferedHttpResult.from_string("{ \"message\": \"Hello JSON\" }", StatusCode.OK, Iterate.single<HttpHeader>(new HttpHeader("Content-Type","application/json")));
     });
 
     server.run();

+ 2 - 0
meson.build

@@ -8,6 +8,8 @@ gobject_dep = dependency('gobject-2.0')
 gio_dep = dependency('gio-2.0')
 mhd_dep = dependency('libmicrohttpd')
 invercargill_dep = dependency('invercargill-1')
+json_glib_dep = dependency('json-glib-1.0')
+invercargill_json_dep = dependency('invercargill-json')
 
 # VAPI Directory
 add_project_arguments(['--vapidir', join_paths(meson.current_source_dir(), 'vapi')], language: 'vala')

+ 18 - 38
src/Context.vala

@@ -3,55 +3,35 @@ using Invercargill;
 
 namespace Astralis {
 
-    public class Request : Object {
-        public string url { get; private set; }
+    public class HttpRequest : Object {
+        public string raw_path { get; private set; }
         public string method { get; private set; }
         public string version { get; private set; }
+
+        public Enumerable<string> path_components { get; private set; }
+        
+        public Enumerable<HttpHeader> headers { get; private set; }
     
-        public Request(string url, string method, string version) {
-            this.url = url;
+        public HttpRequest(string url, string method, string version) {
+            this.raw_path = url;
             this.method = method;
             this.version = version;
+
+            
+
+            var path_parts = this.raw_path.split("?");
+            this.path_components = Wrap.array<string>(path_parts[0].split("/"))
+                .select<string>(p => Uri.unescape_string (p) ?? "")
+                .where (p => p != "")
+                .to_immutable_buffer();
         }
     }
 
     public class HttpContext : Object {
-        public Request request { get; private set; }
-        private Connection connection;
+        public HttpRequest request { get; private set; }
 
-        internal HttpContext(Connection connection, Request request) {
+        internal HttpContext(HttpRequest request) {
             this.request = request;
-            this.connection = connection;
-        }
-
-        public void respond(StatusCode status, Enumerable<HttpHeader> headers, uint8[] buffer) {
-            var mhd_response = new Response.from_buffer(
-                buffer.length, 
-                buffer, 
-                ResponseMemoryMode.RESPMEM_MUST_COPY
-            );
-            add_response_headers(mhd_response, headers);
-            queue_response(status, mhd_response);            
-        }
-
-        public void respond_string(StatusCode status, string response, string content_type = "text/plain") {
-            var headers = Iterate.these<HttpHeader>(new HttpHeader("Content-Type", content_type));
-            respond(status, headers, response.data);     
-        }
-
-
-        internal static void add_response_headers(Response response, Enumerable<HttpHeader> headers) {
-            foreach (var header in headers) {
-                response.add_header(header.header, header.value);
-            }
-        }
-
-        internal void queue_response(StatusCode status, Response response) {
-            var res = MHD.queue_response(connection, status, response);
-            if(res != MHD.Result.YES) {
-                printerr("Astralis Internal Error: Unable to queue response\n");
-            }
-            MHD.resume_connection(connection);
         }
     }
 }

+ 1 - 1
src/RequestHandler.vala

@@ -2,7 +2,7 @@
 namespace Astralis {
 
     public interface HttpHandler : Object {
-        public abstract async void handle(HttpContext context) throws Error;
+        public abstract async HttpResult handle(HttpContext context) throws Error;
     }
 
 }

+ 50 - 0
src/Response.vala

@@ -0,0 +1,50 @@
+using Invercargill;
+using Invercargill.DataStructures;
+
+namespace Astralis {
+
+    public class HttpResult : Object {
+        private Series<HttpHeader> header_list;
+        public StatusCode status { get; set; }
+        public Enumerable<HttpHeader> headers { get; private set; }
+
+        public HttpResult(StatusCode status, Enumerable<HttpHeader>? headers = null) {
+            this.header_list = new Series<HttpHeader>();
+            this.headers = header_list.seal(); 
+            this.status = status;
+            if(headers != null) {
+                header_list.add_all(headers);
+            }
+        }
+
+        public void add_new_header(string name, string value) {
+            header_list.add(new HttpHeader(name, value));
+        }
+
+        public void add_header(HttpHeader header) {
+            header_list.add(header);
+        }
+
+        public void add_headers(Enumerable<HttpHeader> headers) {
+            header_list.add_all(headers);
+        }
+    }
+
+    public class BufferedHttpResult : HttpResult {
+
+        public uint8[] buffer { get; set; }
+
+        public BufferedHttpResult(uint8[] buffer, StatusCode status = StatusCode.OK, Enumerable<HttpHeader>? headers = null) {
+            base(status, headers);
+            this.buffer = buffer;
+            add_new_header("Content-Length", this.buffer.length.to_string());
+        }
+
+        public BufferedHttpResult.from_string(string str, StatusCode status = StatusCode.OK, Enumerable<HttpHeader>? headers = null) {
+            base(status, headers);
+            this.buffer = str.data;
+            add_new_header("Content-Length", this.buffer.length.to_string());
+        }
+    }
+
+}

+ 7 - 4
src/Router.vala

@@ -2,7 +2,7 @@ using Invercargill.DataStructures;
 
 namespace Astralis {
 
-    public delegate void RouteHandler(HttpContext context);
+    public delegate HttpResult RouteHandler(HttpContext context) throws Error;
 
 
     public class RouteEntry {
@@ -21,11 +21,14 @@ namespace Astralis {
             routes.set(path, new RouteEntry((owned) handler));
         }
 
-        public async void handle(HttpContext context) {
+        public async HttpResult handle(HttpContext context) {
             try {
-                routes.get(context.request.url).handler(context);
+                return routes.get(context.request.raw_path).handler(context);
             } catch (Invercargill.IndexError e) {
-                context.respond_string(StatusCode.NOT_FOUND, "Not Found");
+                return new BufferedHttpResult.from_string("Not Found", StatusCode.NOT_FOUND);
+            }
+            catch {
+                return new BufferedHttpResult.from_string("Internal Server Error", StatusCode.INTERNAL_SERVER_ERROR);
             }
         }
     }

+ 38 - 6
src/Server.vala

@@ -28,16 +28,17 @@ namespace Astralis {
                 return Result.YES;
             }
 
-            var request = new Request(url ?? "", method ?? "", version ?? "");
-            var context = new HttpContext(connection, request);
+            var request = new HttpRequest(url ?? "", method ?? "", version ?? "");
+            var context = new HttpContext(request);
             MHD.suspend_connection(connection);
 
             handler.handle.begin(context, (obj, res) => {
                 try {
-                    handler.handle.end(res);
+                    var result = handler.handle.end(res);
+                    respond(connection, result);
                 }
                 catch(Error e) {
-                    handle_error(e, context);
+                    handle_error(e, connection);
                 }
             });
 
@@ -64,9 +65,40 @@ namespace Astralis {
             new MainLoop().run();
         }
 
-        private void handle_error(Error error, HttpContext context) {
+        private void respond(Connection connection, HttpResult result) {
+            Response response;
+
+            if(result.get_type().is_a(typeof(BufferedHttpResult))) {
+                var buffered = (BufferedHttpResult)result;
+                response = new Response.from_buffer(
+                    buffered.buffer.length,
+                    buffered.buffer, 
+                    ResponseMemoryMode.RESPMEM_MUST_COPY
+                );
+            }
+            else {
+                response = new Response.from_buffer(
+                    0,
+                    new uint8[0], 
+                    ResponseMemoryMode.RESPMEM_PERSISTENT
+                );
+            }
+
+
+            foreach (var header in result.headers) {
+                response.add_header(header.header, header.value);
+            }
+
+            var res = MHD.queue_response(connection, result.status, response);
+            if(res != MHD.Result.YES) {
+                printerr("Astralis Internal Error: Unable to queue response\n");
+            }
+            MHD.resume_connection(connection);
+        }
+
+        private void handle_error(Error error, Connection connection) {
             printerr(@"Astralis Internal Server Error: Unhandled Error: $(error.message)\n");
-            context.respond_string(StatusCode.INTERNAL_SERVER_ERROR, "Internal Server Error");
+            respond(connection, new BufferedHttpResult.from_string("Internal Server Error", StatusCode.INTERNAL_SERVER_ERROR));
         }
     }
 }

+ 3 - 2
src/meson.build

@@ -5,16 +5,17 @@ sources = files(
     'HttpValues.vala',
     'RequestHandler.vala',
     'Headers.vala',
+    'Response.vala'
 )
 
 libastralis = shared_library('astralis',
     sources,
-    dependencies: [glib_dep, gobject_dep, mhd_dep, gio_dep, invercargill_dep],
+    dependencies: [glib_dep, gobject_dep, mhd_dep, gio_dep, invercargill_dep, invercargill_json_dep, json_glib_dep],
     install: true
 )
 
 astralis_dep = declare_dependency(
     link_with: libastralis,
     include_directories: include_directories('.'),
-    dependencies: [glib_dep, gobject_dep, invercargill_dep, mhd_dep] # Users of astralis need glib, gobject, invercargill and mhd
+    dependencies: [glib_dep, gobject_dep, invercargill_dep, invercargill_json_dep, mhd_dep, json_glib_dep] # Users of astralis need glib, gobject, invercargill and mhd
 )

+ 6 - 0
vapi/libmicrohttpd.vapi

@@ -89,4 +89,10 @@ namespace MHD {
 
     [CCode (cname = "MHD_lookup_connection_value")]
     public unowned string? lookup_connection_value (Connection connection, ValueKind kind, string key);
+
+    [CCode (instance_pos = 0)]
+    public delegate Result KeyValueIterator (void* cls, ValueKind kind, string key, string? value, size_t value_size);
+
+    [CCode (cname = "MHD_get_connection_values")]
+    public int get_connection_values (Connection connection, ValueKind kind, KeyValueIterator? iterator, void* iterator_cls);
 }