/src/libtorrent/src/http_seed_connection.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | |
3 | | Copyright (c) 2008-2021, Arvid Norberg |
4 | | Copyright (c) 2016-2017, Alden Torres |
5 | | Copyright (c) 2016, Andrei Kurushin |
6 | | Copyright (c) 2016, 2018, Steven Siloti |
7 | | Copyright (c) 2017, Pavel Pimenov |
8 | | Copyright (c) 2018, TheOriginalWinCat |
9 | | Copyright (c) 2021, Dmtry Soloviev |
10 | | All rights reserved. |
11 | | |
12 | | Redistribution and use in source and binary forms, with or without |
13 | | modification, are permitted provided that the following conditions |
14 | | are met: |
15 | | |
16 | | * Redistributions of source code must retain the above copyright |
17 | | notice, this list of conditions and the following disclaimer. |
18 | | * Redistributions in binary form must reproduce the above copyright |
19 | | notice, this list of conditions and the following disclaimer in |
20 | | the documentation and/or other materials provided with the distribution. |
21 | | * Neither the name of the author nor the names of its |
22 | | contributors may be used to endorse or promote products derived |
23 | | from this software without specific prior written permission. |
24 | | |
25 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
26 | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
27 | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
28 | | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
29 | | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
30 | | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
31 | | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
32 | | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
33 | | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
34 | | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
35 | | POSSIBILITY OF SUCH DAMAGE. |
36 | | |
37 | | */ |
38 | | |
39 | | #include <cinttypes> // for PRId64 et.al. |
40 | | |
41 | | #include "libtorrent/config.hpp" |
42 | | #include "libtorrent/http_seed_connection.hpp" |
43 | | #include "libtorrent/aux_/invariant_check.hpp" |
44 | | #include "libtorrent/aux_/session_impl.hpp" |
45 | | #include "libtorrent/peer_info.hpp" |
46 | | #include "libtorrent/hex.hpp" // for is_hex |
47 | | #include "libtorrent/random.hpp" |
48 | | #include "libtorrent/optional.hpp" |
49 | | |
50 | | namespace libtorrent { |
51 | | |
52 | | http_seed_connection::http_seed_connection(peer_connection_args& pack |
53 | | , web_seed_t& web) |
54 | 0 | : web_connection_base(pack, web) |
55 | 0 | , m_url(web.url) |
56 | 0 | , m_web(&web) |
57 | 0 | , m_response_left(0) |
58 | 0 | , m_chunk_pos(0) |
59 | 0 | , m_partial_chunk_header(0) |
60 | 0 | { |
61 | 0 | INVARIANT_CHECK; |
62 | |
|
63 | 0 | if (!m_settings.get_bool(settings_pack::report_web_seed_downloads)) |
64 | 0 | ignore_stats(true); |
65 | |
|
66 | 0 | std::shared_ptr<torrent> tor = pack.tor.lock(); |
67 | 0 | TORRENT_ASSERT(tor); |
68 | 0 | int blocks_per_piece = tor->torrent_file().piece_length() / tor->block_size(); |
69 | | |
70 | | // multiply with the blocks per piece since that many requests are |
71 | | // merged into one http request |
72 | 0 | max_out_request_queue(m_settings.get_int(settings_pack::urlseed_pipeline_size) |
73 | 0 | * blocks_per_piece); |
74 | |
|
75 | 0 | prefer_contiguous_blocks(blocks_per_piece); |
76 | |
|
77 | | #ifndef TORRENT_DISABLE_LOGGING |
78 | | peer_log(peer_log_alert::info, "CONNECT", "http_seed_connection"); |
79 | | #endif |
80 | 0 | } |
81 | | |
82 | | void http_seed_connection::disable(error_code const& ec) |
83 | 0 | { |
84 | | // we should not try this server again. |
85 | 0 | m_web->disabled = true; |
86 | 0 | disconnect(ec, operation_t::bittorrent, peer_error); |
87 | 0 | if (m_web->ephemeral) |
88 | 0 | { |
89 | 0 | std::shared_ptr<torrent> t = associated_torrent().lock(); |
90 | 0 | TORRENT_ASSERT(t); |
91 | 0 | t->remove_web_seed_conn(this); |
92 | 0 | } |
93 | 0 | m_web = nullptr; |
94 | 0 | TORRENT_ASSERT(is_disconnecting()); |
95 | 0 | } |
96 | | |
97 | | void http_seed_connection::on_connected() |
98 | 0 | { |
99 | 0 | peer_id pid; |
100 | 0 | aux::random_bytes(pid); |
101 | 0 | set_pid(pid); |
102 | | |
103 | | // this is always a seed |
104 | 0 | incoming_have_all(); |
105 | 0 | web_connection_base::on_connected(); |
106 | 0 | } |
107 | | |
108 | | void http_seed_connection::disconnect(error_code const& ec |
109 | | , operation_t const op, disconnect_severity_t const error) |
110 | 0 | { |
111 | 0 | if (is_disconnecting()) return; |
112 | | |
113 | 0 | if (op == operation_t::connect && m_web && !m_web->endpoints.empty()) |
114 | 0 | { |
115 | | // we failed to connect to this IP. remove it so that the next attempt |
116 | | // uses the next IP in the list. |
117 | 0 | m_web->endpoints.erase(m_web->endpoints.begin()); |
118 | 0 | } |
119 | |
|
120 | 0 | std::shared_ptr<torrent> t = associated_torrent().lock(); |
121 | 0 | peer_connection::disconnect(ec, op, error); |
122 | 0 | TORRENT_ASSERT(m_web->resolving == false); |
123 | 0 | m_web->peer_info.connection = nullptr; |
124 | 0 | } |
125 | | |
126 | | piece_block_progress http_seed_connection::downloading_piece_progress() const |
127 | 0 | { |
128 | 0 | if (m_requests.empty()) return {}; |
129 | | |
130 | 0 | std::shared_ptr<torrent> t = associated_torrent().lock(); |
131 | 0 | TORRENT_ASSERT(t); |
132 | |
|
133 | 0 | piece_block_progress ret; |
134 | |
|
135 | 0 | peer_request const& pr = m_requests.front(); |
136 | 0 | ret.piece_index = pr.piece; |
137 | 0 | if (!m_parser.header_finished()) |
138 | 0 | { |
139 | 0 | ret.bytes_downloaded = 0; |
140 | 0 | } |
141 | 0 | else |
142 | 0 | { |
143 | 0 | int const receive_buffer_size = int(m_recv_buffer.get().size()) - m_parser.body_start(); |
144 | | // this is an approximation. in chunked encoding mode the chunk headers |
145 | | // should really be subtracted from the receive_buffer_size |
146 | 0 | ret.bytes_downloaded = std::max(0, t->block_size() - receive_buffer_size); |
147 | 0 | } |
148 | | // this is used to make sure that the block_index stays within |
149 | | // bounds. If the entire piece is downloaded, the block_index |
150 | | // would otherwise point to one past the end |
151 | 0 | int const correction = ret.bytes_downloaded ? -1 : 0; |
152 | 0 | ret.block_index = (pr.start + ret.bytes_downloaded + correction) / t->block_size(); |
153 | 0 | ret.full_block_bytes = t->block_size(); |
154 | 0 | piece_index_t const last_piece = t->torrent_file().last_piece(); |
155 | 0 | if (ret.piece_index == last_piece && ret.block_index |
156 | 0 | == t->torrent_file().piece_size(last_piece) / t->block_size()) |
157 | 0 | ret.full_block_bytes = t->torrent_file().piece_size(last_piece) % t->block_size(); |
158 | 0 | return ret; |
159 | 0 | } |
160 | | |
161 | | void http_seed_connection::write_request(peer_request const& r) |
162 | 0 | { |
163 | 0 | INVARIANT_CHECK; |
164 | |
|
165 | 0 | std::shared_ptr<torrent> t = associated_torrent().lock(); |
166 | 0 | TORRENT_ASSERT(t); |
167 | |
|
168 | 0 | TORRENT_ASSERT(t->valid_metadata()); |
169 | | // http_seeds don't support requesting more than one piece |
170 | | // at a time |
171 | 0 | TORRENT_ASSERT(r.length <= t->torrent_file().piece_size(r.piece)); |
172 | |
|
173 | 0 | std::string request; |
174 | 0 | request.reserve(400); |
175 | |
|
176 | 0 | int size = r.length; |
177 | 0 | const int bs = t->block_size(); |
178 | 0 | const int piece_size = t->torrent_file().piece_length(); |
179 | 0 | peer_request pr; |
180 | 0 | while (size > 0) |
181 | 0 | { |
182 | 0 | int request_offset = r.start + r.length - size; |
183 | 0 | pr.start = request_offset % piece_size; |
184 | 0 | pr.length = std::min(bs, size); |
185 | 0 | pr.piece = piece_index_t(static_cast<int>(r.piece) + request_offset / piece_size); |
186 | 0 | m_requests.push_back(pr); |
187 | 0 | size -= pr.length; |
188 | 0 | } |
189 | |
|
190 | 0 | int proxy_type = m_settings.get_int(settings_pack::proxy_type); |
191 | 0 | bool using_proxy = (proxy_type == settings_pack::http |
192 | 0 | || proxy_type == settings_pack::http_pw) && !m_ssl; |
193 | |
|
194 | 0 | request += "GET "; |
195 | 0 | request += using_proxy ? m_url : m_path; |
196 | 0 | request += "?info_hash="; |
197 | 0 | request += escape_string({associated_info_hash().data(), 20}); |
198 | 0 | request += "&piece="; |
199 | 0 | request += to_string(r.piece); |
200 | | |
201 | | // if we're requesting less than an entire piece we need to |
202 | | // add ranges |
203 | 0 | if (r.start > 0 || r.length != t->torrent_file().piece_size(r.piece)) |
204 | 0 | { |
205 | 0 | request += "&ranges="; |
206 | 0 | request += to_string(r.start).data(); |
207 | 0 | request += "-"; |
208 | | // ranges are inclusive, just like HTTP |
209 | 0 | request += to_string(r.start + r.length - 1).data(); |
210 | 0 | } |
211 | |
|
212 | 0 | request += " HTTP/1.1\r\n"; |
213 | 0 | add_headers(request, m_settings, using_proxy); |
214 | 0 | request += "\r\n\r\n"; |
215 | 0 | m_first_request = false; |
216 | |
|
217 | | #ifndef TORRENT_DISABLE_LOGGING |
218 | | peer_log(peer_log_alert::outgoing_message, "REQUEST", "%s", request.c_str()); |
219 | | #endif |
220 | |
|
221 | 0 | send_buffer(request); |
222 | 0 | } |
223 | | |
224 | | // -------------------------- |
225 | | // RECEIVE DATA |
226 | | // -------------------------- |
227 | | |
228 | | void http_seed_connection::on_receive(error_code const& error |
229 | | , std::size_t bytes_transferred) |
230 | 0 | { |
231 | 0 | INVARIANT_CHECK; |
232 | |
|
233 | 0 | if (error) |
234 | 0 | { |
235 | 0 | received_bytes(0, int(bytes_transferred)); |
236 | | #ifndef TORRENT_DISABLE_LOGGING |
237 | | if (should_log(peer_log_alert::info)) |
238 | | { |
239 | | peer_log(peer_log_alert::info, "ERROR" |
240 | | , "http_seed_connection error: %s", error.message().c_str()); |
241 | | } |
242 | | #endif |
243 | 0 | return; |
244 | 0 | } |
245 | | |
246 | 0 | std::shared_ptr<torrent> t = associated_torrent().lock(); |
247 | 0 | TORRENT_ASSERT(t); |
248 | |
|
249 | 0 | for (;;) |
250 | 0 | { |
251 | 0 | span<char const> recv_buffer = m_recv_buffer.get(); |
252 | |
|
253 | 0 | if (bytes_transferred == 0) break; |
254 | 0 | TORRENT_ASSERT(int(recv_buffer.size()) > 0); |
255 | |
|
256 | 0 | TORRENT_ASSERT(!m_requests.empty()); |
257 | 0 | if (m_requests.empty()) |
258 | 0 | { |
259 | 0 | received_bytes(0, int(bytes_transferred)); |
260 | 0 | disconnect(errors::http_error, operation_t::bittorrent, peer_error); |
261 | 0 | return; |
262 | 0 | } |
263 | | |
264 | 0 | peer_request front_request = m_requests.front(); |
265 | |
|
266 | 0 | bool header_finished = m_parser.header_finished(); |
267 | 0 | if (!header_finished) |
268 | 0 | { |
269 | 0 | bool parse_error = false; |
270 | 0 | int protocol = 0; |
271 | 0 | int payload = 0; |
272 | 0 | std::tie(payload, protocol) = m_parser.incoming( |
273 | 0 | recv_buffer, parse_error); |
274 | 0 | received_bytes(0, protocol); |
275 | 0 | TORRENT_ASSERT(bytes_transferred >= aux::numeric_cast<std::size_t>(protocol)); |
276 | 0 | bytes_transferred -= aux::numeric_cast<std::size_t>(protocol); |
277 | 0 | #if TORRENT_USE_ASSERTS |
278 | 0 | if (payload > front_request.length) payload = front_request.length; |
279 | 0 | #endif |
280 | |
|
281 | 0 | if (parse_error) |
282 | 0 | { |
283 | 0 | received_bytes(0, int(bytes_transferred)); |
284 | 0 | disconnect(errors::http_parse_error, operation_t::bittorrent, peer_error); |
285 | 0 | return; |
286 | 0 | } |
287 | | |
288 | 0 | TORRENT_ASSERT(int(recv_buffer.size()) == 0 || recv_buffer.front() == 'H'); |
289 | |
|
290 | 0 | TORRENT_ASSERT(int(recv_buffer.size()) <= m_recv_buffer.packet_size()); |
291 | | |
292 | | // this means the entire status line hasn't been received yet |
293 | 0 | if (m_parser.status_code() == -1) |
294 | 0 | { |
295 | 0 | TORRENT_ASSERT(payload == 0); |
296 | 0 | TORRENT_ASSERT(bytes_transferred == 0); |
297 | 0 | break; |
298 | 0 | } |
299 | | |
300 | | // if the status code is not one of the accepted ones, abort |
301 | 0 | if (!is_ok_status(m_parser.status_code())) |
302 | 0 | { |
303 | 0 | auto const retry_time = value_or(m_parser.header_duration("retry-after") |
304 | 0 | , seconds32(m_settings.get_int(settings_pack::urlseed_wait_retry))); |
305 | | |
306 | | // temporarily unavailable, retry later |
307 | 0 | t->retry_web_seed(this, retry_time); |
308 | |
|
309 | 0 | if (t->alerts().should_post<url_seed_alert>()) |
310 | 0 | { |
311 | 0 | std::string const error_msg = to_string(m_parser.status_code()).data() |
312 | 0 | + (" " + m_parser.message()); |
313 | 0 | t->alerts().emplace_alert<url_seed_alert>(t->get_handle(), url() |
314 | 0 | , error_msg); |
315 | 0 | } |
316 | 0 | received_bytes(0, int(bytes_transferred)); |
317 | 0 | disconnect(error_code(m_parser.status_code(), http_category()), operation_t::bittorrent, failure); |
318 | 0 | return; |
319 | 0 | } |
320 | 0 | if (!m_parser.header_finished()) |
321 | 0 | { |
322 | 0 | TORRENT_ASSERT(payload == 0); |
323 | 0 | TORRENT_ASSERT(bytes_transferred == 0); |
324 | 0 | break; |
325 | 0 | } |
326 | 0 | } |
327 | | |
328 | | // we just completed reading the header |
329 | 0 | if (!header_finished) |
330 | 0 | { |
331 | 0 | if (is_redirect(m_parser.status_code())) |
332 | 0 | { |
333 | | // this means we got a redirection request |
334 | | // look for the location header |
335 | 0 | std::string location = m_parser.header("location"); |
336 | 0 | received_bytes(0, int(bytes_transferred)); |
337 | |
|
338 | 0 | if (location.empty()) |
339 | 0 | { |
340 | | // we should not try this server again. |
341 | 0 | disable(errors::missing_location); |
342 | 0 | return; |
343 | 0 | } |
344 | | |
345 | | // when SSRF mitigation is enabled, a web seed on the internet (is_global()) |
346 | | // is not allowed to redirect to a server on the local network, so we set |
347 | | // the no_local_ips flag |
348 | 0 | auto const web_seed_flags = torrent::ephemeral |
349 | 0 | | ((m_settings.get_bool(settings_pack::ssrf_mitigation) && aux::is_global(remote().address())) |
350 | 0 | ? torrent::no_local_ips : web_seed_flag_t{}); |
351 | | |
352 | | // add the redirected url and remove the current one |
353 | 0 | t->add_web_seed(location, web_seed_entry::http_seed |
354 | 0 | , std::string{}, web_seed_entry::headers_t{} |
355 | 0 | , web_seed_flags); |
356 | 0 | disable(errors::redirecting); |
357 | 0 | return; |
358 | 0 | } |
359 | | |
360 | 0 | std::string const& server_version = m_parser.header("server"); |
361 | 0 | if (!server_version.empty()) |
362 | 0 | m_server_string = server_version; |
363 | 0 | else |
364 | 0 | m_server_string = m_host; |
365 | |
|
366 | 0 | m_response_left = atol(m_parser.header("content-length").c_str()); |
367 | 0 | if (m_response_left == -1) |
368 | 0 | { |
369 | 0 | received_bytes(0, int(bytes_transferred)); |
370 | | // we should not try this server again. |
371 | 0 | disable(errors::no_content_length); |
372 | 0 | return; |
373 | 0 | } |
374 | 0 | if (m_response_left != front_request.length) |
375 | 0 | { |
376 | 0 | received_bytes(0, int(bytes_transferred)); |
377 | | // we should not try this server again. |
378 | 0 | disable(errors::invalid_range); |
379 | 0 | return; |
380 | 0 | } |
381 | 0 | m_body_start = m_parser.body_start(); |
382 | 0 | } |
383 | | |
384 | 0 | recv_buffer = recv_buffer.subspan(m_body_start); |
385 | | |
386 | | // ========================= |
387 | | // === CHUNKED ENCODING === |
388 | | // ========================= |
389 | 0 | while (m_parser.chunked_encoding() |
390 | 0 | && m_chunk_pos >= 0 |
391 | 0 | && m_chunk_pos < recv_buffer.size()) |
392 | 0 | { |
393 | 0 | int header_size = 0; |
394 | 0 | std::int64_t chunk_size = 0; |
395 | 0 | span<char const> chunk_start = recv_buffer.subspan(aux::numeric_cast<std::ptrdiff_t>(m_chunk_pos)); |
396 | 0 | TORRENT_ASSERT(chunk_start[0] == '\r' |
397 | 0 | || aux::is_hex(chunk_start[0])); |
398 | 0 | bool ret = m_parser.parse_chunk_header(chunk_start, &chunk_size, &header_size); |
399 | 0 | if (!ret) |
400 | 0 | { |
401 | 0 | TORRENT_ASSERT(bytes_transferred >= aux::numeric_cast<std::size_t>(chunk_start.size() - m_partial_chunk_header)); |
402 | 0 | bytes_transferred -= aux::numeric_cast<std::size_t>(chunk_start.size() - m_partial_chunk_header); |
403 | 0 | received_bytes(0, aux::numeric_cast<int>(chunk_start.size() - m_partial_chunk_header)); |
404 | 0 | m_partial_chunk_header = aux::numeric_cast<int>(chunk_start.size()); |
405 | 0 | if (bytes_transferred == 0) return; |
406 | 0 | break; |
407 | 0 | } |
408 | 0 | else |
409 | 0 | { |
410 | | #ifndef TORRENT_DISABLE_LOGGING |
411 | | peer_log(peer_log_alert::info, "CHUNKED_ENCODING" |
412 | | , "parsed chunk: %" PRId64 " header_size: %d" |
413 | | , chunk_size, header_size); |
414 | | #endif |
415 | 0 | TORRENT_ASSERT(bytes_transferred >= aux::numeric_cast<std::size_t>(header_size - m_partial_chunk_header)); |
416 | 0 | bytes_transferred -= aux::numeric_cast<std::size_t>(header_size - m_partial_chunk_header); |
417 | |
|
418 | 0 | received_bytes(0, header_size - m_partial_chunk_header); |
419 | 0 | m_partial_chunk_header = 0; |
420 | 0 | TORRENT_ASSERT(chunk_size != 0 || chunk_start.size() <= header_size || chunk_start[header_size] == 'H'); |
421 | | // cut out the chunk header from the receive buffer |
422 | 0 | TORRENT_ASSERT(m_chunk_pos + m_body_start < INT_MAX); |
423 | 0 | m_recv_buffer.cut(header_size, t->block_size() + 1024, aux::numeric_cast<int>(m_chunk_pos + m_body_start)); |
424 | 0 | recv_buffer = m_recv_buffer.get(); |
425 | 0 | recv_buffer = recv_buffer.subspan(m_body_start); |
426 | 0 | m_chunk_pos += chunk_size; |
427 | 0 | if (chunk_size == 0) |
428 | 0 | { |
429 | 0 | TORRENT_ASSERT(m_recv_buffer.get().size() < m_chunk_pos + m_body_start + 1 |
430 | 0 | || m_recv_buffer.get()[static_cast<std::ptrdiff_t>(m_chunk_pos + m_body_start)] == 'H' |
431 | 0 | || (m_parser.chunked_encoding() |
432 | 0 | && m_recv_buffer.get()[static_cast<std::ptrdiff_t>(m_chunk_pos + m_body_start)] == '\r')); |
433 | 0 | m_chunk_pos = -1; |
434 | 0 | } |
435 | 0 | } |
436 | 0 | } |
437 | | |
438 | 0 | int payload = int(bytes_transferred); |
439 | 0 | if (payload > m_response_left) payload = int(m_response_left); |
440 | 0 | if (payload > front_request.length) payload = front_request.length; |
441 | | // TODO: technically, this isn't supposed to happen, but it seems to |
442 | | // sometimes. Some of the accounting is probably wrong in certain |
443 | | // cases |
444 | 0 | if (payload > outstanding_bytes()) payload = outstanding_bytes(); |
445 | 0 | received_bytes(payload, 0); |
446 | 0 | incoming_piece_fragment(payload); |
447 | 0 | m_response_left -= payload; |
448 | |
|
449 | 0 | if (m_parser.status_code() == 503) |
450 | 0 | { |
451 | 0 | if (!m_parser.finished()) return; |
452 | | |
453 | 0 | int retry_time = std::atoi(std::string(recv_buffer.begin(), recv_buffer.end()).c_str()); |
454 | 0 | if (retry_time <= 0) retry_time = 60; |
455 | | #ifndef TORRENT_DISABLE_LOGGING |
456 | | peer_log(peer_log_alert::info, "CONNECT", "retrying in %d seconds", retry_time); |
457 | | #endif |
458 | |
|
459 | 0 | received_bytes(0, int(bytes_transferred)); |
460 | | // temporarily unavailable, retry later |
461 | 0 | t->retry_web_seed(this, seconds32(retry_time)); |
462 | 0 | disconnect(error_code(m_parser.status_code(), http_category()), operation_t::bittorrent, failure); |
463 | 0 | return; |
464 | 0 | } |
465 | | |
466 | | |
467 | | // we only received the header, no data |
468 | 0 | if (recv_buffer.empty()) break; |
469 | | |
470 | 0 | if (recv_buffer.size() < front_request.length) break; |
471 | | |
472 | | // if the response is chunked, we need to receive the last |
473 | | // terminating chunk and the tail headers before we can proceed |
474 | 0 | if (m_parser.chunked_encoding() && m_chunk_pos >= 0) break; |
475 | | |
476 | 0 | m_requests.pop_front(); |
477 | 0 | incoming_piece(front_request, recv_buffer.begin()); |
478 | 0 | if (associated_torrent().expired()) return; |
479 | | |
480 | 0 | int const size_to_cut = m_body_start + front_request.length; |
481 | 0 | TORRENT_ASSERT(m_recv_buffer.get().size() < size_to_cut + 1 |
482 | 0 | || m_recv_buffer.get()[size_to_cut] == 'H' |
483 | 0 | || (m_parser.chunked_encoding() && m_recv_buffer.get()[size_to_cut] == '\r')); |
484 | |
|
485 | 0 | m_recv_buffer.cut(size_to_cut, t->block_size() + 1024); |
486 | 0 | if (m_response_left == 0) m_chunk_pos = 0; |
487 | 0 | else m_chunk_pos -= front_request.length; |
488 | 0 | TORRENT_ASSERT(bytes_transferred >= aux::numeric_cast<std::size_t>(payload)); |
489 | 0 | bytes_transferred -= aux::numeric_cast<std::size_t>(payload); |
490 | 0 | m_body_start = 0; |
491 | 0 | if (m_response_left > 0) continue; |
492 | 0 | TORRENT_ASSERT(m_response_left == 0); |
493 | 0 | m_parser.reset(); |
494 | 0 | } |
495 | 0 | } |
496 | | |
497 | | void http_seed_connection::get_specific_peer_info(peer_info& p) const |
498 | 0 | { |
499 | 0 | web_connection_base::get_specific_peer_info(p); |
500 | 0 | p.flags |= peer_info::local_connection; |
501 | 0 | p.connection_type = peer_info::http_seed; |
502 | 0 | } |
503 | | |
504 | | } |