lots of embedded httpd fixes

This commit is contained in:
Adam D. Ruppe 2012-02-05 21:03:18 -05:00
parent bedea1ec31
commit 491434c645
3 changed files with 47 additions and 12 deletions

31
cgi.d
View File

@ -48,11 +48,12 @@ mixin template ForwardCgiConstructors() {
this(long maxContentLength = 5_000_000, this(long maxContentLength = 5_000_000,
string[string] env = null, string[string] env = null,
const(ubyte)[] delegate() readdata = null, const(ubyte)[] delegate() readdata = null,
void delegate(const(ubyte)[]) _rawDataOutput = null void delegate(const(ubyte)[]) _rawDataOutput = null,
) { super(maxContentLength, env, readdata, _rawDataOutput); } 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) { 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); 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 // and this should return a chunk of data. return empty when done
const(ubyte)[] delegate() readdata = null, const(ubyte)[] delegate() readdata = null,
// finally, use this to do custom output if needed // 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; rawDataOutput = _rawDataOutput;
flushDelegate = _flush;
auto getenv = delegate string(string var) { auto getenv = delegate string(string var) {
if(env is null) if(env is null)
return .getenv(var); return .getenv(var);
@ -743,13 +747,14 @@ class Cgi {
indeed, it should probably just take a file descriptor indeed, it should probably just take a file descriptor
or two and do all the work itself. 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(" "); auto parts = headers[0].split(" ");
https = false; https = false;
port = 80; // FIXME port = 80; // FIXME
rawDataOutput = _rawDataOutput; rawDataOutput = _rawDataOutput;
flushDelegate = _flush;
nph = true; nph = true;
requestMethod = to!RequestMethod(parts[0]); requestMethod = to!RequestMethod(parts[0]);
@ -773,7 +778,7 @@ class Cgi {
remoteAddress = address; remoteAddress = address;
if(headers[0].indexOf("HTTP/1.0")) { if(headers[0].indexOf("HTTP/1.0") != -1) {
http10 = true; http10 = true;
autoBuffer = true; autoBuffer = true;
} }
@ -1219,7 +1224,8 @@ class Cgi {
void flush() { void flush() {
if(rawDataOutput is null) if(rawDataOutput is null)
stdout.flush(); stdout.flush();
// FIXME: also flush to other sources else if(flushDelegate !is null)
flushDelegate();
} }
version(autoBuffer) version(autoBuffer)
@ -1274,6 +1280,7 @@ class Cgi {
/* Hooks for redirecting input and output */ /* Hooks for redirecting input and output */
private void delegate(const(ubyte)[]) rawDataOutput = null; private void delegate(const(ubyte)[]) rawDataOutput = null;
private void delegate() flushDelegate = null;
/* This info is used when handling a more raw HTTP protocol */ /* This info is used when handling a more raw HTTP protocol */
private bool nph; private bool nph;
@ -1590,7 +1597,11 @@ version(embedded_httpd)
return ""; 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 { try {
fun(cgi); fun(cgi);
cgi.close(); cgi.close();
@ -1741,6 +1752,8 @@ version(fastcgi) {
int FCGX_GetChar(FCGX_Stream* stream); int FCGX_GetChar(FCGX_Stream* stream);
int FCGX_PutStr(const ubyte* str, int n, FCGX_Stream* stream); int FCGX_PutStr(const ubyte* str, int n, FCGX_Stream* stream);
int FCGX_HasSeenEOF(FCGX_Stream* stream); int FCGX_HasSeenEOF(FCGX_Stream* stream);
int FCGX_FFlush(FCGX_Stream *stream);
} }
} }

View File

@ -90,7 +90,7 @@ class HttpdConnection(CustomCgi) : Connection /* if(is(CustomCgi : Cgi)) */ {
try { try {
cgi = new CustomCgi(headers, data, peerAddress(), 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) { } catch(Throwable t) {
write("HTTP/1.1 400 Bad Request\r\n"); write("HTTP/1.1 400 Bad Request\r\n");
write("Content-Type: text/plain\r\n"); write("Content-Type: text/plain\r\n");

View File

@ -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 //events to handle: data ready to read, timeout, new connection, connection error
// stuff to do: write data // stuff to do: write data
try_again:
fd_set rdfs; fd_set rdfs;
fd_set writefs; fd_set writefs;
timeval tv; timeval tv;
@ -207,8 +209,14 @@ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, on.sizeof);
else else
ret = linux.select(biggest + 1, &rdfs, &writefs, null, &tv); ret = linux.select(biggest + 1, &rdfs, &writefs, null, &tv);
if(ret == -1) if(ret == -1) {
throw new Exception("select"); 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) { if(ret) {
// data ready somewhere // 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 // Writes the pending data to the socket now instead of waiting for the manager to proceed
void flush(){ 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. // reads the requested amount now, blocking until you get it all.