From 08e525e2de0fe667635204f6a6ef5d9e4f374772 Mon Sep 17 00:00:00 2001 From: "Adam D. Ruppe" Date: Sat, 2 Mar 2019 21:34:46 -0500 Subject: [PATCH 1/7] more stuff --- simpledisplay.d | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/simpledisplay.d b/simpledisplay.d index e6e5585..04c35d3 100644 --- a/simpledisplay.d +++ b/simpledisplay.d @@ -2662,10 +2662,13 @@ static struct GenericCursor { struct EventLoop { @disable this(); + /// Gets a reference to an existing event loop static EventLoop get() { return EventLoop(0, null); } + /// Construct an application-global event loop for yourself + /// See_Also: [SimpleWindow.setEventHandlers] this(long pulseTimeout, void delegate() handlePulse) { if(impl is null) impl = new EventLoopImpl(pulseTimeout, handlePulse); @@ -2693,12 +2696,14 @@ struct EventLoop { impl.refcount++; } + /// Runs the event loop until the whileCondition, if present, returns false int run(bool delegate() whileCondition = null) { assert(impl !is null); impl.notExited = true; return impl.run(whileCondition); } + /// Exits the event loop void exit() { assert(impl !is null); impl.notExited = false; From fe943409d16a17e4d5afdaf170b14733ed8133db Mon Sep 17 00:00:00 2001 From: "Adam D. Ruppe" Date: Mon, 4 Mar 2019 10:19:29 -0500 Subject: [PATCH 2/7] add more context to exceptions --- dom.d | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/dom.d b/dom.d index c80d9f8..1f1cbe0 100644 --- a/dom.d +++ b/dom.d @@ -1416,7 +1416,7 @@ class Element { body { auto e = cast(SomeElementType) getElementById(id); if(e is null) - throw new ElementNotFoundException(SomeElementType.stringof, "id=" ~ id, file, line); + throw new ElementNotFoundException(SomeElementType.stringof, "id=" ~ id, this, file, line); return e; } @@ -1431,7 +1431,7 @@ class Element { body { auto e = cast(SomeElementType) querySelector(selector); if(e is null) - throw new ElementNotFoundException(SomeElementType.stringof, selector, file, line); + throw new ElementNotFoundException(SomeElementType.stringof, selector, this, file, line); return e; } @@ -2140,7 +2140,7 @@ class Element { static if(!is(T == Element)) { auto t = cast(T) par; if(t is null) - throw new ElementNotFoundException("", tagName ~ " parent not found"); + throw new ElementNotFoundException("", tagName ~ " parent not found", this); } else auto t = par; @@ -3806,7 +3806,7 @@ T require(T = Element, string file = __FILE__, int line = __LINE__)(Element e) i body { auto ret = cast(T) e; if(ret is null) - throw new ElementNotFoundException(T.stringof, "passed value", file, line); + throw new ElementNotFoundException(T.stringof, "passed value", e, file, line); return ret; } @@ -5169,9 +5169,12 @@ class MarkupException : Exception { class ElementNotFoundException : Exception { /// type == kind of element you were looking for and search == a selector describing the search. - this(string type, string search, string file = __FILE__, size_t line = __LINE__) { + this(string type, string search, Element searchContext, string file = __FILE__, size_t line = __LINE__) { + this.searchContext = searchContext; super("Element of type '"~type~"' matching {"~search~"} not found.", file, line); } + + Element searchContext; } /// The html struct is used to differentiate between regular text nodes and html in certain functions From 0c328aa3b06cdfca7c3c022b71aa59f10f5af821 Mon Sep 17 00:00:00 2001 From: "Adam D. Ruppe" Date: Thu, 7 Mar 2019 22:23:00 -0500 Subject: [PATCH 3/7] notes to self --- cgi.d | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/cgi.d b/cgi.d index daee713..3c35c64 100644 --- a/cgi.d +++ b/cgi.d @@ -4652,6 +4652,20 @@ bool isInvalidHandle(CgiConnectionHandle h) { return h == INVALID_CGI_CONNECTION_HANDLE; } +/+ +https://docs.microsoft.com/en-us/windows/desktop/api/winsock2/nf-winsock2-wsarecv +https://support.microsoft.com/en-gb/help/181611/socket-overlapped-i-o-versus-blocking-nonblocking-mode +https://stackoverflow.com/questions/18018489/should-i-use-iocps-or-overlapped-wsasend-receive +https://docs.microsoft.com/en-us/windows/desktop/fileio/i-o-completion-ports +https://docs.microsoft.com/en-us/windows/desktop/fileio/createiocompletionport +https://docs.microsoft.com/en-us/windows/desktop/api/mswsock/nf-mswsock-acceptex +https://docs.microsoft.com/en-us/windows/desktop/Sync/waitable-timer-objects +https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-setwaitabletimer +https://docs.microsoft.com/en-us/windows/desktop/Sync/using-a-waitable-timer-with-an-asynchronous-procedure-call +https://docs.microsoft.com/en-us/windows/desktop/api/winsock2/nf-winsock2-wsagetoverlappedresult + ++/ + /++ You can customize your server by subclassing the appropriate server. Then, register your subclass at compile time with the [registerEventIoServer] template, or implement your own From b3df2871d280514f3dbad8b6d7ad59a7a35e527e Mon Sep 17 00:00:00 2001 From: "Adam D. Ruppe" Date: Thu, 7 Mar 2019 22:23:51 -0500 Subject: [PATCH 4/7] stupid dmd warnings are useless --- cgi.d | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/cgi.d b/cgi.d index 3c35c64..17c91d4 100644 --- a/cgi.d +++ b/cgi.d @@ -5134,19 +5134,20 @@ interface EventSourceServer { version(fastcgi) throw new Exception("sending fcgi connections not supported"); + else { + auto fd = cgi.getOutputFileHandle(); + if(isInvalidHandle(fd)) + throw new Exception("bad fd from cgi!"); - auto fd = cgi.getOutputFileHandle(); - if(isInvalidHandle(fd)) - throw new Exception("bad fd from cgi!"); + EventSourceServerImplementation.SendableEventConnection sec; + sec.populate(cgi.responseChunked, eventUrl, lastEventId); - EventSourceServerImplementation.SendableEventConnection sec; - sec.populate(cgi.responseChunked, eventUrl, lastEventId); - - version(Posix) { - auto res = write_fd(s, cast(void*) &sec, sec.sizeof, fd); - assert(res == sec.sizeof); - } else version(Windows) { - // FIXME + version(Posix) { + auto res = write_fd(s, cast(void*) &sec, sec.sizeof, fd); + assert(res == sec.sizeof); + } else version(Windows) { + // FIXME + } } } From b106b2cc5c6eb0b7bbe7b02ee6c4ace3ef8f5d04 Mon Sep 17 00:00:00 2001 From: "Adam D. Ruppe" Date: Fri, 8 Mar 2019 09:16:35 -0500 Subject: [PATCH 5/7] wrong version on windows --- cgi.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cgi.d b/cgi.d index 17c91d4..56f547f 100644 --- a/cgi.d +++ b/cgi.d @@ -4488,7 +4488,7 @@ version(Posix) { enum INVALID_CGI_CONNECTION_HANDLE = -1; } else version(Windows) { alias LocalServerConnectionHandle = HANDLE; - version(embedded_httpd) { + version(embedded_httpd_threads) { alias CgiConnectionHandle = SOCKET; enum INVALID_CGI_CONNECTION_HANDLE = INVALID_SOCKET; } else version(fastcgi) { From b426390abe8159fc368c77272425ffb791270dd9 Mon Sep 17 00:00:00 2001 From: "Adam D. Ruppe" Date: Sat, 9 Mar 2019 12:00:41 -0500 Subject: [PATCH 6/7] sanity check on header size jic --- cgi.d | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cgi.d b/cgi.d index 56f547f..cedb5ad 100644 --- a/cgi.d +++ b/cgi.d @@ -384,6 +384,12 @@ int locationOf(T)(T[] data, string item) { const(ubyte[]) d = cast(const(ubyte[])) data; const(ubyte[]) i = cast(const(ubyte[])) item; + // this is a vague sanity check to ensure we aren't getting insanely + // sized input that will infinite loop below. it should never happen; + // even huge file uploads ought to come in smaller individual pieces. + if(d.length > (int.max/2)) + throw new Exception("excessive block of input"); + for(int a = 0; a < d.length; a++) { if(a + i.length > d.length) return -1; From 6acfc5cb6e28d463fe24c7fc000de7fd27397179 Mon Sep 17 00:00:00 2001 From: "Adam D. Ruppe" Date: Sat, 9 Mar 2019 17:40:13 -0500 Subject: [PATCH 7/7] add unix domain socket support for scgi (and httpd but idk if that is useful) --- cgi.d | 55 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/cgi.d b/cgi.d index cedb5ad..5917cdd 100644 --- a/cgi.d +++ b/cgi.d @@ -3752,10 +3752,12 @@ class ListeningConnectionManager { while(!loopBroken && running) { auto sn = listener.accept(); - // disable Nagle's algorithm to avoid a 40ms delay when we send/recv - // on the socket because we do some buffering internally. I think this helps, - // certainly does for small requests, and I think it does for larger ones too - sn.setOption(SocketOptionLevel.TCP, SocketOption.TCP_NODELAY, 1); + if(tcp) { + // disable Nagle's algorithm to avoid a 40ms delay when we send/recv + // on the socket because we do some buffering internally. I think this helps, + // certainly does for small requests, and I think it does for larger ones too + sn.setOption(SocketOptionLevel.TCP, SocketOption.TCP_NODELAY, 1); + } while(queueLength >= queue.length) Thread.sleep(1.msecs); synchronized(this) { @@ -3771,14 +3773,49 @@ class ListeningConnectionManager { } } } + + // FIXME: i typically stop this with ctrl+c which never + // actually gets here. i need to do a sigint handler. + if(cleanup) + cleanup(); } } + bool tcp; + void delegate() cleanup; + this(string host, ushort port, void function(Socket) handler) { this.handler = handler; - listener = new TcpSocket(); - listener.setOption(SocketOptionLevel.SOCKET, SocketOption.REUSEADDR, true); - listener.bind(host.length ? parseAddress(host, port) : new InternetAddress(port)); + + if(host.startsWith("unix:")) { + version(Posix) { + listener = new Socket(AddressFamily.UNIX, SocketType.STREAM); + string filename = host["unix:".length .. $].idup; + listener.bind(new UnixAddress(filename)); + cleanup = delegate() { + import std.file; + remove(filename); + }; + tcp = false; + } else { + throw new Exception("unix sockets not supported on this system"); + } + } else if(host.startsWith("abstract:")) { + version(linux) { + listener = new Socket(AddressFamily.UNIX, SocketType.STREAM); + string filename = "\0" ~ host["abstract:".length .. $]; + listener.bind(new UnixAddress(filename)); + tcp = false; + } else { + throw new Exception("abstract unix sockets not supported on this system"); + } + } else { + listener = new TcpSocket(); + listener.setOption(SocketOptionLevel.SOCKET, SocketOption.REUSEADDR, true); + listener.bind(host.length ? parseAddress(host, port) : new InternetAddress(port)); + tcp = true; + } + listener.listen(128); } @@ -4501,8 +4538,8 @@ version(Posix) { alias CgiConnectionHandle = void*; // Doesn't actually work! But I don't want compile to fail pointlessly at this point. enum INVALID_CGI_CONNECTION_HANDLE = null; } else version(scgi) { - alias CgiConnectionHandle = HANDLE; - enum INVALID_CGI_CONNECTION_HANDLE = null; + alias CgiConnectionHandle = SOCKET; + enum INVALID_CGI_CONNECTION_HANDLE = INVALID_SOCKET; } else { /* version(plain_cgi) */ alias CgiConnectionHandle = HANDLE; enum INVALID_CGI_CONNECTION_HANDLE = null;