- 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                                 startOfString = current + 1;
    00158                                 isName = false;
    00159                         } else if (!isName && *current == '\0') {
    00160                                 value = StaticString(startOfString, current - startOfString);
    00161                                 startOfString = current + 1;
    00162                                 isName = true;
    00163
    00164                                 output[key] = value;
    00165                                 key   = StaticString();
    00166                                 value = StaticString();
    00167                         }
    00168                 }
    00169
    00170                 return isName;
    00171         }
    00172
    00173         /**
    00174          * Process the given data, which contains header data and possibly
    00175          * some body data as well.
    00176          */
    00177         unsigned int readHeaderData(const char *data, unsigned int size) {
    00178                 unsigned int bytesToRead;
    00179
    00180                 // Calculate how many bytes of header data is left to be read.
    00181                 // Do not read past the header data.
    00182                 if (size < headerSize - headerBuffer.size()) {
    00183                         bytesToRead = size;
    00184                 } else {
    00185                         bytesToRead = headerSize - headerBuffer.size();
    00186                 }
    00187                 // Append the newly received header data to the header data buffer.
    00188                 headerBuffer.append(data, bytesToRead);
    00189
    00190                 if (headerBuffer.size() == headerSize) {
    00191                         // We've received all header data. Now attempt to parse this.
    00192                         if (bytesToRead < size) {
    00193                                 if (data[bytesToRead] == ',') {
    00194                                         if (parseHeaderData(headerBuffer, headers)) {
    00195                                                 state = DONE;
    00196                                                 return bytesToRead + 1;
    00197                                         } else {
    00198                                                 state = ERROR;
    00199                                                 errorReason = INVALID_HEADER_DATA;
    00200                                                 return bytesToRead;
    00201                                         }
    00202                                 } else {
    00203                                         state = ERROR;
    00204                                         errorReason = HEADER_TERMINATOR_EXPECTED;
    00205                                         return bytesToRead;
    00206                                 }
    00207                         } else {
    00208                                 if (parseHeaderData(headerBuffer, headers)) {
    00209                                         state = EXPECTING_COMMA;
    00210                                 } else {
    00211                                         state = ERROR;
    00212                                         errorReason = INVALID_HEADER_DATA;
    00213                                 }
    00214                                 return bytesToRead;
    00215                         }
    00216                 } else {
    00217                         // Not all header data has been received yet.
    00218                         return bytesToRead;
    00219                 }
    00220         }
    00221
    00222 public:
    00223         /**
    00224          * Create a new ScgiRequestParser, ready to parse a request.
    00225          *
    00226          * @param maxSize The maximum size that the SCGI data is allowed to
    00227          *                be, or 0 if no limit is desired.
    00228          */
    00229         ScgiRequestParser(unsigned long maxSize = 0) {
    00230                 this->maxSize = maxSize;
    00231                 state = READING_LENGTH_STRING;
    00232                 errorReason = NONE;
    00233                 lengthStringBufferSize = 0;
    00234                 headerSize = 0;
    00235                 headers.set_empty_key("");
    00236         }
    00237
    00238         /**
    00239          * Feed SCGI request data to the parser.
    00240          *
    00241          * @param data The data to feed.
    00242          * @param size The size of the data, in bytes.
    00243          * @return The number of recognized SCGI header bytes. If this value
    00244          *         equals 'size', then it means all data in 'data' is part of
    00245          *         the SCGI header. If this value is less than size, then it
    00246          *         means only some data in 'data' is part of the SCGI header,
    00247          *         and the remaining 'size - result' bytes are part of the
    00248          *         request body.
    00249          * @pre size > 0
    00250          * @post result <= size
    00251          * @post if result <= size: getState() == DONE || getState() == ERROR
    00252          */
    00253         unsigned int feed(const char *data, unsigned int size) {
    00254                 unsigned int i;
    00255
    00256                 switch (state) {
    00257                 case READING_LENGTH_STRING:
    00258                         // Keep processing length string data...
    00259                         for (i = 0; i < size; i++) {
    00260                                 char byte = data[i];
    00261
    00262                                 if (lengthStringBufferSize == sizeof(lengthStringBuffer) - 1) {
    00263                                         // ...and abort if the length string is too long.
    00264                                         state = ERROR;
    00265                                         errorReason = LENGTH_STRING_TOO_LARGE;
    00266                                         return i;
    00267                                 } else if (!isDigit(byte)) {
    00268                                         if (byte == ':') {
    00269                                                 // ...until the end of the length string has been reached.
    00270                                                 state = READING_HEADER_DATA;
    00271                                                 lengthStringBuffer[lengthStringBufferSize] = '\0';
    00272                                                 headerSize = atol(lengthStringBuffer);
    00273                                                 if (maxSize > 0 && headerSize > maxSize) {
    00274                                                         state = ERROR;
    00275                                                         errorReason = LIMIT_REACHED;
    00276                                                 } else {
    00277                                                         headerBuffer.reserve(headerSize);
    00278                                                         // From here on, process the rest of the data that we've
    00279                                                         // received, as header data.
    00280                                                         return readHeaderData(data + i + 1, size - i - 1) + i + 1;
    00281                                                 }
    00282                                         } else {
    00283                                                 // ...until we encounter a parse error.
    00284                                                 state = ERROR;
    00285                                                 errorReason = INVALID_LENGTH_STRING;
    00286                                                 return i;
    00287                                         }
    00288                                 } else {
    00289                                         lengthStringBuffer[lengthStringBufferSize] = byte;
    00290                                         lengthStringBufferSize++;
    00291                                 }
    00292                         }
    00293                         return i;
    00294
    00295                 case READING_HEADER_DATA:
    00296                         return readHeaderData(data, size);
    00297
    00298                 case EXPECTING_COMMA:
    00299                         if (data[0] == ',') {
    00300                                 state = DONE;
    00301                                 return 1;
    00302                         } else {
    00303                                 state = ERROR;
    00304                                 errorReason = HEADER_TERMINATOR_EXPECTED;
    00305                                 return 0;
    00306                         }
    00307
    00308                 default:
    00309                         return 0;
    00310                 }
    00311         }
    00312
    00313         /**
    00314          * Get the raw header data that has been processed so far.
    00315          */
    00316         string getHeaderData() const {
    00317                 return headerBuffer;
    00318         }
    00319
    00320         /**
    00321          * Get the value of the header with the given name.
    00322          * Lookup is case-sensitive.
    00323          *
    00324          * Returns the empty string if there is no such header.
    00325          *
    00326          * @pre getState() == DONE
    00327          */
    00328         StaticString getHeader(const StaticString &name) const {
    00329                 HeaderMap::const_iterator it(headers.find(name));
    00330                 if (it == headers.end()) {
    00331                         return "";
    00332                 } else {
    00333                         return it->second;
    00334                 }
    00335         }
    00336
    00337         /**
    00338          * Checks whether there is a header with the given name.
    00339          * Lookup is case-sensitive.
    00340          *
    00341          * @pre getState() == DONE
    00342          */
    00343         bool hasHeader(const StaticString &name) const {
    00344                 return headers.find(name) != headers.end();
    00345         }
    00346
    00347         /**
    00348          * Get the parser's current state.
    00349          */
    00350         State getState() const {
    00351                 return state;
    00352         }
    00353
    00354         /**
    00355          * Returns the reason why the parser entered the error state.
    00356          *
    00357          * @pre getState() == ERROR
    00358          */
    00359         ErrorReason getErrorReason() const {
    00360                 return errorReason;
    00361         }
    00362
    00363         /**
    00364          * Checks whether this parser is still capable of accepting input (that
    00365          * is, that this parser is not in a final state).
    00366          */
    00367         bool acceptingInput() const {
    00368                 return state != DONE && state != ERROR;
    00369         }
    00370 };
    00371
    00372 } // namespace Passenger