demux: adaptive: remove unused include
[vlc.git] / modules / demux / adaptive / http / HTTPConnection.cpp
blobbdc109350f2345a0806ceac28cb2cb2eae3cce68
1 /*
2 * HTTPConnection.cpp
3 *****************************************************************************
4 * Copyright (C) 2014-2015 - VideoLAN and VLC Authors
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published
8 * by the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
24 #include "HTTPConnection.hpp"
25 #include "ConnectionParams.hpp"
26 #include "AuthStorage.hpp"
27 #include "Sockets.hpp"
29 #include <cstdio>
30 #include <sstream>
31 #include <vlc_stream.h>
33 using namespace adaptive::http;
35 AbstractConnection::AbstractConnection(vlc_object_t *p_object_)
37 p_object = p_object_;
38 available = true;
39 bytesRead = 0;
40 contentLength = 0;
43 AbstractConnection::~AbstractConnection()
48 bool AbstractConnection::prepare(const ConnectionParams &params_)
50 if (!available)
51 return false;
52 params = params_;
53 available = false;
54 return true;
57 size_t AbstractConnection::getContentLength() const
59 return contentLength;
62 HTTPConnection::HTTPConnection(vlc_object_t *p_object_, AuthStorage *auth,
63 Socket *socket_, const ConnectionParams &proxy, bool persistent)
64 : AbstractConnection( p_object_ )
66 socket = socket_;
67 psz_useragent = var_InheritString(p_object_, "http-user-agent");
68 queryOk = false;
69 retries = 0;
70 authStorage = auth;
71 connectionClose = !persistent;
72 chunked = false;
73 chunked_eof = false;
74 chunkLength = 0;
75 proxyparams = proxy;
78 HTTPConnection::~HTTPConnection()
80 free(psz_useragent);
81 delete socket;
84 bool HTTPConnection::canReuse(const ConnectionParams &params_) const
86 if( !available )
87 return false;
89 char *psz_proxy_url = vlc_getProxyUrl(params_.getUrl().c_str());
90 if(psz_proxy_url)
92 ConnectionParams proxy(psz_proxy_url);
93 free(psz_proxy_url);
94 return (proxyparams.getHostname() == proxy.getHostname() &&
95 proxyparams.getScheme() == proxy.getScheme() &&
96 proxyparams.getPort() == proxy.getPort());
98 else return (params.getHostname() == params_.getHostname() &&
99 params.getScheme() == params_.getScheme() &&
100 params.getPort() == params_.getPort());
103 bool HTTPConnection::connect()
105 if(proxyparams.getHostname().empty())
106 return socket->connect(p_object, params.getHostname().c_str(),
107 params.getPort());
108 else
109 return socket->connect(p_object, proxyparams.getHostname().c_str(),
110 proxyparams.getPort());
113 bool HTTPConnection::connected() const
115 return socket->connected();
118 void HTTPConnection::disconnect()
120 queryOk = false;
121 bytesRead = 0;
122 contentLength = 0;
123 chunked = false;
124 chunkLength = 0;
125 bytesRange = BytesRange();
126 socket->disconnect();
129 int HTTPConnection::request(const std::string &path, const BytesRange &range)
131 queryOk = false;
132 chunked = false;
133 chunked_eof = false;
134 chunkLength = 0;
136 /* Set new path for this query */
137 params.setPath(path);
139 msg_Dbg(p_object, "Retrieving %s @%zu", params.getUrl().c_str(),
140 range.isValid() ? range.getStartByte() : 0);
142 if(!proxyparams.getHostname().empty())
143 msg_Dbg(p_object, "Using proxy %s", proxyparams.getUrl().c_str());
145 if(!connected() && ( params.getHostname().empty() || !connect() ))
146 return VLC_EGENERIC;
148 bytesRange = range;
149 if(range.isValid() && range.getEndByte() > 0)
150 contentLength = range.getEndByte() - range.getStartByte() + 1;
152 std::string header = buildRequestHeader(path);
153 if(connectionClose)
154 header.append("Connection: close\r\n");
155 header.append("\r\n");
157 if(!send( header ))
159 socket->disconnect();
160 if(!connectionClose)
162 /* server closed connection pipeline after last req. need new */
163 connectionClose = true;
164 return request(path, range);
166 return VLC_EGENERIC;
169 int i_ret = parseReply();
170 if(i_ret == VLC_SUCCESS)
172 queryOk = true;
174 else if(i_ret == VLC_ETIMEOUT) /* redir */
176 socket->disconnect();
177 if(locationparams.getScheme().empty())
178 params.setPath(locationparams.getPath());
179 else
180 params = locationparams;
181 locationparams = ConnectionParams();
183 else if(i_ret == VLC_EGENERIC)
185 socket->disconnect();
186 if(!connectionClose)
188 connectionClose = true;
189 return request(path, range);
193 return i_ret;
196 ssize_t HTTPConnection::read(void *p_buffer, size_t len)
198 if( !connected() ||
199 (!queryOk && bytesRead == 0) )
200 return VLC_EGENERIC;
202 if(len == 0)
203 return VLC_SUCCESS;
205 queryOk = false;
207 const size_t toRead = (contentLength) ? contentLength - bytesRead : len;
208 if (toRead == 0)
209 return VLC_SUCCESS;
211 if(len > toRead)
212 len = toRead;
214 ssize_t ret = ( chunked ) ? readChunk(p_buffer, len)
215 : socket->read(p_object, p_buffer, len);
216 if(ret >= 0)
217 bytesRead += ret;
219 if(ret < 0 || (size_t)ret < len || /* set EOF */
220 (contentLength == bytesRead && connectionClose))
222 socket->disconnect();
223 return ret;
226 return ret;
229 bool HTTPConnection::send(const std::string &data)
231 return send(data.c_str(), data.length());
234 bool HTTPConnection::send(const void *buf, size_t size)
236 return socket->send(p_object, buf, size);
239 int HTTPConnection::parseReply()
241 std::string statusline = readLine();
243 if(statusline.empty())
244 return VLC_EGENERIC;
246 if (statusline.compare(0, 9, "HTTP/1.1 ")!=0)
248 if(statusline.compare(0, 9, "HTTP/1.0 ")!=0)
249 return VLC_ENOOBJ;
250 else
251 connectionClose = true;
254 std::istringstream ss(statusline.substr(9));
255 ss.imbue(std::locale("C"));
256 int replycode;
257 ss >> replycode;
259 std::string lines;
260 for( ;; )
262 std::string l = readLine();
263 if(l.empty())
264 break;
265 lines.append(l);
267 size_t split = lines.find_first_of(':');
268 if(split != std::string::npos)
270 size_t value = lines.find_first_not_of(' ', split + 1);
271 if(value == std::string::npos)
272 value = lines.length();
273 onHeader(lines.substr(0, split), lines.substr(value));
274 lines = std::string();
278 if((replycode == 301 || replycode == 302 || replycode == 307 || replycode == 308) &&
279 !locationparams.getUrl().empty())
281 msg_Info(p_object, "%d redirection to %s", replycode, locationparams.getUrl().c_str());
282 return VLC_ETIMEOUT;
284 else if (replycode != 200 && replycode != 206)
286 msg_Err(p_object, "Failed reading %s: %s", params.getUrl().c_str(), statusline.c_str());
287 return VLC_ENOOBJ;
290 return VLC_SUCCESS;
293 ssize_t HTTPConnection::readChunk(void *p_buffer, size_t len)
295 size_t copied = 0;
297 for( ; copied < len && !chunked_eof; )
299 /* adapted from access/http/chunked.c */
300 if(chunkLength == 0)
302 std::string line = readLine();
303 int end;
304 if (std::sscanf(line.c_str(), "%zx%n", &chunkLength, &end) < 1
305 || (line[end] != '\0' && line[end] != ';' /* ignore extension(s) */))
306 return -1;
309 if(chunkLength > 0)
311 size_t toread = len - copied;
312 if(toread > chunkLength)
313 toread = chunkLength;
315 ssize_t in = socket->read(p_object, &((uint8_t*)p_buffer)[copied], toread);
316 if(in < 0)
318 return (copied == 0) ? in : copied;
320 else if((size_t)in < toread)
322 return copied + in;
324 copied += in;
325 chunkLength -= in;
327 else chunked_eof = true;
329 if(chunkLength == 0)
331 char crlf[2];
332 ssize_t in = socket->read(p_object, &crlf, 2);
333 if(in < 2 || memcmp(crlf, "\r\n", 2))
334 return (copied == 0) ? -1 : copied;
338 return copied;
341 std::string HTTPConnection::readLine()
343 return socket->readline(p_object);
346 void HTTPConnection::setUsed( bool b )
348 available = !b;
349 if(available)
351 if(!connectionClose && contentLength == bytesRead )
353 queryOk = false;
354 bytesRead = 0;
355 contentLength = 0;
356 bytesRange = BytesRange();
358 else /* We can't resend request if we haven't finished reading */
359 disconnect();
363 void HTTPConnection::onHeader(const std::string &key,
364 const std::string &value)
366 if(key == "Content-Length")
368 std::istringstream ss(value);
369 ss.imbue(std::locale("C"));
370 size_t length;
371 ss >> length;
372 contentLength = length;
374 else if (key == "Connection" && value =="close")
376 connectionClose = true;
378 else if (key == "Transfer-Encoding" && value == "chunked")
380 chunked = true;
382 else if(key == "Location")
384 locationparams = ConnectionParams( value );
386 else if(key == "Set-Cookie" && authStorage)
388 authStorage->addCookie( value, params );
392 std::string HTTPConnection::buildRequestHeader(const std::string &path) const
394 std::stringstream req;
395 req.imbue(std::locale("C"));
396 req << "GET " << path << " HTTP/1.1\r\n";
397 if((params.getScheme() == "http" && params.getPort() != 80) ||
398 (params.getScheme() == "https" && params.getPort() != 443))
400 req << "Host: " << params.getHostname() << ":" << params.getPort() << "\r\n";
402 else
404 req << "Host: " << params.getHostname() << "\r\n";
406 if(authStorage)
408 std::string cookie = authStorage->getCookie(params,
409 params.getScheme() == "https" ||
410 params.getPort() == 443);
411 if(!cookie.empty())
412 req << "Cookie: " << cookie << "\r\n";
414 req << "Cache-Control: no-cache" << "\r\n" <<
415 "User-Agent: " << std::string(psz_useragent) << "\r\n";
416 req << extraRequestHeaders();
417 return req.str();
420 std::string HTTPConnection::extraRequestHeaders() const
422 std::stringstream ss;
423 ss.imbue(std::locale("C"));
424 if(bytesRange.isValid())
426 ss << "Range: bytes=" << bytesRange.getStartByte() << "-";
427 if(bytesRange.getEndByte())
428 ss << bytesRange.getEndByte();
429 ss << "\r\n";
431 return ss.str();
434 StreamUrlConnection::StreamUrlConnection(vlc_object_t *p_object)
435 : AbstractConnection(p_object)
437 p_streamurl = NULL;
438 bytesRead = 0;
439 contentLength = 0;
442 StreamUrlConnection::~StreamUrlConnection()
444 reset();
447 void StreamUrlConnection::reset()
449 if(p_streamurl)
450 vlc_stream_Delete(p_streamurl);
451 p_streamurl = NULL;
452 bytesRead = 0;
453 contentLength = 0;
454 bytesRange = BytesRange();
457 bool StreamUrlConnection::canReuse(const ConnectionParams &) const
459 return available;
462 int StreamUrlConnection::request(const std::string &path, const BytesRange &range)
464 reset();
466 /* Set new path for this query */
467 params.setPath(path);
469 msg_Dbg(p_object, "Retrieving %s @%zu", params.getUrl().c_str(),
470 range.isValid() ? range.getStartByte() : 0);
472 p_streamurl = vlc_stream_NewURL(p_object, params.getUrl().c_str());
473 if(!p_streamurl)
474 return VLC_EGENERIC;
476 stream_t *p_chain = vlc_stream_FilterNew( p_streamurl, "inflate" );
477 if( p_chain )
478 p_streamurl = p_chain;
480 if(range.isValid() && range.getEndByte() > 0)
482 if(vlc_stream_Seek(p_streamurl, range.getStartByte()) != VLC_SUCCESS)
484 vlc_stream_Delete(p_streamurl);
485 return VLC_EGENERIC;
487 bytesRange = range;
488 contentLength = range.getEndByte() - range.getStartByte() + 1;
491 int64_t i_size = stream_Size(p_streamurl);
492 if(i_size > -1)
494 if(!range.isValid() || contentLength > (size_t) i_size)
495 contentLength = (size_t) i_size;
497 return VLC_SUCCESS;
500 ssize_t StreamUrlConnection::read(void *p_buffer, size_t len)
502 if( !p_streamurl )
503 return VLC_EGENERIC;
505 if(len == 0)
506 return VLC_SUCCESS;
508 const size_t toRead = (contentLength) ? contentLength - bytesRead : len;
509 if (toRead == 0)
510 return VLC_SUCCESS;
512 if(len > toRead)
513 len = toRead;
515 ssize_t ret = vlc_stream_Read(p_streamurl, p_buffer, len);
516 if(ret >= 0)
517 bytesRead += ret;
519 if(ret < 0 || (size_t)ret < len || /* set EOF */
520 contentLength == bytesRead )
522 reset();
523 return ret;
526 return ret;
529 void StreamUrlConnection::setUsed( bool b )
531 available = !b;
532 if(available && contentLength == bytesRead)
533 reset();
536 ConnectionFactory::ConnectionFactory( AuthStorage *auth )
538 authStorage = auth;
541 ConnectionFactory::~ConnectionFactory()
545 AbstractConnection * ConnectionFactory::createConnection(vlc_object_t *p_object,
546 const ConnectionParams &params)
548 if((params.getScheme() != "http" && params.getScheme() != "https") || params.getHostname().empty())
549 return NULL;
551 ConnectionParams proxy;
553 std::string scheme;
554 char *psz_proxy_url = vlc_getProxyUrl(params.getUrl().c_str());
555 if(psz_proxy_url)
557 proxy = ConnectionParams(psz_proxy_url);
558 free(psz_proxy_url);
559 scheme = proxy.getScheme();
561 else scheme = params.getScheme();
563 const int sockettype = (params.getScheme() == "https") ? TLSSocket::TLS : Socket::REGULAR;
564 Socket *socket = (sockettype == TLSSocket::TLS) ? new (std::nothrow) TLSSocket()
565 : new (std::nothrow) Socket();
566 if(!socket)
567 return NULL;
569 /* disable pipelined tls until we have ticket/resume session support */
570 HTTPConnection *conn = new (std::nothrow)
571 HTTPConnection(p_object, authStorage, socket, proxy, sockettype != TLSSocket::TLS);
572 if(!conn)
574 delete socket;
575 return NULL;
578 return conn;
581 StreamUrlConnectionFactory::StreamUrlConnectionFactory()
582 : ConnectionFactory( NULL )
587 AbstractConnection * StreamUrlConnectionFactory::createConnection(vlc_object_t *p_object,
588 const ConnectionParams &)
590 return new (std::nothrow) StreamUrlConnection(p_object);