3 Copyright (c) 2008, Arvid Norberg
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
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"
38 #include "libtorrent/config.hpp"
39 #include "libtorrent/http_parser.hpp"
40 #include "libtorrent/assert.hpp"
42 using namespace libtorrent
;
46 char to_lower(char c
) { return std::tolower(c
); }
51 http_parser::http_parser()
54 , m_content_length(-1)
55 , m_state(read_status
)
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
)
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
;
92 m_state
= error_state
;
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
));
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
);
108 if (m_protocol
.substr(0, 5) == "HTTP/")
110 line
>> m_status_code
;
111 std::getline(line
, m_server_message
);
115 m_method
= m_protocol
;
116 std::transform(m_method
.begin(), m_method
.end(), m_method
.begin(), &to_lower
);
118 line
>> m_path
>> m_protocol
;
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');
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
);
139 m_recv_pos
+= newline
- pos
;
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
149 m_body_start_pos
= m_recv_pos
;
153 std::string name
= line
.substr(0, separator
);
154 std::transform(name
.begin(), name
.end(), name
.begin(), &to_lower
);
157 while (separator
< line
.size()
158 && (line
[separator
] == ' ' || line
[separator
] == '\t'))
160 std::string value
= line
.substr(separator
, std::string::npos
);
161 m_header
.insert(std::make_pair(name
, value
));
163 if (name
== "content-length")
166 m_content_length
= _atoi64(value
.c_str());
168 m_content_length
= atoll(value
.c_str());
171 else if (name
== "content-range")
173 std::stringstream
range_str(value
);
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
)
181 range_str
>> range_start
>> dummy
>> range_end
;
182 if (!range_str
|| range_end
< range_start
)
184 m_state
= error_state
;
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
)
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
));
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()
233 m_body_start_pos
= 0;
235 m_content_length
= -1;
237 m_state
= read_status
;
238 m_recv_buffer
.begin
= 0;
239 m_recv_buffer
.end
= 0;