Coverage Report

Created: 2026-07-16 07:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PcapPlusPlus/Packet++/src/HttpLayer.cpp
Line
Count
Source
1
36.1k
#define LOG_MODULE PacketLogModuleHttpLayer
2
3
#include "Logger.h"
4
#include "GeneralUtils.h"
5
#include "HttpLayer.h"
6
#include <cstring>
7
#include <algorithm>
8
#include <exception>
9
#include <unordered_map>
10
11
namespace pcpp
12
{
13
  // -------- Class HttpMessage -----------------
14
15
  HeaderField* HttpMessage::addField(const std::string& fieldName, const std::string& fieldValue)
16
0
  {
17
0
    if (getFieldByName(fieldName) != nullptr)
18
0
    {
19
0
      PCPP_LOG_ERROR("Field '" << fieldName << "' already exists!");
20
0
      return nullptr;
21
0
    }
22
23
0
    return TextBasedProtocolMessage::addField(fieldName, fieldValue);
24
0
  }
25
26
  HeaderField* HttpMessage::addField(const HeaderField& newField)
27
0
  {
28
0
    if (getFieldByName(newField.getFieldName()) != nullptr)
29
0
    {
30
0
      PCPP_LOG_ERROR("Field '" << newField.getFieldName() << "' already exists!");
31
0
      return nullptr;
32
0
    }
33
34
0
    return TextBasedProtocolMessage::addField(newField);
35
0
  }
36
37
  HeaderField* HttpMessage::insertField(HeaderField* prevField, const std::string& fieldName,
38
                                        const std::string& fieldValue)
39
0
  {
40
0
    if (getFieldByName(fieldName) != nullptr)
41
0
    {
42
0
      PCPP_LOG_ERROR("Field '" << fieldName << "' already exists!");
43
0
      return nullptr;
44
0
    }
45
46
0
    return TextBasedProtocolMessage::insertField(prevField, fieldName, fieldValue);
47
0
  }
48
49
  HeaderField* HttpMessage::insertField(HeaderField* prevField, const HeaderField& newField)
50
0
  {
51
0
    if (getFieldByName(newField.getFieldName()) != nullptr)
52
0
    {
53
0
      PCPP_LOG_ERROR("Field '" << newField.getFieldName() << "' already exists!");
54
0
      return nullptr;
55
0
    }
56
57
0
    return TextBasedProtocolMessage::insertField(prevField, newField);
58
0
  }
59
60
  // -------- Class HttpRequestLayer -----------------
61
62
  HttpRequestLayer::HttpRequestLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)
63
83.6k
      : HttpMessage(data, dataLen, prevLayer, packet, HTTPRequest)
64
83.6k
  {
65
83.6k
    m_FirstLine = new HttpRequestFirstLine(this);
66
83.6k
    m_FieldsOffset = m_FirstLine->getSize();
67
83.6k
    parseFields();
68
83.6k
  }
69
70
  HttpRequestLayer::HttpRequestLayer(HttpMethod method, const std::string& uri, HttpVersion version)
71
0
  {
72
0
    m_Protocol = HTTPRequest;
73
0
    m_FirstLine = new HttpRequestFirstLine(this, method, version, uri);
74
0
    m_FieldsOffset = m_FirstLine->getSize();
75
0
  }
76
77
0
  HttpRequestLayer::HttpRequestLayer(const HttpRequestLayer& other) : HttpMessage(other)
78
0
  {
79
0
    m_FirstLine = new HttpRequestFirstLine(this);
80
0
  }
81
82
  HttpRequestLayer& HttpRequestLayer::operator=(const HttpRequestLayer& other)
83
0
  {
84
0
    HttpMessage::operator=(other);
85
86
0
    if (m_FirstLine != nullptr)
87
0
      delete m_FirstLine;
88
89
0
    m_FirstLine = new HttpRequestFirstLine(this);
90
91
0
    return *this;
92
0
  }
93
94
  std::string HttpRequestLayer::getUrl() const
95
0
  {
96
0
    HeaderField* hostField = getFieldByName(PCPP_HTTP_HOST_FIELD);
97
0
    if (hostField == nullptr)
98
0
      return m_FirstLine->getUri();
99
100
0
    return hostField->getFieldValue() + m_FirstLine->getUri();
101
0
  }
102
103
  HttpRequestLayer::~HttpRequestLayer()
104
83.6k
  {
105
83.6k
    delete m_FirstLine;
106
83.6k
  }
107
108
  std::string HttpRequestLayer::toString() const
109
32.3k
  {
110
32.3k
    static const int maxLengthToPrint = 120;
111
32.3k
    std::string result = "HTTP request, ";
112
32.3k
    int size = m_FirstLine->getSize() - 2;  // the -2 is to remove \r\n at the end of the first line
113
32.3k
    if (size <= 0)
114
0
    {
115
0
      result += std::string("CORRUPT DATA");
116
0
      return result;
117
0
    }
118
32.3k
    if (size <= maxLengthToPrint)
119
23.5k
    {
120
23.5k
      char* firstLine = new char[size + 1];
121
23.5k
      strncpy(firstLine, reinterpret_cast<char*>(m_Data), size);
122
23.5k
      firstLine[size] = 0;
123
23.5k
      result += std::string(firstLine);
124
23.5k
      delete[] firstLine;
125
23.5k
    }
126
8.75k
    else
127
8.75k
    {
128
8.75k
      char firstLine[maxLengthToPrint + 1];
129
8.75k
      strncpy(firstLine, reinterpret_cast<char*>(m_Data), maxLengthToPrint - 3);
130
8.75k
      firstLine[maxLengthToPrint - 3] = '.';
131
8.75k
      firstLine[maxLengthToPrint - 2] = '.';
132
8.75k
      firstLine[maxLengthToPrint - 1] = '.';
133
8.75k
      firstLine[maxLengthToPrint] = 0;
134
8.75k
      result += std::string(firstLine);
135
8.75k
    }
136
137
32.3k
    return result;
138
32.3k
  }
139
140
  // -------- Class HttpRequestFirstLine -----------------
141
142
  const std::string MethodEnumToString[9] = { "GET",   "HEAD",    "POST",    "PUT",  "DELETE",
143
                                            "TRACE", "OPTIONS", "CONNECT", "PATCH" };
144
145
  const std::unordered_map<std::string, HttpRequestLayer::HttpMethod> HttpMethodStringToEnum{
146
    { "GET",     HttpRequestLayer::HttpMethod::HttpGET     },
147
    { "HEAD",    HttpRequestLayer::HttpMethod::HttpHEAD    },
148
    { "POST",    HttpRequestLayer::HttpMethod::HttpPOST    },
149
    { "PUT",     HttpRequestLayer::HttpMethod::HttpPUT     },
150
    { "DELETE",  HttpRequestLayer::HttpMethod::HttpDELETE  },
151
    { "TRACE",   HttpRequestLayer::HttpMethod::HttpTRACE   },
152
    { "OPTIONS", HttpRequestLayer::HttpMethod::HttpOPTIONS },
153
    { "CONNECT", HttpRequestLayer::HttpMethod::HttpCONNECT },
154
    { "PATCH",   HttpRequestLayer::HttpMethod::HttpPATCH   }
155
  };
156
157
  const std::string VersionEnumToString[3] = { "0.9", "1.0", "1.1" };
158
159
  // clang-format off
160
  const std::unordered_map<std::string, HttpVersion> HttpVersionStringToEnum{
161
    { "0.9", HttpVersion::ZeroDotNine },
162
    { "1.0", HttpVersion::OneDotZero  },
163
    { "1.1", HttpVersion::OneDotOne   }
164
  };
165
  // clang-format on
166
83.6k
  HttpRequestFirstLine::HttpRequestFirstLine(HttpRequestLayer* httpRequest) : m_HttpRequest(httpRequest)
167
83.6k
  {
168
83.6k
    m_Method = parseMethod((char*)m_HttpRequest->m_Data, m_HttpRequest->getDataLen());
169
83.6k
    if (m_Method == HttpRequestLayer::HttpMethodUnknown)
170
0
    {
171
0
      m_UriOffset = -1;
172
0
      PCPP_LOG_DEBUG("Couldn't resolve HTTP request method");
173
0
      m_IsComplete = false;
174
0
      m_Version = HttpVersionUnknown;
175
0
      m_VersionOffset = -1;
176
0
      m_FirstLineEndOffset = m_HttpRequest->getDataLen();
177
0
      return;
178
0
    }
179
83.6k
    else
180
83.6k
    {
181
83.6k
      m_UriOffset = MethodEnumToString[m_Method].length() + 1;
182
83.6k
    }
183
184
83.6k
    parseVersion();
185
83.6k
    if (m_VersionOffset < 0)
186
18.1k
    {
187
18.1k
      m_IsComplete = false;
188
18.1k
      m_FirstLineEndOffset = m_HttpRequest->getDataLen();
189
18.1k
      return;
190
18.1k
    }
191
192
65.5k
    char* endOfFirstLine;
193
65.5k
    if ((endOfFirstLine = (char*)memchr((char*)(m_HttpRequest->m_Data + m_VersionOffset), '\n',
194
65.5k
                                        m_HttpRequest->m_DataLen - (size_t)m_VersionOffset)) != nullptr)
195
64.8k
    {
196
64.8k
      m_FirstLineEndOffset = endOfFirstLine - (char*)m_HttpRequest->m_Data + 1;
197
64.8k
      m_IsComplete = true;
198
64.8k
    }
199
738
    else
200
738
    {
201
738
      m_FirstLineEndOffset = m_HttpRequest->getDataLen();
202
738
      m_IsComplete = false;
203
738
    }
204
205
65.5k
    if (Logger::getInstance().isDebugEnabled(PacketLogModuleHttpLayer))
206
0
    {
207
0
      std::string method =
208
0
          m_Method == HttpRequestLayer::HttpMethodUnknown ? "Unknown" : MethodEnumToString[m_Method];
209
0
      PCPP_LOG_DEBUG("Method='" << method << "'; "
210
0
                                << "HTTP version='" << VersionEnumToString[m_Version] << "'; "
211
0
                                << "URI='" << getUri() << "'");
212
0
    }
213
65.5k
  }
214
215
  HttpRequestFirstLine::HttpRequestFirstLine(HttpRequestLayer* httpRequest, HttpRequestLayer::HttpMethod method,
216
                                             HttpVersion version, const std::string& uri)
217
0
  {
218
0
    try  // throw(HttpRequestFirstLineException)
219
0
    {
220
0
      if (method == HttpRequestLayer::HttpMethodUnknown)
221
0
      {
222
0
        m_Exception.setMessage("Method supplied was HttpMethodUnknown");
223
0
        throw m_Exception;
224
0
      }
225
226
0
      if (version == HttpVersionUnknown)
227
0
      {
228
0
        m_Exception.setMessage("Version supplied was HttpVersionUnknown");
229
0
        throw m_Exception;
230
0
      }
231
232
0
      m_HttpRequest = httpRequest;
233
234
0
      m_Method = method;
235
0
      m_Version = version;
236
237
0
      std::string firstLine =
238
0
          MethodEnumToString[m_Method] + " " + uri + " " + "HTTP/" + VersionEnumToString[m_Version] + "\r\n";
239
240
0
      m_UriOffset = MethodEnumToString[m_Method].length() + 1;
241
0
      m_FirstLineEndOffset = firstLine.length();
242
0
      m_VersionOffset = m_UriOffset + uri.length() + 6;
243
244
      // TODO: Alloc Data.
245
0
      m_HttpRequest->m_DataLen = firstLine.length();
246
0
      m_HttpRequest->m_Data = new uint8_t[m_HttpRequest->m_DataLen];
247
0
      memcpy(m_HttpRequest->m_Data, firstLine.c_str(), m_HttpRequest->m_DataLen);
248
249
0
      m_IsComplete = true;
250
0
    }
251
0
    catch (const HttpRequestFirstLineException&)
252
0
    {
253
0
      throw;
254
0
    }
255
0
    catch (...)
256
0
    {
257
0
      std::terminate();
258
0
    }
259
0
  }
260
261
  HttpRequestLayer::HttpMethod HttpRequestFirstLine::parseMethod(const char* data, size_t dataLen)
262
192k
  {
263
192k
    if (!data || dataLen < 4)
264
1.81k
    {
265
1.81k
      return HttpRequestLayer::HttpMethodUnknown;
266
1.81k
    }
267
268
191k
    size_t spaceIndex = 0;
269
1.80M
    while (spaceIndex < dataLen && data[spaceIndex] != ' ')
270
1.61M
    {
271
1.61M
      spaceIndex++;
272
1.61M
    }
273
274
191k
    if (spaceIndex == 0 || spaceIndex == dataLen)
275
3.88k
    {
276
3.88k
      return HttpRequestLayer::HttpMethodUnknown;
277
3.88k
    }
278
279
187k
    auto methodAdEnum = HttpMethodStringToEnum.find(std::string(data, data + spaceIndex));
280
187k
    if (methodAdEnum == HttpMethodStringToEnum.end())
281
19.8k
    {
282
19.8k
      return HttpRequestLayer::HttpMethodUnknown;
283
19.8k
    }
284
167k
    return methodAdEnum->second;
285
187k
  }
286
287
  void HttpRequestFirstLine::parseVersion()
288
83.6k
  {
289
83.6k
    char* data = (char*)(m_HttpRequest->m_Data + m_UriOffset);
290
83.6k
    char* verPos = cross_platform_memmem(data, m_HttpRequest->getDataLen() - m_UriOffset, " HTTP/", 6);
291
83.6k
    if (verPos == nullptr)
292
18.1k
    {
293
18.1k
      m_Version = HttpVersionUnknown;
294
18.1k
      m_VersionOffset = -1;
295
18.1k
      return;
296
18.1k
    }
297
298
    // verify packet doesn't end before the version, meaning still left place for " HTTP/x.y" (9 chars)
299
65.5k
    std::ptrdiff_t actualLen = verPos + 9 - (char*)m_HttpRequest->m_Data;
300
65.5k
    if (static_cast<size_t>(actualLen) > m_HttpRequest->getDataLen())
301
0
    {
302
0
      m_Version = HttpVersionUnknown;
303
0
      m_VersionOffset = -1;
304
0
      return;
305
0
    }
306
307
    // skip " HTTP/" (6 chars)
308
65.5k
    verPos += 6;
309
65.5k
    auto versionAsEnum = HttpVersionStringToEnum.find(std::string(verPos, verPos + 3));
310
65.5k
    if (versionAsEnum == HttpVersionStringToEnum.end())
311
9.99k
    {
312
9.99k
      m_Version = HttpVersionUnknown;
313
9.99k
    }
314
55.5k
    else
315
55.5k
    {
316
55.5k
      m_Version = versionAsEnum->second;
317
55.5k
    }
318
319
65.5k
    m_VersionOffset = verPos - (char*)m_HttpRequest->m_Data;
320
65.5k
  }
321
322
  bool HttpRequestFirstLine::setMethod(HttpRequestLayer::HttpMethod newMethod)
323
0
  {
324
0
    if (newMethod == HttpRequestLayer::HttpMethodUnknown)
325
0
    {
326
0
      PCPP_LOG_ERROR("Requested method is HttpMethodUnknown");
327
0
      return false;
328
0
    }
329
330
    // extend or shorten layer
331
0
    int lengthDifference = MethodEnumToString[newMethod].length() - MethodEnumToString[m_Method].length();
332
0
    if (lengthDifference > 0)
333
0
    {
334
0
      if (!m_HttpRequest->extendLayer(0, lengthDifference))
335
0
      {
336
0
        PCPP_LOG_ERROR("Cannot change layer size");
337
0
        return false;
338
0
      }
339
0
    }
340
0
    else if (lengthDifference < 0)
341
0
    {
342
0
      if (!m_HttpRequest->shortenLayer(0, 0 - lengthDifference))
343
0
      {
344
0
        PCPP_LOG_ERROR("Cannot change layer size");
345
0
        return false;
346
0
      }
347
0
    }
348
349
0
    if (lengthDifference != 0)
350
0
      m_HttpRequest->shiftFieldsOffset(m_HttpRequest->getFirstField(), lengthDifference);
351
352
0
    memcpy(m_HttpRequest->m_Data, MethodEnumToString[newMethod].c_str(), MethodEnumToString[newMethod].length());
353
354
0
    m_Method = newMethod;
355
0
    m_UriOffset += lengthDifference;
356
0
    m_VersionOffset += lengthDifference;
357
358
0
    return true;
359
0
  }
360
361
  std::string HttpRequestFirstLine::getUri() const
362
0
  {
363
0
    std::string result;
364
0
    if (m_UriOffset != -1 && m_VersionOffset != -1)
365
0
      result.assign((const char*)m_HttpRequest->m_Data + m_UriOffset, m_VersionOffset - 6 - m_UriOffset);
366
367
    // else first line is illegal, return empty string
368
369
0
    return result;
370
0
  }
371
372
  bool HttpRequestFirstLine::setUri(std::string newUri)
373
0
  {
374
    // make sure the new URI begins with "/"
375
0
    if (newUri.compare(0, 1, "/") != 0)
376
0
      newUri = "/" + newUri;
377
378
    // extend or shorten layer
379
0
    std::string currentUri = getUri();
380
0
    int lengthDifference = newUri.length() - currentUri.length();
381
0
    if (lengthDifference > 0)
382
0
    {
383
0
      if (!m_HttpRequest->extendLayer(m_UriOffset, lengthDifference))
384
0
      {
385
0
        PCPP_LOG_ERROR("Cannot change layer size");
386
0
        return false;
387
0
      }
388
0
    }
389
0
    else if (lengthDifference < 0)
390
0
    {
391
0
      if (!m_HttpRequest->shortenLayer(m_UriOffset, 0 - lengthDifference))
392
0
      {
393
0
        PCPP_LOG_ERROR("Cannot change layer size");
394
0
        return false;
395
0
      }
396
0
    }
397
398
0
    if (lengthDifference != 0)
399
0
      m_HttpRequest->shiftFieldsOffset(m_HttpRequest->getFirstField(), lengthDifference);
400
401
0
    memcpy(m_HttpRequest->m_Data + m_UriOffset, newUri.c_str(), newUri.length());
402
403
0
    m_VersionOffset += lengthDifference;
404
405
0
    return true;
406
0
  }
407
408
  void HttpRequestFirstLine::setVersion(HttpVersion newVersion)
409
0
  {
410
0
    if (m_VersionOffset == -1)
411
0
      return;
412
413
0
    if (newVersion == HttpVersionUnknown)
414
0
      return;
415
416
0
    char* verPos = (char*)(m_HttpRequest->m_Data + m_VersionOffset);
417
0
    memcpy(verPos, VersionEnumToString[newVersion].c_str(), 3);
418
419
0
    m_Version = newVersion;
420
0
  }
421
422
  // -------- Class HttpResponseLayer -----------------
423
424
  static const std::unordered_map<int, HttpResponseStatusCode> intStatusCodeMap = {
425
    { 100, HttpResponseStatusCode::Http100Continue                         },
426
    { 101, HttpResponseStatusCode::Http101SwitchingProtocols               },
427
    { 102, HttpResponseStatusCode::Http102Processing                       },
428
    { 103, HttpResponseStatusCode::Http103EarlyHints                       },
429
    { 200, HttpResponseStatusCode::Http200OK                               },
430
    { 201, HttpResponseStatusCode::Http201Created                          },
431
    { 202, HttpResponseStatusCode::Http202Accepted                         },
432
    { 203, HttpResponseStatusCode::Http203NonAuthoritativeInformation      },
433
    { 204, HttpResponseStatusCode::Http204NoContent                        },
434
    { 205, HttpResponseStatusCode::Http205ResetContent                     },
435
    { 206, HttpResponseStatusCode::Http206PartialContent                   },
436
    { 207, HttpResponseStatusCode::Http207MultiStatus                      },
437
    { 208, HttpResponseStatusCode::Http208AlreadyReported                  },
438
    { 226, HttpResponseStatusCode::Http226IMUsed                           },
439
    { 300, HttpResponseStatusCode::Http300MultipleChoices                  },
440
    { 301, HttpResponseStatusCode::Http301MovedPermanently                 },
441
    { 302, HttpResponseStatusCode::Http302                                 },
442
    { 303, HttpResponseStatusCode::Http303SeeOther                         },
443
    { 304, HttpResponseStatusCode::Http304NotModified                      },
444
    { 305, HttpResponseStatusCode::Http305UseProxy                         },
445
    { 306, HttpResponseStatusCode::Http306SwitchProxy                      },
446
    { 307, HttpResponseStatusCode::Http307TemporaryRedirect                },
447
    { 308, HttpResponseStatusCode::Http308PermanentRedirect                },
448
    { 400, HttpResponseStatusCode::Http400BadRequest                       },
449
    { 401, HttpResponseStatusCode::Http401Unauthorized                     },
450
    { 402, HttpResponseStatusCode::Http402PaymentRequired                  },
451
    { 403, HttpResponseStatusCode::Http403Forbidden                        },
452
    { 404, HttpResponseStatusCode::Http404NotFound                         },
453
    { 405, HttpResponseStatusCode::Http405MethodNotAllowed                 },
454
    { 406, HttpResponseStatusCode::Http406NotAcceptable                    },
455
    { 407, HttpResponseStatusCode::Http407ProxyAuthenticationRequired      },
456
    { 408, HttpResponseStatusCode::Http408RequestTimeout                   },
457
    { 409, HttpResponseStatusCode::Http409Conflict                         },
458
    { 410, HttpResponseStatusCode::Http410Gone                             },
459
    { 411, HttpResponseStatusCode::Http411LengthRequired                   },
460
    { 412, HttpResponseStatusCode::Http412PreconditionFailed               },
461
    { 413, HttpResponseStatusCode::Http413RequestEntityTooLarge            },
462
    { 414, HttpResponseStatusCode::Http414RequestURITooLong                },
463
    { 415, HttpResponseStatusCode::Http415UnsupportedMediaType             },
464
    { 416, HttpResponseStatusCode::Http416RequestedRangeNotSatisfiable     },
465
    { 417, HttpResponseStatusCode::Http417ExpectationFailed                },
466
    { 418, HttpResponseStatusCode::Http418ImATeapot                        },
467
    { 419, HttpResponseStatusCode::Http419AuthenticationTimeout            },
468
    { 420, HttpResponseStatusCode::Http420                                 },
469
    { 421, HttpResponseStatusCode::Http421MisdirectedRequest               },
470
    { 422, HttpResponseStatusCode::Http422UnprocessableEntity              },
471
    { 423, HttpResponseStatusCode::Http423Locked                           },
472
    { 424, HttpResponseStatusCode::Http424FailedDependency                 },
473
    { 425, HttpResponseStatusCode::Http425TooEarly                         },
474
    { 426, HttpResponseStatusCode::Http426UpgradeRequired                  },
475
    { 428, HttpResponseStatusCode::Http428PreconditionRequired             },
476
    { 429, HttpResponseStatusCode::Http429TooManyRequests                  },
477
    { 431, HttpResponseStatusCode::Http431RequestHeaderFieldsTooLarge      },
478
    { 440, HttpResponseStatusCode::Http440LoginTimeout                     },
479
    { 444, HttpResponseStatusCode::Http444NoResponse                       },
480
    { 449, HttpResponseStatusCode::Http449RetryWith                        },
481
    { 450, HttpResponseStatusCode::Http450BlockedByWindowsParentalControls },
482
    { 451, HttpResponseStatusCode::Http451                                 },
483
    { 494, HttpResponseStatusCode::Http494RequestHeaderTooLarge            },
484
    { 495, HttpResponseStatusCode::Http495CertError                        },
485
    { 496, HttpResponseStatusCode::Http496NoCert                           },
486
    { 497, HttpResponseStatusCode::Http497HTTPtoHTTPS                      },
487
    { 498, HttpResponseStatusCode::Http498TokenExpiredInvalid              },
488
    { 499, HttpResponseStatusCode::Http499                                 },
489
    { 500, HttpResponseStatusCode::Http500InternalServerError              },
490
    { 501, HttpResponseStatusCode::Http501NotImplemented                   },
491
    { 502, HttpResponseStatusCode::Http502BadGateway                       },
492
    { 503, HttpResponseStatusCode::Http503ServiceUnavailable               },
493
    { 504, HttpResponseStatusCode::Http504GatewayTimeout                   },
494
    { 505, HttpResponseStatusCode::Http505HTTPVersionNotSupported          },
495
    { 506, HttpResponseStatusCode::Http506VariantAlsoNegotiates            },
496
    { 507, HttpResponseStatusCode::Http507InsufficientStorage              },
497
    { 508, HttpResponseStatusCode::Http508LoopDetected                     },
498
    { 509, HttpResponseStatusCode::Http509BandwidthLimitExceeded           },
499
    { 510, HttpResponseStatusCode::Http510NotExtended                      },
500
    { 511, HttpResponseStatusCode::Http511NetworkAuthenticationRequired    },
501
    { 520, HttpResponseStatusCode::Http520OriginError                      },
502
    { 521, HttpResponseStatusCode::Http521WebServerIsDown                  },
503
    { 522, HttpResponseStatusCode::Http522ConnectionTimedOut               },
504
    { 523, HttpResponseStatusCode::Http523ProxyDeclinedRequest             },
505
    { 524, HttpResponseStatusCode::Http524aTimeoutOccurred                 },
506
    { 598, HttpResponseStatusCode::Http598NetworkReadTimeoutError          },
507
    { 599, HttpResponseStatusCode::Http599NetworkConnectTimeoutError       },
508
  };
509
510
  HttpResponseStatusCode::HttpResponseStatusCode(const int& statusCodeNumber, const std::string& statusMessage)
511
52.9k
  {
512
52.9k
    if (statusMessage != "")
513
52.9k
    {
514
52.9k
      m_CustomizedMessage = statusMessage;
515
52.9k
    }
516
517
52.9k
    if (intStatusCodeMap.find(statusCodeNumber) != intStatusCodeMap.end())
518
52.3k
    {
519
52.3k
      m_Value = intStatusCodeMap.at(statusCodeNumber);
520
52.3k
      return;
521
52.3k
    }
522
523
636
    if (statusCodeNumber >= 100 && statusCodeNumber <= 199)
524
185
    {
525
185
      m_Value = HttpResponseStatusCode::HttpStatus1xxCodeUnknown;
526
185
    }
527
451
    else if (statusCodeNumber >= 200 && statusCodeNumber <= 299)
528
45
    {
529
45
      m_Value = HttpResponseStatusCode::HttpStatus2xxCodeUnknown;
530
45
    }
531
406
    else if (statusCodeNumber >= 300 && statusCodeNumber <= 399)
532
0
    {
533
0
      m_Value = HttpResponseStatusCode::HttpStatus3xxCodeUnknown;
534
0
    }
535
406
    else if (statusCodeNumber >= 400 && statusCodeNumber <= 499)
536
0
    {
537
0
      m_Value = HttpResponseStatusCode::HttpStatus4xxCodeUnknown;
538
0
    }
539
406
    else if (statusCodeNumber >= 500 && statusCodeNumber <= 599)
540
0
    {
541
0
      m_Value = HttpResponseStatusCode::HttpStatus5xxCodeUnknown;
542
0
    }
543
636
  }
544
545
  /// @struct HttpResponseStatusCodeHash
546
  /// @brief The helper structure for hash HttpResponseStatusCode while using std::unordered_map
547
  struct HttpResponseStatusCodeHash
548
  {
549
    size_t operator()(const HttpResponseStatusCode& status) const
550
356
    {
551
356
      return static_cast<int>(status);
552
356
    }
553
  };
554
555
  static const std::unordered_map<HttpResponseStatusCode, std::string, HttpResponseStatusCodeHash>
556
      statusCodeExplanationStringMap = {
557
        { HttpResponseStatusCode::Http100Continue,                         "Continue"                             },
558
        { HttpResponseStatusCode::Http101SwitchingProtocols,               "Switching Protocols"                  },
559
        { HttpResponseStatusCode::Http102Processing,                       "Processing"                           },
560
        { HttpResponseStatusCode::Http103EarlyHints,                       "Early Hints"                          },
561
        { HttpResponseStatusCode::Http200OK,                               "OK"                                   },
562
        { HttpResponseStatusCode::Http201Created,                          "Created"                              },
563
        { HttpResponseStatusCode::Http202Accepted,                         "Accepted"                             },
564
        { HttpResponseStatusCode::Http203NonAuthoritativeInformation,      "Non-Authoritative Information"        },
565
        { HttpResponseStatusCode::Http204NoContent,                        "No Content"                           },
566
        { HttpResponseStatusCode::Http205ResetContent,                     "Reset Content"                        },
567
        { HttpResponseStatusCode::Http206PartialContent,                   "Partial Content"                      },
568
        { HttpResponseStatusCode::Http207MultiStatus,                      "Multi-Status"                         },
569
        { HttpResponseStatusCode::Http208AlreadyReported,                  "Already Reported"                     },
570
        { HttpResponseStatusCode::Http226IMUsed,                           "IM Used"                              },
571
        { HttpResponseStatusCode::Http300MultipleChoices,                  "Multiple Choices"                     },
572
        { HttpResponseStatusCode::Http301MovedPermanently,                 "Moved Permanently"                    },
573
        { HttpResponseStatusCode::Http302,                                 "(various messages)"                   },
574
        { HttpResponseStatusCode::Http303SeeOther,                         "See Other"                            },
575
        { HttpResponseStatusCode::Http304NotModified,                      "Not Modified"                         },
576
        { HttpResponseStatusCode::Http305UseProxy,                         "Use Proxy"                            },
577
        { HttpResponseStatusCode::Http306SwitchProxy,                      "Switch Proxy"                         },
578
        { HttpResponseStatusCode::Http307TemporaryRedirect,                "Temporary Redirect"                   },
579
        { HttpResponseStatusCode::Http308PermanentRedirect,                "Permanent Redirect"                   },
580
        { HttpResponseStatusCode::Http400BadRequest,                       "Bad Request"                          },
581
        { HttpResponseStatusCode::Http401Unauthorized,                     "Unauthorized"                         },
582
        { HttpResponseStatusCode::Http402PaymentRequired,                  "Payment Required"                     },
583
        { HttpResponseStatusCode::Http403Forbidden,                        "Forbidden"                            },
584
        { HttpResponseStatusCode::Http404NotFound,                         "Not Found"                            },
585
        { HttpResponseStatusCode::Http405MethodNotAllowed,                 "Method Not Allowed"                   },
586
        { HttpResponseStatusCode::Http406NotAcceptable,                    "Not Acceptable"                       },
587
        { HttpResponseStatusCode::Http407ProxyAuthenticationRequired,      "Proxy Authentication Required"        },
588
        { HttpResponseStatusCode::Http408RequestTimeout,                   "Request Timeout"                      },
589
        { HttpResponseStatusCode::Http409Conflict,                         "Conflict"                             },
590
        { HttpResponseStatusCode::Http410Gone,                             "Gone"                                 },
591
        { HttpResponseStatusCode::Http411LengthRequired,                   "Length Required"                      },
592
        { HttpResponseStatusCode::Http412PreconditionFailed,               "Precondition Failed"                  },
593
        { HttpResponseStatusCode::Http413RequestEntityTooLarge,            "Request Entity Too Large"             },
594
        { HttpResponseStatusCode::Http414RequestURITooLong,                "Request-URI Too Long"                 },
595
        { HttpResponseStatusCode::Http415UnsupportedMediaType,             "Unsupported Media Type"               },
596
        { HttpResponseStatusCode::Http416RequestedRangeNotSatisfiable,     "Requested Range Not Satisfiable"      },
597
        { HttpResponseStatusCode::Http417ExpectationFailed,                "Expectation Failed"                   },
598
        { HttpResponseStatusCode::Http418ImATeapot,                        "I'm a teapot"                         },
599
        { HttpResponseStatusCode::Http419AuthenticationTimeout,            "Authentication Timeout"               },
600
        { HttpResponseStatusCode::Http420,                                 "(various messages)"                   },
601
        { HttpResponseStatusCode::Http421MisdirectedRequest,               "Misdirected Request"                  },
602
        { HttpResponseStatusCode::Http422UnprocessableEntity,              "Unprocessable Entity"                 },
603
        { HttpResponseStatusCode::Http423Locked,                           "Locked"                               },
604
        { HttpResponseStatusCode::Http424FailedDependency,                 "Failed Dependency"                    },
605
        { HttpResponseStatusCode::Http425TooEarly,                         "Too Early"                            },
606
        { HttpResponseStatusCode::Http426UpgradeRequired,                  "Upgrade Required"                     },
607
        { HttpResponseStatusCode::Http428PreconditionRequired,             "Precondition Required"                },
608
        { HttpResponseStatusCode::Http429TooManyRequests,                  "Too Many Requests"                    },
609
        { HttpResponseStatusCode::Http431RequestHeaderFieldsTooLarge,      "Request Header Fields Too Large"      },
610
        { HttpResponseStatusCode::Http440LoginTimeout,                     "Login Timeout"                        },
611
        { HttpResponseStatusCode::Http444NoResponse,                       "No Response"                          },
612
        { HttpResponseStatusCode::Http449RetryWith,                        "Retry With"                           },
613
        { HttpResponseStatusCode::Http450BlockedByWindowsParentalControls, "Blocked by Windows Parental Controls" },
614
        { HttpResponseStatusCode::Http451,                                 "(various messages)"                   },
615
        { HttpResponseStatusCode::Http494RequestHeaderTooLarge,            "Request Header Too Large"             },
616
        { HttpResponseStatusCode::Http495CertError,                        "Cert Error"                           },
617
        { HttpResponseStatusCode::Http496NoCert,                           "No Cert"                              },
618
        { HttpResponseStatusCode::Http497HTTPtoHTTPS,                      "HTTP to HTTPS"                        },
619
        { HttpResponseStatusCode::Http498TokenExpiredInvalid,              "Token expired/invalid"                },
620
        { HttpResponseStatusCode::Http499,                                 "(various messages)"                   },
621
        { HttpResponseStatusCode::Http500InternalServerError,              "Internal Server Error"                },
622
        { HttpResponseStatusCode::Http501NotImplemented,                   "Not Implemented"                      },
623
        { HttpResponseStatusCode::Http502BadGateway,                       "Bad Gateway"                          },
624
        { HttpResponseStatusCode::Http503ServiceUnavailable,               "Service Unavailable"                  },
625
        { HttpResponseStatusCode::Http504GatewayTimeout,                   "Gateway Timeout"                      },
626
        { HttpResponseStatusCode::Http505HTTPVersionNotSupported,          "HTTP Version Not Supported"           },
627
        { HttpResponseStatusCode::Http506VariantAlsoNegotiates,            "Variant Also Negotiates"              },
628
        { HttpResponseStatusCode::Http507InsufficientStorage,              "Insufficient Storage"                 },
629
        { HttpResponseStatusCode::Http508LoopDetected,                     "Loop Detected"                        },
630
        { HttpResponseStatusCode::Http509BandwidthLimitExceeded,           "Bandwidth Limit Exceeded"             },
631
        { HttpResponseStatusCode::Http510NotExtended,                      "Not Extended"                         },
632
        { HttpResponseStatusCode::Http511NetworkAuthenticationRequired,    "Network Authentication Required"      },
633
        { HttpResponseStatusCode::Http520OriginError,                      "Origin Error"                         },
634
        { HttpResponseStatusCode::Http521WebServerIsDown,                  "Web server is down"                   },
635
        { HttpResponseStatusCode::Http522ConnectionTimedOut,               "Connection timed out"                 },
636
        { HttpResponseStatusCode::Http523ProxyDeclinedRequest,             "Proxy Declined Request"               },
637
        { HttpResponseStatusCode::Http524aTimeoutOccurred,                 "A timeout occurred"                   },
638
        { HttpResponseStatusCode::Http598NetworkReadTimeoutError,          "Network read timeout error"           },
639
        { HttpResponseStatusCode::Http599NetworkConnectTimeoutError,       "Network connect timeout error"        },
640
        { HttpResponseStatusCode::HttpStatus1xxCodeUnknown,                "1XX Status Code Unknown"              },
641
        { HttpResponseStatusCode::HttpStatus2xxCodeUnknown,                "2XX Status Code Unknown"              },
642
        { HttpResponseStatusCode::HttpStatus3xxCodeUnknown,                "3XX Status Code Unknown"              },
643
        { HttpResponseStatusCode::HttpStatus4xxCodeUnknown,                "4XX Status Code Unknown"              },
644
        { HttpResponseStatusCode::HttpStatus5xxCodeUnknown,                "5XX Status Code Unknown"              },
645
        { HttpResponseStatusCode::HttpStatusCodeUnknown,                   "Status Code Unknown"                  },
646
    };
647
648
  HttpResponseStatusCode::HttpResponseStatusCode(const Value& statusCode, const std::string& statusMessage)
649
0
      : m_Value(statusCode)
650
0
  {
651
0
    if (statusMessage != "")
652
0
    {
653
0
      m_CustomizedMessage = statusMessage;
654
0
    }
655
0
  }
656
657
  std::string HttpResponseStatusCode::getMessage() const
658
0
  {
659
0
    if (m_CustomizedMessage != "")
660
0
    {
661
0
      return m_CustomizedMessage;
662
0
    }
663
0
    return statusCodeExplanationStringMap.at(m_Value);
664
0
  }
665
666
  HttpResponseLayer::HttpResponseLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)
667
26.1k
      : HttpMessage(data, dataLen, prevLayer, packet, HTTPResponse)
668
26.1k
  {
669
26.1k
    m_FirstLine = new HttpResponseFirstLine(this);
670
26.1k
    m_FieldsOffset = m_FirstLine->getSize();
671
26.1k
    parseFields();
672
26.1k
  }
673
674
  HttpResponseLayer::HttpResponseLayer(HttpVersion version, const HttpResponseStatusCode& statusCode,
675
                                       const std::string& statusCodeString)
676
0
  {
677
0
    m_Protocol = HTTPResponse;
678
0
    m_FirstLine = new HttpResponseFirstLine(this, version, HttpResponseStatusCode(statusCode, statusCodeString));
679
0
    m_FieldsOffset = m_FirstLine->getSize();
680
0
  }
681
682
  HttpResponseLayer::HttpResponseLayer(HttpVersion version, const HttpResponseStatusCode& statusCode)
683
0
  {
684
0
    m_Protocol = HTTPResponse;
685
0
    m_FirstLine = new HttpResponseFirstLine(this, version, statusCode);
686
0
    m_FieldsOffset = m_FirstLine->getSize();
687
0
  }
688
689
  HttpResponseLayer::~HttpResponseLayer()
690
26.1k
  {
691
26.1k
    delete m_FirstLine;
692
26.1k
  }
693
694
0
  HttpResponseLayer::HttpResponseLayer(const HttpResponseLayer& other) : HttpMessage(other)
695
0
  {
696
0
    m_FirstLine = new HttpResponseFirstLine(this);
697
0
  }
698
699
  HttpResponseLayer& HttpResponseLayer::operator=(const HttpResponseLayer& other)
700
0
  {
701
0
    HttpMessage::operator=(other);
702
703
0
    if (m_FirstLine != nullptr)
704
0
      delete m_FirstLine;
705
706
0
    m_FirstLine = new HttpResponseFirstLine(this);
707
708
0
    return *this;
709
0
  }
710
711
  HeaderField* HttpResponseLayer::setContentLength(int contentLength, const std::string& prevFieldName)
712
0
  {
713
0
    std::ostringstream contentLengthAsString;
714
0
    contentLengthAsString << contentLength;
715
0
    std::string contentLengthFieldName(PCPP_HTTP_CONTENT_LENGTH_FIELD);
716
0
    HeaderField* contentLengthField = getFieldByName(contentLengthFieldName);
717
0
    if (contentLengthField == nullptr)
718
0
    {
719
0
      HeaderField* prevField = getFieldByName(prevFieldName);
720
0
      contentLengthField = insertField(prevField, PCPP_HTTP_CONTENT_LENGTH_FIELD, contentLengthAsString.str());
721
0
    }
722
0
    else
723
0
      contentLengthField->setFieldValue(contentLengthAsString.str());
724
725
0
    return contentLengthField;
726
0
  }
727
728
  int HttpResponseLayer::getContentLength() const
729
0
  {
730
0
    std::string contentLengthFieldName(PCPP_HTTP_CONTENT_LENGTH_FIELD);
731
0
    std::transform(contentLengthFieldName.begin(), contentLengthFieldName.end(), contentLengthFieldName.begin(),
732
0
                   ::tolower);
733
0
    HeaderField* contentLengthField = getFieldByName(contentLengthFieldName);
734
0
    if (contentLengthField != nullptr)
735
0
      return atoi(contentLengthField->getFieldValue().c_str());
736
0
    return 0;
737
0
  }
738
739
  std::string HttpResponseLayer::toString() const
740
9.52k
  {
741
9.52k
    static const int maxLengthToPrint = 120;
742
9.52k
    std::string result = "HTTP response, ";
743
9.52k
    int size = m_FirstLine->getSize() - 2;  // the -2 is to remove \r\n at the end of the first line
744
9.52k
    if (size <= maxLengthToPrint)
745
9.24k
    {
746
9.24k
      char* firstLine = new char[size + 1];
747
9.24k
      strncpy(firstLine, reinterpret_cast<char*>(m_Data), size);
748
9.24k
      firstLine[size] = 0;
749
9.24k
      result += std::string(firstLine);
750
9.24k
      delete[] firstLine;
751
9.24k
    }
752
276
    else
753
276
    {
754
276
      char firstLine[maxLengthToPrint + 1];
755
276
      strncpy(firstLine, reinterpret_cast<char*>(m_Data), maxLengthToPrint - 3);
756
276
      firstLine[maxLengthToPrint - 3] = '.';
757
276
      firstLine[maxLengthToPrint - 2] = '.';
758
276
      firstLine[maxLengthToPrint - 1] = '.';
759
276
      firstLine[maxLengthToPrint] = 0;
760
276
      result += std::string(firstLine);
761
276
    }
762
763
9.52k
    return result;
764
9.52k
  }
765
766
  // -------- Class HttpResponseFirstLine -----------------
767
768
  int HttpResponseFirstLine::getStatusCodeAsInt() const
769
0
  {
770
0
    return m_StatusCode.toInt();
771
0
  }
772
773
  std::string HttpResponseFirstLine::getStatusCodeString() const
774
0
  {
775
0
    if (!m_StatusCode.isUnsupportedCode())
776
0
    {
777
0
      return m_StatusCode.getMessage();
778
0
    }
779
780
    // else first line is illegal, return empty string
781
0
    return "";
782
0
  }
783
784
  bool HttpResponseFirstLine::setStatusCode(const HttpResponseStatusCode& newStatusCode,
785
                                            const std::string& statusCodeString)
786
0
  {
787
0
    return setStatusCode(HttpResponseStatusCode(newStatusCode, statusCodeString));
788
0
  }
789
790
  bool HttpResponseFirstLine::setStatusCode(const HttpResponseStatusCode& newStatusCode)
791
0
  {
792
0
    if (newStatusCode.isUnsupportedCode())
793
0
    {
794
0
      PCPP_LOG_ERROR("Requested status code is " << newStatusCode.toString() << ":"
795
0
                                                 << statusCodeExplanationStringMap.at(newStatusCode));
796
0
      return false;
797
0
    }
798
799
    // extend or shorten layer
800
801
0
    size_t statusStringOffset = 13;
802
0
    auto newStatusCodeMessage = newStatusCode.getMessage();
803
804
0
    int lengthDifference = newStatusCodeMessage.length() - getStatusCodeString().length();
805
0
    if (lengthDifference > 0)
806
0
    {
807
0
      if (!m_HttpResponse->extendLayer(statusStringOffset, lengthDifference))
808
0
      {
809
0
        PCPP_LOG_ERROR("Cannot change layer size");
810
0
        return false;
811
0
      }
812
0
    }
813
0
    else if (lengthDifference < 0)
814
0
    {
815
0
      if (!m_HttpResponse->shortenLayer(statusStringOffset, 0 - lengthDifference))
816
0
      {
817
0
        PCPP_LOG_ERROR("Cannot change layer size");
818
0
        return false;
819
0
      }
820
0
    }
821
822
0
    if (lengthDifference != 0)
823
0
      m_HttpResponse->shiftFieldsOffset(m_HttpResponse->getFirstField(), lengthDifference);
824
825
    // copy status string
826
0
    memcpy(m_HttpResponse->m_Data + statusStringOffset, newStatusCodeMessage.c_str(),
827
0
           newStatusCodeMessage.length());
828
829
    // change status code
830
0
    memcpy(m_HttpResponse->m_Data + 9, newStatusCode.toString().c_str(), 3);
831
832
0
    m_StatusCode = newStatusCode;
833
834
0
    m_FirstLineEndOffset += lengthDifference;
835
836
0
    return true;
837
0
  }
838
839
  void HttpResponseFirstLine::setVersion(HttpVersion newVersion)
840
0
  {
841
0
    if (newVersion == HttpVersionUnknown)
842
0
      return;
843
844
0
    char* verPos = (char*)(m_HttpResponse->m_Data + 5);
845
0
    memcpy(verPos, VersionEnumToString[newVersion].c_str(), 3);
846
847
0
    m_Version = newVersion;
848
0
  }
849
850
  HttpResponseStatusCode HttpResponseFirstLine::parseStatusCode(const char* data, size_t dataLen)
851
53.3k
  {
852
    // minimum data should be 12B long: "HTTP/x.y XXX"
853
53.3k
    if (!data || dataLen < 12)
854
75
    {
855
75
      return HttpResponseStatusCode::HttpStatusCodeUnknown;
856
75
    }
857
858
53.2k
    const std::string codeString = std::string(data + 9, 3);
859
860
53.2k
    if (codeString.empty() || (std::find_if(codeString.begin(), codeString.end(),
861
159k
                                            [](unsigned char c) { return !std::isdigit(c); }) != codeString.end()))
862
178
    {
863
178
      return HttpResponseStatusCode::HttpStatusCodeUnknown;
864
178
    }
865
866
53.0k
    constexpr size_t messageOffset = 13;  // expect "HTTP/x.y XXX YYY", YYY starts from 13
867
53.0k
    size_t offset = messageOffset;
868
53.0k
    bool isMessageFound = false;
869
562k
    while (offset < dataLen)
870
562k
    {
871
562k
      if (data[offset] == '\n')
872
53.0k
      {
873
53.0k
        isMessageFound = true;
874
53.0k
        break;
875
53.0k
      }
876
509k
      offset++;
877
509k
    }
878
879
53.0k
    if (!isMessageFound)
880
26
    {
881
26
      return HttpResponseStatusCode::HttpStatusCodeUnknown;
882
26
    }
883
884
53.0k
    std::string messageString(data + messageOffset, offset - messageOffset);
885
53.0k
    if (!messageString.empty() && messageString.back() == '\r')
886
50.4k
    {
887
50.4k
      messageString.pop_back();
888
50.4k
    }
889
53.0k
    if (messageString.empty())
890
66
    {
891
66
      return HttpResponseStatusCode::HttpStatusCodeUnknown;
892
66
    }
893
894
52.9k
    return HttpResponseStatusCode(std::stoi(codeString), messageString);
895
53.0k
  }
896
897
26.1k
  HttpResponseFirstLine::HttpResponseFirstLine(HttpResponseLayer* httpResponse) : m_HttpResponse(httpResponse)
898
26.1k
  {
899
26.1k
    m_Version = parseVersion((char*)m_HttpResponse->m_Data, m_HttpResponse->getDataLen());
900
26.1k
    if (m_Version == HttpVersionUnknown)
901
0
    {
902
0
      m_StatusCode = HttpResponseStatusCode::HttpStatusCodeUnknown;
903
0
    }
904
26.1k
    else
905
26.1k
    {
906
26.1k
      m_StatusCode = parseStatusCode((char*)m_HttpResponse->m_Data, m_HttpResponse->getDataLen());
907
26.1k
    }
908
909
26.1k
    char* endOfFirstLine;
910
26.1k
    if ((endOfFirstLine = (char*)memchr((char*)(m_HttpResponse->m_Data), '\n', m_HttpResponse->m_DataLen)) !=
911
26.1k
        nullptr)
912
26.1k
    {
913
26.1k
      m_FirstLineEndOffset = endOfFirstLine - (char*)m_HttpResponse->m_Data + 1;
914
26.1k
      m_IsComplete = true;
915
26.1k
    }
916
0
    else
917
0
    {
918
0
      m_FirstLineEndOffset = m_HttpResponse->getDataLen();
919
0
      m_IsComplete = false;
920
0
    }
921
922
26.1k
    if (Logger::getInstance().isDebugEnabled(PacketLogModuleHttpLayer))
923
0
    {
924
0
      std::string version = (m_Version == HttpVersionUnknown ? "Unknown" : VersionEnumToString[m_Version]);
925
0
      int statusCode = (m_StatusCode == HttpResponseStatusCode::HttpStatusCodeUnknown ? 0 : m_StatusCode.toInt());
926
0
      PCPP_LOG_DEBUG("Version='" << version << "'; Status code=" << statusCode << " '" << getStatusCodeString()
927
0
                                 << "'");
928
0
    }
929
26.1k
  }
930
931
  HttpResponseFirstLine::HttpResponseFirstLine(HttpResponseLayer* httpResponse, HttpVersion version,
932
                                               const HttpResponseStatusCode& statusCode)
933
0
  {
934
0
    if (statusCode.isUnsupportedCode())
935
0
    {
936
0
      m_Exception.setMessage("Status code supplied was " + statusCodeExplanationStringMap.at(statusCode));
937
0
      throw m_Exception;
938
0
    }
939
940
0
    if (version == HttpVersionUnknown)
941
0
    {
942
0
      m_Exception.setMessage("Version supplied was HttpVersionUnknown");
943
0
      throw m_Exception;
944
0
    }
945
946
0
    m_HttpResponse = httpResponse;
947
948
0
    m_StatusCode = statusCode;
949
0
    m_Version = version;
950
951
0
    std::string firstLine = "HTTP/" + VersionEnumToString[m_Version] + " " + m_StatusCode.toString() + " " +
952
0
                            m_StatusCode.getMessage() + "\r\n";
953
954
0
    m_FirstLineEndOffset = firstLine.length();
955
956
    // TODO: Alloc Data.
957
0
    m_HttpResponse->m_DataLen = firstLine.length();
958
0
    m_HttpResponse->m_Data = new uint8_t[m_HttpResponse->m_DataLen];
959
0
    memcpy(m_HttpResponse->m_Data, firstLine.c_str(), m_HttpResponse->m_DataLen);
960
961
0
    m_IsComplete = true;
962
0
  }
963
964
  HttpVersion HttpResponseFirstLine::parseVersion(const char* data, size_t dataLen)
965
90.2k
  {
966
90.2k
    if (!data || dataLen < 8)  // "HTTP/x.y"
967
316
    {
968
316
      PCPP_LOG_DEBUG("HTTP response length < 8, cannot identify version");
969
316
      return HttpVersionUnknown;
970
316
    }
971
972
89.8k
    if (data[0] != 'H' || data[1] != 'T' || data[2] != 'T' || data[3] != 'P' || data[4] != '/')
973
35.8k
    {
974
35.8k
      PCPP_LOG_DEBUG("HTTP response does not begin with 'HTTP/'");
975
35.8k
      return HttpVersionUnknown;
976
35.8k
    }
977
978
54.0k
    const char* verPos = data + 5;
979
54.0k
    auto versionAsEnum = HttpVersionStringToEnum.find(std::string(verPos, verPos + 3));
980
54.0k
    if (versionAsEnum == HttpVersionStringToEnum.end())
981
698
    {
982
698
      return HttpVersionUnknown;
983
698
    }
984
53.3k
    return versionAsEnum->second;
985
54.0k
  }
986
}  // namespace pcpp