diff --git a/cgi.d b/cgi.d index 8112916..5d6e97e 100644 --- a/cgi.d +++ b/cgi.d @@ -48,11 +48,12 @@ mixin template ForwardCgiConstructors() { this(long maxContentLength = 5_000_000, string[string] env = null, const(ubyte)[] delegate() readdata = null, - void delegate(const(ubyte)[]) _rawDataOutput = null - ) { super(maxContentLength, env, readdata, _rawDataOutput); } + void delegate(const(ubyte)[]) _rawDataOutput = null, + void delegate() _flush = null + ) { super(maxContentLength, env, readdata, _rawDataOutput, _flush); } - this(string[] headers, immutable(ubyte)[] data, string address, void delegate(const(ubyte)[]) _rawDataOutput = null, int pathInfoStarts = 0) { - super(headers, data, address, _rawDataOutput, pathInfoStarts); + this(string[] headers, immutable(ubyte)[] data, string address, void delegate(const(ubyte)[]) _rawDataOutput = null, int pathInfoStarts = 0, void delegate() _flush = null) { + super(headers, data, address, _rawDataOutput, pathInfoStarts, _flush); } } @@ -142,10 +143,13 @@ class Cgi { // and this should return a chunk of data. return empty when done const(ubyte)[] delegate() readdata = null, // finally, use this to do custom output if needed - void delegate(const(ubyte)[]) _rawDataOutput = null + void delegate(const(ubyte)[]) _rawDataOutput = null, + // to flush teh custom output + void delegate() _flush = null ) { rawDataOutput = _rawDataOutput; + flushDelegate = _flush; auto getenv = delegate string(string var) { if(env is null) return .getenv(var); @@ -743,13 +747,14 @@ class Cgi { indeed, it should probably just take a file descriptor or two and do all the work itself. */ - this(string[] headers, immutable(ubyte)[] data, string address, void delegate(const(ubyte)[]) _rawDataOutput = null, int pathInfoStarts = 0) { + this(string[] headers, immutable(ubyte)[] data, string address, void delegate(const(ubyte)[]) _rawDataOutput = null, int pathInfoStarts = 0, void delegate() _flush = null) { auto parts = headers[0].split(" "); https = false; port = 80; // FIXME rawDataOutput = _rawDataOutput; + flushDelegate = _flush; nph = true; requestMethod = to!RequestMethod(parts[0]); @@ -773,7 +778,7 @@ class Cgi { remoteAddress = address; - if(headers[0].indexOf("HTTP/1.0")) { + if(headers[0].indexOf("HTTP/1.0") != -1) { http10 = true; autoBuffer = true; } @@ -1219,7 +1224,8 @@ class Cgi { void flush() { if(rawDataOutput is null) stdout.flush(); - // FIXME: also flush to other sources + else if(flushDelegate !is null) + flushDelegate(); } version(autoBuffer) @@ -1274,6 +1280,7 @@ class Cgi { /* Hooks for redirecting input and output */ private void delegate(const(ubyte)[]) rawDataOutput = null; + private void delegate() flushDelegate = null; /* This info is used when handling a more raw HTTP protocol */ private bool nph; @@ -1590,7 +1597,11 @@ version(embedded_httpd) return ""; } - auto cgi = new CustomCgi(5_000_000, fcgienv, &getFcgiChunk, &writeFcgi); + void flushFcgi() { + FCGX_FFlush(output); + } + + auto cgi = new CustomCgi(5_000_000, fcgienv, &getFcgiChunk, &writeFcgi, &flushFcgi); try { fun(cgi); cgi.close(); @@ -1741,6 +1752,8 @@ version(fastcgi) { int FCGX_GetChar(FCGX_Stream* stream); int FCGX_PutStr(const ubyte* str, int n, FCGX_Stream* stream); int FCGX_HasSeenEOF(FCGX_Stream* stream); + int FCGX_FFlush(FCGX_Stream *stream); + } } diff --git a/httpd.d b/httpd.d index e5cd570..e9f4c7f 100644 --- a/httpd.d +++ b/httpd.d @@ -90,7 +90,7 @@ class HttpdConnection(CustomCgi) : Connection /* if(is(CustomCgi : Cgi)) */ { try { cgi = new CustomCgi(headers, data, peerAddress(), - cast(void delegate(const(ubyte)[])) &this.write); + cast(void delegate(const(ubyte)[])) &this.write, 0, &this.flush); } catch(Throwable t) { write("HTTP/1.1 400 Bad Request\r\n"); write("Content-Type: text/plain\r\n"); diff --git a/netman.d b/netman.d index 9b06f5a..cbdeba1 100644 --- a/netman.d +++ b/netman.d @@ -170,6 +170,8 @@ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, on.sizeof); //events to handle: data ready to read, timeout, new connection, connection error // stuff to do: write data + try_again: + fd_set rdfs; fd_set writefs; timeval tv; @@ -207,8 +209,14 @@ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, on.sizeof); else ret = linux.select(biggest + 1, &rdfs, &writefs, null, &tv); - if(ret == -1) - throw new Exception("select"); + if(ret == -1) { + import core.stdc.errno; + import std.conv; + if(errno == 4) // interrupted by signal + goto try_again; + else + throw new Exception("select " ~ to!string(errno)); + } if(ret) { // data ready somewhere @@ -369,7 +377,21 @@ class Connection { // Writes the pending data to the socket now instead of waiting for the manager to proceed void flush(){ + if(writeBufferLength > 0) { + auto b = writeBuffer[writeBufferPosition..(writeBufferPosition+writeBufferLength)]; + auto num = .write(socket, b.ptr, b.length); + if(num < 0) + throw new ConnectionException("send", this); + writeBufferLength -= num; + if(writeBufferLength > 0) + writeBufferPosition += num; + else + writeBufferPosition = 0; + + timeOfLastActivity = now; + fsync(socket); + } } // reads the requested amount now, blocking until you get it all.