00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
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
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
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
00102 LENGTH_STRING_TOO_LARGE,
00103
00104
00105 LIMIT_REACHED,
00106
00107
00108 INVALID_LENGTH_STRING,
00109
00110
00111
00112 HEADER_TERMINATOR_EXPECTED,
00113
00114
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
00137
00138 bool parseHeaderData(const string &data, HeaderMap &output) {
00139 bool isName = true;
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
00175
00176
00177 unsigned int readHeaderData(const char *data, unsigned int size) {
00178 unsigned int bytesToRead;
00179
00180
00181
00182 if (size < headerSize - headerBuffer.size()) {
00183 bytesToRead = size;
00184 } else {
00185 bytesToRead = headerSize - headerBuffer.size();
00186 }
00187
00188 headerBuffer.append(data, bytesToRead);
00189
00190 if (headerBuffer.size() == headerSize) {
00191
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
00218 return bytesToRead;
00219 }
00220 }
00221
00222 public:
00223
00224
00225
00226
00227
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
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
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
00259 for (i = 0; i < size; i++) {
00260 char byte = data[i];
00261
00262 if (lengthStringBufferSize == sizeof(lengthStringBuffer) - 1) {
00263
00264 state = ERROR;
00265 errorReason = LENGTH_STRING_TOO_LARGE;
00266 return i;
00267 } else if (!isDigit(byte)) {
00268 if (byte == ':') {
00269
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
00279
00280 return readHeaderData(data + i + 1, size - i - 1) + i + 1;
00281 }
00282 } else {
00283
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
00315
00316 string getHeaderData() const {
00317 return headerBuffer;
00318 }
00319
00320
00321
00322
00323
00324
00325
00326
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
00339
00340
00341
00342
00343 bool hasHeader(const StaticString &name) const {
00344 return headers.find(name) != headers.end();
00345 }
00346
00347
00348
00349
00350 State getState() const {
00351 return state;
00352 }
00353
00354
00355
00356
00357
00358
00359 ErrorReason getErrorReason() const {
00360 return errorReason;
00361 }
00362
00363
00364
00365
00366
00367 bool acceptingInput() const {
00368 return state != DONE && state != ERROR;
00369 }
00370 };
00371
00372 }