| 258 | } |
| 259 | |
| 260 | Response FetchRequest::parse_http_response(const fl::string& raw) { |
| 261 | const char* data = raw.c_str(); |
| 262 | const size_t len = raw.size(); |
| 263 | |
| 264 | // Find end of headers (double CRLF) |
| 265 | size_t header_end = raw.find("\r\n\r\n"); |
| 266 | if (header_end == fl::string::npos) { |
| 267 | return Response(500, "Internal Server Error"); |
| 268 | } |
| 269 | |
| 270 | // Parse status line: "HTTP/1.1 200 OK\r\n" |
| 271 | size_t first_space = raw.find(' '); |
| 272 | size_t second_space = raw.find(' ', first_space + 1); |
| 273 | if (first_space == fl::string::npos || second_space == fl::string::npos || |
| 274 | first_space >= header_end || second_space >= header_end) { |
| 275 | return Response(500, "Internal Server Error"); |
| 276 | } |
| 277 | |
| 278 | // Parse status code from digits without allocating |
| 279 | int status_code = 0; |
| 280 | for (size_t i = first_space + 1; i < second_space; ++i) { |
| 281 | status_code = status_code * 10 + (data[i] - '0'); |
| 282 | } |
| 283 | |
| 284 | // Status text ends at first \r\n |
| 285 | size_t status_line_end = raw.find("\r\n"); |
| 286 | size_t st_len = (status_line_end != fl::string::npos ? status_line_end : header_end) - (second_space + 1); |
| 287 | |
| 288 | Response resp(status_code, fl::string(data + second_space + 1, st_len)); |
| 289 | resp.set_body(fl::string(data + header_end + 4, len - header_end - 4)); |
| 290 | |
| 291 | // Parse response headers using indices into raw |
| 292 | size_t pos = status_line_end + 2; // Skip past status line CRLF |
| 293 | while (pos < header_end) { |
| 294 | size_t line_end = raw.find("\r\n", pos); |
| 295 | if (line_end == fl::string::npos || line_end > header_end) { |
| 296 | line_end = header_end; |
| 297 | } |
| 298 | |
| 299 | // Find colon separator |
| 300 | size_t colon = fl::string::npos; |
| 301 | for (size_t i = pos; i < line_end; ++i) { |
| 302 | if (data[i] == ':') { |
| 303 | colon = i; |
| 304 | break; |
| 305 | } |
| 306 | } |
| 307 | |
| 308 | if (colon != fl::string::npos) { |
| 309 | // Build lowercase header name |
| 310 | fl::string name(data + pos, colon - pos); |
| 311 | for (size_t i = 0; i < name.size(); ++i) { |
| 312 | if (name[i] >= 'A' && name[i] <= 'Z') { |
| 313 | name[i] += ('a' - 'A'); |
| 314 | } |
| 315 | } |
| 316 | |
| 317 | // Value: skip colon and optional leading space |