• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Razakhel / RaZ / 22284637621

22 Feb 2026 10:51AM UTC coverage: 74.25% (-0.2%) from 74.442%
22284637621

push

github

Razakhel
[Network/HttpClient] Added an HTTP client class

- It internally uses the TCP client and can only handle GET requests for now

9 of 42 new or added lines in 3 files covered. (21.43%)

8587 of 11565 relevant lines covered (74.25%)

1709.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

12.12
/src/RaZ/Network/HttpClient.cpp
1
#include "RaZ/Network/HttpClient.hpp"
2
#include "RaZ/Utils/Logger.hpp"
3

4
#include <algorithm>
5
#include <charconv>
6
#include <ranges>
7

8
namespace Raz {
9

10
namespace {
11

12
struct HttpResponse {
13
  unsigned short statusCode {};
14
  std::size_t contentLength {};
15
  bool isChunked = false;
16
  std::string location;
17
};
18

NEW
19
HttpResponse parseResponse(std::string_view response) {
×
NEW
20
  HttpResponse httpResponse {};
×
21

22
  // The last 4 characters (\r\n\r\n) are ignored to avoid getting two final empty lines
NEW
23
  for (const auto line : std::views::split(response.substr(0, response.size() - 4), std::string_view("\r\n"))) {
×
NEW
24
    const std::string_view header(line.data(), line.size());
×
25

NEW
26
    if (header.starts_with("HTTP"))
×
NEW
27
      std::from_chars(header.data() + 9, header.data() + 12, httpResponse.statusCode);
×
NEW
28
    else if (header.starts_with("Content-Length"))
×
NEW
29
      std::from_chars(header.data() + 15, header.data() + header.size(), httpResponse.contentLength);
×
NEW
30
    else if (header.starts_with("Transfer-Encoding") && header.find("chunked") != std::string::npos)
×
NEW
31
      httpResponse.isChunked = true;
×
NEW
32
    else if (header.starts_with("Location"))
×
NEW
33
      httpResponse.location = header.substr(10);
×
34
  }
35

NEW
36
  return httpResponse;
×
NEW
37
}
×
38

39
} // namespace
40

41
void HttpClient::connect(std::string host) {
3✔
42
  m_host = std::move(host);
3✔
43
  m_tcpClient.connect(m_host, 80);
3✔
44
}
1✔
45

NEW
46
std::string HttpClient::get(std::string_view resource) {
×
NEW
47
  Logger::debug("[HttpClient] Sending GET '{}'...", resource);
×
48

NEW
49
  const std::string request = std::format("GET {} HTTP/1.1\r\nHost: {}\r\n\r\n", resource, m_host);
×
NEW
50
  m_tcpClient.send(request);
×
51

NEW
52
  const std::string headers = m_tcpClient.receiveUntil("\r\n\r\n", true);
×
NEW
53
  const auto [statusCode, contentLength, isChunked, location] = parseResponse(headers);
×
54

NEW
55
  if (statusCode == 301 || statusCode == 302 || statusCode == 303 || statusCode == 307 || statusCode == 308)
×
NEW
56
    return get(location); // TODO: redirections can happen indefinitely in some cases
×
57

NEW
58
  if (contentLength > 0)
×
NEW
59
    return m_tcpClient.receiveExactly(contentLength);
×
60

NEW
61
  if (isChunked) // TODO: properly handle chunked data
×
NEW
62
    return m_tcpClient.receiveUntil("\r\n\r\n");
×
63

NEW
64
  Logger::error("[HttpClient] Failed to get resource '{}': unsupported response method", resource);
×
NEW
65
  return {};
×
NEW
66
}
×
67

68
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc