Coverage Report

Created: 2025-07-18 07:14

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