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

PowerDNS / pdns / 12321902803

13 Dec 2024 07:34PM UTC coverage: 66.359% (+1.6%) from 64.78%
12321902803

Pull #14970

github

web-flow
Merge e3a7df61c into 3dfd8e317
Pull Request #14970: boost > std optional

26084 of 54744 branches covered (47.65%)

Branch coverage included in aggregate %.

14 of 15 new or added lines in 2 files covered. (93.33%)

1863 existing lines in 52 files now uncovered.

85857 of 113946 relevant lines covered (75.35%)

4412729.59 hits per line

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

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

14
  bool isspace(char c, const std::locale& loc) {
33,996✔
15
    return std::isspace(c, loc);
33,996✔
16
  }
33,996✔
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) {
2,097✔
44
    buffer.append(somedata);
2,097✔
45
    while(state < 2) {
15,310!
46
      int cr=0;
15,099✔
47
      pos = buffer.find_first_of("\n");
15,099✔
48
      // need to find CRLF in buffer
49
      if (pos == std::string::npos) return false;
15,099!
50
      if (pos>0 && buffer[pos-1]=='\r')
15,094!
51
        cr=1;
15,094✔
52
      std::string line(buffer.begin(), buffer.begin()+pos-cr); // exclude CRLF
15,094✔
53
      buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer including CRLF
15,094✔
54

55
      if (state == 0) { // startup line
15,094✔
56
        if (target->kind == YAHTTP_TYPE_REQUEST) {
1,881!
57
          std::string ver;
1,609✔
58
          std::string tmpurl;
1,609✔
59
          std::istringstream iss(line);
1,609✔
60
          iss >> target->method >> tmpurl >> ver;
1,609✔
61
          if (ver.size() == 0)
1,609!
62
            target->version = 9;
×
63
          else if (ver.find("HTTP/0.9") == 0)
1,609!
64
            target->version = 9;
×
65
          else if (ver.find("HTTP/1.0") == 0)
1,609!
66
            target->version = 10;
5✔
67
          else if (ver.find("HTTP/1.1") == 0)
1,604!
68
            target->version = 11;
1,604✔
69
          else
×
70
            throw ParseError("HTTP version not supported");
×
71
          // uppercase the target method
72
          std::transform(target->method.begin(), target->method.end(), target->method.begin(), ::toupper);
1,609✔
73
          target->url.parse(tmpurl);
1,609✔
74
          target->getvars = Utility::parseUrlParameters(target->url.parameters);
1,609✔
75
          state = 1;
1,609✔
76
        } else if(target->kind == YAHTTP_TYPE_RESPONSE) {
1,881!
77
          std::string ver;
272✔
78
          std::istringstream iss(line);
272✔
79
          std::string::size_type pos1;
272✔
80
          iss >> ver >> target->status;
272✔
81
          std::getline(iss, target->statusText);
272✔
82
          pos1=0;
272✔
83
          while(pos1 < target->statusText.size() && YaHTTP::isspace(target->statusText.at(pos1))) pos1++;
544!
84
          target->statusText = target->statusText.substr(pos1); 
272✔
85
          if ((pos1 = target->statusText.find("\r")) != std::string::npos) {
272!
86
            target->statusText = target->statusText.substr(0, pos1-1);
×
87
          }
×
88
          if (ver.size() == 0) {
272!
89
            target->version = 9;
×
90
          } else if (ver.find("HTTP/0.9") == 0)
272!
91
            target->version = 9;
×
92
          else if (ver.find("HTTP/1.0") == 0)
272!
93
            target->version = 10;
272✔
94
          else if (ver.find("HTTP/1.1") == 0)
×
95
            target->version = 11;
×
96
          else
×
97
            throw ParseError("HTTP version not supported");
×
98
          state = 1;
272✔
99
        }
272✔
100
      } else if (state == 1) {
13,213!
101
        std::string key,value;
13,213✔
102
        size_t pos1;
13,213✔
103
        if (line.empty()) {
13,213✔
104
          chunked = (target->headers.find("transfer-encoding") != target->headers.end() && target->headers["transfer-encoding"] == "chunked");
1,881!
105
          state = 2;
1,881✔
106
          break;
1,881✔
107
        }
1,881✔
108
        // split headers
109
        if ((pos1 = line.find(":")) == std::string::npos) {
11,332!
110
          throw ParseError("Malformed header line");
×
111
        }
×
112
        key = line.substr(0, pos1);
11,332✔
113
        value = line.substr(pos1 + 1);
11,332✔
114
        for(std::string::iterator it=key.begin(); it != key.end(); it++)
120,279✔
115
          if (YaHTTP::isspace(*it))
108,947!
116
            throw ParseError("Header key contains whitespace which is not allowed by RFC");
×
117

118
        Utility::trim(value);
11,332✔
119
        std::transform(key.begin(), key.end(), key.begin(), ::tolower);
11,332✔
120
        // is it already defined
121

122
        if (key == "set-cookie" && target->kind == YAHTTP_TYPE_RESPONSE) {
11,332!
123
          target->jar.parseSetCookieHeader(value);
×
124
        } else if (key == "cookie" && target->kind == YAHTTP_TYPE_REQUEST) {
11,332!
125
          target->jar.parseCookieHeader(value);
×
126
        } else {
11,332✔
127
          if (key == "host" && target->kind == YAHTTP_TYPE_REQUEST) {
11,332!
128
            // maybe it contains port?
129
            if ((pos1 = value.find(":")) == std::string::npos) {
1,604!
130
              target->url.host = value;
×
131
            } else {
1,604✔
132
              target->url.host = value.substr(0, pos1);
1,604✔
133
              target->url.port = ::atoi(value.substr(pos1).c_str());
1,604✔
134
            }
1,604✔
135
          }
1,604✔
136
          if (target->headers.find(key) != target->headers.end()) {
11,332!
137
            target->headers[key] = target->headers[key] + ";" + value;
×
138
          } else {
11,332✔
139
            target->headers[key] = std::move(value);
11,332✔
140
          }
11,332✔
141
        }
11,332✔
142
      }
11,332✔
143
    }
15,094✔
144

145
    minbody = 0;
2,092✔
146
    // check for expected body size
147
    if (target->kind == YAHTTP_TYPE_REQUEST) maxbody = target->max_request_size;
2,092!
148
    else if (target->kind == YAHTTP_TYPE_RESPONSE) maxbody = target->max_response_size;
483!
149
    else maxbody = 0;
×
150

151
    if (!chunked) {
2,092!
152
      if (target->headers.find("content-length") != target->headers.end()) {
2,092!
153
        std::istringstream maxbodyS(target->headers["content-length"]);
1,614✔
154
        maxbodyS >> minbody;
1,614✔
155
        maxbody = minbody;
1,614✔
156
      }
1,614✔
157
      if (minbody < 1) return true; // guess there isn't anything left.
2,092!
158
      if (target->kind == YAHTTP_TYPE_REQUEST && static_cast<ssize_t>(minbody) > target->max_request_size) throw ParseError("Max request body size exceeded");
1,509!
159
      else if (target->kind == YAHTTP_TYPE_RESPONSE && static_cast<ssize_t>(minbody) > target->max_response_size) throw ParseError("Max response body size exceeded");
1,509!
160
    }
1,509✔
161

162
    if (maxbody == 0) hasBody = false;
1,509!
163
    else hasBody = true;
1,509✔
164

165
    if (buffer.size() == 0) return ready();
1,509!
166

167
    while(buffer.size() > 0) {
2,596✔
168
      if (chunked) {
1,298!
169
        if (chunk_size == 0) {
×
170
          char buf[100];
×
171
          // read chunk length
172
          if ((pos = buffer.find('\n')) == std::string::npos) return false;
×
173
          if (pos > 99)
×
174
            throw ParseError("Impossible chunk_size");
×
175
          buffer.copy(buf, pos);
×
176
          buf[pos]=0; // just in case...
×
177
          buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer
×
178
          if (sscanf(buf, "%x", &chunk_size) != 1) {
×
179
            throw ParseError("Unable to parse chunk size");
×
180
          }
×
181
          if (chunk_size == 0) { state = 3; break; } // last chunk
×
182
          if (chunk_size > (std::numeric_limits<decltype(chunk_size)>::max() - 2)) {
×
183
            throw ParseError("Chunk is too large");
×
184
          }
×
185
        } else {
×
186
          int crlf=1;
×
187
          if (buffer.size() < static_cast<size_t>(chunk_size+1)) return false; // expect newline
×
188
          if (buffer.at(chunk_size) == '\r') {
×
189
            if (buffer.size() < static_cast<size_t>(chunk_size+2) || buffer.at(chunk_size+1) != '\n') return false; // expect newline after carriage return
×
190
            crlf=2;
×
191
          } else if (buffer.at(chunk_size) != '\n') return false;
×
192
          std::string tmp = buffer.substr(0, chunk_size);
×
193
          buffer.erase(buffer.begin(), buffer.begin()+chunk_size+crlf);
×
194
          bodybuf << tmp;
×
195
          chunk_size = 0;
×
196
          if (buffer.size() == 0) break; // just in case
×
197
        }
×
198
      } else {
1,298✔
199
        if (bodybuf.str().length() + buffer.length() > maxbody)
1,298!
200
          bodybuf << buffer.substr(0, maxbody - bodybuf.str().length());
×
201
        else
1,298✔
202
          bodybuf << buffer;
1,298✔
203
        buffer = "";
1,298✔
204
      }
1,298✔
205
    }
1,298✔
206

207
    if (chunk_size!=0) return false; // need more data
1,298!
208

209
    return ready();
1,298✔
210
  };
1,298✔
211

212
  void HTTPBase::write(std::ostream& os) const {
1,903✔
213
    if (kind == YAHTTP_TYPE_REQUEST) {
1,903✔
214
      std::ostringstream getparmbuf;
272✔
215
      std::string getparms;
272✔
216
      // prepare URL
217
      for(strstr_map_t::const_iterator i = getvars.begin(); i != getvars.end(); i++) {
273✔
218
        getparmbuf << Utility::encodeURL(i->first, false) << "=" << Utility::encodeURL(i->second, false) << "&";
1✔
219
      }
1✔
220
      if (getparmbuf.str().length() > 0) {
272✔
221
        std::string buf = getparmbuf.str();
1✔
222
        getparms = "?" + std::string(buf.begin(), buf.end() - 1);
1✔
223
      }
1✔
224
      else
271✔
225
        getparms = "";
271✔
226
      os << method << " " << url.path << getparms << " HTTP/" << versionStr(this->version);
272✔
227
    } else if (kind == YAHTTP_TYPE_RESPONSE) {
1,903!
228
      os << "HTTP/" << versionStr(this->version) << " " << status << " ";
1,631✔
229
      if (statusText.empty())
1,631!
230
        os << Utility::status2text(status);
1,631✔
231
      else
×
232
        os << statusText;
×
233
    }
1,631✔
234
    os << "\r\n";
1,903✔
235

236
    bool cookieSent = false;
1,903✔
237
    bool sendChunked = false;
1,903✔
238

239
    if (this->version > 10) { // 1.1 or better
1,903!
240
      if (headers.find("content-length") == headers.end() && !this->is_multipart) {
1,903!
241
        // must use chunked on response
242
        sendChunked = (kind == YAHTTP_TYPE_RESPONSE);
151✔
243
        if ((headers.find("transfer-encoding") != headers.end() && headers.find("transfer-encoding")->second != "chunked")) {
151!
244
          throw YaHTTP::Error("Transfer-encoding must be chunked, or Content-Length defined");
×
245
        }
×
246
        if ((headers.find("transfer-encoding") == headers.end() && kind == YAHTTP_TYPE_RESPONSE)) {
151!
UNCOV
247
          sendChunked = true;
×
UNCOV
248
          os << "Transfer-Encoding: chunked\r\n";
×
UNCOV
249
        }
×
250
      } else {
1,879✔
251
        sendChunked = false;
1,752✔
252
      }
1,752✔
253
    }
1,903✔
254

255
    // write headers
256
    strstr_map_t::const_iterator iter = headers.begin();
1,903✔
257
    while(iter != headers.end()) {
17,386✔
258
      if (iter->first == "host" && (kind != YAHTTP_TYPE_REQUEST || version < 10)) { iter++; continue; }
15,483!
259
      if (iter->first == "transfer-encoding" && sendChunked) { iter++; continue; }
15,483!
260
      std::string header = Utility::camelizeHeader(iter->first);
15,483✔
261
      if (header == "Cookie" || header == "Set-Cookie") cookieSent = true;
15,483!
262
      os << Utility::camelizeHeader(iter->first) << ": " << iter->second << "\r\n";
15,483✔
263
      iter++;
15,483✔
264
    }
15,483✔
265
    if (version > 9 && !cookieSent && jar.cookies.size() > 0) { // write cookies
1,903!
266
     if (kind == YAHTTP_TYPE_REQUEST) {
×
267
        bool first = true;
×
268
        os << "Cookie: ";
×
269
        for(strcookie_map_t::const_iterator i = jar.cookies.begin(); i != jar.cookies.end(); i++) {
×
270
          if (first)
×
271
            first = false;
×
272
          else
×
273
            os << "; ";
×
274
          os << Utility::encodeURL(i->second.name) << "=" << Utility::encodeURL(i->second.value);
×
275
        }
×
276
     } else if (kind == YAHTTP_TYPE_RESPONSE) {
×
277
        for(strcookie_map_t::const_iterator i = jar.cookies.begin(); i != jar.cookies.end(); i++) {
×
278
          os << "Set-Cookie: ";
×
279
          os << i->second.str() << "\r\n";
×
280
        }
×
281
      }
×
282
    }
×
283
    os << "\r\n";
1,903✔
284
#ifdef HAVE_CPP_FUNC_PTR
1,903✔
285
    this->renderer(this, os, sendChunked);
1,903✔
286
#else
287
    SendbodyRenderer r;
288
    r(this, os, chunked)
289
#endif
290
  };
1,903✔
291

UNCOV
292
  std::ostream& operator<<(std::ostream& os, const Response &resp) {
×
UNCOV
293
    resp.write(os);
×
UNCOV
294
    return os;
×
UNCOV
295
  };
×
296

297
  std::istream& operator>>(std::istream& is, Response &resp) {
×
298
    YaHTTP::AsyncResponseLoader arl;
×
299
    arl.initialize(&resp);
×
300
    while(is.good()) {
×
301
      char buf[1024];
×
302
      is.read(buf, 1024);
×
303
      if (is.gcount()>0) { // did we actually read anything
×
304
        is.clear();
×
305
        if (arl.feed(std::string(buf, is.gcount())) == true) break; // completed
×
306
      }
×
307
    }
×
308
    // throw unless ready
309
    if (arl.ready() == false)
×
310
      throw ParseError("Was not able to extract a valid Response from stream");
×
311
    arl.finalize();
×
312
    return is;
×
313
  };
×
314

315
  std::ostream& operator<<(std::ostream& os, const Request &req) {
272✔
316
    req.write(os);
272✔
317
    return os;
272✔
318
  };
272✔
319

320
  std::istream& operator>>(std::istream& is, Request &req) {
×
321
    YaHTTP::AsyncRequestLoader arl;
×
322
    arl.initialize(&req);
×
323
    while(is.good()) {
×
324
      char buf[1024];
×
325
      is.read(buf, 1024);
×
326
      if (is.gcount() > 0) { // did we actually read anything
×
327
        is.clear();
×
328
        if (arl.feed(std::string(buf, is.gcount())) == true) break; // completed
×
329
      }
×
330
    }
×
331
    if (arl.ready() == false)
×
332
      throw ParseError("Was not able to extract a valid Request from stream");
×
333
    arl.finalize();
×
334
    return is;
×
335
  };
×
336
};
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

© 2025 Coveralls, Inc