This commit is contained in:
Adam Ruppe 2019-03-10 17:19:40 -04:00
commit 63c3cf8a8a
3 changed files with 92 additions and 26 deletions

100
cgi.d
View File

@ -384,6 +384,12 @@ int locationOf(T)(T[] data, string item) {
const(ubyte[]) d = cast(const(ubyte[])) data; const(ubyte[]) d = cast(const(ubyte[])) data;
const(ubyte[]) i = cast(const(ubyte[])) item; 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++) { for(int a = 0; a < d.length; a++) {
if(a + i.length > d.length) if(a + i.length > d.length)
return -1; return -1;
@ -3746,10 +3752,12 @@ class ListeningConnectionManager {
while(!loopBroken && running) { while(!loopBroken && running) {
auto sn = listener.accept(); auto sn = listener.accept();
// disable Nagle's algorithm to avoid a 40ms delay when we send/recv if(tcp) {
// on the socket because we do some buffering internally. I think this helps, // disable Nagle's algorithm to avoid a 40ms delay when we send/recv
// certainly does for small requests, and I think it does for larger ones too // on the socket because we do some buffering internally. I think this helps,
sn.setOption(SocketOptionLevel.TCP, SocketOption.TCP_NODELAY, 1); // 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) while(queueLength >= queue.length)
Thread.sleep(1.msecs); Thread.sleep(1.msecs);
synchronized(this) { synchronized(this) {
@ -3765,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(string host, ushort port, void function(Socket) handler) {
this.handler = handler; this.handler = handler;
listener = new TcpSocket();
listener.setOption(SocketOptionLevel.SOCKET, SocketOption.REUSEADDR, true); if(host.startsWith("unix:")) {
listener.bind(host.length ? parseAddress(host, port) : new InternetAddress(port)); 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); listener.listen(128);
} }
@ -4488,15 +4531,15 @@ version(Posix) {
enum INVALID_CGI_CONNECTION_HANDLE = -1; enum INVALID_CGI_CONNECTION_HANDLE = -1;
} else version(Windows) { } else version(Windows) {
alias LocalServerConnectionHandle = HANDLE; alias LocalServerConnectionHandle = HANDLE;
version(embedded_httpd) { version(embedded_httpd_threads) {
alias CgiConnectionHandle = SOCKET; alias CgiConnectionHandle = SOCKET;
enum INVALID_CGI_CONNECTION_HANDLE = INVALID_SOCKET; enum INVALID_CGI_CONNECTION_HANDLE = INVALID_SOCKET;
} else version(fastcgi) { } else version(fastcgi) {
alias CgiConnectionHandle = void*; // Doesn't actually work! But I don't want compile to fail pointlessly at this point. 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; enum INVALID_CGI_CONNECTION_HANDLE = null;
} else version(scgi) { } else version(scgi) {
alias CgiConnectionHandle = HANDLE; alias CgiConnectionHandle = SOCKET;
enum INVALID_CGI_CONNECTION_HANDLE = null; enum INVALID_CGI_CONNECTION_HANDLE = INVALID_SOCKET;
} else { /* version(plain_cgi) */ } else { /* version(plain_cgi) */
alias CgiConnectionHandle = HANDLE; alias CgiConnectionHandle = HANDLE;
enum INVALID_CGI_CONNECTION_HANDLE = null; enum INVALID_CGI_CONNECTION_HANDLE = null;
@ -4652,6 +4695,20 @@ bool isInvalidHandle(CgiConnectionHandle h) {
return h == INVALID_CGI_CONNECTION_HANDLE; 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 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 subclass at compile time with the [registerEventIoServer] template, or implement your own
@ -5120,19 +5177,20 @@ interface EventSourceServer {
version(fastcgi) version(fastcgi)
throw new Exception("sending fcgi connections not supported"); 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(); EventSourceServerImplementation.SendableEventConnection sec;
if(isInvalidHandle(fd)) sec.populate(cgi.responseChunked, eventUrl, lastEventId);
throw new Exception("bad fd from cgi!");
EventSourceServerImplementation.SendableEventConnection sec; version(Posix) {
sec.populate(cgi.responseChunked, eventUrl, lastEventId); auto res = write_fd(s, cast(void*) &sec, sec.sizeof, fd);
assert(res == sec.sizeof);
version(Posix) { } else version(Windows) {
auto res = write_fd(s, cast(void*) &sec, sec.sizeof, fd); // FIXME
assert(res == sec.sizeof); }
} else version(Windows) {
// FIXME
} }
} }

13
dom.d
View File

@ -1416,7 +1416,7 @@ class Element {
body { body {
auto e = cast(SomeElementType) getElementById(id); auto e = cast(SomeElementType) getElementById(id);
if(e is null) 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; return e;
} }
@ -1431,7 +1431,7 @@ class Element {
body { body {
auto e = cast(SomeElementType) querySelector(selector); auto e = cast(SomeElementType) querySelector(selector);
if(e is null) if(e is null)
throw new ElementNotFoundException(SomeElementType.stringof, selector, file, line); throw new ElementNotFoundException(SomeElementType.stringof, selector, this, file, line);
return e; return e;
} }
@ -2140,7 +2140,7 @@ class Element {
static if(!is(T == Element)) { static if(!is(T == Element)) {
auto t = cast(T) par; auto t = cast(T) par;
if(t is null) if(t is null)
throw new ElementNotFoundException("", tagName ~ " parent not found"); throw new ElementNotFoundException("", tagName ~ " parent not found", this);
} else } else
auto t = par; auto t = par;
@ -3806,7 +3806,7 @@ T require(T = Element, string file = __FILE__, int line = __LINE__)(Element e) i
body { body {
auto ret = cast(T) e; auto ret = cast(T) e;
if(ret is null) 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; return ret;
} }
@ -5169,9 +5169,12 @@ class MarkupException : Exception {
class ElementNotFoundException : Exception { class ElementNotFoundException : Exception {
/// type == kind of element you were looking for and search == a selector describing the search. /// 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); 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 /// The html struct is used to differentiate between regular text nodes and html in certain functions

View File

@ -2666,10 +2666,13 @@ static struct GenericCursor {
struct EventLoop { struct EventLoop {
@disable this(); @disable this();
/// Gets a reference to an existing event loop
static EventLoop get() { static EventLoop get() {
return EventLoop(0, null); return EventLoop(0, null);
} }
/// Construct an application-global event loop for yourself
/// See_Also: [SimpleWindow.setEventHandlers]
this(long pulseTimeout, void delegate() handlePulse) { this(long pulseTimeout, void delegate() handlePulse) {
if(impl is null) if(impl is null)
impl = new EventLoopImpl(pulseTimeout, handlePulse); impl = new EventLoopImpl(pulseTimeout, handlePulse);
@ -2697,12 +2700,14 @@ struct EventLoop {
impl.refcount++; impl.refcount++;
} }
/// Runs the event loop until the whileCondition, if present, returns false
int run(bool delegate() whileCondition = null) { int run(bool delegate() whileCondition = null) {
assert(impl !is null); assert(impl !is null);
impl.notExited = true; impl.notExited = true;
return impl.run(whileCondition); return impl.run(whileCondition);
} }
/// Exits the event loop
void exit() { void exit() {
assert(impl !is null); assert(impl !is null);
impl.notExited = false; impl.notExited = false;