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

PowerDNS / pdns / 25497210653

07 May 2026 12:57PM UTC coverage: 63.049% (+0.03%) from 63.018%
25497210653

push

github

web-flow
Merge pull request #17296 from miodvallat/backport-17284-to-auth-5.0.x

auth 5.0: backport "use less inefficient code in web server"

13319 of 27934 branches covered (47.68%)

Branch coverage included in aggregate %.

9 of 14 new or added lines in 2 files covered. (64.29%)

20 existing lines in 6 files now uncovered.

42506 of 60608 relevant lines covered (70.13%)

6977428.41 hits per line

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

45.24
/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) {
111,028✔
11
    return std::isspace(c) != 0;
111,028✔
12
  }
111,028✔
13

14
  bool isspace(char c, const std::locale& loc) {
34,368✔
15
    return std::isspace(c, loc);
34,368✔
16
  }
34,368✔
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) {
8,682✔
35
    return std::isalnum(c) != 0;
8,682✔
36
  }
8,682✔
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
  {
2,146✔
45
    if (state < 2) {
2,146!
46
      headersize += somedata.length(); // maye include some body data, we don't know yet...
1,900✔
47
      if (headersize > target->max_header_size) {
1,900!
48
        if (target->kind == YAHTTP_TYPE_REQUEST) {
×
49
          throw ParseError("Request header too large");
×
50
        }
×
51
        else {
×
52
          throw ParseError("Response header too large");
×
53
        }
×
54
      }
×
55
    }
1,900✔
56
    buffer.append(somedata);
2,146✔
57
    while(state < 2) {
15,498!
58
      int cr=0;
15,252✔
59
      pos = buffer.find_first_of("\n");
15,252✔
60
      // need to find CRLF in buffer
61
      if (pos == std::string::npos) return false;
15,252!
62
      if (pos>0 && buffer[pos-1]=='\r')
15,248!
63
        cr=1;
15,248✔
64
      std::string line(buffer.begin(), buffer.begin()+pos-cr); // exclude CRLF
15,248✔
65
      buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer including CRLF
15,248✔
66

67
      if (state == 0) { // startup line
15,248✔
68
        if (target->kind == YAHTTP_TYPE_REQUEST) {
1,896!
69
          std::string ver;
1,624✔
70
          std::string tmpurl;
1,624✔
71
          std::istringstream iss(line);
1,624✔
72
          iss >> target->method >> tmpurl >> ver;
1,624✔
73
          if (ver.size() == 0)
1,624!
74
            target->version = 9;
×
75
          else if (ver.find("HTTP/0.9") == 0)
1,624!
76
            target->version = 9;
×
77
          else if (ver.find("HTTP/1.0") == 0)
1,624!
78
            target->version = 10;
4✔
79
          else if (ver.find("HTTP/1.1") == 0)
1,620!
80
            target->version = 11;
1,620✔
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);
1,624✔
85
          target->url.parse(tmpurl);
1,624✔
86
          target->getvars = Utility::parseUrlParameters(target->url.parameters);
1,624✔
87
          state = 1;
1,624✔
88
        } else if(target->kind == YAHTTP_TYPE_RESPONSE) {
1,896!
89
          std::string ver;
272✔
90
          std::istringstream iss(line);
272✔
91
          std::string::size_type pos1;
272✔
92
          iss >> ver >> target->status;
272✔
93
          std::getline(iss, target->statusText);
272✔
94
          pos1=0;
272✔
95
          while(pos1 < target->statusText.size() && YaHTTP::isspace(target->statusText.at(pos1))) pos1++;
544!
96
          target->statusText = target->statusText.substr(pos1); 
272✔
97
          if ((pos1 = target->statusText.find("\r")) != std::string::npos) {
272!
98
            target->statusText = target->statusText.substr(0, pos1-1);
×
99
          }
×
100
          if (ver.size() == 0) {
272!
101
            target->version = 9;
×
102
          } else if (ver.find("HTTP/0.9") == 0)
272!
103
            target->version = 9;
×
104
          else if (ver.find("HTTP/1.0") == 0)
272!
105
            target->version = 10;
272✔
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;
272✔
111
        }
272✔
112
      } else if (state == 1) {
13,352!
113
        std::string key,value;
13,352✔
114
        size_t pos1;
13,352✔
115
        if (line.empty()) {
13,352✔
116
          chunked = (target->headers.find("transfer-encoding") != target->headers.end() && target->headers["transfer-encoding"] == "chunked");
1,896!
117
          state = 2;
1,896✔
118
          break;
1,896✔
119
        }
1,896✔
120
        // split headers
121
        if ((pos1 = line.find(":")) == std::string::npos) {
11,456!
122
          throw ParseError("Malformed header line");
×
123
        }
×
124
        key = line.substr(0, pos1);
11,456✔
125
        value = line.substr(pos1 + 1);
11,456✔
126
        for(std::string::iterator it=key.begin(); it != key.end(); it++)
121,940✔
127
          if (YaHTTP::isspace(*it))
110,484!
128
            throw ParseError("Header key contains whitespace which is not allowed by RFC");
×
129

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

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

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

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

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

177
    if (buffer.size() == 0) return ready();
1,611!
178

179
    while(buffer.size() > 0) {
2,730✔
180
      if (chunked) {
1,365!
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
×
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
×
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;
×
199
          if (buffer.size() < chunk_size+1) return false; // expect newline
×
200
          if (buffer.at(chunk_size) == '\r') {
×
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 (bodysize + chunk_size > maxbody) {
×
205
            throw ParseError("Chunked body is too large");
×
206
          }
×
207
          std::string tmp = buffer.substr(0, chunk_size);
×
208
          buffer.erase(buffer.begin(), buffer.begin()+chunk_size+crlf);
×
209
          bodybuf << tmp;
×
NEW
210
          bodysize += chunk_size;
×
211
          chunk_size = 0;
×
212
          if (buffer.size() == 0) break; // just in case
×
213
        }
×
214
      } else {
1,365✔
215
        if (bodysize + buffer.length() > maxbody) {
1,365!
216
          bodybuf << buffer.substr(0, maxbody - bodybuf.str().length());
×
NEW
217
          bodysize = maxbody;
×
NEW
218
        }
×
219
        else {
1,365✔
220
          bodybuf << buffer;
1,365✔
221
          bodysize += buffer.length();
1,365✔
222
        }
1,365✔
223
        buffer = "";
1,365✔
224
      }
1,365✔
225
    }
1,365✔
226

227
    if (chunk_size!=0) return false; // need more data
1,365!
228

229
    return ready();
1,365✔
230
  };
1,365✔
231

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

256
    bool cookieSent = false;
1,896✔
257
    bool sendChunked = false;
1,896✔
258

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

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

312
  std::ostream& operator<<(std::ostream& os, const Response &resp) {
×
313
    resp.write(os);
×
314
    return os;
×
315
  };
×
316

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

335
  std::ostream& operator<<(std::ostream& os, const Request &req) {
272✔
336
    req.write(os);
272✔
337
    return os;
272✔
338
  };
272✔
339

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