| 70 | HttpRequestParser::~HttpRequestParser() FL_NOEXCEPT = default; |
| 71 | |
| 72 | void HttpRequestParser::feed(fl::span<const u8> data) { |
| 73 | mBuffer.insert(mBuffer.end(), data.begin(), data.end()); |
| 74 | |
| 75 | // State machine: parse incrementally |
| 76 | bool progress = true; |
| 77 | while (progress && mState != COMPLETE) { |
| 78 | progress = false; |
| 79 | |
| 80 | switch (mState) { |
| 81 | case READ_REQUEST_LINE: |
| 82 | |
| 83 | if (parseRequestLine()) { |
| 84 | |
| 85 | mState = READ_HEADERS; |
| 86 | progress = true; |
| 87 | } |
| 88 | break; |
| 89 | |
| 90 | case READ_HEADERS: |
| 91 | |
| 92 | if (parseHeaders()) { |
| 93 | |
| 94 | // Check if body is expected |
| 95 | auto contentLength = getHeader("Content-Length"); |
| 96 | auto transferEncoding = getHeader("Transfer-Encoding"); |
| 97 | |
| 98 | if (transferEncoding.has_value() && |
| 99 | toLower(transferEncoding.value()).find("chunked") != fl::string::npos) { |
| 100 | mIsChunked = true; |
| 101 | mState = READ_BODY; |
| 102 | } else if (contentLength.has_value()) { |
| 103 | int contentLengthInt = 0; |
| 104 | if (parseInt(contentLength.value(), contentLengthInt)) { |
| 105 | mContentLength = static_cast<size_t>(contentLengthInt); |
| 106 | mState = READ_BODY; |
| 107 | } else { |
| 108 | // Invalid Content-Length, skip to complete |
| 109 | mState = COMPLETE; |
| 110 | } |
| 111 | } else { |
| 112 | // No body |
| 113 | mState = COMPLETE; |
| 114 | } |
| 115 | progress = true; |
| 116 | } |
| 117 | break; |
| 118 | |
| 119 | case READ_BODY: |
| 120 | |
| 121 | parseBody(); |
| 122 | // parseBody() will set mState to COMPLETE when done |
| 123 | // Continue processing if state changed to COMPLETE |
| 124 | if (mState == COMPLETE) { |
| 125 | |
| 126 | progress = true; |
| 127 | } |
| 128 | break; |
| 129 | |