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

PowerDNS / pdns / 24771673775

22 Apr 2026 09:46AM UTC coverage: 55.756% (+0.007%) from 55.749%
24771673775

push

github

web-flow
Merge pull request #17187 from rgacogne/dnsdist-2.0.4-to-be

dnsdist-2.0.x: Backports for SA-2026-04

18789 of 57522 branches covered (32.66%)

Branch coverage included in aggregate %.

62 of 109 new or added lines in 14 files covered. (56.88%)

16 existing lines in 6 files now uncovered.

52597 of 70511 relevant lines covered (74.59%)

2253166.42 hits per line

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

31.13
/ext/yahttp/yahttp/reqresp.cpp
1
#include "yahttp.hpp"
2

3
#include <limits>
4

5
namespace YaHTTP {
6

7
  template class AsyncLoader<Request>;
8
  template class AsyncLoader<Response>;
9

10
  bool isspace(char c) {
18,933✔
11
    return std::isspace(c) != 0;
18,933✔
12
  }
18,933✔
13

14
  bool isspace(char c, const std::locale& loc) {
6,261✔
15
    return std::isspace(c, loc);
6,261✔
16
  }
6,261✔
17

18
  bool isxdigit(char c) {
×
19
    return std::isxdigit(c) != 0;
×
20
  }
×
21

22
  bool isxdigit(char c, const std::locale& loc) {
×
23
    return std::isxdigit(c, loc);
×
24
  }
×
25

26
  bool isdigit(char c) {
×
27
    return std::isdigit(c) != 0;
×
28
  }
×
29

30
  bool isdigit(char c, const std::locale& loc) {
×
31
    return std::isdigit(c, loc);
×
32
  }
×
33

34
  bool isalnum(char c) {
×
35
    return std::isalnum(c) != 0;
×
36
  }
×
37

38
  bool isalnum(char c, const std::locale& loc) {
×
39
    return std::isalnum(c, loc);
×
40
  }
×
41

42
  template <class T>
43
  bool AsyncLoader<T>::feed(const std::string& somedata)
44
  {
347✔
45
    if (state < 2) {
347!
46
      headersize += somedata.length(); // maye include some body data, we don't know yet...
347✔
47
      if (headersize > target->max_header_size) {
347!
NEW
48
        if (target->kind == YAHTTP_TYPE_REQUEST) {
×
NEW
49
          throw ParseError("Request header too large");
×
NEW
50
        }
×
NEW
51
        else {
×
NEW
52
          throw ParseError("Response header too large");
×
NEW
53
        }
×
NEW
54
      }
×
55
    }
347✔
56
    buffer.append(somedata);
347✔
57
    while(state < 2) {
2,781!
58
      int cr=0;
2,781✔
59
      pos = buffer.find_first_of("\n");
2,781✔
60
      // need to find CRLF in buffer
61
      if (pos == std::string::npos) return false;
2,781!
62
      if (pos>0 && buffer[pos-1]=='\r')
2,781!
63
        cr=1;
2,781✔
64
      std::string line(buffer.begin(), buffer.begin()+pos-cr); // exclude CRLF
2,781✔
65
      buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer including CRLF
2,781✔
66

67
      if (state == 0) { // startup line
2,781!
68
        if (target->kind == YAHTTP_TYPE_REQUEST) {
347!
69
          std::string ver;
347✔
70
          std::string tmpurl;
347✔
71
          std::istringstream iss(line);
347✔
72
          iss >> target->method >> tmpurl >> ver;
347✔
73
          if (ver.size() == 0)
347!
74
            target->version = 9;
×
75
          else if (ver.find("HTTP/0.9") == 0)
347!
76
            target->version = 9;
×
77
          else if (ver.find("HTTP/1.0") == 0)
347!
78
            target->version = 10;
×
79
          else if (ver.find("HTTP/1.1") == 0)
347!
80
            target->version = 11;
347✔
81
          else
×
82
            throw ParseError("HTTP version not supported");
×
83
          // uppercase the target method
84
          std::transform(target->method.begin(), target->method.end(), target->method.begin(), ::toupper);
347✔
85
          target->url.parse(tmpurl);
347✔
86
          target->getvars = Utility::parseUrlParameters(target->url.parameters);
347✔
87
          state = 1;
347✔
88
        } else if(target->kind == YAHTTP_TYPE_RESPONSE) {
347!
89
          std::string ver;
×
90
          std::istringstream iss(line);
×
91
          std::string::size_type pos1;
×
92
          iss >> ver >> target->status;
×
93
          std::getline(iss, target->statusText);
×
94
          pos1=0;
×
95
          while(pos1 < target->statusText.size() && YaHTTP::isspace(target->statusText.at(pos1))) pos1++;
×
96
          target->statusText = target->statusText.substr(pos1); 
×
97
          if ((pos1 = target->statusText.find("\r")) != std::string::npos) {
×
98
            target->statusText = target->statusText.substr(0, pos1-1);
×
99
          }
×
100
          if (ver.size() == 0) {
×
101
            target->version = 9;
×
102
          } else if (ver.find("HTTP/0.9") == 0)
×
103
            target->version = 9;
×
104
          else if (ver.find("HTTP/1.0") == 0)
×
105
            target->version = 10;
×
106
          else if (ver.find("HTTP/1.1") == 0)
×
107
            target->version = 11;
×
108
          else
×
109
            throw ParseError("HTTP version not supported");
×
110
          state = 1;
×
111
        }
×
112
      } else if (state == 1) {
2,434!
113
        std::string key,value;
2,434✔
114
        size_t pos1;
2,434✔
115
        if (line.empty()) {
2,434!
116
          chunked = (target->headers.find("transfer-encoding") != target->headers.end() && target->headers["transfer-encoding"] == "chunked");
347!
117
          state = 2;
347✔
118
          break;
347✔
119
        }
347✔
120
        // split headers
121
        if ((pos1 = line.find(":")) == std::string::npos) {
2,087!
122
          throw ParseError("Malformed header line");
×
123
        }
×
124
        key = line.substr(0, pos1);
2,087✔
125
        value = line.substr(pos1 + 1);
2,087✔
126
        for(std::string::iterator it=key.begin(); it != key.end(); it++)
21,020!
127
          if (YaHTTP::isspace(*it))
18,933!
128
            throw ParseError("Header key contains whitespace which is not allowed by RFC");
×
129

130
        Utility::trim(value);
2,087✔
131
        std::transform(key.begin(), key.end(), key.begin(), ::tolower);
2,087✔
132
        // is it already defined
133

134
        if (key == "set-cookie" && target->kind == YAHTTP_TYPE_RESPONSE) {
2,087!
135
          target->jar.parseSetCookieHeader(value);
×
136
        } else if (key == "cookie" && target->kind == YAHTTP_TYPE_REQUEST) {
2,087!
137
          target->jar.parseCookieHeader(value);
×
138
        } else {
2,087✔
139
          if (key == "host" && target->kind == YAHTTP_TYPE_REQUEST) {
2,087!
140
            // maybe it contains port?
141
            if ((pos1 = value.find(":")) == std::string::npos) {
347!
142
              target->url.host = value;
×
143
            } else {
347✔
144
              target->url.host = value.substr(0, pos1);
347✔
145
              target->url.port = ::atoi(value.substr(pos1).c_str());
347✔
146
            }
347✔
147
          }
347✔
148
          if (target->headers.find(key) != target->headers.end()) {
2,087!
149
            target->headers[key] = target->headers[key] + ";" + value;
×
150
          } else {
2,087✔
151
            target->headers[key] = std::move(value);
2,087✔
152
          }
2,087✔
153
        }
2,087✔
154
      }
2,087✔
155
    }
2,781✔
156

157
    minbody = 0;
347✔
158
    // check for expected body size
159
    if (target->kind == YAHTTP_TYPE_REQUEST) maxbody = target->max_request_size;
347!
160
    else if (target->kind == YAHTTP_TYPE_RESPONSE) maxbody = target->max_response_size;
×
161
    else maxbody = 0;
×
162

163
    if (!chunked) {
347!
164
      if (target->headers.find("content-length") != target->headers.end()) {
347!
165
        std::istringstream maxbodyS(target->headers["content-length"]);
11✔
166
        maxbodyS >> minbody;
11✔
167
        maxbody = minbody;
11✔
168
      }
11✔
169
      if (minbody < 1) return true; // guess there isn't anything left.
347!
170
      if (target->kind == YAHTTP_TYPE_REQUEST && minbody > target->max_request_size) throw ParseError("Max request body size exceeded");
2!
171
      else if (target->kind == YAHTTP_TYPE_RESPONSE && minbody > target->max_response_size) throw ParseError("Max response body size exceeded");
2!
172
    }
2✔
173

174
    if (maxbody == 0) hasBody = false;
2!
175
    else hasBody = true;
2✔
176

177
    if (buffer.size() == 0) return ready();
2!
178

179
    while(buffer.size() > 0) {
4!
180
      if (chunked) {
2!
181
        if (chunk_size == 0) {
×
182
          char buf[100];
×
183
          // read chunk length
184
          if ((pos = buffer.find('\n')) == std::string::npos) return false;
×
185
          if (pos > 99)
×
186
            throw ParseError("Impossible chunk_size");
×
187
          buffer.copy(buf, pos);
×
188
          buf[pos]=0; // just in case...
×
189
          buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer
×
NEW
190
          if (sscanf(buf, "%zx", &chunk_size) != 1) {
×
191
            throw ParseError("Unable to parse chunk size");
×
192
          }
×
193
          if (chunk_size == 0) { state = 3; break; } // last chunk
×
NEW
194
          if (chunk_size > (std::numeric_limits<decltype(chunk_size)>::max() - 2) || chunk_size > maxbody) {
×
195
            throw ParseError("Chunk is too large");
×
196
          }
×
197
        } else {
×
198
          int crlf=1;
×
NEW
199
          if (buffer.size() < chunk_size+1) return false; // expect newline
×
200
          if (buffer.at(chunk_size) == '\r') {
×
NEW
201
            if (buffer.size() < chunk_size+2 || buffer.at(chunk_size+1) != '\n') return false; // expect newline after carriage return
×
202
            crlf=2;
×
203
          } else if (buffer.at(chunk_size) != '\n') return false;
×
NEW
204
          if (bodybuf.str().length() + chunk_size > maxbody) {
×
NEW
205
            throw ParseError("Chunked body is too large");
×
NEW
206
          }
×
207
          std::string tmp = buffer.substr(0, chunk_size);
×
208
          buffer.erase(buffer.begin(), buffer.begin()+chunk_size+crlf);
×
209
          bodybuf << tmp;
×
210
          chunk_size = 0;
×
211
          if (buffer.size() == 0) break; // just in case
×
212
        }
×
213
      } else {
2✔
214
        if (bodybuf.str().length() + buffer.length() > maxbody)
2!
215
          bodybuf << buffer.substr(0, maxbody - bodybuf.str().length());
×
216
        else
2✔
217
          bodybuf << buffer;
2✔
218
        buffer = "";
2✔
219
      }
2✔
220
    }
2✔
221

222
    if (chunk_size!=0) return false; // need more data
2!
223

224
    return ready();
2✔
225
  };
2✔
226

227
  void HTTPBase::write(std::ostream& os) const {
360✔
228
    if (kind == YAHTTP_TYPE_REQUEST) {
360!
229
      std::ostringstream getparmbuf;
×
230
      std::string getparms;
×
231
      // prepare URL
232
      for(strstr_map_t::const_iterator i = getvars.begin(); i != getvars.end(); i++) {
×
233
        getparmbuf << Utility::encodeURL(i->first, false) << "=" << Utility::encodeURL(i->second, false) << "&";
×
234
      }
×
235
      if (getparmbuf.str().length() > 0) {
×
236
        std::string buf = getparmbuf.str();
×
237
        getparms = "?" + std::string(buf.begin(), buf.end() - 1);
×
238
      }
×
239
      else
×
240
        getparms = "";
×
241
      os << method << " " << url.path << getparms << " HTTP/" << versionStr(this->version);
×
242
    } else if (kind == YAHTTP_TYPE_RESPONSE) {
360!
243
      os << "HTTP/" << versionStr(this->version) << " " << status << " ";
360✔
244
      if (statusText.empty())
360!
245
        os << Utility::status2text(status);
360✔
246
      else
×
247
        os << statusText;
×
248
    }
360✔
249
    os << "\r\n";
360✔
250

251
    bool cookieSent = false;
360✔
252
    bool sendChunked = false;
360✔
253

254
    if (this->version > 10) { // 1.1 or better
360!
255
      if (headers.find("content-length") == headers.end() && !this->is_multipart) {
360!
256
        // must use chunked on response
257
        sendChunked = (kind == YAHTTP_TYPE_RESPONSE);
360✔
258
        if ((headers.find("transfer-encoding") != headers.end() && headers.find("transfer-encoding")->second != "chunked")) {
360!
259
          throw YaHTTP::Error("Transfer-encoding must be chunked, or Content-Length defined");
×
260
        }
×
261
        if ((headers.find("transfer-encoding") == headers.end() && kind == YAHTTP_TYPE_RESPONSE)) {
360!
262
          sendChunked = true;
360✔
263
          os << "Transfer-Encoding: chunked\r\n";
360✔
264
        }
360✔
265
      } else {
360✔
266
        sendChunked = false;
×
267
      }
×
268
    }
360✔
269

270
    // write headers
271
    strstr_map_t::const_iterator iter = headers.begin();
360✔
272
    while(iter != headers.end()) {
2,872✔
273
      if (iter->first == "host" && (kind != YAHTTP_TYPE_REQUEST || version < 10)) { iter++; continue; }
2,512!
274
      if (iter->first == "transfer-encoding" && sendChunked) { iter++; continue; }
2,512!
275
      std::string header = Utility::camelizeHeader(iter->first);
2,512✔
276
      if (header == "Cookie" || header == "Set-Cookie") cookieSent = true;
2,512!
277
      os << Utility::camelizeHeader(iter->first) << ": " << iter->second << "\r\n";
2,512✔
278
      iter++;
2,512✔
279
    }
2,512✔
280
    if (version > 9 && !cookieSent && jar.cookies.size() > 0) { // write cookies
360!
281
     if (kind == YAHTTP_TYPE_REQUEST) {
×
282
        bool first = true;
×
283
        os << "Cookie: ";
×
284
        for(strcookie_map_t::const_iterator i = jar.cookies.begin(); i != jar.cookies.end(); i++) {
×
285
          if (first)
×
286
            first = false;
×
287
          else
×
288
            os << "; ";
×
289
          os << Utility::encodeURL(i->second.name) << "=" << Utility::encodeURL(i->second.value);
×
290
        }
×
291
     } else if (kind == YAHTTP_TYPE_RESPONSE) {
×
292
        for(strcookie_map_t::const_iterator i = jar.cookies.begin(); i != jar.cookies.end(); i++) {
×
293
          os << "Set-Cookie: ";
×
294
          os << i->second.str() << "\r\n";
×
295
        }
×
296
      }
×
297
    }
×
298
    os << "\r\n";
360✔
299
#ifdef HAVE_CPP_FUNC_PTR
360✔
300
    this->renderer(this, os, sendChunked);
360✔
301
#else
302
    SendbodyRenderer r;
303
    r(this, os, chunked)
304
#endif
305
  };
360✔
306

307
  std::ostream& operator<<(std::ostream& os, const Response &resp) {
360✔
308
    resp.write(os);
360✔
309
    return os;
360✔
310
  };
360✔
311

312
  std::istream& operator>>(std::istream& is, Response &resp) {
×
313
    YaHTTP::AsyncResponseLoader arl;
×
314
    arl.initialize(&resp);
×
315
    while(is.good()) {
×
316
      char buf[1024];
×
317
      is.read(buf, 1024);
×
318
      if (is.gcount()>0) { // did we actually read anything
×
319
        is.clear();
×
320
        if (arl.feed(std::string(buf, is.gcount())) == true) break; // completed
×
321
      }
×
322
    }
×
323
    // throw unless ready
324
    if (arl.ready() == false)
×
325
      throw ParseError("Was not able to extract a valid Response from stream");
×
326
    arl.finalize();
×
327
    return is;
×
328
  };
×
329

330
  std::ostream& operator<<(std::ostream& os, const Request &req) {
×
331
    req.write(os);
×
332
    return os;
×
333
  };
×
334

335
  std::istream& operator>>(std::istream& is, Request &req) {
×
336
    YaHTTP::AsyncRequestLoader arl;
×
337
    arl.initialize(&req);
×
338
    while(is.good()) {
×
339
      char buf[1024];
×
340
      is.read(buf, 1024);
×
341
      if (is.gcount() > 0) { // did we actually read anything
×
342
        is.clear();
×
343
        if (arl.feed(std::string(buf, is.gcount())) == true) break; // completed
×
344
      }
×
345
    }
×
346
    if (arl.ready() == false)
×
347
      throw ParseError("Was not able to extract a valid Request from stream");
×
348
    arl.finalize();
×
349
    return is;
×
350
  };
×
351
};
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