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

llnl / dftracer-utils / 29087613398

10 Jul 2026 10:51AM UTC coverage: 52.665% (+0.2%) from 52.475%
29087613398

Pull #92

github

web-flow
Merge f2c60f488 into 20c268ff5
Pull Request #92: feat: interactive web trace viewer for dftracer_server

39238 of 96516 branches covered (40.65%)

Branch coverage included in aggregate %.

1569 of 2002 new or added lines in 10 files covered. (78.37%)

3 existing lines in 3 files now uncovered.

34868 of 44197 relevant lines covered (78.89%)

21266.26 hits per line

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

60.07
/src/dftracer/utils/server/router.cpp
1
#include <dftracer/utils/server/http_request.h>
2
#include <dftracer/utils/server/http_response.h>
3
#include <dftracer/utils/server/router.h>
4

5
#include <algorithm>
6
#include <charconv>
7
#include <cstdlib>
8

9
namespace dftracer::utils::server {
10

11
// ============================================================================
12
// QueryParams
13
// ============================================================================
14

15
static std::string url_decode(std::string_view sv) {
260✔
16
    std::string result;
260✔
17
    result.reserve(sv.size());
260!
18
    for (std::size_t i = 0; i < sv.size(); ++i) {
1,766✔
19
        if (sv[i] == '%' && i + 2 < sv.size()) {
1,506!
20
            char hex[3] = {sv[i + 1], sv[i + 2], '\0'};
122✔
21
            char* end = nullptr;
122✔
22
            unsigned long val = std::strtoul(hex, &end, 16);
122!
23
            if (end == hex + 2) {
122!
24
                result.push_back(static_cast<char>(val));
122!
25
                i += 2;
122✔
26
                continue;
122✔
27
            }
28
        } else if (sv[i] == '+') {
1,384✔
29
            result.push_back(' ');
2!
30
            continue;
2✔
31
        }
32
        result.push_back(sv[i]);
1,382!
33
    }
712✔
34
    return result;
260✔
35
}
130!
36

37
QueryParams QueryParams::parse(std::string_view query) {
88✔
38
    QueryParams params;
88✔
39
    while (!query.empty()) {
222✔
40
        auto amp = query.find('&');
134✔
41
        auto pair = query.substr(0, amp);
134!
42
        query = (amp == std::string_view::npos) ? std::string_view{}
172✔
43
                                                : query.substr(amp + 1);
76!
44

45
        auto eq = pair.find('=');
134✔
46
        if (eq == std::string_view::npos) {
134✔
47
            params.params_.emplace_back(url_decode(pair), "");
8!
48
        } else {
4✔
49
            params.params_.emplace_back(url_decode(pair.substr(0, eq)),
189!
50
                                        url_decode(pair.substr(eq + 1)));
189!
51
        }
52
    }
53
    return params;
88✔
54
}
44!
55

56
std::string_view QueryParams::get(std::string_view key,
458✔
57
                                  std::string_view default_value) const {
58
    for (const auto& [k, v] : params_) {
1,482✔
59
        if (k == key) return v;
1,138✔
60
    }
61
    return default_value;
344✔
62
}
229✔
63

64
bool QueryParams::has(std::string_view key) const {
80✔
65
    for (const auto& [k, v] : params_) {
136✔
66
        if (k == key) return true;
130✔
67
    }
68
    return false;
6✔
69
}
40✔
70

71
int QueryParams::get_int(std::string_view key, int default_value) const {
50✔
72
    auto sv = get(key);
50!
73
    if (sv.empty()) return default_value;
50✔
74
    int val = default_value;
24✔
75
    std::from_chars(sv.data(), sv.data() + sv.size(), val);
24!
76
    return val;
24✔
77
}
25✔
78

79
double QueryParams::get_double(std::string_view key,
110✔
80
                               double default_value) const {
81
    auto sv = get(key);
110!
82
    if (sv.empty()) return default_value;
110✔
83
    // std::from_chars for doubles not universally available in C++17
84
    // compilers. Use strtod as fallback.
85
    char* end = nullptr;
54✔
86
    std::string tmp(sv);
54!
87
    double val = std::strtod(tmp.c_str(), &end);
54!
88
    if (end == tmp.c_str()) return default_value;
54✔
89
    return val;
52✔
90
}
82✔
91

92
// ============================================================================
93
// Router
94
// ============================================================================
95

96
void Router::get(const std::string& path, RouteHandler handler) {
8✔
97
    routes_.push_back(Route{"GET", path, std::move(handler), {}});
8!
98
}
8✔
99

100
void Router::get(const std::string& path, RouteHandler handler, RouteDoc doc) {
64✔
101
    routes_.push_back(Route{"GET", path, std::move(handler), std::move(doc)});
64!
102
}
64✔
103

104
void Router::post(const std::string& path, RouteHandler handler) {
×
NEW
105
    routes_.push_back(Route{"POST", path, std::move(handler), {}});
×
NEW
106
}
×
107

NEW
108
void Router::post(const std::string& path, RouteHandler handler, RouteDoc doc) {
×
NEW
109
    routes_.push_back(Route{"POST", path, std::move(handler), std::move(doc)});
×
UNCOV
110
}
×
111

112
coro::CoroTask<HttpResponse> Router::handle(const HttpRequest& req) {
162!
113
    // Split path and query string.
114
    auto path = req.path;
27✔
115
    std::string_view query_str;
27✔
116
    auto qpos = path.find('?');
27✔
117
    if (qpos != std::string_view::npos) {
27✔
118
        query_str = path.substr(qpos + 1);
13!
119
        path = path.substr(0, qpos);
13!
120
    }
13✔
121

122
    auto params = QueryParams::parse(query_str);
27!
123

124
    // Optional access token: accept ?token= or "Authorization: Bearer <token>".
125
    if (!auth_token_.empty()) {
27!
126
        bool ok = params.get("token") == auth_token_;
×
127
        if (!ok) {
×
128
            auto h = req.header("Authorization");
×
129
            constexpr std::string_view BEARER = "Bearer ";
130
            if (h.size() > BEARER.size() &&
×
131
                h.substr(0, BEARER.size()) == BEARER)
×
132
                ok = h.substr(BEARER.size()) == auth_token_;
×
133
        }
134
        if (!ok) {
×
135
            co_return HttpResponse{.status_code = 401,
27!
136
                                   .status_text = "Unauthorized",
×
137
                                   .headers = {{"Content-Type", "text/plain"}},
×
138
                                   .body = "Unauthorized"};
×
139
        }
140
    }
×
141

142
    // Match routes (exact prefix match).
143
    for (const auto& route : routes_) {
290✔
144
        if (req.method == route.method && path == route.path) {
263✔
145
            co_return co_await route.handler(req, params);
104!
146
        }
147
    }
211✔
148

149
    // Try prefix matches for parameterized routes
150
    // (e.g., "/api/v1/files/:file/info" matches "/api/v1/files/foo/info")
151
    // For now, use exact match only — parameterized routes can be added later.
152

153
    co_return HttpResponse::not_found();
1!
154
}
133!
155

156
}  // namespace dftracer::utils::server
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc