AUTO_LT_SYNC
[tore.git] / libtorrent / src / http_parser.cpp
blobeb717420f3d1cf0b50e7ab2b405e1982354c2d1e
1 /*
3 Copyright (c) 2008, Arvid Norberg
4 All rights reserved.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
10 * Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in
14 the documentation and/or other materials provided with the distribution.
15 * Neither the name of the author nor the names of its
16 contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 POSSIBILITY OF SUCH DAMAGE.
33 #include "libtorrent/pch.hpp"
35 #include <cctype>
36 #include <algorithm>
38 #include "libtorrent/config.hpp"
39 #include "libtorrent/http_parser.hpp"
40 #include "libtorrent/assert.hpp"
42 using namespace libtorrent;
44 namespace
46 char to_lower(char c) { return std::tolower(c); }
49 namespace libtorrent
51 http_parser::http_parser()
52 : m_recv_pos(0)
53 , m_status_code(-1)
54 , m_content_length(-1)
55 , m_state(read_status)
56 , m_recv_buffer(0, 0)
57 , m_body_start_pos(0)
58 , m_finished(false)
61 boost::tuple<int, int> http_parser::incoming(
62 buffer::const_interval recv_buffer, bool& error)
64 TORRENT_ASSERT(recv_buffer.left() >= m_recv_buffer.left());
65 boost::tuple<int, int> ret(0, 0);
66 int start_pos = m_recv_buffer.left();
68 // early exit if there's nothing new in the receive buffer
69 if (start_pos == recv_buffer.left()) return ret;
70 m_recv_buffer = recv_buffer;
72 if (m_state == error_state)
74 error = true;
75 return ret;
78 char const* pos = recv_buffer.begin + m_recv_pos;
79 if (m_state == read_status)
81 TORRENT_ASSERT(!m_finished);
82 char const* newline = std::find(pos, recv_buffer.end, '\n');
83 // if we don't have a full line yet, wait.
84 if (newline == recv_buffer.end)
86 boost::get<1>(ret) += m_recv_buffer.left() - start_pos;
87 return ret;
90 if (newline == pos)
92 m_state = error_state;
93 error = true;
94 return ret;
97 char const* line_end = newline;
98 if (pos != line_end && *(line_end - 1) == '\r') --line_end;
100 std::istringstream line(std::string(pos, line_end));
101 ++newline;
102 int incoming = (int)std::distance(pos, newline);
103 m_recv_pos += incoming;
104 boost::get<1>(ret) += newline - (m_recv_buffer.begin + start_pos);
105 pos = newline;
107 line >> m_protocol;
108 if (m_protocol.substr(0, 5) == "HTTP/")
110 line >> m_status_code;
111 std::getline(line, m_server_message);
113 else
115 m_method = m_protocol;
116 std::transform(m_method.begin(), m_method.end(), m_method.begin(), &to_lower);
117 m_protocol.clear();
118 line >> m_path >> m_protocol;
119 m_status_code = 0;
121 m_state = read_header;
122 start_pos = pos - recv_buffer.begin;
125 if (m_state == read_header)
127 TORRENT_ASSERT(!m_finished);
128 char const* newline = std::find(pos, recv_buffer.end, '\n');
129 std::string line;
131 while (newline != recv_buffer.end && m_state == read_header)
133 // if the LF character is preceeded by a CR
134 // charachter, don't copy it into the line string.
135 char const* line_end = newline;
136 if (pos != line_end && *(line_end - 1) == '\r') --line_end;
137 line.assign(pos, line_end);
138 ++newline;
139 m_recv_pos += newline - pos;
140 pos = newline;
142 std::string::size_type separator = line.find(':');
143 if (separator == std::string::npos)
145 // this means we got a blank line,
146 // the header is finished and the body
147 // starts.
148 m_state = read_body;
149 m_body_start_pos = m_recv_pos;
150 break;
153 std::string name = line.substr(0, separator);
154 std::transform(name.begin(), name.end(), name.begin(), &to_lower);
155 ++separator;
156 // skip whitespace
157 while (separator < line.size()
158 && (line[separator] == ' ' || line[separator] == '\t'))
159 ++separator;
160 std::string value = line.substr(separator, std::string::npos);
161 m_header.insert(std::make_pair(name, value));
163 if (name == "content-length")
165 #ifdef WIN32
166 m_content_length = _atoi64(value.c_str());
167 #else
168 m_content_length = atoll(value.c_str());
169 #endif
171 else if (name == "content-range")
173 std::stringstream range_str(value);
174 char dummy;
175 std::string bytes;
176 size_type range_start, range_end;
177 // apparently some web servers do not send the "bytes"
178 // in their content-range
179 if (value.find(' ') != std::string::npos)
180 range_str >> bytes;
181 range_str >> range_start >> dummy >> range_end;
182 if (!range_str || range_end < range_start)
184 m_state = error_state;
185 error = true;
186 return ret;
188 // the http range is inclusive
189 m_content_length = range_end - range_start + 1;
192 TORRENT_ASSERT(m_recv_pos <= (int)recv_buffer.left());
193 newline = std::find(pos, recv_buffer.end, '\n');
195 boost::get<1>(ret) += newline - (m_recv_buffer.begin + start_pos);
198 if (m_state == read_body)
200 int incoming = recv_buffer.end - pos;
201 if (m_recv_pos - m_body_start_pos + incoming > m_content_length
202 && m_content_length >= 0)
203 incoming = m_content_length - m_recv_pos + m_body_start_pos;
205 TORRENT_ASSERT(incoming >= 0);
206 m_recv_pos += incoming;
207 boost::get<0>(ret) += incoming;
209 if (m_content_length >= 0
210 && m_recv_pos - m_body_start_pos >= m_content_length)
212 m_finished = true;
215 return ret;
218 buffer::const_interval http_parser::get_body() const
220 TORRENT_ASSERT(m_state == read_body);
221 if (m_content_length >= 0)
222 return buffer::const_interval(m_recv_buffer.begin + m_body_start_pos
223 , m_recv_buffer.begin + (std::min)(size_type(m_recv_pos)
224 , m_body_start_pos + m_content_length));
225 else
226 return buffer::const_interval(m_recv_buffer.begin + m_body_start_pos
227 , m_recv_buffer.begin + m_recv_pos);
230 void http_parser::reset()
232 m_recv_pos = 0;
233 m_body_start_pos = 0;
234 m_status_code = -1;
235 m_content_length = -1;
236 m_finished = false;
237 m_state = read_status;
238 m_recv_buffer.begin = 0;
239 m_recv_buffer.end = 0;
240 m_header.clear();