Coverage Report

Created: 2025-06-13 06:28

/src/pdns/ext/yahttp/yahttp/reqresp.cpp
Line
Count
Source (jump to first uncovered line)
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
12.9M
  bool isspace(char c) {
11
12.9M
    return std::isspace(c) != 0;
12
12.9M
  }
13
14
21.0k
  bool isspace(char c, const std::locale& loc) {
15
21.0k
    return std::isspace(c, loc);
16
21.0k
  }
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
4.22k
  bool AsyncLoader<T>::feed(const std::string& somedata) {
44
4.22k
    buffer.append(somedata);
45
34.5k
    while(state < 2) {
46
34.4k
      int cr=0;
47
34.4k
      pos = buffer.find_first_of("\n");
48
      // need to find CRLF in buffer
49
34.4k
      if (pos == std::string::npos) return false;
50
31.5k
      if (pos>0 && buffer[pos-1]=='\r')
51
247
        cr=1;
52
31.5k
      std::string line(buffer.begin(), buffer.begin()+pos-cr); // exclude CRLF
53
31.5k
      buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer including CRLF
54
55
31.5k
      if (state == 0) { // startup line
56
4.20k
        if (target->kind == YAHTTP_TYPE_REQUEST) {
57
4.20k
          std::string ver;
58
4.20k
          std::string tmpurl;
59
4.20k
          std::istringstream iss(line);
60
4.20k
          iss >> target->method >> tmpurl >> ver;
61
4.20k
          if (ver.size() == 0)
62
4.04k
            target->version = 9;
63
154
          else if (ver.find("HTTP/0.9") == 0)
64
1
            target->version = 9;
65
153
          else if (ver.find("HTTP/1.0") == 0)
66
3
            target->version = 10;
67
150
          else if (ver.find("HTTP/1.1") == 0)
68
1
            target->version = 11;
69
149
          else
70
149
            throw ParseError("HTTP version not supported");
71
          // uppercase the target method
72
4.05k
          std::transform(target->method.begin(), target->method.end(), target->method.begin(), ::toupper);
73
4.05k
          target->url.parse(tmpurl);
74
4.05k
          target->getvars = Utility::parseUrlParameters(target->url.parameters);
75
4.05k
          state = 1;
76
4.05k
        } 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
27.3k
      } else if (state == 1) {
101
27.3k
        std::string key,value;
102
27.3k
        size_t pos1;
103
27.3k
        if (line.empty()) {
104
1.02k
          chunked = (target->headers.find("transfer-encoding") != target->headers.end() && target->headers["transfer-encoding"] == "chunked");
105
1.02k
          state = 2;
106
1.02k
          break;
107
1.02k
        }
108
        // split headers
109
26.3k
        if ((pos1 = line.find(":")) == std::string::npos) {
110
15
          throw ParseError("Malformed header line");
111
15
        }
112
26.3k
        key = line.substr(0, pos1);
113
26.3k
        value = line.substr(pos1 + 1);
114
12.9M
        for(std::string::iterator it=key.begin(); it != key.end(); it++)
115
12.9M
          if (YaHTTP::isspace(*it))
116
16
            throw ParseError("Header key contains whitespace which is not allowed by RFC");
117
118
26.3k
        Utility::trim(value);
119
26.3k
        std::transform(key.begin(), key.end(), key.begin(), ::tolower);
120
        // is it already defined
121
122
26.3k
        if (key == "set-cookie" && target->kind == YAHTTP_TYPE_RESPONSE) {
123
0
          target->jar.parseSetCookieHeader(value);
124
26.3k
        } else if (key == "cookie" && target->kind == YAHTTP_TYPE_REQUEST) {
125
2.09k
          target->jar.parseCookieHeader(value);
126
24.2k
        } else {
127
24.2k
          if (key == "host" && target->kind == YAHTTP_TYPE_REQUEST) {
128
            // maybe it contains port?
129
4.06k
            if ((pos1 = value.find(":")) == std::string::npos) {
130
480
              target->url.host = value;
131
3.58k
            } else {
132
3.58k
              target->url.host = value.substr(0, pos1);
133
3.58k
              target->url.port = ::atoi(value.substr(pos1).c_str());
134
3.58k
            }
135
4.06k
          }
136
24.2k
          if (target->headers.find(key) != target->headers.end()) {
137
13.8k
            target->headers[key] = target->headers[key] + ";" + value;
138
13.8k
          } else {
139
10.3k
            target->headers[key] = std::move(value);
140
10.3k
          }
141
24.2k
        }
142
26.3k
      }
143
31.5k
    }
144
145
1.14k
    minbody = 0;
146
    // check for expected body size
147
1.14k
    if (target->kind == YAHTTP_TYPE_REQUEST) maxbody = target->max_request_size;
148
124
    else if (target->kind == YAHTTP_TYPE_RESPONSE) maxbody = target->max_response_size;
149
124
    else maxbody = 0;
150
151
1.14k
    if (!chunked) {
152
686
      if (target->headers.find("content-length") != target->headers.end()) {
153
480
        std::istringstream maxbodyS(target->headers["content-length"]);
154
480
        maxbodyS >> minbody;
155
480
        maxbody = minbody;
156
480
      }
157
686
      if (minbody < 1) return true; // guess there isn't anything left.
158
468
      if (target->kind == YAHTTP_TYPE_REQUEST && static_cast<ssize_t>(minbody) > target->max_request_size) throw ParseError("Max request body size exceeded");
159
402
      else if (target->kind == YAHTTP_TYPE_RESPONSE && static_cast<ssize_t>(minbody) > target->max_response_size) throw ParseError("Max response body size exceeded");
160
468
    }
161
162
862
    if (maxbody == 0) hasBody = false;
163
862
    else hasBody = true;
164
165
862
    if (buffer.size() == 0) return ready();
166
167
4.35k
    while(buffer.size() > 0) {
168
3.86k
      if (chunked) {
169
3.56k
        if (chunk_size == 0) {
170
1.83k
          char buf[100];
171
          // read chunk length
172
1.83k
          if ((pos = buffer.find('\n')) == std::string::npos) return false;
173
1.82k
          if (pos > 99)
174
17
            throw ParseError("Impossible chunk_size");
175
1.81k
          buffer.copy(buf, pos);
176
1.81k
          buf[pos]=0; // just in case...
177
1.81k
          buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer
178
1.81k
          if (sscanf(buf, "%x", &chunk_size) != 1) {
179
19
            throw ParseError("Unable to parse chunk size");
180
19
          }
181
1.79k
          if (chunk_size == 0) { state = 3; break; } // last chunk
182
1.78k
          if (chunk_size > (std::numeric_limits<decltype(chunk_size)>::max() - 2)) {
183
2
            throw ParseError("Chunk is too large");
184
2
          }
185
1.78k
        } else {
186
1.72k
          int crlf=1;
187
1.72k
          if (buffer.size() < static_cast<size_t>(chunk_size+1)) return false; // expect newline
188
1.64k
          if (buffer.at(chunk_size) == '\r') {
189
495
            if (buffer.size() < static_cast<size_t>(chunk_size+2) || buffer.at(chunk_size+1) != '\n') return false; // expect newline after carriage return
190
457
            crlf=2;
191
1.14k
          } else if (buffer.at(chunk_size) != '\n') return false;
192
1.57k
          std::string tmp = buffer.substr(0, chunk_size);
193
1.57k
          buffer.erase(buffer.begin(), buffer.begin()+chunk_size+crlf);
194
1.57k
          bodybuf << tmp;
195
1.57k
          chunk_size = 0;
196
1.57k
          if (buffer.size() == 0) break; // just in case
197
1.57k
        }
198
3.56k
      } else {
199
301
        if (bodybuf.str().length() + buffer.length() > maxbody)
200
97
          bodybuf << buffer.substr(0, maxbody - bodybuf.str().length());
201
204
        else
202
204
          bodybuf << buffer;
203
301
        buffer = "";
204
301
      }
205
3.86k
    }
206
207
556
    if (chunk_size!=0) return false; // need more data
208
209
496
    return ready();
210
556
  };
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
4.22k
  bool AsyncLoader<T>::feed(const std::string& somedata) {
44
4.22k
    buffer.append(somedata);
45
34.5k
    while(state < 2) {
46
34.4k
      int cr=0;
47
34.4k
      pos = buffer.find_first_of("\n");
48
      // need to find CRLF in buffer
49
34.4k
      if (pos == std::string::npos) return false;
50
31.5k
      if (pos>0 && buffer[pos-1]=='\r')
51
247
        cr=1;
52
31.5k
      std::string line(buffer.begin(), buffer.begin()+pos-cr); // exclude CRLF
53
31.5k
      buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer including CRLF
54
55
31.5k
      if (state == 0) { // startup line
56
4.20k
        if (target->kind == YAHTTP_TYPE_REQUEST) {
57
4.20k
          std::string ver;
58
4.20k
          std::string tmpurl;
59
4.20k
          std::istringstream iss(line);
60
4.20k
          iss >> target->method >> tmpurl >> ver;
61
4.20k
          if (ver.size() == 0)
62
4.04k
            target->version = 9;
63
154
          else if (ver.find("HTTP/0.9") == 0)
64
1
            target->version = 9;
65
153
          else if (ver.find("HTTP/1.0") == 0)
66
3
            target->version = 10;
67
150
          else if (ver.find("HTTP/1.1") == 0)
68
1
            target->version = 11;
69
149
          else
70
149
            throw ParseError("HTTP version not supported");
71
          // uppercase the target method
72
4.05k
          std::transform(target->method.begin(), target->method.end(), target->method.begin(), ::toupper);
73
4.05k
          target->url.parse(tmpurl);
74
4.05k
          target->getvars = Utility::parseUrlParameters(target->url.parameters);
75
4.05k
          state = 1;
76
4.05k
        } 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
27.3k
      } else if (state == 1) {
101
27.3k
        std::string key,value;
102
27.3k
        size_t pos1;
103
27.3k
        if (line.empty()) {
104
1.02k
          chunked = (target->headers.find("transfer-encoding") != target->headers.end() && target->headers["transfer-encoding"] == "chunked");
105
1.02k
          state = 2;
106
1.02k
          break;
107
1.02k
        }
108
        // split headers
109
26.3k
        if ((pos1 = line.find(":")) == std::string::npos) {
110
15
          throw ParseError("Malformed header line");
111
15
        }
112
26.3k
        key = line.substr(0, pos1);
113
26.3k
        value = line.substr(pos1 + 1);
114
12.9M
        for(std::string::iterator it=key.begin(); it != key.end(); it++)
115
12.9M
          if (YaHTTP::isspace(*it))
116
16
            throw ParseError("Header key contains whitespace which is not allowed by RFC");
117
118
26.3k
        Utility::trim(value);
119
26.3k
        std::transform(key.begin(), key.end(), key.begin(), ::tolower);
120
        // is it already defined
121
122
26.3k
        if (key == "set-cookie" && target->kind == YAHTTP_TYPE_RESPONSE) {
123
0
          target->jar.parseSetCookieHeader(value);
124
26.3k
        } else if (key == "cookie" && target->kind == YAHTTP_TYPE_REQUEST) {
125
2.09k
          target->jar.parseCookieHeader(value);
126
24.2k
        } else {
127
24.2k
          if (key == "host" && target->kind == YAHTTP_TYPE_REQUEST) {
128
            // maybe it contains port?
129
4.06k
            if ((pos1 = value.find(":")) == std::string::npos) {
130
480
              target->url.host = value;
131
3.58k
            } else {
132
3.58k
              target->url.host = value.substr(0, pos1);
133
3.58k
              target->url.port = ::atoi(value.substr(pos1).c_str());
134
3.58k
            }
135
4.06k
          }
136
24.2k
          if (target->headers.find(key) != target->headers.end()) {
137
13.8k
            target->headers[key] = target->headers[key] + ";" + value;
138
13.8k
          } else {
139
10.3k
            target->headers[key] = std::move(value);
140
10.3k
          }
141
24.2k
        }
142
26.3k
      }
143
31.5k
    }
144
145
1.14k
    minbody = 0;
146
    // check for expected body size
147
1.14k
    if (target->kind == YAHTTP_TYPE_REQUEST) maxbody = target->max_request_size;
148
124
    else if (target->kind == YAHTTP_TYPE_RESPONSE) maxbody = target->max_response_size;
149
124
    else maxbody = 0;
150
151
1.14k
    if (!chunked) {
152
686
      if (target->headers.find("content-length") != target->headers.end()) {
153
480
        std::istringstream maxbodyS(target->headers["content-length"]);
154
480
        maxbodyS >> minbody;
155
480
        maxbody = minbody;
156
480
      }
157
686
      if (minbody < 1) return true; // guess there isn't anything left.
158
468
      if (target->kind == YAHTTP_TYPE_REQUEST && static_cast<ssize_t>(minbody) > target->max_request_size) throw ParseError("Max request body size exceeded");
159
402
      else if (target->kind == YAHTTP_TYPE_RESPONSE && static_cast<ssize_t>(minbody) > target->max_response_size) throw ParseError("Max response body size exceeded");
160
468
    }
161
162
862
    if (maxbody == 0) hasBody = false;
163
862
    else hasBody = true;
164
165
862
    if (buffer.size() == 0) return ready();
166
167
4.35k
    while(buffer.size() > 0) {
168
3.86k
      if (chunked) {
169
3.56k
        if (chunk_size == 0) {
170
1.83k
          char buf[100];
171
          // read chunk length
172
1.83k
          if ((pos = buffer.find('\n')) == std::string::npos) return false;
173
1.82k
          if (pos > 99)
174
17
            throw ParseError("Impossible chunk_size");
175
1.81k
          buffer.copy(buf, pos);
176
1.81k
          buf[pos]=0; // just in case...
177
1.81k
          buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer
178
1.81k
          if (sscanf(buf, "%x", &chunk_size) != 1) {
179
19
            throw ParseError("Unable to parse chunk size");
180
19
          }
181
1.79k
          if (chunk_size == 0) { state = 3; break; } // last chunk
182
1.78k
          if (chunk_size > (std::numeric_limits<decltype(chunk_size)>::max() - 2)) {
183
2
            throw ParseError("Chunk is too large");
184
2
          }
185
1.78k
        } else {
186
1.72k
          int crlf=1;
187
1.72k
          if (buffer.size() < static_cast<size_t>(chunk_size+1)) return false; // expect newline
188
1.64k
          if (buffer.at(chunk_size) == '\r') {
189
495
            if (buffer.size() < static_cast<size_t>(chunk_size+2) || buffer.at(chunk_size+1) != '\n') return false; // expect newline after carriage return
190
457
            crlf=2;
191
1.14k
          } else if (buffer.at(chunk_size) != '\n') return false;
192
1.57k
          std::string tmp = buffer.substr(0, chunk_size);
193
1.57k
          buffer.erase(buffer.begin(), buffer.begin()+chunk_size+crlf);
194
1.57k
          bodybuf << tmp;
195
1.57k
          chunk_size = 0;
196
1.57k
          if (buffer.size() == 0) break; // just in case
197
1.57k
        }
198
3.56k
      } else {
199
301
        if (bodybuf.str().length() + buffer.length() > maxbody)
200
97
          bodybuf << buffer.substr(0, maxbody - bodybuf.str().length());
201
204
        else
202
204
          bodybuf << buffer;
203
301
        buffer = "";
204
301
      }
205
3.86k
    }
206
207
556
    if (chunk_size!=0) return false; // need more data
208
209
496
    return ready();
210
556
  };
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
};