Browse Source

D/Hunt: The old project Kiss has been renamed to Hunt (#4238)

* The project Kiss has been renamd to project Hunt

* New Hunt from Kiss

* Missing tools for building

* Update hunt.dockerfile

* header missing: Content-Type
Heromyth 6 years ago
parent
commit
c71afa285e

+ 3 - 3
frameworks/D/kiss/README.md → frameworks/D/hunt/README.md

@@ -1,10 +1,10 @@
-# Kiss Benchmarking Test
+# Hunt Benchmarking Test
 
-This is the Kiss portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
+This is the Hunt portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
 
 
 ## Requirements
-* Dlang > 2.079
+* Dlang > 2.083
 
 ## Test URLs
 

+ 9 - 9
frameworks/D/kiss/benchmark_config.json → frameworks/D/hunt/benchmark_config.json

@@ -1,5 +1,5 @@
 {
-  "framework": "kiss",
+  "framework": "hunt",
   "tests": [{
     "default": {
       "json_url": "/json",
@@ -8,17 +8,17 @@
       "approach": "Stripped",
       "classification": "Platform",
       "database": "None",
-      "framework": "Kiss",
+      "framework": "Hunt",
       "language": "D",
       "flavor": "DLang2",
       "orm": "Raw",
-      "platform": "Kiss",
+      "platform": "Hunt",
       "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "Kiss",
+      "display_name": "Hunt",
       "notes": "",
-      "versus": "Kiss"
+      "versus": "Hunt"
   },
     "ldc": {
       "json_url": "/json",
@@ -27,17 +27,17 @@
       "approach": "Stripped",
       "classification": "Platform",
       "database": "None",
-      "framework": "Kiss",
+      "framework": "Hunt",
       "language": "D",
       "flavor": "DLang2",
       "orm": "Raw",
-      "platform": "Kiss",
+      "platform": "Hunt",
       "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "Kiss",
+      "display_name": "Hunt",
       "notes": "",
-      "versus": "Kiss"
+      "versus": "Hunt"
     }
   }]
 }

+ 15 - 0
frameworks/D/hunt/dub.json

@@ -0,0 +1,15 @@
+{
+	"name": "hunt-benchmark",
+	"targetType": "executable",
+	"authors": [
+		"Putao"
+	],
+	"description": "A simple http server powered by Hunt.",
+	"copyright": "Copyright © 2017-2018, HuntLabs.cn",
+	"license": "Apache-2.0",
+	"libs-posix": [ "http_parser" ],
+	"lflags-posix": ["-Lhttp-parser/"],
+	"dependencies": {
+		"hunt": "~>1.0.0-rc.5"
+	}
+}

BIN
frameworks/D/hunt/hunt-benchmark


+ 15 - 0
frameworks/D/hunt/hunt-ldc.dockerfile

@@ -0,0 +1,15 @@
+FROM dlanguage/ldc:1.7.0
+
+RUN apt update -yqq && apt install -yqq git make
+
+ADD ./ /hunt
+WORKDIR /hunt
+
+RUN git clone https://github.com/nodejs/http-parser.git && \
+    cd http-parser && \
+    make package
+    
+RUN dub upgrade --verbose
+RUN dub build -f --arch=x86_64 --build=release --compiler=ldc2
+
+CMD ["./hunt-benchmark"]

+ 15 - 0
frameworks/D/hunt/hunt.dockerfile

@@ -0,0 +1,15 @@
+FROM dlanguage/ldc:1.7.0
+
+RUN apt update -yqq && apt install -yqq git make
+
+ADD ./ /hunt
+WORKDIR /hunt
+
+RUN git clone https://github.com/nodejs/http-parser.git && \
+    cd http-parser && \
+    make package
+    
+RUN dub upgrade --verbose
+RUN dub build -f --arch=x86_64 --build=release
+
+CMD ["./hunt-benchmark"]

+ 30 - 0
frameworks/D/hunt/source/DemoProcessor.d

@@ -0,0 +1,30 @@
+module DemoProcessor;
+
+import hunt.io;
+import http.Processor;
+import std.json;
+
+class DemoProcessor : HttpProcessor {
+    this(TcpStream client) {
+        super(client);
+    }
+
+    override void onComplete(HttpRequest req) {
+        switch (req.uri) {
+        case "/plaintext":
+            respondWith("Hello, World!", 200, HttpHeader("Content-Type", "text/plain"));
+            break;
+
+        case "/json":
+            JSONValue js;
+            js["message"] = "Hello, World!";
+            string content = js.toString();
+            respondWith(content, 200, HttpHeader("Content-Type", "application/json"));
+            break;
+
+        default:
+            respondWith("The accessable path are: /plaintext and /json", 404);
+            break;
+        }
+    }
+}

+ 36 - 0
frameworks/D/hunt/source/app.d

@@ -0,0 +1,36 @@
+/*
+ * Collie - An asynchronous event-driven network framework using Dlang development
+ *
+ * Copyright (C) 2015-2018  Shanghai Putao Technology Co., Ltd 
+ *
+ * Developer: Putao's Dlang team
+ *
+ * Licensed under the Apache-2.0 License.
+ *
+ */
+import std.getopt;
+import std.stdio;
+
+import hunt.io;
+import hunt.util.memory : totalCPUs;
+import http.Processor;
+import http.Server;
+import DemoProcessor;
+
+void main(string[] args) {
+
+	ushort port = 8080;
+	GetoptResult o = getopt(args, "port|p", "Port (default 8080)", &port);
+	if (o.helpWanted) {
+		defaultGetoptPrinter("A simple http server powered by Hunt!", o.options);
+		return;
+	}
+
+	HttpServer httpServer = new HttpServer("0.0.0.0", port, totalCPUs-1);
+	httpServer.onProcessorCreate(delegate HttpProcessor (TcpStream client) {
+		return new DemoProcessor(client);
+	});
+
+	writefln("listening on http://%s", httpServer.bindingAddress.toString());
+	httpServer.start();
+}

+ 281 - 0
frameworks/D/hunt/source/http/Parser.d

@@ -0,0 +1,281 @@
+/// Minimalistic low-overhead wrapper for nodejs/http-parser
+/// Used for benchmarks with simple server
+module http.Parser;
+
+private:
+
+import std.range.primitives;
+import core.stdc.string;
+
+alias http_data_cb = extern (C) int function(http_parser*, const ubyte* at, size_t length);
+alias http_cb = extern (C) int function(http_parser*);
+
+public enum HttpParserType : uint {
+	request = 0,
+	response = 1,
+	both = 2
+}
+
+public enum HttpMethod : uint {
+	DELETE = 0,
+	GET = 1,
+	HEAD = 2,
+	POST = 3,
+	PUT = 4,
+	/* pathological */
+	CONNECT = 5,
+	OPTIONS = 6,
+	TRACE = 7,
+	/* WebDAV */
+	COPY = 8,
+	LOCK = 9,
+	MKCOL = 10,
+	MOVE = 11,
+	PROPFIND = 12,
+	PROPPATCH = 13,
+	SEARCH = 14,
+	UNLOCK = 15,
+	BIND = 16,
+	REBIND = 17,
+	UNBIND = 18,
+	ACL = 19,
+	/* subversion */
+	REPORT = 20,
+	MKACTIVITY = 21,
+	CHECKOUT = 22,
+	MERGE = 23,
+	/* upnp */
+	MSEARCH = 24,
+	NOTIFY = 25,
+	SUBSCRIBE = 26,
+	UNSUBSCRIBE = 27,
+	/* RFC-5789 */
+	PATCH = 28,
+	PURGE = 29,
+	/* CalDAV */
+	MKCALENDAR = 30,
+	/* RFC-2068, section 19.6.1.2 */
+	LINK = 31,
+	UNLINK = 32,
+	/* icecast */
+	SOURCE = 33,
+}
+
+enum HttpError : uint {
+	OK,
+	/* Callback-related errors */
+	CB_message_begin,
+	CB_url,
+	CB_header_field,
+	CB_header_value,
+	CB_headers_complete,
+	CB_body,
+	CB_message_complete,
+	CB_status,
+	CB_chunk_header,
+	CB_chunk_complete,
+	/* Parsing-related errors */
+	INVALID_EOF_STATE,
+	HEADER_OVERFLOW,
+	CLOSED_CONNECTION,
+	INVALID_VERSION,
+	INVALID_STATUS,
+	INVALID_METHOD,
+	INVALID_URL,
+	INVALID_HOST,
+	INVALID_PORT,
+	INVALID_PATH,
+	INVALID_QUERY_STRING,
+	INVALID_FRAGMENT,
+	LF_EXPECTED,
+	INVALID_HEADER_TOKEN,
+	INVALID_CONTENT_LENGTH,
+	UNEXPECTED_CONTENT_LENGTH,
+	INVALID_CHUNK_SIZE,
+	INVALID_CONSTANT,
+	INVALID_INTERNAL_STATE,
+	STRICT,
+	PAUSED,
+	UNKNOWN,
+}
+
+struct http_parser {
+	/** PRIVATE **/
+	uint state; // bitfield
+	uint nread; /* # bytes read in various scenarios */
+	ulong content_length; /* # bytes in body (0 if no Content-Length header) */
+
+	/** READ-ONLY **/
+	ushort http_major;
+	ushort http_minor;
+	// bitfield
+	uint status_code_method_http_errono_upgrade;
+	/** PUBLIC **/
+	void* data; /* A pointer to get hook to the "connection" or "socket" object */
+}
+
+struct http_parser_settings {
+	http_cb on_message_begin;
+	http_data_cb on_url;
+	http_data_cb on_status;
+	http_data_cb on_header_field;
+	http_data_cb on_header_value;
+	http_cb on_headers_complete;
+	http_data_cb on_body;
+	http_cb on_message_complete;
+	/* When on_chunk_header is called, the current chunk length is stored
+   * in parser->content_length.
+   */
+	http_cb on_chunk_header;
+	http_cb on_chunk_complete;
+}
+
+extern (C) pure @nogc nothrow void http_parser_init(http_parser* parser, HttpParserType type);
+
+extern (C) pure @nogc nothrow int http_should_keep_alive(const http_parser* parser);
+
+/* Return a string description of the given error */
+extern (C) pure @nogc nothrow immutable(char)* http_errno_description(HttpError err);
+
+/* Checks if this is the final chunk of the body. */
+extern (C) pure @nogc nothrow int http_body_is_final(const http_parser* parser);
+
+/* Executes the parser. Returns number of parsed bytes. Sets
+* `parser->http_errno` on error. */
+extern (C) pure @nogc nothrow size_t http_parser_execute(http_parser* parser,
+		const http_parser_settings* settings, const ubyte* data, size_t len);
+
+// extern (C) uint http_parser_flags(const http_parser* parser);
+
+uint http_parser_flags(const http_parser* parser) {
+	// return parser.status_code | (parser.method<<16) | (parser.http_errno << 24) | (parser.upgrade << 31);
+	return parser.status_code_method_http_errono_upgrade;
+}
+
+// =========== Public interface starts here =============
+
+public:
+
+class HttpException : Exception {
+	HttpError error;
+
+	pure @nogc nothrow this(HttpError error, string file = __FILE__,
+			size_t line = __LINE__, Throwable nextInChain = null) {
+		this.error = error;
+		immutable char* str = http_errno_description(error);
+		super(str[0 .. strlen(str)], file, line, nextInChain);
+	}
+}
+
+struct HttpParser(Interceptor) {
+	http_parser parser;
+	http_parser_settings settings;
+	Interceptor interceptor;
+	Throwable failure;
+	uint flags;
+
+	static generateCallback(string cName, string dName) {
+		import std.format;
+
+		return format(`
+			static if(__traits(hasMember, interceptor, "%2$s"))
+			{
+				extern(C) static int %1$s(http_parser* p) {
+					auto parser = cast(HttpParser*)p;
+					try {
+						parser.flags = http_parser_flags(p);
+						return parser.interceptor.%2$s(parser);
+					}
+					catch (Throwable t) {
+						parser.failure = t;
+						return 1;
+					}
+				}
+				settings.%1$s = &%1$s;
+			}
+		`, cName, dName);
+	}
+
+	static generateCallbackWithData(string cName, string dName) {
+		import std.format;
+
+		return format(`
+			static if(__traits(hasMember, interceptor, "%2$s"))
+			{
+				extern(C) static int %1$s(http_parser* p, const ubyte* at, size_t size) {
+					auto parser = cast(HttpParser*)p;
+					try {
+						parser.flags = http_parser_flags(p);
+						return parser.interceptor.%2$s(parser, at[0..size]);
+					}
+					catch (Throwable t) {
+						parser.failure = t;
+						return 1;
+					}
+				}
+				settings.%1$s = &%1$s;
+			}
+		`, cName, dName);
+	}
+
+	@property HttpError errorCode() pure @safe nothrow {
+		return cast(HttpError)((flags >> 24) & 0x7f);
+	}
+
+public:
+	alias interceptor this;
+
+	@property uint status() pure @safe nothrow {
+		return flags & 0xffff;
+	}
+
+	@property HttpMethod method() pure @safe nothrow {
+		return cast(HttpMethod)((flags >> 16) & 0xFF);
+	}
+
+	this(Interceptor interceptor, HttpParserType type) {
+		this.interceptor = interceptor;
+		http_parser_init(&parser, type);
+		mixin(generateCallback("on_message_begin", "onMessageBegin"));
+		mixin(generateCallbackWithData("on_url", "onUrl"));
+		mixin(generateCallbackWithData("on_status", "onStatus"));
+		mixin(generateCallbackWithData("on_body", "onBody"));
+		mixin(generateCallbackWithData("on_header_field", "onHeaderField"));
+		mixin(generateCallbackWithData("on_header_value", "onHeaderValue"));
+		mixin(generateCallback("on_headers_complete", "onHeadersComplete"));
+		mixin(generateCallback("on_message_complete", "onMessageComplete"));
+	}
+
+	@property bool shouldKeepAlive() pure nothrow {
+		return http_should_keep_alive(&parser) == 1;
+	}
+
+	@property ushort httpMajor() @safe pure nothrow {
+		return parser.http_major;
+	}
+
+	@property ushort httpMinor() @safe pure nothrow {
+		return parser.http_minor;
+	}
+
+	size_t execute(const(ubyte)[] chunk) {
+		size_t size = http_parser_execute(&parser, &settings, chunk.ptr, chunk.length);
+		flags = http_parser_flags(&parser);
+		if (errorCode) {
+			auto f = failure;
+			failure = null;
+			if (f is null)
+				f = new HttpException(errorCode);
+			throw f;
+		}
+		return size;
+	}
+
+	size_t execute(const(char)[] str) {
+		return execute(cast(const(ubyte)[]) str);
+	}
+}
+
+auto httpParser(Interceptor)(Interceptor interceptor, HttpParserType type) {
+	return HttpParser!Interceptor(interceptor, type);
+}

+ 209 - 0
frameworks/D/hunt/source/http/Processor.d

@@ -0,0 +1,209 @@
+/// An example "HTTP server" with poor usability but sensible performance
+///
+module http.Processor;
+
+import std.array, std.exception, std.format, std.algorithm.mutation, std.socket;
+import core.stdc.stdlib;
+import core.thread, core.atomic;
+import http.Parser;
+
+import hunt.datetime;
+import hunt.logging;
+import hunt.io;
+
+struct HttpHeader {
+	string name, value;
+}
+
+struct HttpRequest {
+	HttpHeader[] headers;
+	HttpMethod method;
+	string uri;
+}
+
+abstract class HttpProcessor {
+private:
+	enum State {
+		url,
+		field,
+		value,
+		done
+	}
+
+	ubyte[] buffer;
+	Appender!(char[]) outBuf;
+	HttpHeader[] headers; // buffer for headers
+	size_t header; // current header
+	string url; // url
+	alias Parser = HttpParser!HttpProcessor;
+	Parser parser;
+	ScratchPad pad;
+	HttpRequest request;
+	State state;
+	bool serving;
+public:
+	TcpStream client;
+
+	this(TcpStream sock) {
+		serving = true;
+		client = sock;
+		buffer = new ubyte[2048];
+		headers = new HttpHeader[1];
+		pad = ScratchPad(16 * 1024);
+		parser = httpParser(this, HttpParserType.request);
+	}
+
+	void run() {
+		client.onDataReceived((const ubyte[] data) { 
+			parser.execute(data);
+		})
+		.onClosed(() {
+			notifyClientClosed();
+		})
+		.onError((string msg) { warning("Error: ", msg); })
+		.start();
+	}
+
+	protected void notifyClientClosed() {
+		debug tracef("The connection[%s] is closed", client.remoteAddress());
+	}
+
+	void respondWith(string _body, uint status, HttpHeader[] headers...) {
+		return respondWith(cast(const(ubyte)[]) _body, status, headers);
+	}
+
+	void respondWith(const(ubyte)[] _body, uint status, HttpHeader[] headers...) {
+		formattedWrite(outBuf, "HTTP/1.1 %s OK\r\n", status);
+		outBuf.put("Server: Hunt/1.0\r\n");
+
+		formattedWrite(outBuf, "Date: %s\r\n", DateTimeHelper.getDateAsGMT());
+		if (!parser.shouldKeepAlive)
+			outBuf.put("Connection: close\r\n");
+		foreach (ref hdr; headers) {
+			outBuf.put(hdr.name);
+			outBuf.put(": ");
+			outBuf.put(hdr.value);
+			outBuf.put("\r\n");
+		}
+		formattedWrite(outBuf, "Content-Length: %d\r\n\r\n", _body.length);
+		outBuf.put(cast(string) _body);
+		client.write(cast(ubyte[]) outBuf.data); // TODO: short-writes are quite possible
+	}
+
+	void onStart(HttpRequest req) {
+	}
+
+	void onChunk(HttpRequest req, const(ubyte)[] chunk) {
+	}
+
+	void onComplete(HttpRequest req);
+
+	final int onMessageBegin(Parser* parser) {
+		outBuf.clear();
+		header = 0;
+		pad.reset();
+		state = State.url;
+		return 0;
+	}
+
+	final int onUrl(Parser* parser, const(ubyte)[] chunk) {
+		pad.put(chunk);
+		return 0;
+	}
+
+	final int onBody(Parser* parser, const(ubyte)[] chunk) {
+		onChunk(request, chunk);
+		return 0;
+	}
+
+	final int onHeaderField(Parser* parser, const(ubyte)[] chunk) {
+		final switch (state) {
+		case State.url:
+			url = pad.sliceStr;
+			break;
+		case State.value:
+			headers[header].value = pad.sliceStr;
+			header += 1;
+			if (headers.length <= header)
+				headers.length += 1;
+			break;
+		case State.field:
+		case State.done:
+			break;
+		}
+		state = State.field;
+		pad.put(chunk);
+		return 0;
+	}
+
+	final int onHeaderValue(Parser* parser, const(ubyte)[] chunk) {
+		if (state == State.field) {
+			headers[header].name = pad.sliceStr;
+		}
+		pad.put(chunk);
+		state = State.value;
+		return 0;
+	}
+
+	final int onHeadersComplete(Parser* parser) {
+		headers[header].value = pad.sliceStr;
+		header += 1;
+		request = HttpRequest(headers[0 .. header], parser.method, url);
+		onStart(request);
+		state = State.done;
+		return 0;
+	}
+
+	final int onMessageComplete(Parser* parser) {
+		import std.stdio;
+
+		if (state == State.done)
+			onComplete(request);
+		if (!parser.shouldKeepAlive)
+			serving = false;
+		return 0;
+	}
+
+}
+
+// ==================================== IMPLEMENTATION DETAILS ==============================================
+private:
+
+struct ScratchPad {
+	ubyte* ptr;
+	size_t capacity;
+	size_t last, current;
+
+	this(size_t size) {
+		ptr = cast(ubyte*) malloc(size);
+		capacity = size;
+	}
+
+	void put(const(ubyte)[] slice) {
+		enforce(current + slice.length <= capacity, "HTTP headers too long");
+		ptr[current .. current + slice.length] = slice[];
+		current += slice.length;
+	}
+
+	const(ubyte)[] slice() {
+		auto data = ptr[last .. current];
+		last = current;
+		return data;
+	}
+
+	string sliceStr() {
+		return cast(string) slice;
+	}
+
+	void reset() {
+		current = 0;
+		last = 0;
+	}
+
+	@disable this(this);
+
+	~this() {
+		free(ptr);
+		ptr = null;
+	}
+}

+ 111 - 0
frameworks/D/hunt/source/http/Server.d

@@ -0,0 +1,111 @@
+module http.Server;
+
+import hunt.datetime;
+import hunt.event;
+import hunt.io;
+import hunt.logging;
+import hunt.util.memory : totalCPUs;
+
+import std.array;
+import std.conv;
+import std.json;
+import std.socket;
+import std.string;
+
+import http.Parser;
+import http.Processor;
+
+shared static this() {
+	DateTimeHelper.startClock();
+}
+
+/**
+*/
+abstract class AbstractTcpServer {
+	protected EventLoopGroup _group = null;
+	protected bool _isStarted = false;
+	protected Address _address;
+	TcpStreamOption _tcpStreamoption;
+
+	this(Address address, int thread = (totalCPUs - 1)) {
+		this._address = address;
+		_tcpStreamoption = TcpStreamOption.createOption();
+		_group = new EventLoopGroup(cast(uint) thread);
+	}
+
+	@property Address bindingAddress() {
+		return _address;
+	}
+
+	void start() {
+		if (_isStarted)
+			return;
+		debug trace("start to listen:");
+		_isStarted = true;
+
+		Socket server = new TcpSocket();
+		server.setOption(SocketOptionLevel.SOCKET, SocketOption.REUSEADDR, true);
+		server.bind(new InternetAddress("0.0.0.0", 8080));
+		server.listen(1000);
+
+		debug trace("Launching server");
+
+		_group.start();
+
+		while (true) {
+			try {
+				version (HUNT_DEBUG)
+					trace("Waiting for server.accept()");
+				Socket socket = server.accept();
+				version (HUNT_DEBUG) {
+					infof("new client from %s, fd=%d", socket.remoteAddress.toString(), socket.handle());
+				}
+				EventLoop loop = _group.nextLoop();
+				TcpStream stream = new TcpStream(loop, socket, _tcpStreamoption);
+				onConnectionAccepted(stream);
+			} catch (Exception e) {
+				warningf("Failure on accept %s", e);
+				break;
+			}
+		}
+		_isStarted = false;
+	}
+
+	protected void onConnectionAccepted(TcpStream client);
+
+	void stop() {
+		if (!_isStarted)
+			return;
+		_isStarted = false;
+		_group.stop();
+	}
+}
+
+alias ProcessorCreater = HttpProcessor delegate(TcpStream client);
+
+/**
+*/
+class HttpServer : AbstractTcpServer {
+
+	ProcessorCreater processorCreater;
+
+	this(string ip, ushort port, int thread = (totalCPUs - 1)) {
+		super(new InternetAddress(ip, port), thread);
+	}
+
+	this(Address address, int thread = (totalCPUs - 1)) {
+		super(address, thread);
+	}
+
+	override protected void onConnectionAccepted(TcpStream client) {
+		if(processorCreater !is null) {
+			HttpProcessor httpProcessor = processorCreater(client);
+			httpProcessor.run();
+		}
+	}
+
+	HttpServer onProcessorCreate(ProcessorCreater handler) {
+		this.processorCreater = handler;
+		return this;
+	}
+}

+ 0 - 13
frameworks/D/kiss/dub.json

@@ -1,13 +0,0 @@
-{
-	"name": "kiss-benchmark",
-	"targetType": "executable",
-	"authors": [
-		"Putao"
-	],
-	"description": "A simple http server powered by KISS.",
-	"copyright": "Copyright © 2017-2018, HuntLabs.cn",
-	"license": "Apache-2.0",
-	"dependencies": {
-		"kiss" : "~>0.3.2"
-	}
-}

+ 0 - 9
frameworks/D/kiss/kiss-ldc.dockerfile

@@ -1,9 +0,0 @@
-FROM dlanguage/ldc:1.7.0
-
-ADD ./ /kiss
-WORKDIR /kiss
-
-RUN dub upgrade --verbose
-RUN dub build -f --arch=x86_64 --build=release --compiler=ldc2
-
-CMD ["./kiss-benchmark"]

+ 0 - 9
frameworks/D/kiss/kiss.dockerfile

@@ -1,9 +0,0 @@
-FROM dlanguage/ldc:1.7.0
-
-ADD ./ /kiss
-WORKDIR /kiss
-
-RUN dub upgrade --verbose
-RUN dub build -f --arch=x86_64 --build=release
-
-CMD ["./kiss-benchmark"]

+ 0 - 220
frameworks/D/kiss/source/app.d

@@ -1,220 +0,0 @@
-/*
- * Collie - An asynchronous event-driven network framework using Dlang development
- *
- * Copyright (C) 2015-2018  Shanghai Putao Technology Co., Ltd 
- *
- * Developer: putao's Dlang team
- *
- * Licensed under the Apache-2.0 License.
- *
- */
-import std.stdio;
-
-import kiss.event;
-import kiss.net;
-import kiss.util.thread;
-
-import std.array;
-import std.conv;
-import std.json;
-import std.functional;
-import std.getopt;
-import std.exception;
-import std.datetime;
-import std.parallelism;
-import std.socket;
-import std.string;
-
-/**
-*/
-abstract class AbstractTcpServer
-{
-	protected EventLoopGroup _group = null;
-	protected bool _isStarted = false;
-	protected Address _address;
-
-	this(Address address, int thread = (totalCPUs - 1))
-	{
-		this._address = address;
-		_group = new EventLoopGroup(cast(uint) thread);
-	}
-
-	@property Address bindingAddress()
-	{
-		return _address;
-	}
-
-	void start()
-	{
-		if (_isStarted)
-			return;
-
-		for (size_t i = 0; i < _group.length; ++i)
-			createServer(_group[i]);
-		_group.start();
-		_isStarted = true;
-	}
-
-	protected void createServer(EventLoop loop)
-	{
-		TcpListener listener = new TcpListener(loop, _address.addressFamily);
-
-		listener.reusePort(true);
-		listener.bind(_address).listen(1024);
-		listener.acceptHandler = &onConnectionAccepted;
-		listener.start();
-	}
-
-	protected void onConnectionAccepted(TcpListener sender, TcpStream client);
-
-	void stop()
-	{
-		if (!_isStarted)
-			return;
-		_isStarted = false;
-		_group.stop();
-	}
-}
-
-/**
-*/
-class HttpServer : AbstractTcpServer
-{
-	this(string ip, ushort port, int thread = (totalCPUs - 1))
-	{
-		super(new InternetAddress(ip, port), thread);
-	}
-
-	this(Address address, int thread = (totalCPUs - 1))
-	{
-		super(address, thread);
-	}
-
-	override protected void onConnectionAccepted(TcpListener sender, TcpStream client)
-	{
-		client.onDataReceived((in ubyte[] data) {
-			handleReceivedData(client, data);
-		}).onClosed(() { notifyClientClosed(client); }).onError((string msg) {
-			writeln("Error: ", msg);
-		});
-	}
-
-	protected void handleReceivedData(TcpStream client, in ubyte[] data)
-	{
-		string request = cast(string) data;
-		bool keepAlive = indexOf(request, " keep-alive", CaseSensitive.no) > 0;
-
-		ptrdiff_t index = indexOf(request, "/plaintext ", CaseSensitive.no);
-		if (index > 0)
-			respondPlaintext(client, keepAlive);
-		else if (indexOf(request, "/json ", CaseSensitive.no) > 0)
-		{
-			respondJson(client, keepAlive);
-		}
-		else
-		{
-			badRequest(client);
-		}
-
-	}
-
-	private void respondPlaintext(TcpStream client, bool keepAlive)
-	{
-		string content = "Hello, World!";
-		Appender!string sb;
-		sb.put("HTTP/1.1 200 OK");
-		sb.put("\r\n");
-		sb.put("Server: Kiss/0.3");
-		sb.put("\r\n");
-		sb.put("Connection: Keep-Alive");
-		sb.put("\r\n");
-		sb.put("Content-Type: text/plain");
-		sb.put("\r\n");
-		sb.put("Content-Length: " ~ to!string(content.length));
-		sb.put("\r\n");
-		sb.put("Date: " ~ Clock.currTime.toString());
-		sb.put("\r\n\r\n");
-		sb.put(content);
-
-		client.write(cast(ubyte[]) sb.data, (in ubyte[], size_t) {
-			if (!keepAlive)
-				client.close();
-		});
-	}
-
-	private void respondJson(TcpStream client, bool keepAlive)
-	{
-		JSONValue js;
-		js["message"] = "Hello, World!";
-		string content = js.toString();
-
-		Appender!string sb;
-		sb.put("HTTP/1.1 200 OK");
-		sb.put("\r\n");
-		sb.put("Server: Kiss/0.3");
-		sb.put("\r\n");
-		sb.put("Connection: Keep-Alive");
-		sb.put("\r\n");
-		sb.put("Content-Type: application/json");
-		sb.put("\r\n");
-		sb.put("Content-Length: " ~ to!string(content.length));
-		sb.put("\r\n");
-		sb.put("Date: " ~ Clock.currTime.toString());
-		sb.put("\r\n\r\n");
-		sb.put(content);
-
-		client.write(cast(ubyte[]) sb.data, (in ubyte[], size_t) {
-			if (!keepAlive)
-				client.close();
-		});
-	}
-
-	private void badRequest(TcpStream client)
-	{
-		string content = `<html>
-<head><title>404 Not Found</title></head>
-<body bgcolor="white">
-<center><h1>404 Not Found</h1></center>
-<hr><center>Kiss/0.3</center>
-</body>
-</html>`;
-		Appender!string sb;
-		sb.put("HTTP/1.1 404 Not Found");
-		sb.put("\r\n");
-		sb.put("Server: Kiss/0.3");
-		sb.put("\r\n");
-		sb.put("Connection: keep-alive");
-		sb.put("\r\n");
-		sb.put("Content-Type: text/html");
-		sb.put("\r\n");
-		sb.put("Content-Length: " ~ to!string(content.length));
-		sb.put("\r\n");
-		sb.put("Date: " ~ Clock.currTime.toString());
-		sb.put("\r\n\r\n");
-		sb.put(content);
-
-		client.write(cast(ubyte[]) sb.data);
-		client.close();
-	}
-
-	protected void notifyClientClosed(TcpStream client)
-	{
-		debug writefln("The connection[%s] is closed on thread %s",
-				client.remoteAddress(), getTid());
-	}
-}
-
-void main(string[] args)
-{
-	ushort port = 8080;
-	GetoptResult o = getopt(args, "port|p", "Port (default 8080)", &port);
-	if (o.helpWanted)
-	{
-		defaultGetoptPrinter("A simple http server powered by KISS!", o.options);
-		return;
-	}
-
-	HttpServer httpServer = new HttpServer("0.0.0.0", 8080, totalCPUs);
-	writefln("listening on %s", httpServer.bindingAddress.toString());
-	httpServer.start();
-}