- Main Page
- Modules
- Namespaces
- Classes
- Files

- File List

# ScgiRequestParser.h

    00001 /*
    00002  *  Phusion Passenger - http://www.modrails.com/
    00003  *  Copyright (c) 2010 Phusion
    00004  *
    00005  *  "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
    00006  *
    00007  *  Permission is hereby granted, free of charge, to any person obtaining a copy
    00008  *  of this software and associated documentation files (the "Software"), to deal
    00009  *  in the Software without restriction, including without limitation the rights
    00010  *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    00011  *  copies of the Software, and to permit persons to whom the Software is
    00012  *  furnished to do so, subject to the following conditions:
    00013  *
    00014  *  The above copyright notice and this permission notice shall be included in
    00015  *  all copies or substantial portions of the Software.
    00016  *
    00017  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    00018  *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    00019  *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    00020  *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    00021  *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    00022  *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    00023  *  THE SOFTWARE.
    00024  */
    00025
    00026 #include <google/dense_hash_map>
    00027
    00028 #include <string>
    00029 #include <map>
    00030 #include <cstdlib>
    00031
    00032 #include "StaticString.h"
    00033
    00034 namespace Passenger {
    00035
    00036 using namespace std;
    00037 using namespace google;
    00038
    00039 /**
    00040  * A parser for SCGI requests. It parses the request header and ignores the
    00041  * body data.
    00042  *
    00043  * You can use it by constructing a parser object, then feeding data to the
    00044  * parser until it has reached a final state.
    00045  *
    00046  * Example:
    00047  * @code
    00048  *    ScgiRequestParser parser;
    00049  *    char buf[1024 * 16];
    00050  *    ssize_t size;
    00051  *    unsigned in bytesAccepted;
    00052  *
    00053  *    do {
    00054  *        size = read(fd, buf, sizeof(buf));
    00055  *        bytesAccepted = parser.feed(buf, size);
    00056  *    } while (parser.acceptingInput());
    00057  *    // Parser is done when its return value isn't equal to the input size.
    00058  *
    00059  *    // Check whether a parse error occured.
    00060  *    if (parser.getState() == ScgiRequestParser::ERROR) {
    00061  *        bailOut();
    00062  *    } else {
    00063  *        // All good! Do something with the SCGI header that the parser parsed.
    00064  *        processHeader(parser.getHeaderData());
    00065  *
    00066  *        // If the last buffer passed to the parser also contains body data,
    00067  *        // then the body data starts at 'buf + bytesAccepted'.
    00068  *        if (bytesAccepted < size) {
    00069  *            processBody(buf + bytesAccepted);
    00070  *        }
    00071  *        while (!end_of_file(fd)) {
    00072  *            ... read(...) ...
    00073  *            processBody(...);
    00074  *        }
    00075  *    }
    00076  * @endcode
    00077  *
    00078  * Parser properties:
    00079  * - A parser object can only process a single SCGI request. You must discard
    00080  *   the existing parser object and create a new one if you want to process
    00081  *   another SCGI request.
    00082  * - This parser checks whether the header netstring is valid. It will enter
    00083  *   the error state if it encounters a parse error.
    00084  * - However, this parser does not perform any validation of the actual header
    00085  *   contents. For example, it doesn't check that CONTENT_LENGTH is the first
    00086  *   header, or that the SCGI header is present.
    00087  */
    00088 class ScgiRequestParser {
    00089 public:
    00090         enum State {
    00091                 READING_LENGTH_STRING,
    00092                 READING_HEADER_DATA,
    00093                 EXPECTING_COMMA,
    00094                 DONE,
    00095                 ERROR
    00096         };
    00097
    00098         enum ErrorReason {
    00099                 NONE,
    00100
    00101                 /** The length string is too large. */
    00102                 LENGTH_STRING_TOO_LARGE,
    00103
    00104                 /** The header is larger than the maxSize value provided to the constructor. */
    00105                 LIMIT_REACHED,
    00106
    00107                 /** The length string contains an invalid character. */
    00108                 INVALID_LENGTH_STRING,
    00109
    00110                 /** A header terminator character (",") was expected, but some else
    00111                  * was encountered instead. */
    00112                 HEADER_TERMINATOR_EXPECTED,
    00113
    00114                 /** The header data itself contains errors. */
    00115                 INVALID_HEADER_DATA
    00116         };
    00117
    00118 private:
    00119         typedef dense_hash_map<StaticString, StaticString, StaticString::Hash> HeaderMap;
    00120
    00121         unsigned long maxSize;
    00122
    00123         State state;
    00124         ErrorReason errorReason;
    00125         char lengthStringBuffer[sizeof("4294967296")];
    00126         unsigned int lengthStringBufferSize;
    00127         unsigned long headerSize;
    00128         string headerBuffer;
    00129         HeaderMap headers;
    00130
    00131         static inline bool isDigit(char byte) {
    00132                 return byte >= '0' && byte <= '9';
    00133         }
    00134
    00135         /**
    00136          * Parse the given header data into key-value pairs.
    00137          */
    00138         bool parseHeaderData(const string &data, HeaderMap &output) {
    00139                 bool isName = true; // Whether we're currently expecting a name or a value.
    00140                 const char *startOfString, *current, *end;
    00141                 StaticString key, value;
    00142
    00143                 if (data.size() == 0) {
    00144                         return true;
    00145                 }
    00146
    00147                 startOfString = data.c_str();
    00148                 end           = data.c_str() + data.size();
    00149
    00150                 if (*(end - 1) != '\0') {
    00151                         return false;
    00152                 }
    00153
    00154                 for (current = data.c_str(); current != end; current++) {
    00155                         if (isName && *current == '\0') {
    00156                                 key = StaticString(startOfString, current - startOfString);
    00157                                 if (key.empty()) {
    00158                                         return false;
    00159                                 }
    00160                                 startOfString = current + 1;
    00161                                 isName = false;
    00162                         } else if (!isName && *current == '\0') {
    00163                                 value = StaticString(startOfString, current - startOfString);
    00164                                 startOfString = current + 1;
    00165                                 isName = true;
    00166
    00167                                 output[key] = value;
    00168                                 key   = StaticString();
    00169                                 value = StaticString();
    00170                         }
    00171                 }
    00172
    00173                 return isName;
    00174         }
    00175
    00176         /**
    00177          * Process the given data, which contains header data and possibly
    00178          * some body data as well.
    00179          */
    00180         unsigned int readHeaderData(const char *data, unsigned int size) {
    00181                 unsigned int bytesToRead;
    00182
    00183                 // Calculate how many bytes of header data is left to be read.
    00184                 // Do not read past the header data.
    00185                 if (size < headerSize - headerBuffer.size()) {
    00186                         bytesToRead = size;
    00187                 } else {
    00188                         bytesToRead = headerSize - headerBuffer.size();
    00189                 }
    00190                 // Append the newly received header data to the header data buffer.
    00191                 headerBuffer.append(data, bytesToRead);
    00192
    00193                 if (headerBuffer.size() == headerSize) {
    00194                         // We've received all header data. Now attempt to parse this.
    00195                         if (bytesToRead < size) {
    00196                                 if (data[bytesToRead] == ',') {
    00197                                         if (parseHeaderData(headerBuffer, headers)) {
    00198                                                 state = DONE;
    00199                                                 return bytesToRead + 1;
    00200                                         } else {
    00201                                                 state = ERROR;
    00202                                                 errorReason = INVALID_HEADER_DATA;
    00203                                                 return bytesToRead;
    00204                                         }
    00205                                 } else {
    00206                                         state = ERROR;
    00207                                         errorReason = HEADER_TERMINATOR_EXPECTED;
    00208                                         return bytesToRead;
    00209                                 }
    00210                         } else {
    00211                                 if (parseHeaderData(headerBuffer, headers)) {
    00212                                         state = EXPECTING_COMMA;
    00213                                 } else {
    00214                                         state = ERROR;
    00215                                         errorReason = INVALID_HEADER_DATA;
    00216                                 }
    00217                                 return bytesToRead;
    00218                         }
    00219                 } else {
    00220                         // Not all header data has been received yet.
    00221                         return bytesToRead;
    00222                 }
    00223         }
    00224
    00225 public:
    00226         /**
    00227          * Create a new ScgiRequestParser, ready to parse a request.
    00228          *
    00229          * @param maxSize The maximum size that the SCGI data is allowed to
    00230          *                be, or 0 if no limit is desired.
    00231          */
    00232         ScgiRequestParser(unsigned long maxSize = 0) {
    00233                 this->maxSize = maxSize;
    00234                 state = READING_LENGTH_STRING;
    00235                 errorReason = NONE;
    00236                 lengthStringBufferSize = 0;
    00237                 headerSize = 0;
    00238                 headers.set_empty_key("");
    00239         }
    00240
    00241         /**
    00242          * Feed SCGI request data to the parser.
    00243          *
    00244          * @param data The data to feed.
    00245          * @param size The size of the data, in bytes.
    00246          * @return The number of recognized SCGI header bytes. If this value
    00247          *         equals 'size', then it means all data in 'data' is part of
    00248          *         the SCGI header. If this value is less than size, then it
    00249          *         means only some data in 'data' is part of the SCGI header,
    00250          *         and the remaining 'size - result' bytes are part of the
    00251          *         request body.
    00252          * @pre size > 0
    00253          * @post result <= size
    00254          * @post if result <= size: getState() == DONE || getState() == ERROR
    00255          */
    00256         unsigned int feed(const char *data, unsigned int size) {
    00257                 unsigned int i;
    00258
    00259                 switch (state) {
    00260                 case READING_LENGTH_STRING:
    00261                         // Keep processing length string data...
    00262                         for (i = 0; i < size; i++) {
    00263                                 char byte = data[i];
    00264
    00265                                 if (lengthStringBufferSize == sizeof(lengthStringBuffer) - 1) {
    00266                                         // ...and abort if the length string is too long.
    00267                                         state = ERROR;
    00268                                         errorReason = LENGTH_STRING_TOO_LARGE;
    00269                                         return i;
    00270                                 } else if (!isDigit(byte)) {
    00271                                         if (byte == ':') {
    00272                                                 // ...until the end of the length string has been reached.
    00273                                                 state = READING_HEADER_DATA;
    00274                                                 lengthStringBuffer[lengthStringBufferSize] = '\0';
    00275                                                 headerSize = atol(lengthStringBuffer);
    00276                                                 if (maxSize > 0 && headerSize > maxSize) {
    00277                                                         state = ERROR;
    00278                                                         errorReason = LIMIT_REACHED;
    00279                                                 } else {
    00280                                                         headerBuffer.reserve(headerSize);
    00281                                                         // From here on, process the rest of the data that we've
    00282                                                         // received, as header data.
    00283                                                         return readHeaderData(data + i + 1, size - i - 1) + i + 1;
    00284                                                 }
    00285                                         } else {
    00286                                                 // ...until we encounter a parse error.
    00287                                                 state = ERROR;
    00288                                                 errorReason = INVALID_LENGTH_STRING;
    00289                                                 return i;
    00290                                         }
    00291                                 } else {
    00292                                         lengthStringBuffer[lengthStringBufferSize] = byte;
    00293                                         lengthStringBufferSize++;
    00294                                 }
    00295                         }
    00296                         return i;
    00297
    00298                 case READING_HEADER_DATA:
    00299                         return readHeaderData(data, size);
    00300
    00301                 case EXPECTING_COMMA:
    00302                         if (data[0] == ',') {
    00303                                 state = DONE;
    00304                                 return 1;
    00305                         } else {
    00306                                 state = ERROR;
    00307                                 errorReason = HEADER_TERMINATOR_EXPECTED;
    00308                                 return 0;
    00309                         }
    00310
    00311                 default:
    00312                         return 0;
    00313                 }
    00314         }
    00315
    00316         /**
    00317          * Get the raw header data that has been processed so far.
    00318          */
    00319         string getHeaderData() const {
    00320                 return headerBuffer;
    00321         }
    00322
    00323         /**
    00324          * Get the value of the header with the given name.
    00325          * Lookup is case-sensitive.
    00326          *
    00327          * Returns the empty string if there is no such header.
    00328          *
    00329          * @pre getState() == DONE
    00330          */
    00331         StaticString getHeader(const StaticString &name) const {
    00332                 HeaderMap::const_iterator it(headers.find(name));
    00333                 if (it == headers.end()) {
    00334                         return "";
    00335                 } else {
    00336                         return it->second;
    00337                 }
    00338         }
    00339
    00340         /**
    00341          * Checks whether there is a header with the given name.
    00342          * Lookup is case-sensitive.
    00343          *
    00344          * @pre getState() == DONE
    00345          */
    00346         bool hasHeader(const StaticString &name) const {
    00347                 return headers.find(name) != headers.end();
    00348         }
    00349
    00350         /**
    00351          * Get the parser's current state.
    00352          */
    00353         State getState() const {
    00354                 return state;
    00355         }
    00356
    00357         /**
    00358          * Returns the reason why the parser entered the error state.
    00359          *
    00360          * @pre getState() == ERROR
    00361          */
    00362         ErrorReason getErrorReason() const {
    00363                 return errorReason;
    00364         }
    00365
    00366         /**
    00367          * Checks whether this parser is still capable of accepting input (that
    00368          * is, that this parser is not in a final state).
    00369          */
    00370         bool acceptingInput() const {
    00371                 return state != DONE && state != ERROR;
    00372         }
    00373 };
    00374
    00375 } // namespace Passenger