Coverage Report

Created: 2026-09-07 06:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pdns/ext/yahttp/yahttp/reqresp.cpp
Line
Count
Source
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
1.11M
  bool isspace(char c) {
11
1.11M
    return std::isspace(c) != 0;
12
1.11M
  }
13
14
19.3k
  bool isspace(char c, const std::locale& loc) {
15
19.3k
    return std::isspace(c, loc);
16
19.3k
  }
17
18
0
  bool isxdigit(char c) {
19
0
    return std::isxdigit(c) != 0;
20
0
  }
21
22
0
  bool isxdigit(char c, const std::locale& loc) {
23
0
    return std::isxdigit(c, loc);
24
0
  }
25
26
0
  bool isdigit(char c) {
27
0
    return std::isdigit(c) != 0;
28
0
  }
29
30
0
  bool isdigit(char c, const std::locale& loc) {
31
0
    return std::isdigit(c, loc);
32
0
  }
33
34
0
  bool isalnum(char c) {
35
0
    return std::isalnum(c) != 0;
36
0
  }
37
38
0
  bool isalnum(char c, const std::locale& loc) {
39
0
    return std::isalnum(c, loc);
40
0
  }
41
42
  template <class T>
43
  bool AsyncLoader<T>::feed(const std::string& somedata)
44
3.91k
  {
45
3.91k
    if (state < 2) {
46
3.91k
      headersize += somedata.length(); // maye include some body data, we don't know yet...
47
3.91k
      if (headersize > target->max_header_size) {
48
45
        if (target->kind == YAHTTP_TYPE_REQUEST) {
49
45
          throw ParseError("Request header too large");
50
45
        }
51
0
        else {
52
0
          throw ParseError("Response header too large");
53
0
        }
54
45
      }
55
3.91k
    }
56
3.86k
    buffer.append(somedata);
57
34.9k
    while(state < 2) {
58
34.8k
      int cr=0;
59
34.8k
      pos = buffer.find_first_of("\n");
60
      // need to find CRLF in buffer
61
34.8k
      if (pos == std::string::npos) return false;
62
32.1k
      if (pos>0 && buffer[pos-1]=='\r')
63
213
        cr=1;
64
32.1k
      std::string line(buffer.begin(), buffer.begin()+pos-cr); // exclude CRLF
65
32.1k
      buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer including CRLF
66
67
32.1k
      if (state == 0) { // startup line
68
3.85k
        if (target->kind == YAHTTP_TYPE_REQUEST) {
69
3.85k
          std::string ver;
70
3.85k
          std::string tmpurl;
71
3.85k
          std::istringstream iss(line);
72
3.85k
          iss >> target->method >> tmpurl >> ver;
73
3.85k
          if (ver.size() == 0)
74
3.78k
            target->version = 9;
75
71
          else if (ver.find("HTTP/0.9") == 0)
76
1
            target->version = 9;
77
70
          else if (ver.find("HTTP/1.0") == 0)
78
2
            target->version = 10;
79
68
          else if (ver.find("HTTP/1.1") == 0)
80
2
            target->version = 11;
81
66
          else
82
66
            throw ParseError("HTTP version not supported");
83
          // uppercase the target method
84
3.78k
          std::transform(target->method.begin(), target->method.end(), target->method.begin(), ::toupper);
85
3.78k
          target->url.parse(tmpurl);
86
3.78k
          target->getvars = Utility::parseUrlParameters(target->url.parameters);
87
3.78k
          state = 1;
88
3.78k
        } else if(target->kind == YAHTTP_TYPE_RESPONSE) {
89
0
          std::string ver;
90
0
          std::istringstream iss(line);
91
0
          std::string::size_type pos1;
92
0
          iss >> ver >> target->status;
93
0
          std::getline(iss, target->statusText);
94
0
          pos1=0;
95
0
          while(pos1 < target->statusText.size() && YaHTTP::isspace(target->statusText.at(pos1))) pos1++;
96
0
          target->statusText = target->statusText.substr(pos1); 
97
0
          if ((pos1 = target->statusText.find("\r")) != std::string::npos) {
98
0
            target->statusText = target->statusText.substr(0, pos1-1);
99
0
          }
100
0
          if (ver.size() == 0) {
101
0
            target->version = 9;
102
0
          } else if (ver.find("HTTP/0.9") == 0)
103
0
            target->version = 9;
104
0
          else if (ver.find("HTTP/1.0") == 0)
105
0
            target->version = 10;
106
0
          else if (ver.find("HTTP/1.1") == 0)
107
0
            target->version = 11;
108
0
          else
109
0
            throw ParseError("HTTP version not supported");
110
0
          state = 1;
111
0
        }
112
28.3k
      } else if (state == 1) {
113
28.3k
        std::string key,value;
114
28.3k
        size_t pos1;
115
28.3k
        if (line.empty()) {
116
1.00k
          chunked = (target->headers.find("transfer-encoding") != target->headers.end() && target->headers["transfer-encoding"] == "chunked");
117
1.00k
          state = 2;
118
1.00k
          break;
119
1.00k
        }
120
        // split headers
121
27.3k
        if ((pos1 = line.find(':')) == std::string::npos) {
122
33
          throw ParseError("Malformed header line");
123
33
        }
124
27.3k
        key = line.substr(0, pos1);
125
27.3k
        value = line.substr(pos1 + 1);
126
1.14M
        for(std::string::iterator it=key.begin(); it != key.end(); it++)
127
1.11M
          if (YaHTTP::isspace(*it))
128
16
            throw ParseError("Header key contains whitespace which is not allowed by RFC");
129
130
27.2k
        Utility::trim(value);
131
27.2k
        std::transform(key.begin(), key.end(), key.begin(), ::tolower);
132
        // is it already defined
133
134
27.2k
        if (key == "set-cookie" && target->kind == YAHTTP_TYPE_RESPONSE) {
135
0
          target->jar.parseSetCookieHeader(value);
136
27.2k
        } else if (key == "cookie" && target->kind == YAHTTP_TYPE_REQUEST) {
137
2.36k
          target->jar.parseCookieHeader(value);
138
24.9k
        } else {
139
24.9k
          if (key == "host" && target->kind == YAHTTP_TYPE_REQUEST) {
140
            // maybe it contains port?
141
2.23k
            if ((pos1 = value.find(':')) == std::string::npos) {
142
446
              target->url.host = value;
143
1.78k
            } else {
144
1.78k
              target->url.host = value.substr(0, pos1);
145
1.78k
              target->url.port = ::atoi(value.substr(pos1).c_str());
146
1.78k
            }
147
2.23k
          }
148
24.9k
          if (target->headers.find(key) != target->headers.end()) {
149
12.4k
            target->headers[key] = target->headers[key] + ";" + value;
150
12.4k
          } else {
151
12.4k
            target->headers[key] = std::move(value);
152
12.4k
          }
153
24.9k
        }
154
27.2k
      }
155
32.1k
    }
156
157
1.11k
    minbody = 0;
158
    // check for expected body size
159
1.11k
    if (target->kind == YAHTTP_TYPE_REQUEST) maxbody = target->max_request_size;
160
117
    else if (target->kind == YAHTTP_TYPE_RESPONSE) maxbody = target->max_response_size;
161
117
    else maxbody = 0;
162
163
1.11k
    if (!chunked) {
164
575
      if (target->headers.find("content-length") != target->headers.end()) {
165
368
        std::istringstream maxbodyS(target->headers["content-length"]);
166
368
        maxbodyS >> minbody;
167
368
        maxbody = minbody;
168
368
      }
169
575
      if (minbody < 1) return true; // guess there isn't anything left.
170
361
      if (target->kind == YAHTTP_TYPE_REQUEST && minbody > target->max_request_size) throw ParseError("Max request body size exceeded");
171
219
      else if (target->kind == YAHTTP_TYPE_RESPONSE && minbody > target->max_response_size) throw ParseError("Max response body size exceeded");
172
361
    }
173
174
763
    if (maxbody == 0) hasBody = false;
175
763
    else hasBody = true;
176
177
763
    if (buffer.size() == 0) return ready();
178
179
4.26k
    while(buffer.size() > 0) {
180
3.91k
      if (chunked) {
181
3.72k
        if (chunk_size == 0) {
182
2.00k
          char buf[100];
183
          // read chunk length
184
2.00k
          if ((pos = buffer.find('\n')) == std::string::npos) {
185
41
            if (buffer.size() > 99) {
186
12
              throw ParseError("Nonsensical chunk_size");
187
12
            }
188
29
            return false;
189
41
          }
190
1.96k
          if (pos > 99)
191
21
            throw ParseError("Impossible chunk_size");
192
1.94k
          buffer.copy(buf, pos);
193
1.94k
          buf[pos]=0; // just in case...
194
1.94k
          buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer
195
1.94k
          if (sscanf(buf, "%zx", &chunk_size) != 1) {
196
24
            throw ParseError("Unable to parse chunk size");
197
24
          }
198
1.91k
          if (chunk_size == 0) { state = 3; break; } // last chunk
199
1.90k
          if (chunk_size > (std::numeric_limits<decltype(chunk_size)>::max() - 2) || chunk_size > maxbody) {
200
154
            throw ParseError("Chunk is too large");
201
154
          }
202
1.90k
        } else {
203
1.72k
          int crlf=1;
204
1.72k
          if (buffer.size() < chunk_size+1) return false; // expect newline
205
1.68k
          if (buffer.at(chunk_size) == '\r') {
206
387
            if (buffer.size() < chunk_size+2 || buffer.at(chunk_size+1) != '\n') return false; // expect newline after carriage return
207
365
            crlf=2;
208
1.29k
          } else if (buffer.at(chunk_size) != '\n') return false;
209
1.64k
          if (bodysize + chunk_size > maxbody) {
210
0
            throw ParseError("Chunked body is too large");
211
0
          }
212
1.64k
          std::string tmp = buffer.substr(0, chunk_size);
213
1.64k
          buffer.erase(buffer.begin(), buffer.begin()+chunk_size+crlf);
214
1.64k
          bodybuf << tmp;
215
1.64k
          bodysize += chunk_size;
216
1.64k
          chunk_size = 0;
217
1.64k
          if (buffer.size() == 0) break; // just in case
218
1.64k
        }
219
3.72k
      } else {
220
195
        if (bodysize + buffer.length() > maxbody) {
221
92
          bodybuf << buffer.substr(0, maxbody - bodybuf.str().length());
222
92
          bodysize = maxbody;
223
92
        }
224
103
        else {
225
103
          bodybuf << buffer;
226
103
          bodysize += buffer.length();
227
103
        }
228
195
        buffer = "";
229
195
      }
230
3.91k
    }
231
232
417
    if (chunk_size!=0) return false; // need more data
233
234
388
    return ready();
235
417
  };
Unexecuted instantiation: YaHTTP::AsyncLoader<YaHTTP::Response>::feed(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
YaHTTP::AsyncLoader<YaHTTP::Request>::feed(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Line
Count
Source
44
3.91k
  {
45
3.91k
    if (state < 2) {
46
3.91k
      headersize += somedata.length(); // maye include some body data, we don't know yet...
47
3.91k
      if (headersize > target->max_header_size) {
48
45
        if (target->kind == YAHTTP_TYPE_REQUEST) {
49
45
          throw ParseError("Request header too large");
50
45
        }
51
0
        else {
52
0
          throw ParseError("Response header too large");
53
0
        }
54
45
      }
55
3.91k
    }
56
3.86k
    buffer.append(somedata);
57
34.9k
    while(state < 2) {
58
34.8k
      int cr=0;
59
34.8k
      pos = buffer.find_first_of("\n");
60
      // need to find CRLF in buffer
61
34.8k
      if (pos == std::string::npos) return false;
62
32.1k
      if (pos>0 && buffer[pos-1]=='\r')
63
213
        cr=1;
64
32.1k
      std::string line(buffer.begin(), buffer.begin()+pos-cr); // exclude CRLF
65
32.1k
      buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer including CRLF
66
67
32.1k
      if (state == 0) { // startup line
68
3.85k
        if (target->kind == YAHTTP_TYPE_REQUEST) {
69
3.85k
          std::string ver;
70
3.85k
          std::string tmpurl;
71
3.85k
          std::istringstream iss(line);
72
3.85k
          iss >> target->method >> tmpurl >> ver;
73
3.85k
          if (ver.size() == 0)
74
3.78k
            target->version = 9;
75
71
          else if (ver.find("HTTP/0.9") == 0)
76
1
            target->version = 9;
77
70
          else if (ver.find("HTTP/1.0") == 0)
78
2
            target->version = 10;
79
68
          else if (ver.find("HTTP/1.1") == 0)
80
2
            target->version = 11;
81
66
          else
82
66
            throw ParseError("HTTP version not supported");
83
          // uppercase the target method
84
3.78k
          std::transform(target->method.begin(), target->method.end(), target->method.begin(), ::toupper);
85
3.78k
          target->url.parse(tmpurl);
86
3.78k
          target->getvars = Utility::parseUrlParameters(target->url.parameters);
87
3.78k
          state = 1;
88
3.78k
        } else if(target->kind == YAHTTP_TYPE_RESPONSE) {
89
0
          std::string ver;
90
0
          std::istringstream iss(line);
91
0
          std::string::size_type pos1;
92
0
          iss >> ver >> target->status;
93
0
          std::getline(iss, target->statusText);
94
0
          pos1=0;
95
0
          while(pos1 < target->statusText.size() && YaHTTP::isspace(target->statusText.at(pos1))) pos1++;
96
0
          target->statusText = target->statusText.substr(pos1); 
97
0
          if ((pos1 = target->statusText.find("\r")) != std::string::npos) {
98
0
            target->statusText = target->statusText.substr(0, pos1-1);
99
0
          }
100
0
          if (ver.size() == 0) {
101
0
            target->version = 9;
102
0
          } else if (ver.find("HTTP/0.9") == 0)
103
0
            target->version = 9;
104
0
          else if (ver.find("HTTP/1.0") == 0)
105
0
            target->version = 10;
106
0
          else if (ver.find("HTTP/1.1") == 0)
107
0
            target->version = 11;
108
0
          else
109
0
            throw ParseError("HTTP version not supported");
110
0
          state = 1;
111
0
        }
112
28.3k
      } else if (state == 1) {
113
28.3k
        std::string key,value;
114
28.3k
        size_t pos1;
115
28.3k
        if (line.empty()) {
116
1.00k
          chunked = (target->headers.find("transfer-encoding") != target->headers.end() && target->headers["transfer-encoding"] == "chunked");
117
1.00k
          state = 2;
118
1.00k
          break;
119
1.00k
        }
120
        // split headers
121
27.3k
        if ((pos1 = line.find(':')) == std::string::npos) {
122
33
          throw ParseError("Malformed header line");
123
33
        }
124
27.3k
        key = line.substr(0, pos1);
125
27.3k
        value = line.substr(pos1 + 1);
126
1.14M
        for(std::string::iterator it=key.begin(); it != key.end(); it++)
127
1.11M
          if (YaHTTP::isspace(*it))
128
16
            throw ParseError("Header key contains whitespace which is not allowed by RFC");
129
130
27.2k
        Utility::trim(value);
131
27.2k
        std::transform(key.begin(), key.end(), key.begin(), ::tolower);
132
        // is it already defined
133
134
27.2k
        if (key == "set-cookie" && target->kind == YAHTTP_TYPE_RESPONSE) {
135
0
          target->jar.parseSetCookieHeader(value);
136
27.2k
        } else if (key == "cookie" && target->kind == YAHTTP_TYPE_REQUEST) {
137
2.36k
          target->jar.parseCookieHeader(value);
138
24.9k
        } else {
139
24.9k
          if (key == "host" && target->kind == YAHTTP_TYPE_REQUEST) {
140
            // maybe it contains port?
141
2.23k
            if ((pos1 = value.find(':')) == std::string::npos) {
142
446
              target->url.host = value;
143
1.78k
            } else {
144
1.78k
              target->url.host = value.substr(0, pos1);
145
1.78k
              target->url.port = ::atoi(value.substr(pos1).c_str());
146
1.78k
            }
147
2.23k
          }
148
24.9k
          if (target->headers.find(key) != target->headers.end()) {
149
12.4k
            target->headers[key] = target->headers[key] + ";" + value;
150
12.4k
          } else {
151
12.4k
            target->headers[key] = std::move(value);
152
12.4k
          }
153
24.9k
        }
154
27.2k
      }
155
32.1k
    }
156
157
1.11k
    minbody = 0;
158
    // check for expected body size
159
1.11k
    if (target->kind == YAHTTP_TYPE_REQUEST) maxbody = target->max_request_size;
160
117
    else if (target->kind == YAHTTP_TYPE_RESPONSE) maxbody = target->max_response_size;
161
117
    else maxbody = 0;
162
163
1.11k
    if (!chunked) {
164
575
      if (target->headers.find("content-length") != target->headers.end()) {
165
368
        std::istringstream maxbodyS(target->headers["content-length"]);
166
368
        maxbodyS >> minbody;
167
368
        maxbody = minbody;
168
368
      }
169
575
      if (minbody < 1) return true; // guess there isn't anything left.
170
361
      if (target->kind == YAHTTP_TYPE_REQUEST && minbody > target->max_request_size) throw ParseError("Max request body size exceeded");
171
219
      else if (target->kind == YAHTTP_TYPE_RESPONSE && minbody > target->max_response_size) throw ParseError("Max response body size exceeded");
172
361
    }
173
174
763
    if (maxbody == 0) hasBody = false;
175
763
    else hasBody = true;
176
177
763
    if (buffer.size() == 0) return ready();
178
179
4.26k
    while(buffer.size() > 0) {
180
3.91k
      if (chunked) {
181
3.72k
        if (chunk_size == 0) {
182
2.00k
          char buf[100];
183
          // read chunk length
184
2.00k
          if ((pos = buffer.find('\n')) == std::string::npos) {
185
41
            if (buffer.size() > 99) {
186
12
              throw ParseError("Nonsensical chunk_size");
187
12
            }
188
29
            return false;
189
41
          }
190
1.96k
          if (pos > 99)
191
21
            throw ParseError("Impossible chunk_size");
192
1.94k
          buffer.copy(buf, pos);
193
1.94k
          buf[pos]=0; // just in case...
194
1.94k
          buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer
195
1.94k
          if (sscanf(buf, "%zx", &chunk_size) != 1) {
196
24
            throw ParseError("Unable to parse chunk size");
197
24
          }
198
1.91k
          if (chunk_size == 0) { state = 3; break; } // last chunk
199
1.90k
          if (chunk_size > (std::numeric_limits<decltype(chunk_size)>::max() - 2) || chunk_size > maxbody) {
200
154
            throw ParseError("Chunk is too large");
201
154
          }
202
1.90k
        } else {
203
1.72k
          int crlf=1;
204
1.72k
          if (buffer.size() < chunk_size+1) return false; // expect newline
205
1.68k
          if (buffer.at(chunk_size) == '\r') {
206
387
            if (buffer.size() < chunk_size+2 || buffer.at(chunk_size+1) != '\n') return false; // expect newline after carriage return
207
365
            crlf=2;
208
1.29k
          } else if (buffer.at(chunk_size) != '\n') return false;
209
1.64k
          if (bodysize + chunk_size > maxbody) {
210
0
            throw ParseError("Chunked body is too large");
211
0
          }
212
1.64k
          std::string tmp = buffer.substr(0, chunk_size);
213
1.64k
          buffer.erase(buffer.begin(), buffer.begin()+chunk_size+crlf);
214
1.64k
          bodybuf << tmp;
215
1.64k
          bodysize += chunk_size;
216
1.64k
          chunk_size = 0;
217
1.64k
          if (buffer.size() == 0) break; // just in case
218
1.64k
        }
219
3.72k
      } else {
220
195
        if (bodysize + buffer.length() > maxbody) {
221
92
          bodybuf << buffer.substr(0, maxbody - bodybuf.str().length());
222
92
          bodysize = maxbody;
223
92
        }
224
103
        else {
225
103
          bodybuf << buffer;
226
103
          bodysize += buffer.length();
227
103
        }
228
195
        buffer = "";
229
195
      }
230
3.91k
    }
231
232
417
    if (chunk_size!=0) return false; // need more data
233
234
388
    return ready();
235
417
  };
236
237
0
  void HTTPBase::write(std::ostream& os) const {
238
0
    if (kind == YAHTTP_TYPE_REQUEST) {
239
0
      std::ostringstream getparmbuf;
240
0
      std::string getparms;
241
      // prepare URL
242
0
      for(strstr_map_t::const_iterator i = getvars.begin(); i != getvars.end(); i++) {
243
0
        getparmbuf << Utility::encodeURL(i->first, false) << "=" << Utility::encodeURL(i->second, false) << "&";
244
0
      }
245
0
      if (getparmbuf.str().length() > 0) {
246
0
        std::string buf = getparmbuf.str();
247
0
        getparms = "?" + std::string(buf.begin(), buf.end() - 1);
248
0
      }
249
0
      else
250
0
        getparms = "";
251
0
      os << method << " " << url.path << getparms << " HTTP/" << versionStr(this->version);
252
0
    } else if (kind == YAHTTP_TYPE_RESPONSE) {
253
0
      os << "HTTP/" << versionStr(this->version) << " " << status << " ";
254
0
      if (statusText.empty())
255
0
        os << Utility::status2text(status);
256
0
      else
257
0
        os << statusText;
258
0
    }
259
0
    os << "\r\n";
260
261
0
    bool cookieSent = false;
262
0
    bool sendChunked = false;
263
264
0
    if (this->version > 10) { // 1.1 or better
265
0
      if (headers.find("content-length") == headers.end() && !this->is_multipart) {
266
        // must use chunked on response
267
0
        sendChunked = (kind == YAHTTP_TYPE_RESPONSE);
268
0
        if ((headers.find("transfer-encoding") != headers.end() && headers.find("transfer-encoding")->second != "chunked")) {
269
0
          throw YaHTTP::Error("Transfer-encoding must be chunked, or Content-Length defined");
270
0
        }
271
0
        if ((headers.find("transfer-encoding") == headers.end() && kind == YAHTTP_TYPE_RESPONSE)) {
272
0
          sendChunked = true;
273
0
          os << "Transfer-Encoding: chunked\r\n";
274
0
        }
275
0
      } else {
276
0
  sendChunked = false;
277
0
      }
278
0
    }
279
280
    // write headers
281
0
    strstr_map_t::const_iterator iter = headers.begin();
282
0
    while(iter != headers.end()) {
283
0
      if (iter->first == "host" && (kind != YAHTTP_TYPE_REQUEST || version < 10)) { iter++; continue; }
284
0
      if (iter->first == "transfer-encoding" && sendChunked) { iter++; continue; }
285
0
      std::string header = Utility::camelizeHeader(iter->first);
286
0
      if (header == "Cookie" || header == "Set-Cookie") cookieSent = true;
287
0
      os << Utility::camelizeHeader(iter->first) << ": " << iter->second << "\r\n";
288
0
      iter++;
289
0
    }
290
0
    if (version > 9 && !cookieSent && jar.cookies.size() > 0) { // write cookies
291
0
     if (kind == YAHTTP_TYPE_REQUEST) {
292
0
        bool first = true;
293
0
        os << "Cookie: ";
294
0
        for(strcookie_map_t::const_iterator i = jar.cookies.begin(); i != jar.cookies.end(); i++) {
295
0
          if (first)
296
0
            first = false;
297
0
          else
298
0
            os << "; ";
299
0
          os << Utility::encodeURL(i->second.name) << "=" << Utility::encodeURL(i->second.value);
300
0
        }
301
0
     } else if (kind == YAHTTP_TYPE_RESPONSE) {
302
0
        for(strcookie_map_t::const_iterator i = jar.cookies.begin(); i != jar.cookies.end(); i++) {
303
0
          os << "Set-Cookie: ";
304
0
          os << i->second.str() << "\r\n";
305
0
        }
306
0
      }
307
0
    }
308
0
    os << "\r\n";
309
0
#ifdef HAVE_CPP_FUNC_PTR
310
0
    this->renderer(this, os, sendChunked);
311
#else
312
    SendbodyRenderer r;
313
    r(this, os, chunked)
314
#endif
315
0
  };
316
317
0
  std::ostream& operator<<(std::ostream& os, const Response &resp) {
318
0
    resp.write(os);
319
0
    return os;
320
0
  };
321
322
0
  std::istream& operator>>(std::istream& is, Response &resp) {
323
0
    YaHTTP::AsyncResponseLoader arl;
324
0
    arl.initialize(&resp);
325
0
    while(is.good()) {
326
0
      char buf[1024];
327
0
      is.read(buf, 1024);
328
0
      if (is.gcount()>0) { // did we actually read anything
329
0
        is.clear();
330
0
        if (arl.feed(std::string(buf, is.gcount())) == true) break; // completed
331
0
      }
332
0
    }
333
    // throw unless ready
334
0
    if (arl.ready() == false)
335
0
      throw ParseError("Was not able to extract a valid Response from stream");
336
0
    arl.finalize();
337
0
    return is;
338
0
  };
339
340
0
  std::ostream& operator<<(std::ostream& os, const Request &req) {
341
0
    req.write(os);
342
0
    return os;
343
0
  };
344
345
0
  std::istream& operator>>(std::istream& is, Request &req) {
346
0
    YaHTTP::AsyncRequestLoader arl;
347
0
    arl.initialize(&req);
348
0
    while(is.good()) {
349
0
      char buf[1024];
350
0
      is.read(buf, 1024);
351
0
      if (is.gcount() > 0) { // did we actually read anything
352
0
        is.clear();
353
0
        if (arl.feed(std::string(buf, is.gcount())) == true) break; // completed
354
0
      }
355
0
    }
356
0
    if (arl.ready() == false)
357
0
      throw ParseError("Was not able to extract a valid Request from stream");
358
0
    arl.finalize();
359
0
    return is;
360
0
  };
361
};