Coverage Report

Created: 2026-04-06 06:13

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
11.1M
  bool isspace(char c) {
11
11.1M
    return std::isspace(c) != 0;
12
11.1M
  }
13
14
28.7k
  bool isspace(char c, const std::locale& loc) {
15
28.7k
    return std::isspace(c, loc);
16
28.7k
  }
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
3.68k
  bool AsyncLoader<T>::feed(const std::string& somedata) {
44
3.68k
    buffer.append(somedata);
45
45.4k
    while(state < 2) {
46
45.3k
      int cr=0;
47
45.3k
      pos = buffer.find_first_of("\n");
48
      // need to find CRLF in buffer
49
45.3k
      if (pos == std::string::npos) return false;
50
42.8k
      if (pos>0 && buffer[pos-1]=='\r')
51
228
        cr=1;
52
42.8k
      std::string line(buffer.begin(), buffer.begin()+pos-cr); // exclude CRLF
53
42.8k
      buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer including CRLF
54
55
42.8k
      if (state == 0) { // startup line
56
3.66k
        if (target->kind == YAHTTP_TYPE_REQUEST) {
57
3.66k
          std::string ver;
58
3.66k
          std::string tmpurl;
59
3.66k
          std::istringstream iss(line);
60
3.66k
          iss >> target->method >> tmpurl >> ver;
61
3.66k
          if (ver.size() == 0)
62
3.58k
            target->version = 9;
63
84
          else if (ver.find("HTTP/0.9") == 0)
64
2
            target->version = 9;
65
82
          else if (ver.find("HTTP/1.0") == 0)
66
1
            target->version = 10;
67
81
          else if (ver.find("HTTP/1.1") == 0)
68
4
            target->version = 11;
69
77
          else
70
77
            throw ParseError("HTTP version not supported");
71
          // uppercase the target method
72
3.59k
          std::transform(target->method.begin(), target->method.end(), target->method.begin(), ::toupper);
73
3.59k
          target->url.parse(tmpurl);
74
3.59k
          target->getvars = Utility::parseUrlParameters(target->url.parameters);
75
3.59k
          state = 1;
76
3.59k
        } else if(target->kind == YAHTTP_TYPE_RESPONSE) {
77
0
          std::string ver;
78
0
          std::istringstream iss(line);
79
0
          std::string::size_type pos1;
80
0
          iss >> ver >> target->status;
81
0
          std::getline(iss, target->statusText);
82
0
          pos1=0;
83
0
          while(pos1 < target->statusText.size() && YaHTTP::isspace(target->statusText.at(pos1))) pos1++;
84
0
          target->statusText = target->statusText.substr(pos1); 
85
0
          if ((pos1 = target->statusText.find("\r")) != std::string::npos) {
86
0
            target->statusText = target->statusText.substr(0, pos1-1);
87
0
          }
88
0
          if (ver.size() == 0) {
89
0
            target->version = 9;
90
0
          } else if (ver.find("HTTP/0.9") == 0)
91
0
            target->version = 9;
92
0
          else if (ver.find("HTTP/1.0") == 0)
93
0
            target->version = 10;
94
0
          else if (ver.find("HTTP/1.1") == 0)
95
0
            target->version = 11;
96
0
          else
97
0
            throw ParseError("HTTP version not supported");
98
0
          state = 1;
99
0
        }
100
39.1k
      } else if (state == 1) {
101
39.1k
        std::string key,value;
102
39.1k
        size_t pos1;
103
39.1k
        if (line.empty()) {
104
908
          chunked = (target->headers.find("transfer-encoding") != target->headers.end() && target->headers["transfer-encoding"] == "chunked");
105
908
          state = 2;
106
908
          break;
107
908
        }
108
        // split headers
109
38.2k
        if ((pos1 = line.find(":")) == std::string::npos) {
110
27
          throw ParseError("Malformed header line");
111
27
        }
112
38.2k
        key = line.substr(0, pos1);
113
38.2k
        value = line.substr(pos1 + 1);
114
11.1M
        for(std::string::iterator it=key.begin(); it != key.end(); it++)
115
11.1M
          if (YaHTTP::isspace(*it))
116
16
            throw ParseError("Header key contains whitespace which is not allowed by RFC");
117
118
38.2k
        Utility::trim(value);
119
38.2k
        std::transform(key.begin(), key.end(), key.begin(), ::tolower);
120
        // is it already defined
121
122
38.2k
        if (key == "set-cookie" && target->kind == YAHTTP_TYPE_RESPONSE) {
123
0
          target->jar.parseSetCookieHeader(value);
124
38.2k
        } else if (key == "cookie" && target->kind == YAHTTP_TYPE_REQUEST) {
125
2.22k
          target->jar.parseCookieHeader(value);
126
35.9k
        } else {
127
35.9k
          if (key == "host" && target->kind == YAHTTP_TYPE_REQUEST) {
128
            // maybe it contains port?
129
7.70k
            if ((pos1 = value.find(":")) == std::string::npos) {
130
1.58k
              target->url.host = value;
131
6.11k
            } else {
132
6.11k
              target->url.host = value.substr(0, pos1);
133
6.11k
              target->url.port = ::atoi(value.substr(pos1).c_str());
134
6.11k
            }
135
7.70k
          }
136
35.9k
          if (target->headers.find(key) != target->headers.end()) {
137
24.5k
            target->headers[key] = target->headers[key] + ";" + value;
138
24.5k
          } else {
139
11.4k
            target->headers[key] = std::move(value);
140
11.4k
          }
141
35.9k
        }
142
38.2k
      }
143
42.8k
    }
144
145
1.02k
    minbody = 0;
146
    // check for expected body size
147
1.02k
    if (target->kind == YAHTTP_TYPE_REQUEST) maxbody = target->max_request_size;
148
118
    else if (target->kind == YAHTTP_TYPE_RESPONSE) maxbody = target->max_response_size;
149
118
    else maxbody = 0;
150
151
1.02k
    if (!chunked) {
152
592
      if (target->headers.find("content-length") != target->headers.end()) {
153
402
        std::istringstream maxbodyS(target->headers["content-length"]);
154
402
        maxbodyS >> minbody;
155
402
        maxbody = minbody;
156
402
      }
157
592
      if (minbody < 1) return true; // guess there isn't anything left.
158
393
      if (target->kind == YAHTTP_TYPE_REQUEST && static_cast<ssize_t>(minbody) > target->max_request_size) throw ParseError("Max request body size exceeded");
159
338
      else if (target->kind == YAHTTP_TYPE_RESPONSE && static_cast<ssize_t>(minbody) > target->max_response_size) throw ParseError("Max response body size exceeded");
160
393
    }
161
162
772
    if (maxbody == 0) hasBody = false;
163
772
    else hasBody = true;
164
165
772
    if (buffer.size() == 0) return ready();
166
167
5.08k
    while(buffer.size() > 0) {
168
4.63k
      if (chunked) {
169
4.37k
        if (chunk_size == 0) {
170
2.25k
          char buf[100];
171
          // read chunk length
172
2.25k
          if ((pos = buffer.find('\n')) == std::string::npos) return false;
173
2.24k
          if (pos > 99)
174
19
            throw ParseError("Impossible chunk_size");
175
2.22k
          buffer.copy(buf, pos);
176
2.22k
          buf[pos]=0; // just in case...
177
2.22k
          buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer
178
2.22k
          if (sscanf(buf, "%x", &chunk_size) != 1) {
179
22
            throw ParseError("Unable to parse chunk size");
180
22
          }
181
2.20k
          if (chunk_size == 0) { state = 3; break; } // last chunk
182
2.19k
          if (chunk_size > (std::numeric_limits<decltype(chunk_size)>::max() - 2)) {
183
2
            throw ParseError("Chunk is too large");
184
2
          }
185
2.19k
        } else {
186
2.12k
          int crlf=1;
187
2.12k
          if (buffer.size() < static_cast<size_t>(chunk_size+1)) return false; // expect newline
188
2.04k
          if (buffer.at(chunk_size) == '\r') {
189
1.03k
            if (buffer.size() < static_cast<size_t>(chunk_size+2) || buffer.at(chunk_size+1) != '\n') return false; // expect newline after carriage return
190
1.00k
            crlf=2;
191
1.01k
          } else if (buffer.at(chunk_size) != '\n') return false;
192
1.99k
          std::string tmp = buffer.substr(0, chunk_size);
193
1.99k
          buffer.erase(buffer.begin(), buffer.begin()+chunk_size+crlf);
194
1.99k
          bodybuf << tmp;
195
1.99k
          chunk_size = 0;
196
1.99k
          if (buffer.size() == 0) break; // just in case
197
1.99k
        }
198
4.37k
      } else {
199
259
        if (bodybuf.str().length() + buffer.length() > maxbody)
200
101
          bodybuf << buffer.substr(0, maxbody - bodybuf.str().length());
201
158
        else
202
158
          bodybuf << buffer;
203
259
        buffer = "";
204
259
      }
205
4.63k
    }
206
207
513
    if (chunk_size!=0) return false; // need more data
208
209
439
    return ready();
210
513
  };
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
43
3.68k
  bool AsyncLoader<T>::feed(const std::string& somedata) {
44
3.68k
    buffer.append(somedata);
45
45.4k
    while(state < 2) {
46
45.3k
      int cr=0;
47
45.3k
      pos = buffer.find_first_of("\n");
48
      // need to find CRLF in buffer
49
45.3k
      if (pos == std::string::npos) return false;
50
42.8k
      if (pos>0 && buffer[pos-1]=='\r')
51
228
        cr=1;
52
42.8k
      std::string line(buffer.begin(), buffer.begin()+pos-cr); // exclude CRLF
53
42.8k
      buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer including CRLF
54
55
42.8k
      if (state == 0) { // startup line
56
3.66k
        if (target->kind == YAHTTP_TYPE_REQUEST) {
57
3.66k
          std::string ver;
58
3.66k
          std::string tmpurl;
59
3.66k
          std::istringstream iss(line);
60
3.66k
          iss >> target->method >> tmpurl >> ver;
61
3.66k
          if (ver.size() == 0)
62
3.58k
            target->version = 9;
63
84
          else if (ver.find("HTTP/0.9") == 0)
64
2
            target->version = 9;
65
82
          else if (ver.find("HTTP/1.0") == 0)
66
1
            target->version = 10;
67
81
          else if (ver.find("HTTP/1.1") == 0)
68
4
            target->version = 11;
69
77
          else
70
77
            throw ParseError("HTTP version not supported");
71
          // uppercase the target method
72
3.59k
          std::transform(target->method.begin(), target->method.end(), target->method.begin(), ::toupper);
73
3.59k
          target->url.parse(tmpurl);
74
3.59k
          target->getvars = Utility::parseUrlParameters(target->url.parameters);
75
3.59k
          state = 1;
76
3.59k
        } else if(target->kind == YAHTTP_TYPE_RESPONSE) {
77
0
          std::string ver;
78
0
          std::istringstream iss(line);
79
0
          std::string::size_type pos1;
80
0
          iss >> ver >> target->status;
81
0
          std::getline(iss, target->statusText);
82
0
          pos1=0;
83
0
          while(pos1 < target->statusText.size() && YaHTTP::isspace(target->statusText.at(pos1))) pos1++;
84
0
          target->statusText = target->statusText.substr(pos1); 
85
0
          if ((pos1 = target->statusText.find("\r")) != std::string::npos) {
86
0
            target->statusText = target->statusText.substr(0, pos1-1);
87
0
          }
88
0
          if (ver.size() == 0) {
89
0
            target->version = 9;
90
0
          } else if (ver.find("HTTP/0.9") == 0)
91
0
            target->version = 9;
92
0
          else if (ver.find("HTTP/1.0") == 0)
93
0
            target->version = 10;
94
0
          else if (ver.find("HTTP/1.1") == 0)
95
0
            target->version = 11;
96
0
          else
97
0
            throw ParseError("HTTP version not supported");
98
0
          state = 1;
99
0
        }
100
39.1k
      } else if (state == 1) {
101
39.1k
        std::string key,value;
102
39.1k
        size_t pos1;
103
39.1k
        if (line.empty()) {
104
908
          chunked = (target->headers.find("transfer-encoding") != target->headers.end() && target->headers["transfer-encoding"] == "chunked");
105
908
          state = 2;
106
908
          break;
107
908
        }
108
        // split headers
109
38.2k
        if ((pos1 = line.find(":")) == std::string::npos) {
110
27
          throw ParseError("Malformed header line");
111
27
        }
112
38.2k
        key = line.substr(0, pos1);
113
38.2k
        value = line.substr(pos1 + 1);
114
11.1M
        for(std::string::iterator it=key.begin(); it != key.end(); it++)
115
11.1M
          if (YaHTTP::isspace(*it))
116
16
            throw ParseError("Header key contains whitespace which is not allowed by RFC");
117
118
38.2k
        Utility::trim(value);
119
38.2k
        std::transform(key.begin(), key.end(), key.begin(), ::tolower);
120
        // is it already defined
121
122
38.2k
        if (key == "set-cookie" && target->kind == YAHTTP_TYPE_RESPONSE) {
123
0
          target->jar.parseSetCookieHeader(value);
124
38.2k
        } else if (key == "cookie" && target->kind == YAHTTP_TYPE_REQUEST) {
125
2.22k
          target->jar.parseCookieHeader(value);
126
35.9k
        } else {
127
35.9k
          if (key == "host" && target->kind == YAHTTP_TYPE_REQUEST) {
128
            // maybe it contains port?
129
7.70k
            if ((pos1 = value.find(":")) == std::string::npos) {
130
1.58k
              target->url.host = value;
131
6.11k
            } else {
132
6.11k
              target->url.host = value.substr(0, pos1);
133
6.11k
              target->url.port = ::atoi(value.substr(pos1).c_str());
134
6.11k
            }
135
7.70k
          }
136
35.9k
          if (target->headers.find(key) != target->headers.end()) {
137
24.5k
            target->headers[key] = target->headers[key] + ";" + value;
138
24.5k
          } else {
139
11.4k
            target->headers[key] = std::move(value);
140
11.4k
          }
141
35.9k
        }
142
38.2k
      }
143
42.8k
    }
144
145
1.02k
    minbody = 0;
146
    // check for expected body size
147
1.02k
    if (target->kind == YAHTTP_TYPE_REQUEST) maxbody = target->max_request_size;
148
118
    else if (target->kind == YAHTTP_TYPE_RESPONSE) maxbody = target->max_response_size;
149
118
    else maxbody = 0;
150
151
1.02k
    if (!chunked) {
152
592
      if (target->headers.find("content-length") != target->headers.end()) {
153
402
        std::istringstream maxbodyS(target->headers["content-length"]);
154
402
        maxbodyS >> minbody;
155
402
        maxbody = minbody;
156
402
      }
157
592
      if (minbody < 1) return true; // guess there isn't anything left.
158
393
      if (target->kind == YAHTTP_TYPE_REQUEST && static_cast<ssize_t>(minbody) > target->max_request_size) throw ParseError("Max request body size exceeded");
159
338
      else if (target->kind == YAHTTP_TYPE_RESPONSE && static_cast<ssize_t>(minbody) > target->max_response_size) throw ParseError("Max response body size exceeded");
160
393
    }
161
162
772
    if (maxbody == 0) hasBody = false;
163
772
    else hasBody = true;
164
165
772
    if (buffer.size() == 0) return ready();
166
167
5.08k
    while(buffer.size() > 0) {
168
4.63k
      if (chunked) {
169
4.37k
        if (chunk_size == 0) {
170
2.25k
          char buf[100];
171
          // read chunk length
172
2.25k
          if ((pos = buffer.find('\n')) == std::string::npos) return false;
173
2.24k
          if (pos > 99)
174
19
            throw ParseError("Impossible chunk_size");
175
2.22k
          buffer.copy(buf, pos);
176
2.22k
          buf[pos]=0; // just in case...
177
2.22k
          buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer
178
2.22k
          if (sscanf(buf, "%x", &chunk_size) != 1) {
179
22
            throw ParseError("Unable to parse chunk size");
180
22
          }
181
2.20k
          if (chunk_size == 0) { state = 3; break; } // last chunk
182
2.19k
          if (chunk_size > (std::numeric_limits<decltype(chunk_size)>::max() - 2)) {
183
2
            throw ParseError("Chunk is too large");
184
2
          }
185
2.19k
        } else {
186
2.12k
          int crlf=1;
187
2.12k
          if (buffer.size() < static_cast<size_t>(chunk_size+1)) return false; // expect newline
188
2.04k
          if (buffer.at(chunk_size) == '\r') {
189
1.03k
            if (buffer.size() < static_cast<size_t>(chunk_size+2) || buffer.at(chunk_size+1) != '\n') return false; // expect newline after carriage return
190
1.00k
            crlf=2;
191
1.01k
          } else if (buffer.at(chunk_size) != '\n') return false;
192
1.99k
          std::string tmp = buffer.substr(0, chunk_size);
193
1.99k
          buffer.erase(buffer.begin(), buffer.begin()+chunk_size+crlf);
194
1.99k
          bodybuf << tmp;
195
1.99k
          chunk_size = 0;
196
1.99k
          if (buffer.size() == 0) break; // just in case
197
1.99k
        }
198
4.37k
      } else {
199
259
        if (bodybuf.str().length() + buffer.length() > maxbody)
200
101
          bodybuf << buffer.substr(0, maxbody - bodybuf.str().length());
201
158
        else
202
158
          bodybuf << buffer;
203
259
        buffer = "";
204
259
      }
205
4.63k
    }
206
207
513
    if (chunk_size!=0) return false; // need more data
208
209
439
    return ready();
210
513
  };
211
212
0
  void HTTPBase::write(std::ostream& os) const {
213
0
    if (kind == YAHTTP_TYPE_REQUEST) {
214
0
      std::ostringstream getparmbuf;
215
0
      std::string getparms;
216
      // prepare URL
217
0
      for(strstr_map_t::const_iterator i = getvars.begin(); i != getvars.end(); i++) {
218
0
        getparmbuf << Utility::encodeURL(i->first, false) << "=" << Utility::encodeURL(i->second, false) << "&";
219
0
      }
220
0
      if (getparmbuf.str().length() > 0) {
221
0
        std::string buf = getparmbuf.str();
222
0
        getparms = "?" + std::string(buf.begin(), buf.end() - 1);
223
0
      }
224
0
      else
225
0
        getparms = "";
226
0
      os << method << " " << url.path << getparms << " HTTP/" << versionStr(this->version);
227
0
    } else if (kind == YAHTTP_TYPE_RESPONSE) {
228
0
      os << "HTTP/" << versionStr(this->version) << " " << status << " ";
229
0
      if (statusText.empty())
230
0
        os << Utility::status2text(status);
231
0
      else
232
0
        os << statusText;
233
0
    }
234
0
    os << "\r\n";
235
236
0
    bool cookieSent = false;
237
0
    bool sendChunked = false;
238
239
0
    if (this->version > 10) { // 1.1 or better
240
0
      if (headers.find("content-length") == headers.end() && !this->is_multipart) {
241
        // must use chunked on response
242
0
        sendChunked = (kind == YAHTTP_TYPE_RESPONSE);
243
0
        if ((headers.find("transfer-encoding") != headers.end() && headers.find("transfer-encoding")->second != "chunked")) {
244
0
          throw YaHTTP::Error("Transfer-encoding must be chunked, or Content-Length defined");
245
0
        }
246
0
        if ((headers.find("transfer-encoding") == headers.end() && kind == YAHTTP_TYPE_RESPONSE)) {
247
0
          sendChunked = true;
248
0
          os << "Transfer-Encoding: chunked\r\n";
249
0
        }
250
0
      } else {
251
0
  sendChunked = false;
252
0
      }
253
0
    }
254
255
    // write headers
256
0
    strstr_map_t::const_iterator iter = headers.begin();
257
0
    while(iter != headers.end()) {
258
0
      if (iter->first == "host" && (kind != YAHTTP_TYPE_REQUEST || version < 10)) { iter++; continue; }
259
0
      if (iter->first == "transfer-encoding" && sendChunked) { iter++; continue; }
260
0
      std::string header = Utility::camelizeHeader(iter->first);
261
0
      if (header == "Cookie" || header == "Set-Cookie") cookieSent = true;
262
0
      os << Utility::camelizeHeader(iter->first) << ": " << iter->second << "\r\n";
263
0
      iter++;
264
0
    }
265
0
    if (version > 9 && !cookieSent && jar.cookies.size() > 0) { // write cookies
266
0
     if (kind == YAHTTP_TYPE_REQUEST) {
267
0
        bool first = true;
268
0
        os << "Cookie: ";
269
0
        for(strcookie_map_t::const_iterator i = jar.cookies.begin(); i != jar.cookies.end(); i++) {
270
0
          if (first)
271
0
            first = false;
272
0
          else
273
0
            os << "; ";
274
0
          os << Utility::encodeURL(i->second.name) << "=" << Utility::encodeURL(i->second.value);
275
0
        }
276
0
     } else if (kind == YAHTTP_TYPE_RESPONSE) {
277
0
        for(strcookie_map_t::const_iterator i = jar.cookies.begin(); i != jar.cookies.end(); i++) {
278
0
          os << "Set-Cookie: ";
279
0
          os << i->second.str() << "\r\n";
280
0
        }
281
0
      }
282
0
    }
283
0
    os << "\r\n";
284
0
#ifdef HAVE_CPP_FUNC_PTR
285
0
    this->renderer(this, os, sendChunked);
286
#else
287
    SendbodyRenderer r;
288
    r(this, os, chunked)
289
#endif
290
0
  };
291
292
0
  std::ostream& operator<<(std::ostream& os, const Response &resp) {
293
0
    resp.write(os);
294
0
    return os;
295
0
  };
296
297
0
  std::istream& operator>>(std::istream& is, Response &resp) {
298
0
    YaHTTP::AsyncResponseLoader arl;
299
0
    arl.initialize(&resp);
300
0
    while(is.good()) {
301
0
      char buf[1024];
302
0
      is.read(buf, 1024);
303
0
      if (is.gcount()>0) { // did we actually read anything
304
0
        is.clear();
305
0
        if (arl.feed(std::string(buf, is.gcount())) == true) break; // completed
306
0
      }
307
0
    }
308
    // throw unless ready
309
0
    if (arl.ready() == false)
310
0
      throw ParseError("Was not able to extract a valid Response from stream");
311
0
    arl.finalize();
312
0
    return is;
313
0
  };
314
315
0
  std::ostream& operator<<(std::ostream& os, const Request &req) {
316
0
    req.write(os);
317
0
    return os;
318
0
  };
319
320
0
  std::istream& operator>>(std::istream& is, Request &req) {
321
0
    YaHTTP::AsyncRequestLoader arl;
322
0
    arl.initialize(&req);
323
0
    while(is.good()) {
324
0
      char buf[1024];
325
0
      is.read(buf, 1024);
326
0
      if (is.gcount() > 0) { // did we actually read anything
327
0
        is.clear();
328
0
        if (arl.feed(std::string(buf, is.gcount())) == true) break; // completed
329
0
      }
330
0
    }
331
0
    if (arl.ready() == false)
332
0
      throw ParseError("Was not able to extract a valid Request from stream");
333
0
    arl.finalize();
334
0
    return is;
335
0
  };
336
};