#include #include "FcgiFrontEnd.h" ///////////////////////////////////////////////////////////////////// // Structures and constants for FCGI protocol ///////////////////////////////////////////////////////////////////// /* * Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT records */ #define FCGI_MAX_CONNS "FCGI_MAX_CONNS" #define FCGI_MAX_REQS "FCGI_MAX_REQS" #define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS" /* * Values for type component of FCGI_Header */ #define FCGI_BEGIN_REQUEST 1 #define FCGI_ABORT_REQUEST 2 #define FCGI_END_REQUEST 3 #define FCGI_PARAMS 4 #define FCGI_STDIN 5 #define FCGI_STDOUT 6 #define FCGI_STDERR 7 #define FCGI_DATA 8 #define FCGI_GET_VALUES 9 #define FCGI_GET_VALUES_RESULT 10 #define FCGI_UNKNOWN_TYPE 11 #define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE) /* * Values for protocolStatus component of FCGI_EndRequestBody */ #define FCGI_REQUEST_COMPLETE 0 #define FCGI_CANT_MPX_CONN 1 #define FCGI_OVERLOADED 2 #define FCGI_UNKNOWN_ROLE 3 /* * Values for role component of FCGI_BeginRequestBody */ #define FCGI_RESPONDER 1 #define FCGI_AUTHORIZER 2 #define FCGI_FILTER 3 typedef struct { unsigned char version; unsigned char type; unsigned char requestIdB1; unsigned char requestIdB0; unsigned char contentLengthB1; unsigned char contentLengthB0; unsigned char paddingLength; unsigned char reserved; } FCGI_Header; typedef struct { unsigned char appStatusB3; unsigned char appStatusB2; unsigned char appStatusB1; unsigned char appStatusB0; unsigned char protocolStatus; unsigned char reserved[3]; } FCGI_EndRequestBody; typedef struct { unsigned char roleB1; unsigned char roleB0; unsigned char flags; unsigned char reserved[5]; } FCGI_BeginRequestBody; typedef struct { unsigned char type; unsigned char reserved[7]; } FCGI_UnknownTypeBody; ///////////////////////////////////////////////////////////////////// // Misc ///////////////////////////////////////////////////////////////////// // Tries to parse the string. Results are added to the "to" paramater. // Returns true on success. The state of "to" is undefined on error. static bool parseNameValuePairs(std::string from, PropertyList &to) { int start = 0; long int remaining = from.size(); char const *data = from.data(); while (true) { if (!remaining) { return true; } if (remaining == 1) { // We need a minimum of 2 bytes to be legal. return false; } int nameLength, valueLength; if (data[0] & 0x80) { if (remaining < 5) { // We needed 4 bytes for this, plus at least 1 for the value. return false; } nameLength = ((data[0] & 0x7f) << 24) + (data[1] << 16) + (data[2] << 8) + data[3]; data += 4; remaining -= 4; } else { nameLength = data[0]; data++; remaining--; } if (data[0] & 0x80) { if (remaining < 4) { return false; } valueLength = ((data[0] & 0x7f) << 24) + (data[1] << 16) + (data[2] << 8) + data[3]; data += 4; remaining -= 4; } else { valueLength = data[0]; data++; remaining--; } if (remaining - nameLength - valueLength < 0) { return false; } std::string name(data, nameLength); data += nameLength; remaining -= nameLength; std::string value(data, valueLength); data += valueLength; remaining -= valueLength; to[name] = value; } } static void encodeStringLength(std::string addTo, std::string::size_type length) { assert(length < 0x8000); if (length < 0x80) { addTo += (unsigned char)length; } else { unsigned char encoded[4]; encoded[0] = 0x80 | (length>>24); encoded[1] = length>>16; encoded[2] = length>>8; encoded[3] = length; addTo.append((char *)encoded, 4); } } static void encodeNameValuePair(std::string addTo, std::string name, std::string value) { encodeStringLength(addTo, name.size()); encodeStringLength(addTo, value.size()); addTo += name; addTo += value; } static void encodeNameValuePairs(std::string addTo, PropertyList pairs) { for (PropertyList::const_iterator it = pairs.begin(); it != pairs.end(); it++) { encodeNameValuePair(addTo, it->first, it->second); } } ///////////////////////////////////////////////////////////////////// // FcgiFrontEnd::Record ///////////////////////////////////////////////////////////////////// std::string FcgiFrontEnd::Record::asString() { // assert(content.size() < 65536); FCGI_Header h; h.version = 1; h.type = type; h.requestIdB1 = id >> 8; h.requestIdB0 = id; h.contentLengthB1 = content.size() >> 8; h.contentLengthB0 = content.size(); h.paddingLength = 7 - ((content.size() + 7) % 8); h.reserved = 0; std::string result((char *)&h, sizeof(h)); result += content; result += std::string(h.paddingLength, '\0'); return result; } //#include bool FcgiFrontEnd::Record::read(std::string &from) { if (from.size() >= sizeof(FCGI_Header)) { FCGI_Header &h = *(FCGI_Header *)from.data(); /*std::cout<<"FCGI_Header {" <<(int)h.version<<"," <<(int)h.type<<"," <<(int)h.requestIdB1<<"," <<(int)h.requestIdB0<<"," <<(int)h.contentLengthB1<<"," <<(int)h.contentLengthB0<<"," <<(int)h.paddingLength<<"," <<(int)h.reserved<<"}\n"; */ unsigned int contentSize = (h.contentLengthB1 << 8) + h.contentLengthB0; //std::cout<<"contentSize="<= totalSize) { type = h.type; id = (h.requestIdB1 << 8) + h.requestIdB0; content.assign(from, sizeof(FCGI_Header), contentSize); from.erase(0, totalSize); return true; } } return false; } ///////////////////////////////////////////////////////////////////// // FcgiFrontEnd::Request ///////////////////////////////////////////////////////////////////// void FcgiFrontEnd::Request::sendStream(unsigned char type, std::string s) { _actions->debugInformation("sendStream(" + ntoa(type) + "," + urlEncode(s) + ")"); static const unsigned int maxChunk = 8192; Record r; r.type = type; r.id = _id; for (unsigned int start = 0; ; ) { // Run at least once. r.content.assign(s, start, maxChunk); _actions->sendBytes(r.asString()); start += maxChunk; if (start >= s.size()) { // Exit after we've sent everything. break; } } } void FcgiFrontEnd::Request::paramRecord(Record const &r) { assert(r.id == _id); if (r.content.empty()) { // End of stream. if (_receivedParams) { _actions->assertionFailed("Duplicate end of stream for params."); } else { if (!parseNameValuePairs(_paramsInProgress, _params)) { _actions->assertionFailed("Error parsing params"); } else { _paramsInProgress.clear(); _receivedParams = true; if (_receivedStdIn) { _actions->newResponderRequest(_id, _params, _stdIn); } } } } else { // Add to stream. if (_receivedParams) { _actions->assertionFailed("Received params data after params " "stream ended!"); } else { _paramsInProgress += r.content; } } } void FcgiFrontEnd::Request::stdInRecord(Record const &r) { assert(r.id == _id); if (r.content.empty()) { // End of stream. if (_receivedStdIn) { _actions->assertionFailed("Duplicate end of stream for stdin."); } else { _receivedStdIn = true; if (_receivedParams) { _actions->newResponderRequest(_id, _params, _stdIn); } } } else { // Add to stream. if (_receivedStdIn) { _actions->assertionFailed("Received stdin data after stdin " "stream ended!"); } else { _stdIn += r.content; } } } void FcgiFrontEnd::Request::sendStdOut(std::string s) { sendStream(FCGI_STDOUT, s); } void FcgiFrontEnd::Request::sendStdErr(std::string s) { sendStream(FCGI_STDERR, s); _sentSomeStdErr = true; } void FcgiFrontEnd::Request::flush() { // There is not a flush command in the specification. As soon as we send // data to the server, the server sends the data to the client. Flush would // only be meaningful if we do some buffering ourselves. } void FcgiFrontEnd::Request::close(AppStatus status) { ensureHeadersSent(); sendStdOut(""); if (_sentSomeStdErr) { sendStdErr(""); } //_actions->debugInformation("Streams are closed. Pausing 5 seconds."); //sleep(5); _actions->debugInformation("Sending FCGI_REQUEST_COMPLETE."); _actions->sendBytes(endRequestMessage(_id, status, FCGI_REQUEST_COMPLETE)); } void FcgiFrontEnd::Request::setHeader(std::string name, std::string value) { if (_sentHeaders) { _actions->assertionFailed("Headers already sent! '" + name + "' => '" + value + "'"); } else { _headers[name] = value; } } void FcgiFrontEnd::Request::clearHeader(std::string name) { if (_sentHeaders) { _actions->assertionFailed("Headers already sent! Clearning '" + name + "'"); } else { _headers.erase(name); } } void FcgiFrontEnd::Request::sendBody(std::string text) { if (!text.empty()) { ensureHeadersSent(); sendStdOut(text); } } void FcgiFrontEnd::Request::sendError(std::string text) { if (!text.empty()) { sendStdErr(text); } } void FcgiFrontEnd::Request::ensureHeadersSent() { // Warning! We're not checking the data. We're not quoting anything. // Be careful what you put into a header. if (!_sentHeaders) { std::string toSend; for (PropertyList::const_iterator it = _headers.begin(); it != _headers.end(); it++) { toSend += it->first + ": " + it->second + "\r\n"; } toSend += "\r\n"; sendStdOut(toSend); _headers.clear(); _sentHeaders = true; } } ///////////////////////////////////////////////////////////////////// // FcgiFrontEnd ///////////////////////////////////////////////////////////////////// std::string FcgiFrontEnd::endRequestMessage(int id, int appStatus, int protocolStatus) { Record r; r.type = FCGI_END_REQUEST; r.id = id; FCGI_EndRequestBody body; body.appStatusB3 = appStatus >> 24; body.appStatusB2 = appStatus >> 16; body.appStatusB1 = appStatus >> 8; body.appStatusB0 = appStatus; body.protocolStatus = protocolStatus; body.reserved[0] = 0; body.reserved[1] = 0; body.reserved[2] = 0; r.content.assign((char *)&body, sizeof(body)); return r.asString(); } void FcgiFrontEnd::bytesReceived(std::string b) { //_actions->debugInformation("Received bytes from server: " + urlEncode(b)); _incoming += b; Record record; while (record.read(b)) { _actions->debugInformation("Received record from server. Type=" + ntoa(record.type) + ", id=" + ntoa(record.id) + ", contentsize=" + ntoa(record.content.size()) + ", content =" + urlEncode(record.content)); Request *request = getPropertyDefault(_allRequests, record.id); switch (record.type) { case FCGI_BEGIN_REQUEST: if (request) { _actions->assertionFailed("Duplicate FCGI_BEGIN_REQUEST"); } else if (record.content.size() != sizeof(FCGI_BeginRequestBody)) { _actions->assertionFailed("Invalid body in FCGI_BEGIN_REQUEST.\n" "Expecting " + ntoa(sizeof(FCGI_BeginRequestBody)) + " bytes.\n" "Found " + ntoa(record.content.size()) + " bytes.\n"); } else { FCGI_BeginRequestBody &body = *(FCGI_BeginRequestBody *)record.content.data(); unsigned int role = (body.roleB1 << 8) + body.roleB0; if (role != FCGI_RESPONDER) { _actions->sendBytes (endRequestMessage(record.id, 0, FCGI_UNKNOWN_ROLE)); } else { // Success! _allRequests[record.id] = new Request(_actions, record.id); } } break; case FCGI_ABORT_REQUEST: if (request) { _actions->abort(record.id); } break; case FCGI_PARAMS: if (request) { request->paramRecord(record); } break; case FCGI_STDIN: if (request) { request->stdInRecord(record); } break; case FCGI_GET_VALUES: { // We're just sending everything that we've got. I think a better // implementation, based on the spec, would be to only copy the // records that were in the input. Record output; output.type = FCGI_GET_VALUES_RESULT; output.id = 0; encodeNameValuePairs(output.content, _valuesForGetValues); _actions->sendBytes(output.asString()); break; } default: { Record output; output.type = FCGI_UNKNOWN_TYPE; output.id = 0; FCGI_UnknownTypeBody body; body.type = record.type; body.reserved[0] = 0; body.reserved[1] = 0; body.reserved[2] = 0; body.reserved[3] = 0; body.reserved[4] = 0; body.reserved[5] = 0; body.reserved[6] = 0; output.content.assign((char *)&body, sizeof(body)); _actions->sendBytes(output.asString()); } } } } void FcgiFrontEnd::setHeader(FcgiRequestId id, std::string name, std::string value) { if (Request *r = getPropertyDefault(_allRequests, id)) { r->setHeader(name, value); } } void FcgiFrontEnd::clearHeader(FcgiRequestId id, std::string name) { if (Request *r = getPropertyDefault(_allRequests, id)) { r->clearHeader(name); } } void FcgiFrontEnd::sendError(FcgiRequestId id, std::string text) { if (Request *r = getPropertyDefault(_allRequests, id)) { r->sendError(text); } } void FcgiFrontEnd::sendBody(FcgiRequestId id, std::string text) { _actions->debugInformation("FcgiFrontEnd::sendBody(" + ntoa(id) + ", '" + text + "'"); if (Request *r = getPropertyDefault(_allRequests, id)) { _actions->debugInformation("(forwarding to request object"); r->sendBody(text); } } void FcgiFrontEnd::flush(FcgiRequestId id) { if (Request *r = getPropertyDefault(_allRequests, id)) { r->flush(); } } void FcgiFrontEnd::close(FcgiRequestId id, AppStatus status) { std::map< FcgiRequestId, Request * >::iterator it = _allRequests.find(id); if (it != _allRequests.end()) { it->second->close(status); delete it->second; _allRequests.erase(it); } } FcgiFrontEnd::FcgiFrontEnd(FcgiExternalActions * actions) : _actions(actions) { // Allow multiplexing. _valuesForGetValues[FCGI_MPXS_CONNS] = "1"; }