#include #include "../shared/LogFile.h" #include "../shared/GlobalConfigFile.h" #include "SCGI.h" ///////////////////////////////////////////////////////////////////// // ScgiFromServer ///////////////////////////////////////////////////////////////////// static bool verboseDebugging() { static bool value = getConfigItem("verbose_debugging") == "1"; return value; } static const std::string S_SCRIPT_NAME = "SCRIPT_NAME"; std::string ScgiFromServer::mountPoint() const { return getPropertyDefault(_headers, S_SCRIPT_NAME); } static const std::string S_PATH_INFO = "PATH_INFO"; std::string ScgiFromServer::relativePath() const { return getPropertyDefault(_headers, S_PATH_INFO); } static const std::string S_HTTP_HOST = "HTTP_HOST"; static const std::string S_UNKNOWN = "unknown"; std::string ScgiFromServer::rootDirNoSlash() const { // I can't find this documented anywhere, but it seems to work with Apache. const bool https = getPropertyDefault(_headers, "HTTPS") == "on"; const bool forwarded_https = getPropertyDefault(_headers, "HTTP_X_FORWARDED_PROTO") == "https"; const bool cloudfront_forwarded_https = getPropertyDefault(_headers, "HTTP_CLOUDFRONT_FORWARDED_PROTO") == "https"; std::string result; if (https || forwarded_https || cloudfront_forwarded_https) result = "https://"; else result = "http://"; result += getProperty(_headers, S_HTTP_HOST, S_UNKNOWN); if (verboseDebugging()) { TclList logMsg; logMsg<= (int)_toParse.size()) return; if (firstColon >= 8) { // We don't really expect to be able to read something 1 billion // bytes long. If we go much higher we will be over the length // of an integer, so we might have trouble catching it later. _errorMsg = "Error looking for initial colon"; _internalState = BadState; return; } const char ch = _toParse[firstColon]; if (ch == ':') break; if ((ch < '0') || (ch > '9')) { // Should be nothing but digits before the colon. _errorMsg = "Invalid character before initial colon"; _internalState = BadState; return; } } _sectionLength = strtolDefault(_toParse.substr(0, firstColon), -1); if (_sectionLength < 0) { _errorMsg = "Invalid header size"; _internalState = BadState; return; } _internalState = ReadHeadersState; _toParse.erase(0, firstColon + 1); } void ScgiFromServer::parseHeader() { assert(_internalState == ReadHeadersState); const int bodyStart = _sectionLength + 1; if ((int)_toParse.size() < bodyStart) // Not even the header is complete. return; if (_toParse[bodyStart - 1] != ',') { _errorMsg = "Could not find comma after header"; _internalState = BadState; return; } std::vector< std::string > headerPieces = explode(std::string(1, '\00'), _toParse.substr(0, _sectionLength)); if ((headerPieces.size() % 2) == 0) { // null is actually a terminator, not a seperator. So we expect to // have an odd number of pieces, and the last piece should be the // empty string. _errorMsg = "Odd number of header items (pieces = " + ntoa(headerPieces.size()) + ")"; _internalState = BadState; return; } if (headerPieces.size() < 5) { // There should be a minimum of two items, per the spec. _errorMsg = "Not enough header items"; _internalState = BadState; return; } if (headerPieces[headerPieces.size() - 1] != "") { _errorMsg = "Something found after header"; _internalState = BadState; return; } const int headerPairCount = headerPieces.size() / 2; for (int pair = 0; pair < headerPairCount; pair++) _headers[headerPieces[pair*2]] = headerPieces[pair*2+1]; _sectionLength = strtolDefault(_headers["CONTENT_LENGTH"], -1); if (_sectionLength < 0) { _errorMsg = "Content length invalid or not found"; _internalState = BadState; return; } _internalState = ReadBodyState; _toParse.erase(0, bodyStart); } void ScgiFromServer::parseBody() { assert(_internalState == ReadBodyState); if ((int)_toParse.size() < _sectionLength) return; _body = _toParse; _toParse.clear(); _body.erase(_sectionLength, _body.size()); _internalState = DoneState; } ///////////////////////////////////////////////////////////////////// // ScgiToServer ///////////////////////////////////////////////////////////////////// void ScgiToServer::allowCrossOriginRequests() { // This seems to be safe for anything that doesn't use cookies. I wish I // understood the security ramifications better! _headers["Access-Control-Allow-Origin"] = "*"; _headers["Access-Control-Allow-Methods"] = "GET,POST"; } void ScgiToServer::setContentType(std::string const &type) { _headers["Content-Type"] = type; } void ScgiToServer::setContentTypeHtmlUtf8() { setContentType("text/html; charset=utf-8"); } void ScgiToServer::setContentTypePlainUtf8() { setContentType("text/plain; charset=utf-8"); } void ScgiToServer::setContentTypeXml() { setContentType("text/xml"); } void ScgiToServer::reset() { _headers.clear(); _body.clear(); setStatusOK(); setContentTypeXml(); allowCrossOriginRequests(); } std::string ScgiToServer::toString() const { std::string result; for (PropertyList::const_iterator it = _headers.begin(); it != _headers.end(); it++) { result += it->first; result += ": "; result += it->second; result += "\r\n"; } result += "\r\n"; result += _body; return result; } void ScgiToServer::setStatusOK() { _headers["Status"] = "200 OK"; } void ScgiToServer::setStatusNotFound() { _headers["Status"] = "404 Not Found"; } ///////////////////////////////////////////////////////////////////// // Unit Test ///////////////////////////////////////////////////////////////////// #ifdef __UNIT_TEST_SIMPLE_ #include static void tryToParse(std::string notes, std::string input) { ScgiFromServer parser; // parser.addBytes(input); for (int i = 0; i < (int)input.size(); i++) parser.addBytes(std::string(1, input[i])); TclList msg; msg<<"notes"<first<second; msg<<"headers"< #include #include #include #include #include #include "../shared/XmlSupport.h" int main(int, char**) { const int port = 4433; int listenerHandle = socket(PF_INET, SOCK_STREAM, 0); if (listenerHandle == -1) { perror("socket"); return 2; } // http://hea-www.harvard.edu/~fine/Tech/addrinuse.html int on=1; setsockopt(listenerHandle, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); struct sockaddr_in my_addr; my_addr.sin_family = AF_INET; my_addr.sin_port = htons(port); my_addr.sin_addr.s_addr = INADDR_ANY; int bind_result = bind(listenerHandle, (sockaddr*)&my_addr, sizeof(my_addr)); if (bind_result) { perror("bind()"); close(listenerHandle); return 2; } int listen_result = listen(listenerHandle, 1024); if (listen_result) { perror("listen()"); close(listenerHandle); return 2; } std::cout<<"listening"<first; p.properties["value"] = it->second; } responseBody["httpRootDir"].text = parser.httpRootDir(); responseBody["locationBase"].text = parser.locationBase(); responseBody["shortScriptName"].text = parser.shortScriptName(); PropertyList inputs; parser.getGetValues(inputs); for (PropertyList::const_iterator it = inputs.begin(); it != inputs.end(); it++) { XmlNode &p = responseBody[-1]; p.name = "get"; p.properties["name"] = it->first; p.properties["value"] = it->second; } inputs.clear(); parser.getPostValues(inputs); for (PropertyList::const_iterator it = inputs.begin(); it != inputs.end(); it++) { XmlNode &p = responseBody[-1]; p.name = "post"; p.properties["name"] = it->first; p.properties["value"] = it->second; } responseBody.properties["body"] = parser.getBody(); } else if (parser.error()) responseBody.properties["state"] = "error"; else responseBody.properties["state"] = "incomplete"; ScgiToServer response; response._body = responseBody.asString(); std::string responseBytes = response.toString(); write(conversationHandle, responseBytes.data(), responseBytes.size()); close(conversationHandle); std::cout<<"closed"<