2004-03-03 14:23:47 +01:00
|
|
|
/*****************************************************************************
|
|
|
|
* httpd.c
|
|
|
|
*****************************************************************************
|
LGPL
Re-license almost all of libVLC and libVLCcore to LGPLv2.1+
This move was authorized by the developers, either:
- by e-mail,
- by vote at the VideoLAN Dev Days 2011,
- on the license website,
- in a contract, oral or written.
No objection was raised, so far.
The developers agreeing are:
Justus Piater
Alexis Ballier
Alexander Bethke
Mohammed Adnène Trojette
Alex Converse
Alexey Sokolov
Alexis de Lattre
Andre Pang
Anthony Loiseau
Cyril Deguet
André Weber
Boris Dorès
Brieuc Jeunhomme
Benjamin Drung
Hugo Beauzée-Luyssen
Benoit Steiner
Benjamin Pracht
Bernie Purcell
Przemyslaw Fiala
Arnaud de Bossoreille de Ribou
Brad Smith
Nick Briggs
Christopher Rath
Christophe Courtaut
Christopher Mueller
Clement Chesnin
Andres Krapf
Damien Fouilleul
David Flynn
Sebastien Zwickert
Antoine Cellerier
Jérôme Decoodt
Jérome Decoodt
Dylan Yudaken
Eduard Babayan
Eugenio Jarosiewicz
Elliot Murphy
Eric Petit
Erwan Tulou
Etienne Membrives
Ludovic Fauvet
Fabio Ritrovato
Tobias Güntner
Jakub Wieczorek
Frédéric Crozat
Francois Cartegnie
Laurent Aimar
Florian G. Pflug
Felix Paul Kühne
Frank Enderle
Rafaël Carré
Simon Latapie
Gildas Bazin
Geoffroy Couprie
Julien / Gellule
Gildas Bazin
Arnaud Schauly
Toralf Niebuhr
Vicente Jimenez Aguilar
Derk-Jan Hartman
Henri Fallon
Ilkka Ollakka
Olivier Teulière
Rémi Duraffort
Jakob Leben
Jean-Baptiste Kempf
Jean-Paul Saman
Jean-Philippe Grimaldi
Jean-François Massol
Gaël Hendryckx
Jakob Leben
Jean-Marc Dressler
Jai Menon
Johan Bilien
Johann Ransay
Joris van Rooij
JP Dinger
Jean-Philippe André
Adrien Grand
Juha Jeronen
Juho Vähä-Herttua
Kaarlo Raiha
Kaarlo Raiha
Kamil Baldyga
Keary Griffin
Ken Self
KO Myung-Hun
Pierre Ynard
Filippo Carone
Loïc Minier
Luca Barbato
Lucas C. Villa Real
Lukas Durfina
Adrien Maglo
Marc Ariberti
Mark Lee
Mark Moriarty
Martin Storsjö
Christophe Massiot
Michel Kaempf
Marian Ďurkovič
Mirsal Ennaime
Carlo Calabrò
Damien Lucas
Naohiro Koriyama
Basos G
Pierre Baillet
Vincent Penquerc'h
Olivier Aubert
Pankaj Yadav
Paul Corke
Pierre d'Herbemont
Philippe Morin
Antoine Lejeune
Michael Ploujnikov
Jean-Marc Dressler
Michael Hanselmann
Rafaël Carré
Ramiro Polla
Rémi Denis-Courmont
Renaud Dartus
Richard Shepherd
Faustino Osuna
Arnaud Vallat
Rob Jonson
Robert Jedrzejczyk
Steve Lhomme
Rocky Bernstein
Romain Goyet
Rov Juvano
Sam Hocevar
Martin T. H. Sandsmark
Sebastian Birk
Sébastien Escudier
Vincent Seguin
Fabio Ritrovato
Sigmund Augdal Helberg
Casian Andrei
Srikanth Raju
Hannes Domani
Stéphane Borel
Stephan Krempel
Stephan Assmus
Tony Castley
Pavlov Konstantin
Eric Petit
Tanguy Krotoff
Dennis van Amerongen
Michel Lespinasse
Can Wu
Xavier Marchesini
Sébastien Toque
Christophe Mutricy
Yoann Peronneau
Yohann Martineau
Yuval Tze
Scott Caudle
Clément Stenac
It is possible, that some minor piece of code was badly tracked, for
some reasons (SVN, mainly) or that some small developers did not answer.
However, as an "œuvre collective", defined as in "CPI 113-2 alinéa 3",
and seeing "Cour. Cass. 17 Mai 1978", and seeing that the editor and
the very vast majority of developers have agreed (> 99.99% of the code,
> 99% of developers), we are fine here.
2011-11-27 21:44:15 +01:00
|
|
|
* Copyright (C) 2004-2006 VLC authors and VideoLAN
|
2007-04-05 18:42:16 +02:00
|
|
|
* Copyright © 2004-2007 Rémi Denis-Courmont
|
2004-03-03 14:23:47 +01:00
|
|
|
*
|
|
|
|
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
|
2019-09-04 21:10:51 +02:00
|
|
|
* Rémi Denis-Courmont
|
2004-03-03 14:23:47 +01:00
|
|
|
*
|
LGPL
Re-license almost all of libVLC and libVLCcore to LGPLv2.1+
This move was authorized by the developers, either:
- by e-mail,
- by vote at the VideoLAN Dev Days 2011,
- on the license website,
- in a contract, oral or written.
No objection was raised, so far.
The developers agreeing are:
Justus Piater
Alexis Ballier
Alexander Bethke
Mohammed Adnène Trojette
Alex Converse
Alexey Sokolov
Alexis de Lattre
Andre Pang
Anthony Loiseau
Cyril Deguet
André Weber
Boris Dorès
Brieuc Jeunhomme
Benjamin Drung
Hugo Beauzée-Luyssen
Benoit Steiner
Benjamin Pracht
Bernie Purcell
Przemyslaw Fiala
Arnaud de Bossoreille de Ribou
Brad Smith
Nick Briggs
Christopher Rath
Christophe Courtaut
Christopher Mueller
Clement Chesnin
Andres Krapf
Damien Fouilleul
David Flynn
Sebastien Zwickert
Antoine Cellerier
Jérôme Decoodt
Jérome Decoodt
Dylan Yudaken
Eduard Babayan
Eugenio Jarosiewicz
Elliot Murphy
Eric Petit
Erwan Tulou
Etienne Membrives
Ludovic Fauvet
Fabio Ritrovato
Tobias Güntner
Jakub Wieczorek
Frédéric Crozat
Francois Cartegnie
Laurent Aimar
Florian G. Pflug
Felix Paul Kühne
Frank Enderle
Rafaël Carré
Simon Latapie
Gildas Bazin
Geoffroy Couprie
Julien / Gellule
Gildas Bazin
Arnaud Schauly
Toralf Niebuhr
Vicente Jimenez Aguilar
Derk-Jan Hartman
Henri Fallon
Ilkka Ollakka
Olivier Teulière
Rémi Duraffort
Jakob Leben
Jean-Baptiste Kempf
Jean-Paul Saman
Jean-Philippe Grimaldi
Jean-François Massol
Gaël Hendryckx
Jakob Leben
Jean-Marc Dressler
Jai Menon
Johan Bilien
Johann Ransay
Joris van Rooij
JP Dinger
Jean-Philippe André
Adrien Grand
Juha Jeronen
Juho Vähä-Herttua
Kaarlo Raiha
Kaarlo Raiha
Kamil Baldyga
Keary Griffin
Ken Self
KO Myung-Hun
Pierre Ynard
Filippo Carone
Loïc Minier
Luca Barbato
Lucas C. Villa Real
Lukas Durfina
Adrien Maglo
Marc Ariberti
Mark Lee
Mark Moriarty
Martin Storsjö
Christophe Massiot
Michel Kaempf
Marian Ďurkovič
Mirsal Ennaime
Carlo Calabrò
Damien Lucas
Naohiro Koriyama
Basos G
Pierre Baillet
Vincent Penquerc'h
Olivier Aubert
Pankaj Yadav
Paul Corke
Pierre d'Herbemont
Philippe Morin
Antoine Lejeune
Michael Ploujnikov
Jean-Marc Dressler
Michael Hanselmann
Rafaël Carré
Ramiro Polla
Rémi Denis-Courmont
Renaud Dartus
Richard Shepherd
Faustino Osuna
Arnaud Vallat
Rob Jonson
Robert Jedrzejczyk
Steve Lhomme
Rocky Bernstein
Romain Goyet
Rov Juvano
Sam Hocevar
Martin T. H. Sandsmark
Sebastian Birk
Sébastien Escudier
Vincent Seguin
Fabio Ritrovato
Sigmund Augdal Helberg
Casian Andrei
Srikanth Raju
Hannes Domani
Stéphane Borel
Stephan Krempel
Stephan Assmus
Tony Castley
Pavlov Konstantin
Eric Petit
Tanguy Krotoff
Dennis van Amerongen
Michel Lespinasse
Can Wu
Xavier Marchesini
Sébastien Toque
Christophe Mutricy
Yoann Peronneau
Yohann Martineau
Yuval Tze
Scott Caudle
Clément Stenac
It is possible, that some minor piece of code was badly tracked, for
some reasons (SVN, mainly) or that some small developers did not answer.
However, as an "œuvre collective", defined as in "CPI 113-2 alinéa 3",
and seeing "Cour. Cass. 17 Mai 1978", and seeing that the editor and
the very vast majority of developers have agreed (> 99.99% of the code,
> 99% of developers), we are fine here.
2011-11-27 21:44:15 +01:00
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2.1 of the License, or
|
2004-03-03 14:23:47 +01:00
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
LGPL
Re-license almost all of libVLC and libVLCcore to LGPLv2.1+
This move was authorized by the developers, either:
- by e-mail,
- by vote at the VideoLAN Dev Days 2011,
- on the license website,
- in a contract, oral or written.
No objection was raised, so far.
The developers agreeing are:
Justus Piater
Alexis Ballier
Alexander Bethke
Mohammed Adnène Trojette
Alex Converse
Alexey Sokolov
Alexis de Lattre
Andre Pang
Anthony Loiseau
Cyril Deguet
André Weber
Boris Dorès
Brieuc Jeunhomme
Benjamin Drung
Hugo Beauzée-Luyssen
Benoit Steiner
Benjamin Pracht
Bernie Purcell
Przemyslaw Fiala
Arnaud de Bossoreille de Ribou
Brad Smith
Nick Briggs
Christopher Rath
Christophe Courtaut
Christopher Mueller
Clement Chesnin
Andres Krapf
Damien Fouilleul
David Flynn
Sebastien Zwickert
Antoine Cellerier
Jérôme Decoodt
Jérome Decoodt
Dylan Yudaken
Eduard Babayan
Eugenio Jarosiewicz
Elliot Murphy
Eric Petit
Erwan Tulou
Etienne Membrives
Ludovic Fauvet
Fabio Ritrovato
Tobias Güntner
Jakub Wieczorek
Frédéric Crozat
Francois Cartegnie
Laurent Aimar
Florian G. Pflug
Felix Paul Kühne
Frank Enderle
Rafaël Carré
Simon Latapie
Gildas Bazin
Geoffroy Couprie
Julien / Gellule
Gildas Bazin
Arnaud Schauly
Toralf Niebuhr
Vicente Jimenez Aguilar
Derk-Jan Hartman
Henri Fallon
Ilkka Ollakka
Olivier Teulière
Rémi Duraffort
Jakob Leben
Jean-Baptiste Kempf
Jean-Paul Saman
Jean-Philippe Grimaldi
Jean-François Massol
Gaël Hendryckx
Jakob Leben
Jean-Marc Dressler
Jai Menon
Johan Bilien
Johann Ransay
Joris van Rooij
JP Dinger
Jean-Philippe André
Adrien Grand
Juha Jeronen
Juho Vähä-Herttua
Kaarlo Raiha
Kaarlo Raiha
Kamil Baldyga
Keary Griffin
Ken Self
KO Myung-Hun
Pierre Ynard
Filippo Carone
Loïc Minier
Luca Barbato
Lucas C. Villa Real
Lukas Durfina
Adrien Maglo
Marc Ariberti
Mark Lee
Mark Moriarty
Martin Storsjö
Christophe Massiot
Michel Kaempf
Marian Ďurkovič
Mirsal Ennaime
Carlo Calabrò
Damien Lucas
Naohiro Koriyama
Basos G
Pierre Baillet
Vincent Penquerc'h
Olivier Aubert
Pankaj Yadav
Paul Corke
Pierre d'Herbemont
Philippe Morin
Antoine Lejeune
Michael Ploujnikov
Jean-Marc Dressler
Michael Hanselmann
Rafaël Carré
Ramiro Polla
Rémi Denis-Courmont
Renaud Dartus
Richard Shepherd
Faustino Osuna
Arnaud Vallat
Rob Jonson
Robert Jedrzejczyk
Steve Lhomme
Rocky Bernstein
Romain Goyet
Rov Juvano
Sam Hocevar
Martin T. H. Sandsmark
Sebastian Birk
Sébastien Escudier
Vincent Seguin
Fabio Ritrovato
Sigmund Augdal Helberg
Casian Andrei
Srikanth Raju
Hannes Domani
Stéphane Borel
Stephan Krempel
Stephan Assmus
Tony Castley
Pavlov Konstantin
Eric Petit
Tanguy Krotoff
Dennis van Amerongen
Michel Lespinasse
Can Wu
Xavier Marchesini
Sébastien Toque
Christophe Mutricy
Yoann Peronneau
Yohann Martineau
Yuval Tze
Scott Caudle
Clément Stenac
It is possible, that some minor piece of code was badly tracked, for
some reasons (SVN, mainly) or that some small developers did not answer.
However, as an "œuvre collective", defined as in "CPI 113-2 alinéa 3",
and seeing "Cour. Cass. 17 Mai 1978", and seeing that the editor and
the very vast majority of developers have agreed (> 99.99% of the code,
> 99% of developers), we are fine here.
2011-11-27 21:44:15 +01:00
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU Lesser General Public License for more details.
|
2004-03-03 14:23:47 +01:00
|
|
|
*
|
LGPL
Re-license almost all of libVLC and libVLCcore to LGPLv2.1+
This move was authorized by the developers, either:
- by e-mail,
- by vote at the VideoLAN Dev Days 2011,
- on the license website,
- in a contract, oral or written.
No objection was raised, so far.
The developers agreeing are:
Justus Piater
Alexis Ballier
Alexander Bethke
Mohammed Adnène Trojette
Alex Converse
Alexey Sokolov
Alexis de Lattre
Andre Pang
Anthony Loiseau
Cyril Deguet
André Weber
Boris Dorès
Brieuc Jeunhomme
Benjamin Drung
Hugo Beauzée-Luyssen
Benoit Steiner
Benjamin Pracht
Bernie Purcell
Przemyslaw Fiala
Arnaud de Bossoreille de Ribou
Brad Smith
Nick Briggs
Christopher Rath
Christophe Courtaut
Christopher Mueller
Clement Chesnin
Andres Krapf
Damien Fouilleul
David Flynn
Sebastien Zwickert
Antoine Cellerier
Jérôme Decoodt
Jérome Decoodt
Dylan Yudaken
Eduard Babayan
Eugenio Jarosiewicz
Elliot Murphy
Eric Petit
Erwan Tulou
Etienne Membrives
Ludovic Fauvet
Fabio Ritrovato
Tobias Güntner
Jakub Wieczorek
Frédéric Crozat
Francois Cartegnie
Laurent Aimar
Florian G. Pflug
Felix Paul Kühne
Frank Enderle
Rafaël Carré
Simon Latapie
Gildas Bazin
Geoffroy Couprie
Julien / Gellule
Gildas Bazin
Arnaud Schauly
Toralf Niebuhr
Vicente Jimenez Aguilar
Derk-Jan Hartman
Henri Fallon
Ilkka Ollakka
Olivier Teulière
Rémi Duraffort
Jakob Leben
Jean-Baptiste Kempf
Jean-Paul Saman
Jean-Philippe Grimaldi
Jean-François Massol
Gaël Hendryckx
Jakob Leben
Jean-Marc Dressler
Jai Menon
Johan Bilien
Johann Ransay
Joris van Rooij
JP Dinger
Jean-Philippe André
Adrien Grand
Juha Jeronen
Juho Vähä-Herttua
Kaarlo Raiha
Kaarlo Raiha
Kamil Baldyga
Keary Griffin
Ken Self
KO Myung-Hun
Pierre Ynard
Filippo Carone
Loïc Minier
Luca Barbato
Lucas C. Villa Real
Lukas Durfina
Adrien Maglo
Marc Ariberti
Mark Lee
Mark Moriarty
Martin Storsjö
Christophe Massiot
Michel Kaempf
Marian Ďurkovič
Mirsal Ennaime
Carlo Calabrò
Damien Lucas
Naohiro Koriyama
Basos G
Pierre Baillet
Vincent Penquerc'h
Olivier Aubert
Pankaj Yadav
Paul Corke
Pierre d'Herbemont
Philippe Morin
Antoine Lejeune
Michael Ploujnikov
Jean-Marc Dressler
Michael Hanselmann
Rafaël Carré
Ramiro Polla
Rémi Denis-Courmont
Renaud Dartus
Richard Shepherd
Faustino Osuna
Arnaud Vallat
Rob Jonson
Robert Jedrzejczyk
Steve Lhomme
Rocky Bernstein
Romain Goyet
Rov Juvano
Sam Hocevar
Martin T. H. Sandsmark
Sebastian Birk
Sébastien Escudier
Vincent Seguin
Fabio Ritrovato
Sigmund Augdal Helberg
Casian Andrei
Srikanth Raju
Hannes Domani
Stéphane Borel
Stephan Krempel
Stephan Assmus
Tony Castley
Pavlov Konstantin
Eric Petit
Tanguy Krotoff
Dennis van Amerongen
Michel Lespinasse
Can Wu
Xavier Marchesini
Sébastien Toque
Christophe Mutricy
Yoann Peronneau
Yohann Martineau
Yuval Tze
Scott Caudle
Clément Stenac
It is possible, that some minor piece of code was badly tracked, for
some reasons (SVN, mainly) or that some small developers did not answer.
However, as an "œuvre collective", defined as in "CPI 113-2 alinéa 3",
and seeing "Cour. Cass. 17 Mai 1978", and seeing that the editor and
the very vast majority of developers have agreed (> 99.99% of the code,
> 99% of developers), we are fine here.
2011-11-27 21:44:15 +01:00
|
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
2004-03-03 14:23:47 +01:00
|
|
|
*****************************************************************************/
|
|
|
|
|
2008-01-23 22:50:58 +01:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include "config.h"
|
|
|
|
#endif
|
|
|
|
|
2018-06-17 19:41:53 +02:00
|
|
|
#include <stdatomic.h>
|
|
|
|
|
2008-05-31 20:56:22 +02:00
|
|
|
#include <vlc_common.h>
|
2008-02-25 16:45:21 +01:00
|
|
|
#include <vlc_httpd.h>
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2006-03-04 11:46:27 +01:00
|
|
|
#include <assert.h>
|
|
|
|
|
2018-06-17 19:32:13 +02:00
|
|
|
#include <vlc_list.h>
|
2006-11-26 15:20:34 +01:00
|
|
|
#include <vlc_network.h>
|
|
|
|
#include <vlc_tls.h>
|
2007-03-06 18:27:18 +01:00
|
|
|
#include <vlc_strings.h>
|
2010-03-06 11:21:02 +01:00
|
|
|
#include <vlc_rand.h>
|
2011-01-12 19:37:51 +01:00
|
|
|
#include <vlc_charset.h>
|
2011-12-18 18:58:23 +01:00
|
|
|
#include <vlc_url.h>
|
2012-08-28 18:07:06 +02:00
|
|
|
#include <vlc_mime.h>
|
2013-08-16 16:42:40 +02:00
|
|
|
#include <vlc_block.h>
|
2006-12-01 21:26:05 +01:00
|
|
|
#include "../libvlc.h"
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2004-07-05 17:43:56 +02:00
|
|
|
#include <string.h>
|
2004-10-04 15:34:42 +02:00
|
|
|
#include <errno.h>
|
2013-12-29 15:15:46 +01:00
|
|
|
#include <unistd.h>
|
2017-03-20 17:57:18 +01:00
|
|
|
#ifdef HAVE_SYS_UIO_H
|
|
|
|
# include <sys/uio.h>
|
|
|
|
#endif
|
2004-10-01 17:56:10 +02:00
|
|
|
|
2007-02-21 18:58:12 +01:00
|
|
|
#ifdef HAVE_POLL
|
|
|
|
# include <poll.h>
|
|
|
|
#endif
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
#if defined(_WIN32)
|
2004-03-03 14:23:47 +01:00
|
|
|
# include <winsock2.h>
|
2018-04-12 13:00:54 +02:00
|
|
|
#elif defined(HAVE_SYS_SOCKET_H)
|
2004-03-03 14:23:47 +01:00
|
|
|
# include <sys/socket.h>
|
|
|
|
#endif
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
#if defined(_WIN32)
|
2005-10-22 18:23:01 +02:00
|
|
|
/* We need HUGE buffer otherwise TCP throughput is very limited */
|
|
|
|
#define HTTPD_CL_BUFSIZE 1000000
|
|
|
|
#else
|
|
|
|
#define HTTPD_CL_BUFSIZE 10000
|
|
|
|
#endif
|
|
|
|
|
2016-01-12 21:09:48 +01:00
|
|
|
static void httpd_ClientDestroy(httpd_client_t *cl);
|
2014-02-20 18:56:33 +01:00
|
|
|
static void httpd_AppendData(httpd_stream_t *stream, uint8_t *p_data, int i_data);
|
2004-03-09 21:46:43 +01:00
|
|
|
|
2006-12-01 21:26:05 +01:00
|
|
|
/* each host run in his own thread */
|
|
|
|
struct httpd_host_t
|
|
|
|
{
|
2019-04-14 21:09:02 +02:00
|
|
|
struct vlc_object_t obj;
|
2018-06-17 19:32:13 +02:00
|
|
|
struct vlc_list node;
|
2006-12-01 21:26:05 +01:00
|
|
|
|
|
|
|
/* ref count */
|
2018-06-17 19:41:53 +02:00
|
|
|
atomic_uint ref;
|
2006-12-01 21:26:05 +01:00
|
|
|
|
|
|
|
/* address/port and socket for listening at connections */
|
2007-02-21 18:58:12 +01:00
|
|
|
int *fds;
|
|
|
|
unsigned nfd;
|
2011-10-24 03:33:11 +02:00
|
|
|
unsigned port;
|
2006-12-01 21:26:05 +01:00
|
|
|
|
2009-01-17 17:42:38 +01:00
|
|
|
vlc_thread_t thread;
|
2006-12-01 21:26:05 +01:00
|
|
|
vlc_mutex_t lock;
|
2009-01-14 21:07:31 +01:00
|
|
|
vlc_cond_t wait;
|
2006-12-01 21:26:05 +01:00
|
|
|
|
|
|
|
/* all registered url (becarefull that 2 httpd_url_t could point at the same url)
|
|
|
|
* This will slow down the url research but make my live easier
|
|
|
|
* All url will have their cb trigger, but only the first one can answer
|
|
|
|
* */
|
2018-06-17 20:09:44 +02:00
|
|
|
struct vlc_list urls;
|
2006-12-01 21:26:05 +01:00
|
|
|
|
2018-06-17 20:03:46 +02:00
|
|
|
size_t client_count;
|
|
|
|
struct vlc_list clients;
|
2006-12-01 21:26:05 +01:00
|
|
|
|
|
|
|
/* TLS data */
|
2018-11-18 13:35:41 +01:00
|
|
|
vlc_tls_server_t *p_tls;
|
2006-12-01 21:26:05 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2004-03-03 14:23:47 +01:00
|
|
|
struct httpd_url_t
|
|
|
|
{
|
|
|
|
httpd_host_t *host;
|
2018-06-17 20:09:44 +02:00
|
|
|
struct vlc_list node;
|
2004-03-03 14:23:47 +01:00
|
|
|
vlc_mutex_t lock;
|
|
|
|
|
2005-07-12 18:00:43 +02:00
|
|
|
char *psz_url;
|
|
|
|
char *psz_user;
|
|
|
|
char *psz_password;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
httpd_callback_t cb;
|
|
|
|
httpd_callback_sys_t *p_sys;
|
|
|
|
} catch[HTTPD_MSG_MAX];
|
|
|
|
};
|
|
|
|
|
|
|
|
/* status */
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
HTTPD_CLIENT_RECEIVING,
|
|
|
|
HTTPD_CLIENT_RECEIVE_DONE,
|
|
|
|
|
|
|
|
HTTPD_CLIENT_SENDING,
|
|
|
|
HTTPD_CLIENT_SEND_DONE,
|
|
|
|
|
|
|
|
HTTPD_CLIENT_WAITING,
|
|
|
|
|
|
|
|
HTTPD_CLIENT_DEAD,
|
2004-12-25 22:35:09 +01:00
|
|
|
|
|
|
|
HTTPD_CLIENT_TLS_HS_IN,
|
|
|
|
HTTPD_CLIENT_TLS_HS_OUT
|
2004-03-03 14:23:47 +01:00
|
|
|
};
|
2006-02-04 20:48:38 +01:00
|
|
|
|
2004-03-03 14:23:47 +01:00
|
|
|
struct httpd_client_t
|
|
|
|
{
|
|
|
|
httpd_url_t *url;
|
2017-02-26 14:37:52 +01:00
|
|
|
vlc_tls_t *sock;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2018-06-17 20:03:46 +02:00
|
|
|
struct vlc_list node;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2011-11-03 18:41:56 +01:00
|
|
|
bool b_stream_mode;
|
|
|
|
uint8_t i_state;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2018-06-22 13:19:24 +02:00
|
|
|
vlc_tick_t i_activity_date;
|
|
|
|
vlc_tick_t i_activity_timeout;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
|
|
|
/* buffer for reading header */
|
|
|
|
int i_buffer_size;
|
|
|
|
int i_buffer;
|
|
|
|
uint8_t *p_buffer;
|
|
|
|
|
2013-08-16 16:42:40 +02:00
|
|
|
/*
|
|
|
|
* If waiting for a keyframe, this is the position (in bytes) of the
|
|
|
|
* last keyframe the stream saw before this client connected.
|
|
|
|
* Otherwise, -1.
|
|
|
|
*/
|
|
|
|
int64_t i_keyframe_wait_to_pass;
|
|
|
|
|
2004-03-03 14:23:47 +01:00
|
|
|
/* */
|
|
|
|
httpd_message_t query; /* client -> httpd */
|
|
|
|
httpd_message_t answer; /* httpd -> client */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* Various functions
|
|
|
|
*****************************************************************************/
|
2014-02-20 18:56:33 +01:00
|
|
|
static const char *httpd_ReasonFromCode(unsigned i_code)
|
2006-03-04 11:46:27 +01:00
|
|
|
{
|
2014-02-16 11:36:33 +01:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
unsigned i_code;
|
|
|
|
const char psz_reason[36];
|
|
|
|
} http_status_info;
|
|
|
|
|
|
|
|
static const http_status_info http_reason[] =
|
|
|
|
{
|
|
|
|
/*{ 100, "Continue" },
|
|
|
|
{ 101, "Switching Protocols" },*/
|
|
|
|
{ 200, "OK" },
|
|
|
|
/*{ 201, "Created" },
|
|
|
|
{ 202, "Accepted" },
|
|
|
|
{ 203, "Non-authoritative information" },
|
|
|
|
{ 204, "No content" },
|
2015-12-18 10:02:11 +01:00
|
|
|
{ 205, "Reset content" },
|
2014-02-16 11:36:33 +01:00
|
|
|
{ 206, "Partial content" },
|
|
|
|
{ 250, "Low on storage space" },
|
|
|
|
{ 300, "Multiple choices" },*/
|
|
|
|
{ 301, "Moved permanently" },
|
|
|
|
/*{ 302, "Moved temporarily" },
|
|
|
|
{ 303, "See other" },
|
|
|
|
{ 304, "Not modified" },
|
|
|
|
{ 305, "Use proxy" },
|
|
|
|
{ 307, "Temporary redirect" },
|
|
|
|
{ 400, "Bad request" },*/
|
|
|
|
{ 401, "Unauthorized" },
|
|
|
|
/*{ 402, "Payment Required" },*/
|
|
|
|
{ 403, "Forbidden" },
|
|
|
|
{ 404, "Not found" },
|
|
|
|
{ 405, "Method not allowed" },
|
|
|
|
/*{ 406, "Not acceptable" },
|
|
|
|
{ 407, "Proxy authentication required" },
|
|
|
|
{ 408, "Request time-out" },
|
|
|
|
{ 409, "Conflict" },
|
|
|
|
{ 410, "Gone" },
|
|
|
|
{ 411, "Length required" },
|
|
|
|
{ 412, "Precondition failed" },
|
|
|
|
{ 413, "Request entity too large" },
|
|
|
|
{ 414, "Request-URI too large" },
|
|
|
|
{ 415, "Unsupported media Type" },
|
|
|
|
{ 416, "Requested range not satisfiable" },
|
|
|
|
{ 417, "Expectation failed" },
|
|
|
|
{ 451, "Parameter not understood" },
|
|
|
|
{ 452, "Conference not found" },
|
|
|
|
{ 453, "Not enough bandwidth" },*/
|
|
|
|
{ 454, "Session not found" },
|
|
|
|
{ 455, "Method not valid in this State" },
|
|
|
|
{ 456, "Header field not valid for resource" },
|
|
|
|
{ 457, "Invalid range" },
|
|
|
|
/*{ 458, "Read-only parameter" },*/
|
|
|
|
{ 459, "Aggregate operation not allowed" },
|
|
|
|
{ 460, "Non-aggregate operation not allowed" },
|
|
|
|
{ 461, "Unsupported transport" },
|
|
|
|
/*{ 462, "Destination unreachable" },*/
|
|
|
|
{ 500, "Internal server error" },
|
|
|
|
{ 501, "Not implemented" },
|
|
|
|
/*{ 502, "Bad gateway" },*/
|
|
|
|
{ 503, "Service unavailable" },
|
|
|
|
/*{ 504, "Gateway time-out" },*/
|
|
|
|
{ 505, "Protocol version not supported" },
|
|
|
|
{ 551, "Option not supported" },
|
|
|
|
{ 999, "" }
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char psz_fallback_reason[5][16] = {
|
|
|
|
"Continue", "OK", "Found", "Client error", "Server error"
|
|
|
|
};
|
2006-03-04 11:46:27 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
assert((i_code >= 100) && (i_code <= 599));
|
2007-08-22 18:19:36 +02:00
|
|
|
|
2014-02-16 11:36:33 +01:00
|
|
|
const http_status_info *p = http_reason;
|
|
|
|
while (i_code < p->i_code)
|
|
|
|
p++;
|
2006-03-04 11:46:27 +01:00
|
|
|
|
2014-02-16 11:36:33 +01:00
|
|
|
if (p->i_code == i_code)
|
2006-03-04 11:46:27 +01:00
|
|
|
return p->psz_reason;
|
|
|
|
|
|
|
|
return psz_fallback_reason[(i_code / 100) - 1];
|
|
|
|
}
|
2006-11-24 16:46:43 +01:00
|
|
|
|
|
|
|
static size_t httpd_HtmlError (char **body, int code, const char *url)
|
|
|
|
{
|
|
|
|
const char *errname = httpd_ReasonFromCode (code);
|
2014-02-20 18:56:33 +01:00
|
|
|
assert (errname);
|
2006-11-24 16:46:43 +01:00
|
|
|
|
2015-11-29 13:05:15 +01:00
|
|
|
char *url_Encoded = vlc_xml_encode (url ? url : "");
|
2014-02-18 13:38:56 +01:00
|
|
|
|
2006-11-24 16:46:43 +01:00
|
|
|
int res = asprintf (body,
|
|
|
|
"<?xml version=\"1.0\" encoding=\"ascii\" ?>\n"
|
|
|
|
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\""
|
|
|
|
" \"http://www.w3.org/TR/xhtml10/DTD/xhtml10strict.dtd\">\n"
|
|
|
|
"<html lang=\"en\">\n"
|
|
|
|
"<head>\n"
|
|
|
|
"<title>%s</title>\n"
|
|
|
|
"</head>\n"
|
|
|
|
"<body>\n"
|
|
|
|
"<h1>%d %s%s%s%s</h1>\n"
|
|
|
|
"<hr />\n"
|
|
|
|
"<a href=\"http://www.videolan.org\">VideoLAN</a>\n"
|
|
|
|
"</body>\n"
|
|
|
|
"</html>\n", errname, code, errname,
|
2014-02-18 13:38:56 +01:00
|
|
|
(url_Encoded ? " (" : ""), (url_Encoded ? url_Encoded : ""), (url_Encoded ? ")" : ""));
|
|
|
|
|
|
|
|
free (url_Encoded);
|
2006-11-24 16:46:43 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (res == -1) {
|
2006-11-24 16:46:43 +01:00
|
|
|
*body = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (size_t)res;
|
|
|
|
}
|
|
|
|
|
2006-03-04 11:46:27 +01:00
|
|
|
|
2004-03-03 14:23:47 +01:00
|
|
|
/*****************************************************************************
|
2005-08-26 20:15:21 +02:00
|
|
|
* High Level Functions: httpd_file_t
|
2004-03-03 14:23:47 +01:00
|
|
|
*****************************************************************************/
|
|
|
|
struct httpd_file_t
|
|
|
|
{
|
|
|
|
httpd_url_t *url;
|
|
|
|
httpd_file_callback_t pf_fill;
|
|
|
|
httpd_file_sys_t *p_sys;
|
2014-04-27 15:57:05 +02:00
|
|
|
char mime[1];
|
2004-03-03 14:23:47 +01:00
|
|
|
};
|
|
|
|
|
2007-08-30 23:29:51 +02:00
|
|
|
static int
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_FileCallBack(httpd_callback_sys_t *p_sys, httpd_client_t *cl,
|
|
|
|
httpd_message_t *answer, const httpd_message_t *query)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
|
|
|
httpd_file_t *file = (httpd_file_t*)p_sys;
|
2017-10-08 16:52:43 +02:00
|
|
|
uint8_t **pp_body, *p_body;
|
2005-08-26 20:15:21 +02:00
|
|
|
int *pi_body, i_body;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (!answer || !query )
|
2004-03-03 14:23:47 +01:00
|
|
|
return VLC_SUCCESS;
|
2014-02-20 18:56:33 +01:00
|
|
|
|
2004-03-03 14:23:47 +01:00
|
|
|
answer->i_proto = HTTPD_PROTO_HTTP;
|
2007-09-02 11:06:57 +02:00
|
|
|
answer->i_version= 1;
|
2004-03-03 14:23:47 +01:00
|
|
|
answer->i_type = HTTPD_MSG_ANSWER;
|
|
|
|
|
|
|
|
answer->i_status = 200;
|
|
|
|
|
2014-04-27 15:57:05 +02:00
|
|
|
httpd_MsgAdd(answer, "Content-type", "%s", file->mime);
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_MsgAdd(answer, "Cache-Control", "%s", "no-cache");
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (query->i_type != HTTPD_MSG_HEAD) {
|
2005-08-26 20:15:21 +02:00
|
|
|
pp_body = &answer->p_body;
|
|
|
|
pi_body = &answer->i_body;
|
2014-02-20 18:56:33 +01:00
|
|
|
} else {
|
2005-08-26 20:15:21 +02:00
|
|
|
/* The file still needs to be executed. */
|
|
|
|
p_body = NULL;
|
|
|
|
i_body = 0;
|
|
|
|
pp_body = &p_body;
|
|
|
|
pi_body = &i_body;
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
2005-08-26 20:15:21 +02:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (query->i_type == HTTPD_MSG_POST) {
|
2005-08-26 20:15:21 +02:00
|
|
|
/* msg_Warn not supported */
|
|
|
|
}
|
|
|
|
|
2008-05-30 17:56:42 +02:00
|
|
|
uint8_t *psz_args = query->psz_args;
|
2014-02-20 18:56:33 +01:00
|
|
|
file->pf_fill(file->p_sys, file, psz_args, pp_body, pi_body);
|
2005-08-26 20:15:21 +02:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (query->i_type == HTTPD_MSG_HEAD)
|
|
|
|
free(p_body);
|
2005-08-26 20:15:21 +02:00
|
|
|
|
2004-03-06 02:14:28 +01:00
|
|
|
/* We respect client request */
|
2017-10-08 16:52:43 +02:00
|
|
|
if (httpd_MsgGet(&cl->query, "Connection") != NULL)
|
|
|
|
httpd_MsgAdd(answer, "Connection", "close");
|
2004-03-06 02:14:28 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_MsgAdd(answer, "Content-Length", "%d", answer->i_body);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
|
|
|
return VLC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_file_t *httpd_FileNew(httpd_host_t *host,
|
2005-08-04 16:58:44 +02:00
|
|
|
const char *psz_url, const char *psz_mime,
|
|
|
|
const char *psz_user, const char *psz_password,
|
2012-04-01 22:03:23 +02:00
|
|
|
httpd_file_callback_t pf_fill,
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_file_sys_t *p_sys)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
2014-04-27 15:57:05 +02:00
|
|
|
const char *mime = psz_mime;
|
|
|
|
if (mime == NULL || mime[0] == '\0')
|
|
|
|
mime = vlc_mime_Ext2Mime(psz_url);
|
|
|
|
|
|
|
|
size_t mimelen = strlen(mime);
|
|
|
|
httpd_file_t *file = malloc(sizeof(*file) + mimelen);
|
|
|
|
if (unlikely(file == NULL))
|
2014-02-20 18:56:33 +01:00
|
|
|
return NULL;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
file->url = httpd_UrlNew(host, psz_url, psz_user, psz_password);
|
|
|
|
if (!file->url) {
|
|
|
|
free(file);
|
2004-03-03 14:23:47 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
file->pf_fill = pf_fill;
|
|
|
|
file->p_sys = p_sys;
|
2014-04-27 15:57:05 +02:00
|
|
|
memcpy(file->mime, mime, mimelen + 1);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_UrlCatch(file->url, HTTPD_MSG_HEAD, httpd_FileCallBack,
|
|
|
|
(httpd_callback_sys_t*)file);
|
|
|
|
httpd_UrlCatch(file->url, HTTPD_MSG_GET, httpd_FileCallBack,
|
|
|
|
(httpd_callback_sys_t*)file);
|
|
|
|
httpd_UrlCatch(file->url, HTTPD_MSG_POST, httpd_FileCallBack,
|
|
|
|
(httpd_callback_sys_t*)file);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_file_sys_t *httpd_FileDelete(httpd_file_t *file)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
2007-11-07 20:36:02 +01:00
|
|
|
httpd_file_sys_t *p_sys = file->p_sys;
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_UrlDelete(file->url);
|
|
|
|
free(file);
|
2007-11-07 20:36:02 +01:00
|
|
|
return p_sys;
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
|
|
|
|
2005-08-26 20:15:21 +02:00
|
|
|
/*****************************************************************************
|
|
|
|
* High Level Functions: httpd_handler_t (for CGIs)
|
|
|
|
*****************************************************************************/
|
|
|
|
struct httpd_handler_t
|
|
|
|
{
|
|
|
|
httpd_url_t *url;
|
|
|
|
|
|
|
|
httpd_handler_callback_t pf_fill;
|
2017-02-18 12:04:16 +01:00
|
|
|
void *p_sys;
|
2005-08-26 20:15:21 +02:00
|
|
|
|
|
|
|
};
|
|
|
|
|
2007-08-30 23:29:51 +02:00
|
|
|
static int
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_HandlerCallBack(httpd_callback_sys_t *p_sys, httpd_client_t *cl,
|
|
|
|
httpd_message_t *answer, const httpd_message_t *query)
|
2005-08-26 20:15:21 +02:00
|
|
|
{
|
|
|
|
httpd_handler_t *handler = (httpd_handler_t*)p_sys;
|
2006-03-02 23:39:03 +01:00
|
|
|
char psz_remote_addr[NI_MAXNUMERICHOST];
|
2005-08-26 20:15:21 +02:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (!answer || !query)
|
2005-08-26 20:15:21 +02:00
|
|
|
return VLC_SUCCESS;
|
2014-02-20 18:56:33 +01:00
|
|
|
|
2005-08-26 20:15:21 +02:00
|
|
|
answer->i_proto = HTTPD_PROTO_NONE;
|
|
|
|
answer->i_type = HTTPD_MSG_ANSWER;
|
|
|
|
|
|
|
|
/* We do it ourselves, thanks */
|
|
|
|
answer->i_status = 0;
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (!httpd_ClientIP(cl, psz_remote_addr, NULL))
|
2006-03-02 23:39:03 +01:00
|
|
|
*psz_remote_addr = '\0';
|
2005-08-26 20:15:21 +02:00
|
|
|
|
2008-05-30 17:56:42 +02:00
|
|
|
uint8_t *psz_args = query->psz_args;
|
2014-02-20 18:56:33 +01:00
|
|
|
handler->pf_fill(handler->p_sys, handler, query->psz_url, psz_args,
|
2005-08-26 20:15:21 +02:00
|
|
|
query->i_type, query->p_body, query->i_body,
|
|
|
|
psz_remote_addr, NULL,
|
2014-02-20 18:56:33 +01:00
|
|
|
&answer->p_body, &answer->i_body);
|
2005-08-26 20:15:21 +02:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (query->i_type == HTTPD_MSG_HEAD) {
|
2006-02-01 14:28:59 +01:00
|
|
|
char *p = (char *)answer->p_body;
|
2007-09-15 15:43:38 +02:00
|
|
|
|
|
|
|
/* Looks for end of header (i.e. one empty line) */
|
2014-02-20 18:56:33 +01:00
|
|
|
while ((p = strchr(p, '\r')))
|
|
|
|
if (p[1] == '\n' && p[2] == '\r' && p[3] == '\n')
|
2005-08-26 20:15:21 +02:00
|
|
|
break;
|
2007-09-15 15:43:38 +02:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (p) {
|
2005-08-26 20:15:21 +02:00
|
|
|
p[4] = '\0';
|
2006-02-01 14:28:59 +01:00
|
|
|
answer->i_body = strlen((char*)answer->p_body) + 1;
|
2014-02-20 18:56:33 +01:00
|
|
|
answer->p_body = xrealloc(answer->p_body, answer->i_body);
|
2005-08-26 20:15:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (strncmp((char *)answer->p_body, "HTTP/1.", 7)) {
|
2005-08-26 20:15:21 +02:00
|
|
|
int i_status, i_headers;
|
2006-11-24 16:16:18 +01:00
|
|
|
char *psz_headers, *psz_new;
|
|
|
|
const char *psz_status;
|
2006-02-01 14:28:59 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (!strncmp((char *)answer->p_body, "Status: ", 8)) {
|
2005-08-26 20:15:21 +02:00
|
|
|
/* Apache-style */
|
2014-02-20 18:56:33 +01:00
|
|
|
i_status = strtol((char *)&answer->p_body[8], &psz_headers, 0);
|
|
|
|
if (*psz_headers == '\r' || *psz_headers == '\n') psz_headers++;
|
|
|
|
if (*psz_headers == '\n') psz_headers++;
|
2005-08-26 20:15:21 +02:00
|
|
|
i_headers = answer->i_body - (psz_headers - (char *)answer->p_body);
|
2014-02-20 18:56:33 +01:00
|
|
|
} else {
|
2005-08-26 20:15:21 +02:00
|
|
|
i_status = 200;
|
2006-02-01 14:28:59 +01:00
|
|
|
psz_headers = (char *)answer->p_body;
|
2005-08-26 20:15:21 +02:00
|
|
|
i_headers = answer->i_body;
|
|
|
|
}
|
2007-08-21 21:11:34 +02:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
psz_status = httpd_ReasonFromCode(i_status);
|
2006-02-01 14:28:59 +01:00
|
|
|
answer->i_body = sizeof("HTTP/1.0 xxx \r\n")
|
|
|
|
+ strlen(psz_status) + i_headers - 1;
|
2014-02-20 18:56:33 +01:00
|
|
|
psz_new = (char *)xmalloc(answer->i_body + 1);
|
|
|
|
sprintf(psz_new, "HTTP/1.0 %03d %s\r\n", i_status, psz_status);
|
|
|
|
memcpy(&psz_new[strlen(psz_new)], psz_headers, i_headers);
|
|
|
|
free(answer->p_body);
|
2006-02-01 14:28:59 +01:00
|
|
|
answer->p_body = (uint8_t *)psz_new;
|
2005-08-26 20:15:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return VLC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_handler_t *httpd_HandlerNew(httpd_host_t *host, const char *psz_url,
|
2017-02-18 12:04:16 +01:00
|
|
|
const char *psz_user,
|
|
|
|
const char *psz_password,
|
|
|
|
httpd_handler_callback_t pf_fill,
|
|
|
|
void *p_sys)
|
2005-08-26 20:15:21 +02:00
|
|
|
{
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_handler_t *handler = malloc(sizeof(*handler));
|
|
|
|
if (!handler)
|
|
|
|
return NULL;
|
2005-08-26 20:15:21 +02:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
handler->url = httpd_UrlNew(host, psz_url, psz_user, psz_password);
|
|
|
|
if (!handler->url) {
|
|
|
|
free(handler);
|
2005-08-26 20:15:21 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
handler->pf_fill = pf_fill;
|
|
|
|
handler->p_sys = p_sys;
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_UrlCatch(handler->url, HTTPD_MSG_HEAD, httpd_HandlerCallBack,
|
|
|
|
(httpd_callback_sys_t*)handler);
|
|
|
|
httpd_UrlCatch(handler->url, HTTPD_MSG_GET, httpd_HandlerCallBack,
|
|
|
|
(httpd_callback_sys_t*)handler);
|
|
|
|
httpd_UrlCatch(handler->url, HTTPD_MSG_POST, httpd_HandlerCallBack,
|
|
|
|
(httpd_callback_sys_t*)handler);
|
2005-08-26 20:15:21 +02:00
|
|
|
|
|
|
|
return handler;
|
|
|
|
}
|
|
|
|
|
2017-02-18 12:04:16 +01:00
|
|
|
void *httpd_HandlerDelete(httpd_handler_t *handler)
|
2005-08-26 20:15:21 +02:00
|
|
|
{
|
2017-02-18 12:04:16 +01:00
|
|
|
void *p_sys = handler->p_sys;
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_UrlDelete(handler->url);
|
|
|
|
free(handler);
|
2007-11-07 20:36:02 +01:00
|
|
|
return p_sys;
|
2005-08-26 20:15:21 +02:00
|
|
|
}
|
|
|
|
|
2004-03-03 14:23:47 +01:00
|
|
|
/*****************************************************************************
|
2005-08-12 21:11:54 +02:00
|
|
|
* High Level Functions: httpd_redirect_t
|
2004-03-03 14:23:47 +01:00
|
|
|
*****************************************************************************/
|
|
|
|
struct httpd_redirect_t
|
|
|
|
{
|
|
|
|
httpd_url_t *url;
|
2014-04-27 15:59:19 +02:00
|
|
|
char dst[1];
|
2004-03-03 14:23:47 +01:00
|
|
|
};
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
static int httpd_RedirectCallBack(httpd_callback_sys_t *p_sys,
|
2004-03-09 21:46:43 +01:00
|
|
|
httpd_client_t *cl, httpd_message_t *answer,
|
2014-02-20 18:56:33 +01:00
|
|
|
const httpd_message_t *query)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
|
|
|
httpd_redirect_t *rdir = (httpd_redirect_t*)p_sys;
|
2006-09-03 17:08:52 +02:00
|
|
|
char *p_body;
|
2007-04-05 18:42:16 +02:00
|
|
|
(void)cl;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (!answer || !query)
|
2004-03-03 14:23:47 +01:00
|
|
|
return VLC_SUCCESS;
|
2014-02-20 18:56:33 +01:00
|
|
|
|
2007-09-02 10:51:01 +02:00
|
|
|
answer->i_proto = HTTPD_PROTO_HTTP;
|
2007-09-02 11:06:57 +02:00
|
|
|
answer->i_version= 1;
|
2004-03-03 14:23:47 +01:00
|
|
|
answer->i_type = HTTPD_MSG_ANSWER;
|
|
|
|
answer->i_status = 301;
|
|
|
|
|
2014-04-27 15:59:19 +02:00
|
|
|
answer->i_body = httpd_HtmlError (&p_body, 301, rdir->dst);
|
2006-09-03 17:08:52 +02:00
|
|
|
answer->p_body = (unsigned char *)p_body;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
|
|
|
/* XXX check if it's ok or we need to set an absolute url */
|
2014-04-27 15:59:19 +02:00
|
|
|
httpd_MsgAdd(answer, "Location", "%s", rdir->dst);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_MsgAdd(answer, "Content-Length", "%d", answer->i_body);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2017-10-08 17:07:52 +02:00
|
|
|
if (httpd_MsgGet(&cl->query, "Connection") != NULL)
|
|
|
|
httpd_MsgAdd(answer, "Connection", "close");
|
|
|
|
|
2004-03-03 14:23:47 +01:00
|
|
|
return VLC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_redirect_t *httpd_RedirectNew(httpd_host_t *host, const char *psz_url_dst,
|
|
|
|
const char *psz_url_src)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
2014-04-27 15:59:19 +02:00
|
|
|
size_t dstlen = strlen(psz_url_dst);
|
|
|
|
|
|
|
|
httpd_redirect_t *rdir = malloc(sizeof(*rdir) + dstlen);
|
|
|
|
if (unlikely(rdir == NULL))
|
2014-02-20 18:56:33 +01:00
|
|
|
return NULL;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
rdir->url = httpd_UrlNew(host, psz_url_src, NULL, NULL);
|
|
|
|
if (!rdir->url) {
|
|
|
|
free(rdir);
|
2004-03-03 14:23:47 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
2014-04-27 15:59:19 +02:00
|
|
|
memcpy(rdir->dst, psz_url_dst, dstlen + 1);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2004-03-09 21:46:43 +01:00
|
|
|
/* Redirect apply for all HTTP request and RTSP DESCRIBE resquest */
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_UrlCatch(rdir->url, HTTPD_MSG_HEAD, httpd_RedirectCallBack,
|
|
|
|
(httpd_callback_sys_t*)rdir);
|
|
|
|
httpd_UrlCatch(rdir->url, HTTPD_MSG_GET, httpd_RedirectCallBack,
|
|
|
|
(httpd_callback_sys_t*)rdir);
|
|
|
|
httpd_UrlCatch(rdir->url, HTTPD_MSG_POST, httpd_RedirectCallBack,
|
|
|
|
(httpd_callback_sys_t*)rdir);
|
|
|
|
httpd_UrlCatch(rdir->url, HTTPD_MSG_DESCRIBE, httpd_RedirectCallBack,
|
|
|
|
(httpd_callback_sys_t*)rdir);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
|
|
|
return rdir;
|
|
|
|
}
|
2014-02-20 18:56:33 +01:00
|
|
|
void httpd_RedirectDelete(httpd_redirect_t *rdir)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_UrlDelete(rdir->url);
|
|
|
|
free(rdir);
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* High Level Funtions: httpd_stream_t
|
|
|
|
*****************************************************************************/
|
|
|
|
struct httpd_stream_t
|
|
|
|
{
|
|
|
|
vlc_mutex_t lock;
|
|
|
|
httpd_url_t *url;
|
|
|
|
|
|
|
|
char *psz_mime;
|
|
|
|
|
|
|
|
/* Header to send as first packet */
|
|
|
|
uint8_t *p_header;
|
|
|
|
int i_header;
|
|
|
|
|
2013-08-16 16:42:40 +02:00
|
|
|
/* Some muxes, in particular the avformat mux, can mark given blocks
|
|
|
|
* as keyframes, to ensure that the stream starts with one.
|
|
|
|
* (This is particularly important for WebM streaming to certain
|
|
|
|
* browsers.) Store if we've ever seen any such keyframe blocks,
|
|
|
|
* and if so, the byte position of the start of the last one. */
|
|
|
|
bool b_has_keyframes;
|
|
|
|
int64_t i_last_keyframe_seen_pos;
|
|
|
|
|
2004-03-03 14:23:47 +01:00
|
|
|
/* circular buffer */
|
|
|
|
int i_buffer_size; /* buffer size, can't be reallocated smaller */
|
|
|
|
uint8_t *p_buffer; /* buffer */
|
2016-04-02 16:50:32 +02:00
|
|
|
int64_t i_buffer_pos; /* absolute position from beginning */
|
2004-03-03 14:23:47 +01:00
|
|
|
int64_t i_buffer_last_pos; /* a new connection will start with that */
|
2014-02-15 23:15:49 +01:00
|
|
|
|
|
|
|
/* custom headers */
|
|
|
|
size_t i_http_headers;
|
|
|
|
httpd_header * p_http_headers;
|
2004-03-03 14:23:47 +01:00
|
|
|
};
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
static int httpd_StreamCallBack(httpd_callback_sys_t *p_sys,
|
2004-03-08 16:55:49 +01:00
|
|
|
httpd_client_t *cl, httpd_message_t *answer,
|
2014-02-20 18:56:33 +01:00
|
|
|
const httpd_message_t *query)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
|
|
|
httpd_stream_t *stream = (httpd_stream_t*)p_sys;
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (!answer || !query || !cl)
|
2004-03-03 14:23:47 +01:00
|
|
|
return VLC_SUCCESS;
|
2006-02-04 20:48:38 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (answer->i_body_offset > 0) {
|
2004-03-03 14:23:47 +01:00
|
|
|
int i_pos;
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (answer->i_body_offset >= stream->i_buffer_pos)
|
2004-03-03 14:23:47 +01:00
|
|
|
return VLC_EGENERIC; /* wait, no data available */
|
2014-02-20 18:56:33 +01:00
|
|
|
|
|
|
|
if (cl->i_keyframe_wait_to_pass >= 0) {
|
|
|
|
if (stream->i_last_keyframe_seen_pos <= cl->i_keyframe_wait_to_pass)
|
2013-08-16 16:42:40 +02:00
|
|
|
/* still waiting for the next keyframe */
|
|
|
|
return VLC_EGENERIC;
|
|
|
|
|
|
|
|
/* seek to the new keyframe */
|
|
|
|
answer->i_body_offset = stream->i_last_keyframe_seen_pos;
|
|
|
|
cl->i_keyframe_wait_to_pass = -1;
|
|
|
|
}
|
2014-02-20 18:56:33 +01:00
|
|
|
|
|
|
|
if (answer->i_body_offset + stream->i_buffer_size < stream->i_buffer_pos)
|
|
|
|
answer->i_body_offset = stream->i_buffer_last_pos; /* this client isn't fast enough */
|
2004-03-03 14:23:47 +01:00
|
|
|
|
|
|
|
i_pos = answer->i_body_offset % stream->i_buffer_size;
|
2014-02-20 18:56:33 +01:00
|
|
|
int64_t i_write = stream->i_buffer_pos - answer->i_body_offset;
|
|
|
|
|
|
|
|
if (i_write > HTTPD_CL_BUFSIZE)
|
2005-10-22 18:23:01 +02:00
|
|
|
i_write = HTTPD_CL_BUFSIZE;
|
2014-02-20 18:56:33 +01:00
|
|
|
else if (i_write <= 0)
|
2004-03-03 14:23:47 +01:00
|
|
|
return VLC_EGENERIC; /* wait, no data available */
|
|
|
|
|
2004-03-08 16:55:49 +01:00
|
|
|
/* Don't go past the end of the circular buffer */
|
2014-02-20 18:56:33 +01:00
|
|
|
i_write = __MIN(i_write, stream->i_buffer_size - i_pos);
|
2004-03-08 16:55:49 +01:00
|
|
|
|
2004-03-03 14:23:47 +01:00
|
|
|
/* using HTTPD_MSG_ANSWER -> data available */
|
|
|
|
answer->i_proto = HTTPD_PROTO_HTTP;
|
|
|
|
answer->i_version= 0;
|
|
|
|
answer->i_type = HTTPD_MSG_ANSWER;
|
|
|
|
|
|
|
|
answer->i_body = i_write;
|
2014-02-20 18:56:33 +01:00
|
|
|
answer->p_body = xmalloc(i_write);
|
|
|
|
memcpy(answer->p_body, &stream->p_buffer[i_pos], i_write);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
|
|
|
answer->i_body_offset += i_write;
|
|
|
|
|
|
|
|
return VLC_SUCCESS;
|
2014-02-20 18:56:33 +01:00
|
|
|
} else {
|
2004-03-03 14:23:47 +01:00
|
|
|
answer->i_proto = HTTPD_PROTO_HTTP;
|
|
|
|
answer->i_version= 0;
|
|
|
|
answer->i_type = HTTPD_MSG_ANSWER;
|
|
|
|
|
|
|
|
answer->i_status = 200;
|
|
|
|
|
2014-02-15 23:15:49 +01:00
|
|
|
bool b_has_content_type = false;
|
|
|
|
bool b_has_cache_control = false;
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_mutex_lock(&stream->lock);
|
|
|
|
for (size_t i = 0; i < stream->i_http_headers; i++)
|
|
|
|
if (strncasecmp(stream->p_http_headers[i].name, "Content-Length", 14)) {
|
2014-02-25 11:39:43 +01:00
|
|
|
httpd_MsgAdd(answer, stream->p_http_headers[i].name, "%s",
|
2014-02-20 18:56:33 +01:00
|
|
|
stream->p_http_headers[i].value);
|
2014-02-15 23:15:49 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (!strncasecmp(stream->p_http_headers[i].name, "Content-Type", 12))
|
2014-02-15 23:15:49 +01:00
|
|
|
b_has_content_type = true;
|
2014-02-20 18:56:33 +01:00
|
|
|
else if (!strncasecmp(stream->p_http_headers[i].name, "Cache-Control", 13))
|
2014-02-15 23:15:49 +01:00
|
|
|
b_has_cache_control = true;
|
|
|
|
}
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_mutex_unlock(&stream->lock);
|
2014-02-15 23:15:49 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (query->i_type != HTTPD_MSG_HEAD) {
|
2011-11-03 18:41:56 +01:00
|
|
|
cl->b_stream_mode = true;
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_mutex_lock(&stream->lock);
|
2004-03-03 14:23:47 +01:00
|
|
|
/* Send the header */
|
2014-02-20 18:56:33 +01:00
|
|
|
if (stream->i_header > 0) {
|
2004-03-03 14:23:47 +01:00
|
|
|
answer->i_body = stream->i_header;
|
2014-02-20 18:56:33 +01:00
|
|
|
answer->p_body = xmalloc(stream->i_header);
|
|
|
|
memcpy(answer->p_body, stream->p_header, stream->i_header);
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
|
|
|
answer->i_body_offset = stream->i_buffer_last_pos;
|
2014-02-20 18:56:33 +01:00
|
|
|
if (stream->b_has_keyframes)
|
2013-08-16 16:42:40 +02:00
|
|
|
cl->i_keyframe_wait_to_pass = stream->i_last_keyframe_seen_pos;
|
|
|
|
else
|
|
|
|
cl->i_keyframe_wait_to_pass = -1;
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_mutex_unlock(&stream->lock);
|
|
|
|
} else {
|
|
|
|
httpd_MsgAdd(answer, "Content-Length", "0");
|
2004-04-19 10:27:34 +02:00
|
|
|
answer->i_body_offset = 0;
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
|
|
|
|
2014-02-15 23:15:49 +01:00
|
|
|
/* FIXME: move to http access_output */
|
2014-02-20 18:56:33 +01:00
|
|
|
if (!strcmp(stream->psz_mime, "video/x-ms-asf-stream")) {
|
2008-04-14 00:08:29 +02:00
|
|
|
bool b_xplaystream = false;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_MsgAdd(answer, "Content-type", "application/octet-stream");
|
|
|
|
httpd_MsgAdd(answer, "Server", "Cougar 4.1.0.3921");
|
|
|
|
httpd_MsgAdd(answer, "Pragma", "no-cache");
|
|
|
|
httpd_MsgAdd(answer, "Pragma", "client-id=%lu",
|
|
|
|
vlc_mrand48()&0x7fff);
|
|
|
|
httpd_MsgAdd(answer, "Pragma", "features=\"broadcast\"");
|
2004-04-19 10:27:34 +02:00
|
|
|
|
2004-03-03 14:23:47 +01:00
|
|
|
/* Check if there is a xPlayStrm=1 */
|
2014-02-20 18:56:33 +01:00
|
|
|
for (size_t i = 0; i < query->i_headers; i++)
|
|
|
|
if (!strcasecmp(query->p_headers[i].name, "Pragma") &&
|
|
|
|
strstr(query->p_headers[i].value, "xPlayStrm=1"))
|
2008-04-14 00:08:29 +02:00
|
|
|
b_xplaystream = true;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (!b_xplaystream)
|
2004-03-03 14:23:47 +01:00
|
|
|
answer->i_body_offset = 0;
|
2014-02-20 18:56:33 +01:00
|
|
|
} else if (!b_has_content_type)
|
2014-02-25 11:39:43 +01:00
|
|
|
httpd_MsgAdd(answer, "Content-type", "%s", stream->psz_mime);
|
2014-02-20 18:56:33 +01:00
|
|
|
|
|
|
|
if (!b_has_cache_control)
|
|
|
|
httpd_MsgAdd(answer, "Cache-Control", "no-cache");
|
2017-10-08 17:08:07 +02:00
|
|
|
|
|
|
|
httpd_MsgAdd(answer, "Connection", "close");
|
|
|
|
|
2004-03-03 14:23:47 +01:00
|
|
|
return VLC_SUCCESS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_stream_t *httpd_StreamNew(httpd_host_t *host,
|
2005-08-04 16:58:44 +02:00
|
|
|
const char *psz_url, const char *psz_mime,
|
2014-02-20 18:56:33 +01:00
|
|
|
const char *psz_user, const char *psz_password)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_stream_t *stream = malloc(sizeof(*stream));
|
|
|
|
if (!stream)
|
|
|
|
return NULL;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
stream->url = httpd_UrlNew(host, psz_url, psz_user, psz_password);
|
|
|
|
if (!stream->url) {
|
|
|
|
free(stream);
|
2004-03-03 14:23:47 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
2014-02-20 18:56:33 +01:00
|
|
|
|
|
|
|
vlc_mutex_init(&stream->lock);
|
2014-04-27 16:02:15 +02:00
|
|
|
if (psz_mime == NULL || psz_mime[0] == '\0')
|
|
|
|
psz_mime = vlc_mime_Ext2Mime(psz_url);
|
|
|
|
stream->psz_mime = xstrdup(psz_mime);
|
2014-02-20 18:56:33 +01:00
|
|
|
|
2004-03-03 14:23:47 +01:00
|
|
|
stream->i_header = 0;
|
|
|
|
stream->p_header = NULL;
|
|
|
|
stream->i_buffer_size = 5000000; /* 5 Mo per stream */
|
2014-02-20 18:56:33 +01:00
|
|
|
stream->p_buffer = xmalloc(stream->i_buffer_size);
|
2004-03-09 21:46:43 +01:00
|
|
|
/* We set to 1 to make life simpler
|
|
|
|
* (this way i_body_offset can never be 0) */
|
2004-03-03 14:23:47 +01:00
|
|
|
stream->i_buffer_pos = 1;
|
|
|
|
stream->i_buffer_last_pos = 1;
|
2013-08-16 16:42:40 +02:00
|
|
|
stream->b_has_keyframes = false;
|
|
|
|
stream->i_last_keyframe_seen_pos = 0;
|
2014-02-15 23:15:49 +01:00
|
|
|
stream->i_http_headers = 0;
|
|
|
|
stream->p_http_headers = NULL;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_UrlCatch(stream->url, HTTPD_MSG_HEAD, httpd_StreamCallBack,
|
|
|
|
(httpd_callback_sys_t*)stream);
|
|
|
|
httpd_UrlCatch(stream->url, HTTPD_MSG_GET, httpd_StreamCallBack,
|
|
|
|
(httpd_callback_sys_t*)stream);
|
|
|
|
httpd_UrlCatch(stream->url, HTTPD_MSG_POST, httpd_StreamCallBack,
|
|
|
|
(httpd_callback_sys_t*)stream);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
|
|
|
return stream;
|
|
|
|
}
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
int httpd_StreamHeader(httpd_stream_t *stream, uint8_t *p_data, int i_data)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_mutex_lock(&stream->lock);
|
|
|
|
free(stream->p_header);
|
2008-03-12 00:30:25 +01:00
|
|
|
stream->p_header = NULL;
|
|
|
|
|
2004-03-03 14:23:47 +01:00
|
|
|
stream->i_header = i_data;
|
2014-02-20 18:56:33 +01:00
|
|
|
if (i_data > 0) {
|
|
|
|
stream->p_header = xmalloc(i_data);
|
|
|
|
memcpy(stream->p_header, p_data, i_data);
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_mutex_unlock(&stream->lock);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
|
|
|
return VLC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
static void httpd_AppendData(httpd_stream_t *stream, uint8_t *p_data, int i_data)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
2013-08-16 16:42:40 +02:00
|
|
|
int i_pos = stream->i_buffer_pos % stream->i_buffer_size;
|
|
|
|
int i_count = i_data;
|
2014-02-20 18:56:33 +01:00
|
|
|
while (i_count > 0) {
|
|
|
|
int i_copy = __MIN(i_count, stream->i_buffer_size - i_pos);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
|
|
|
/* Ok, we can't go past the end of our buffer */
|
2014-02-20 18:56:33 +01:00
|
|
|
memcpy(&stream->p_buffer[i_pos], p_data, i_copy);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
i_pos = (i_pos + i_copy) % stream->i_buffer_size;
|
2004-03-03 14:23:47 +01:00
|
|
|
i_count -= i_copy;
|
|
|
|
p_data += i_copy;
|
|
|
|
}
|
|
|
|
|
|
|
|
stream->i_buffer_pos += i_data;
|
2013-08-16 16:42:40 +02:00
|
|
|
}
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
int httpd_StreamSend(httpd_stream_t *stream, const block_t *p_block)
|
2013-08-16 16:42:40 +02:00
|
|
|
{
|
2014-02-20 18:56:33 +01:00
|
|
|
if (!p_block || !p_block->p_buffer)
|
2013-08-16 16:42:40 +02:00
|
|
|
return VLC_SUCCESS;
|
2014-02-20 18:56:33 +01:00
|
|
|
|
|
|
|
vlc_mutex_lock(&stream->lock);
|
2013-08-16 16:42:40 +02:00
|
|
|
|
|
|
|
/* save this pointer (to be used by new connection) */
|
|
|
|
stream->i_buffer_last_pos = stream->i_buffer_pos;
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (p_block->i_flags & BLOCK_FLAG_TYPE_I) {
|
2013-08-16 16:42:40 +02:00
|
|
|
stream->b_has_keyframes = true;
|
|
|
|
stream->i_last_keyframe_seen_pos = stream->i_buffer_pos;
|
|
|
|
}
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_AppendData(stream, p_block->p_buffer, p_block->i_buffer);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_mutex_unlock(&stream->lock);
|
2004-03-03 14:23:47 +01:00
|
|
|
return VLC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
void httpd_StreamDelete(httpd_stream_t *stream)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_UrlDelete(stream->url);
|
|
|
|
for (size_t i = 0; i < stream->i_http_headers; i++) {
|
|
|
|
free(stream->p_http_headers[i].name);
|
|
|
|
free(stream->p_http_headers[i].value);
|
|
|
|
}
|
|
|
|
free(stream->p_http_headers);
|
|
|
|
vlc_mutex_destroy(&stream->lock);
|
|
|
|
free(stream->psz_mime);
|
|
|
|
free(stream->p_header);
|
|
|
|
free(stream->p_buffer);
|
|
|
|
free(stream);
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* Low level
|
|
|
|
*****************************************************************************/
|
2014-02-20 18:56:33 +01:00
|
|
|
static void* httpd_HostThread(void *);
|
|
|
|
static httpd_host_t *httpd_HostCreate(vlc_object_t *, const char *,
|
2018-11-18 13:35:41 +01:00
|
|
|
const char *, vlc_tls_server_t *);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
|
|
|
/* create a new host */
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_host_t *vlc_http_HostNew(vlc_object_t *p_this)
|
2004-11-04 23:55:54 +01:00
|
|
|
{
|
2014-02-20 18:56:33 +01:00
|
|
|
return httpd_HostCreate(p_this, "http-host", "http-port", NULL);
|
2011-08-02 17:26:40 +02:00
|
|
|
}
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_host_t *vlc_https_HostNew(vlc_object_t *obj)
|
2011-08-02 17:26:40 +02:00
|
|
|
{
|
2014-02-20 18:56:33 +01:00
|
|
|
char *cert = var_InheritString(obj, "http-cert");
|
|
|
|
if (!cert) {
|
|
|
|
msg_Err(obj, "HTTP/TLS certificate not specified!");
|
2011-08-02 17:26:40 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
char *key = var_InheritString(obj, "http-key");
|
2018-11-18 13:35:41 +01:00
|
|
|
vlc_tls_server_t *tls = vlc_tls_ServerCreate(obj, cert, key);
|
2011-08-02 17:26:40 +02:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (!tls) {
|
|
|
|
msg_Err(obj, "HTTP/TLS certificate error (%s and %s)",
|
|
|
|
cert, key ? key : cert);
|
|
|
|
free(key);
|
|
|
|
free(cert);
|
2011-08-02 17:26:40 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
2014-02-20 18:56:33 +01:00
|
|
|
free(key);
|
|
|
|
free(cert);
|
2011-08-02 17:26:40 +02:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
return httpd_HostCreate(obj, "http-host", "https-port", tls);
|
2004-11-04 23:55:54 +01:00
|
|
|
}
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_host_t *vlc_rtsp_HostNew(vlc_object_t *p_this)
|
2011-08-02 17:37:19 +02:00
|
|
|
{
|
2014-02-20 18:56:33 +01:00
|
|
|
return httpd_HostCreate(p_this, "rtsp-host", "rtsp-port", NULL);
|
2011-08-02 17:37:19 +02:00
|
|
|
}
|
|
|
|
|
2012-04-02 19:14:12 +02:00
|
|
|
static struct httpd
|
2011-10-24 03:33:11 +02:00
|
|
|
{
|
|
|
|
vlc_mutex_t mutex;
|
2018-06-17 19:32:13 +02:00
|
|
|
struct vlc_list hosts;
|
|
|
|
} httpd = { VLC_STATIC_MUTEX, VLC_LIST_INITIALIZER(&httpd.hosts) };
|
2011-10-24 03:33:11 +02:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
static httpd_host_t *httpd_HostCreate(vlc_object_t *p_this,
|
2011-08-02 18:06:29 +02:00
|
|
|
const char *hostvar,
|
|
|
|
const char *portvar,
|
2018-11-18 13:35:41 +01:00
|
|
|
vlc_tls_server_t *p_tls)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
2005-05-22 13:36:59 +02:00
|
|
|
httpd_host_t *host;
|
2014-02-20 18:56:33 +01:00
|
|
|
unsigned port = var_InheritInteger(p_this, portvar);
|
2004-07-17 14:30:59 +02:00
|
|
|
|
2004-03-03 14:23:47 +01:00
|
|
|
/* to be sure to avoid multiple creation */
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_mutex_lock(&httpd.mutex);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2005-05-22 13:36:59 +02:00
|
|
|
/* verify if it already exist */
|
2018-06-17 19:32:13 +02:00
|
|
|
vlc_list_foreach(host, &httpd.hosts, node) {
|
2011-10-24 03:33:11 +02:00
|
|
|
/* cannot mix TLS and non-TLS hosts */
|
2014-02-20 18:56:33 +01:00
|
|
|
if (host->port != port
|
|
|
|
|| (host->p_tls != NULL) != (p_tls != NULL))
|
2011-10-24 03:33:11 +02:00
|
|
|
continue;
|
|
|
|
|
2018-06-17 19:41:53 +02:00
|
|
|
/* Increase existing matching host reference count. */
|
|
|
|
atomic_fetch_add_explicit(&host->ref, 1, memory_order_relaxed);
|
2004-07-17 14:30:59 +02:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_mutex_unlock(&httpd.mutex);
|
2018-11-18 13:35:41 +01:00
|
|
|
vlc_tls_ServerDelete(p_tls);
|
2005-05-22 13:36:59 +02:00
|
|
|
return host;
|
2004-07-24 11:06:58 +02:00
|
|
|
}
|
2004-07-17 14:30:59 +02:00
|
|
|
|
|
|
|
/* create the new host */
|
2014-02-20 18:56:33 +01:00
|
|
|
host = (httpd_host_t *)vlc_custom_create(p_this, sizeof (*host),
|
|
|
|
"http host");
|
|
|
|
if (!host)
|
2006-12-01 21:26:05 +01:00
|
|
|
goto error;
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_mutex_init(&host->lock);
|
|
|
|
vlc_cond_init(&host->wait);
|
2018-06-17 19:41:53 +02:00
|
|
|
atomic_init(&host->ref, 1);
|
2004-07-17 14:30:59 +02:00
|
|
|
|
2018-06-27 18:33:10 +02:00
|
|
|
char *hostname = var_InheritString(p_this, hostvar);
|
|
|
|
|
|
|
|
host->fds = net_ListenTCP(p_this, hostname, port);
|
|
|
|
free(hostname);
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (!host->fds) {
|
|
|
|
msg_Err(p_this, "cannot create socket(s) for HTTP host");
|
2005-05-22 13:36:59 +02:00
|
|
|
goto error;
|
|
|
|
}
|
2007-02-21 18:58:12 +01:00
|
|
|
for (host->nfd = 0; host->fds[host->nfd] != -1; host->nfd++);
|
2006-02-10 15:43:05 +01:00
|
|
|
|
2011-10-24 03:33:11 +02:00
|
|
|
host->port = port;
|
2018-06-17 20:09:44 +02:00
|
|
|
vlc_list_init(&host->urls);
|
2018-06-17 20:03:46 +02:00
|
|
|
host->client_count = 0;
|
|
|
|
vlc_list_init(&host->clients);
|
2011-08-02 18:06:29 +02:00
|
|
|
host->p_tls = p_tls;
|
2004-07-24 11:06:58 +02:00
|
|
|
|
2004-03-03 14:23:47 +01:00
|
|
|
/* create the thread */
|
2014-02-20 18:56:33 +01:00
|
|
|
if (vlc_clone(&host->thread, httpd_HostThread, host,
|
|
|
|
VLC_THREAD_PRIORITY_LOW)) {
|
|
|
|
msg_Err(p_this, "cannot spawn http host thread");
|
2004-07-17 14:30:59 +02:00
|
|
|
goto error;
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
|
|
|
|
2011-10-24 03:33:11 +02:00
|
|
|
/* now add it to httpd */
|
2018-06-17 19:32:13 +02:00
|
|
|
vlc_list_append(&host->node, &httpd.hosts);
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_mutex_unlock(&httpd.mutex);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
|
|
|
return host;
|
|
|
|
|
2004-07-17 14:30:59 +02:00
|
|
|
error:
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_mutex_unlock(&httpd.mutex);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (host) {
|
|
|
|
net_ListenClose(host->fds);
|
|
|
|
vlc_cond_destroy(&host->wait);
|
|
|
|
vlc_mutex_destroy(&host->lock);
|
2019-03-06 19:15:18 +01:00
|
|
|
vlc_object_delete(host);
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
|
|
|
|
2018-11-18 13:35:41 +01:00
|
|
|
vlc_tls_ServerDelete(p_tls);
|
2004-03-03 14:23:47 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* delete a host */
|
2014-02-20 18:56:33 +01:00
|
|
|
void httpd_HostDelete(httpd_host_t *host)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
2018-06-17 20:03:46 +02:00
|
|
|
httpd_client_t *client;
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_mutex_lock(&httpd.mutex);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2018-06-17 19:41:53 +02:00
|
|
|
if (atomic_fetch_sub_explicit(&host->ref, 1, memory_order_relaxed) > 1) {
|
2004-03-03 14:23:47 +01:00
|
|
|
/* still used */
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_mutex_unlock(&httpd.mutex);
|
|
|
|
msg_Dbg(host, "httpd_HostDelete: host still in use");
|
2004-03-03 14:23:47 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-06-17 19:32:13 +02:00
|
|
|
vlc_list_remove(&host->node);
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_cancel(host->thread);
|
|
|
|
vlc_join(host->thread, NULL);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
msg_Dbg(host, "HTTP host removed");
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2018-06-17 20:03:46 +02:00
|
|
|
vlc_list_foreach(client, &host->clients, node) {
|
2014-02-20 18:56:33 +01:00
|
|
|
msg_Warn(host, "client still connected");
|
2018-06-17 20:03:46 +02:00
|
|
|
httpd_ClientDestroy(client);
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
|
|
|
|
2018-06-17 20:14:59 +02:00
|
|
|
assert(vlc_list_is_empty(&host->urls));
|
2018-11-18 13:35:41 +01:00
|
|
|
vlc_tls_ServerDelete(host->p_tls);
|
2014-02-20 18:56:33 +01:00
|
|
|
net_ListenClose(host->fds);
|
|
|
|
vlc_cond_destroy(&host->wait);
|
|
|
|
vlc_mutex_destroy(&host->lock);
|
2019-03-06 19:15:18 +01:00
|
|
|
vlc_object_delete(host);
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_mutex_unlock(&httpd.mutex);
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* register a new url */
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_url_t *httpd_UrlNew(httpd_host_t *host, const char *psz_url,
|
|
|
|
const char *psz_user, const char *psz_password)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
|
|
|
httpd_url_t *url;
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
assert(psz_url);
|
2007-09-17 18:56:12 +02:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_mutex_lock(&host->lock);
|
2018-06-17 20:09:44 +02:00
|
|
|
vlc_list_foreach(url, &host->urls, node)
|
|
|
|
if (!strcmp(psz_url, url->psz_url)) {
|
2014-02-20 18:56:33 +01:00
|
|
|
msg_Warn(host, "cannot add '%s' (url already defined)", psz_url);
|
|
|
|
vlc_mutex_unlock(&host->lock);
|
2012-04-01 21:06:13 +02:00
|
|
|
return NULL;
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
|
|
|
|
2018-06-17 20:11:28 +02:00
|
|
|
url = malloc(sizeof (*url));
|
|
|
|
if (unlikely(url == NULL)) {
|
|
|
|
vlc_mutex_unlock(&host->lock);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2004-03-03 14:23:47 +01:00
|
|
|
url->host = host;
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_mutex_init(&url->lock);
|
2014-04-27 16:02:15 +02:00
|
|
|
url->psz_url = xstrdup(psz_url);
|
|
|
|
url->psz_user = xstrdup(psz_user ? psz_user : "");
|
|
|
|
url->psz_password = xstrdup(psz_password ? psz_password : "");
|
2014-02-20 18:56:33 +01:00
|
|
|
for (int i = 0; i < HTTPD_MSG_MAX; i++) {
|
2004-03-03 14:23:47 +01:00
|
|
|
url->catch[i].cb = NULL;
|
|
|
|
url->catch[i].p_sys = NULL;
|
|
|
|
}
|
|
|
|
|
2018-06-17 20:09:44 +02:00
|
|
|
vlc_list_append(&url->node, &host->urls);
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_cond_signal(&host->wait);
|
|
|
|
vlc_mutex_unlock(&host->lock);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
|
|
|
return url;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* register callback on a url */
|
2014-02-20 18:56:33 +01:00
|
|
|
int httpd_UrlCatch(httpd_url_t *url, int i_msg, httpd_callback_t cb,
|
|
|
|
httpd_callback_sys_t *p_sys)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_mutex_lock(&url->lock);
|
2004-03-03 14:23:47 +01:00
|
|
|
url->catch[i_msg].cb = cb;
|
|
|
|
url->catch[i_msg].p_sys= p_sys;
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_mutex_unlock(&url->lock);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
|
|
|
return VLC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2011-04-03 21:53:49 +02:00
|
|
|
/* delete a url */
|
2014-02-20 18:56:33 +01:00
|
|
|
void httpd_UrlDelete(httpd_url_t *url)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
|
|
|
httpd_host_t *host = url->host;
|
2018-06-17 20:03:46 +02:00
|
|
|
httpd_client_t *client;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_mutex_lock(&host->lock);
|
2018-06-17 20:09:44 +02:00
|
|
|
vlc_list_remove(&url->node);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_mutex_destroy(&url->lock);
|
|
|
|
free(url->psz_url);
|
|
|
|
free(url->psz_user);
|
|
|
|
free(url->psz_password);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2018-06-17 20:03:46 +02:00
|
|
|
vlc_list_foreach(client, &host->clients, node) {
|
2014-02-20 18:56:33 +01:00
|
|
|
if (client->url != url)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* TODO complete it */
|
|
|
|
msg_Warn(host, "force closing connections");
|
2018-06-17 20:03:46 +02:00
|
|
|
host->client_count--;
|
2016-01-12 21:09:48 +01:00
|
|
|
httpd_ClientDestroy(client);
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
2014-02-20 18:56:33 +01:00
|
|
|
free(url);
|
|
|
|
vlc_mutex_unlock(&host->lock);
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
static void httpd_MsgInit(httpd_message_t *msg)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
|
|
|
msg->cl = NULL;
|
|
|
|
msg->i_type = HTTPD_MSG_NONE;
|
|
|
|
msg->i_proto = HTTPD_PROTO_NONE;
|
2007-09-02 10:51:01 +02:00
|
|
|
msg->i_version = -1; /* FIXME */
|
2004-03-03 14:23:47 +01:00
|
|
|
|
|
|
|
msg->i_status = 0;
|
|
|
|
|
2006-02-10 15:43:05 +01:00
|
|
|
msg->psz_url = NULL;
|
|
|
|
msg->psz_args = NULL;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-15 23:15:49 +01:00
|
|
|
msg->i_headers = 0;
|
|
|
|
msg->p_headers = NULL;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
|
|
|
msg->i_body_offset = 0;
|
|
|
|
msg->i_body = 0;
|
2007-08-21 21:16:09 +02:00
|
|
|
msg->p_body = NULL;
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
static void httpd_MsgClean(httpd_message_t *msg)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
2014-02-20 18:56:33 +01:00
|
|
|
free(msg->psz_url);
|
|
|
|
free(msg->psz_args);
|
|
|
|
for (size_t i = 0; i < msg->i_headers; i++) {
|
|
|
|
free(msg->p_headers[i].name);
|
|
|
|
free(msg->p_headers[i].value);
|
|
|
|
}
|
|
|
|
free(msg->p_headers);
|
|
|
|
free(msg->p_body);
|
|
|
|
httpd_MsgInit(msg);
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
const char *httpd_MsgGet(const httpd_message_t *msg, const char *name)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
2014-02-20 18:56:33 +01:00
|
|
|
for (size_t i = 0; i < msg->i_headers; i++)
|
|
|
|
if (!strcasecmp(msg->p_headers[i].name, name))
|
2014-02-15 23:15:49 +01:00
|
|
|
return msg->p_headers[i].value;
|
2006-03-22 22:01:09 +01:00
|
|
|
return NULL;
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
2006-02-04 20:48:38 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
void httpd_MsgAdd(httpd_message_t *msg, const char *name, const char *psz_value, ...)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_header *p_tmp = realloc(msg->p_headers, sizeof(httpd_header) * (msg->i_headers + 1));
|
2014-02-20 18:34:56 +01:00
|
|
|
if (!p_tmp)
|
|
|
|
return;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:34:56 +01:00
|
|
|
msg->p_headers = p_tmp;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:34:56 +01:00
|
|
|
httpd_header *h = &msg->p_headers[msg->i_headers];
|
|
|
|
h->name = strdup(name);
|
|
|
|
if (!h->name)
|
2007-08-20 20:45:41 +02:00
|
|
|
return;
|
|
|
|
|
2014-02-20 18:34:56 +01:00
|
|
|
h->value = NULL;
|
|
|
|
|
|
|
|
va_list args;
|
2014-02-20 18:56:33 +01:00
|
|
|
va_start(args, psz_value);
|
2014-02-20 18:34:56 +01:00
|
|
|
int ret = us_vasprintf(&h->value, psz_value, args);
|
2014-02-20 18:56:33 +01:00
|
|
|
va_end(args);
|
2014-02-20 18:34:56 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (ret == -1) {
|
2014-02-20 18:34:56 +01:00
|
|
|
free(h->name);
|
2007-08-20 20:45:41 +02:00
|
|
|
return;
|
|
|
|
}
|
2014-02-20 18:34:56 +01:00
|
|
|
|
|
|
|
msg->i_headers++;
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
|
|
|
|
2018-06-22 13:19:24 +02:00
|
|
|
static void httpd_ClientInit(httpd_client_t *cl, vlc_tick_t now)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
|
|
|
cl->i_state = HTTPD_CLIENT_RECEIVING;
|
2007-02-21 19:03:40 +01:00
|
|
|
cl->i_activity_date = now;
|
2018-07-03 10:34:08 +02:00
|
|
|
cl->i_activity_timeout = VLC_TICK_FROM_SEC(10);
|
2005-10-22 18:23:01 +02:00
|
|
|
cl->i_buffer_size = HTTPD_CL_BUFSIZE;
|
2004-03-03 14:23:47 +01:00
|
|
|
cl->i_buffer = 0;
|
2014-02-20 18:56:33 +01:00
|
|
|
cl->p_buffer = xmalloc(cl->i_buffer_size);
|
2013-08-16 16:42:40 +02:00
|
|
|
cl->i_keyframe_wait_to_pass = -1;
|
2011-11-03 18:41:56 +01:00
|
|
|
cl->b_stream_mode = false;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_MsgInit(&cl->query);
|
|
|
|
httpd_MsgInit(&cl->answer);
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
2004-03-09 21:46:43 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
char* httpd_ClientIP(const httpd_client_t *cl, char *ip, int *port)
|
2004-03-22 13:33:29 +01:00
|
|
|
{
|
2017-02-26 14:37:52 +01:00
|
|
|
return net_GetPeerAddress(vlc_tls_GetFD(cl->sock), ip, port) ? NULL : ip;
|
2005-07-29 19:53:50 +02:00
|
|
|
}
|
2005-06-26 12:24:35 +02:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
char* httpd_ServerIP(const httpd_client_t *cl, char *ip, int *port)
|
2005-07-29 19:53:50 +02:00
|
|
|
{
|
2017-02-26 14:37:52 +01:00
|
|
|
return net_GetSockAddress(vlc_tls_GetFD(cl->sock), ip, port) ? NULL : ip;
|
2004-03-22 13:33:29 +01:00
|
|
|
}
|
|
|
|
|
2016-01-12 21:09:48 +01:00
|
|
|
static void httpd_ClientDestroy(httpd_client_t *cl)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
2018-06-17 20:03:46 +02:00
|
|
|
vlc_list_remove(&cl->node);
|
2017-02-26 14:37:52 +01:00
|
|
|
vlc_tls_Close(cl->sock);
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_MsgClean(&cl->answer);
|
|
|
|
httpd_MsgClean(&cl->query);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
free(cl->p_buffer);
|
2016-01-12 21:09:48 +01:00
|
|
|
free(cl);
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
|
|
|
|
2018-06-22 13:19:24 +02:00
|
|
|
static httpd_client_t *httpd_ClientNew(vlc_tls_t *sock, vlc_tick_t now)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_client_t *cl = malloc(sizeof(httpd_client_t));
|
2006-02-10 15:43:05 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (!cl) return NULL;
|
2006-02-10 15:43:05 +01:00
|
|
|
|
2017-02-26 14:37:52 +01:00
|
|
|
cl->sock = sock;
|
2004-03-03 14:23:47 +01:00
|
|
|
cl->url = NULL;
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_ClientInit(cl, now);
|
2004-03-03 14:23:47 +01:00
|
|
|
return cl;
|
|
|
|
}
|
|
|
|
|
2008-06-12 18:52:12 +02:00
|
|
|
static
|
|
|
|
ssize_t httpd_NetRecv (httpd_client_t *cl, uint8_t *p, size_t i_len)
|
2004-11-04 23:55:54 +01:00
|
|
|
{
|
2017-02-26 14:37:52 +01:00
|
|
|
vlc_tls_t *sock = cl->sock;
|
2017-02-26 15:03:16 +01:00
|
|
|
struct iovec iov = { .iov_base = p, .iov_len = i_len };
|
2018-07-29 11:00:59 +02:00
|
|
|
return sock->ops->readv(sock, &iov, 1);
|
2004-11-04 23:55:54 +01:00
|
|
|
}
|
|
|
|
|
2008-06-12 18:52:12 +02:00
|
|
|
static
|
|
|
|
ssize_t httpd_NetSend (httpd_client_t *cl, const uint8_t *p, size_t i_len)
|
2004-11-04 23:55:54 +01:00
|
|
|
{
|
2017-02-26 14:37:52 +01:00
|
|
|
vlc_tls_t *sock = cl->sock;
|
2017-02-26 15:03:16 +01:00
|
|
|
const struct iovec iov = { .iov_base = (void *)p, .iov_len = i_len };
|
2018-07-29 11:00:59 +02:00
|
|
|
return sock->ops->writev(sock, &iov, 1);
|
2004-11-04 23:55:54 +01:00
|
|
|
}
|
|
|
|
|
2007-09-15 15:43:38 +02:00
|
|
|
|
|
|
|
static const struct
|
|
|
|
{
|
|
|
|
const char name[16];
|
|
|
|
int i_type;
|
|
|
|
int i_proto;
|
|
|
|
}
|
|
|
|
msg_type[] =
|
|
|
|
{
|
|
|
|
{ "OPTIONS", HTTPD_MSG_OPTIONS, HTTPD_PROTO_RTSP },
|
|
|
|
{ "DESCRIBE", HTTPD_MSG_DESCRIBE, HTTPD_PROTO_RTSP },
|
|
|
|
{ "SETUP", HTTPD_MSG_SETUP, HTTPD_PROTO_RTSP },
|
|
|
|
{ "PLAY", HTTPD_MSG_PLAY, HTTPD_PROTO_RTSP },
|
|
|
|
{ "PAUSE", HTTPD_MSG_PAUSE, HTTPD_PROTO_RTSP },
|
|
|
|
{ "GET_PARAMETER", HTTPD_MSG_GETPARAMETER, HTTPD_PROTO_RTSP },
|
|
|
|
{ "TEARDOWN", HTTPD_MSG_TEARDOWN, HTTPD_PROTO_RTSP },
|
|
|
|
{ "GET", HTTPD_MSG_GET, HTTPD_PROTO_HTTP },
|
|
|
|
{ "HEAD", HTTPD_MSG_HEAD, HTTPD_PROTO_HTTP },
|
|
|
|
{ "POST", HTTPD_MSG_POST, HTTPD_PROTO_HTTP },
|
|
|
|
{ "", HTTPD_MSG_NONE, HTTPD_PROTO_NONE }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
static void httpd_ClientRecv(httpd_client_t *cl)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
|
|
|
int i_len;
|
|
|
|
|
2007-09-24 18:22:33 +02:00
|
|
|
/* ignore leading whites */
|
2014-02-20 18:56:33 +01:00
|
|
|
if (cl->query.i_proto == HTTPD_PROTO_NONE && cl->i_buffer == 0) {
|
2007-09-24 18:22:33 +02:00
|
|
|
unsigned char c;
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
i_len = httpd_NetRecv(cl, &c, 1);
|
2007-09-24 18:22:33 +02:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (i_len > 0 && !strchr("\r\n\t ", c)) {
|
2007-09-24 18:22:33 +02:00
|
|
|
cl->p_buffer[0] = c;
|
|
|
|
cl->i_buffer++;
|
|
|
|
}
|
2014-02-20 18:56:33 +01:00
|
|
|
} else if (cl->query.i_proto == HTTPD_PROTO_NONE) {
|
2007-09-15 15:43:38 +02:00
|
|
|
/* enough to see if it's Interleaved RTP over RTSP or RTSP/HTTP */
|
2014-02-20 18:56:33 +01:00
|
|
|
i_len = httpd_NetRecv(cl, &cl->p_buffer[cl->i_buffer],
|
|
|
|
7 - cl->i_buffer);
|
|
|
|
if (i_len > 0)
|
2004-03-03 14:23:47 +01:00
|
|
|
cl->i_buffer += i_len;
|
|
|
|
|
2007-09-24 19:07:42 +02:00
|
|
|
/* The smallest legal request is 7 bytes ("GET /\r\n"),
|
|
|
|
* this is the maximum we can ask at this point. */
|
2014-02-20 18:56:33 +01:00
|
|
|
if (cl->i_buffer >= 7) {
|
|
|
|
if (!memcmp(cl->p_buffer, "HTTP/1.", 7)) {
|
2004-03-03 14:23:47 +01:00
|
|
|
cl->query.i_proto = HTTPD_PROTO_HTTP;
|
|
|
|
cl->query.i_type = HTTPD_MSG_ANSWER;
|
2014-02-20 18:56:33 +01:00
|
|
|
} else if (!memcmp(cl->p_buffer, "RTSP/1.", 7)) {
|
2004-03-03 14:23:47 +01:00
|
|
|
cl->query.i_proto = HTTPD_PROTO_RTSP;
|
|
|
|
cl->query.i_type = HTTPD_MSG_ANSWER;
|
2014-02-20 18:56:33 +01:00
|
|
|
} else {
|
2007-09-24 19:07:42 +02:00
|
|
|
/* We need the full request line to determine the protocol. */
|
|
|
|
cl->query.i_proto = HTTPD_PROTO_HTTP0;
|
2004-03-03 14:23:47 +01:00
|
|
|
cl->query.i_type = HTTPD_MSG_NONE;
|
|
|
|
}
|
|
|
|
}
|
2014-02-20 18:56:33 +01:00
|
|
|
} else if (cl->query.i_body > 0) {
|
2004-03-03 14:23:47 +01:00
|
|
|
/* we are reading the body of a request or a channel */
|
2014-08-25 19:55:42 +02:00
|
|
|
assert (cl->query.p_body != NULL);
|
2014-02-20 18:56:33 +01:00
|
|
|
i_len = httpd_NetRecv(cl, &cl->query.p_body[cl->i_buffer],
|
|
|
|
cl->query.i_body - cl->i_buffer);
|
|
|
|
if (i_len > 0)
|
2004-03-03 14:23:47 +01:00
|
|
|
cl->i_buffer += i_len;
|
2014-02-20 18:56:33 +01:00
|
|
|
|
|
|
|
if (cl->i_buffer >= cl->query.i_body)
|
2004-03-03 14:23:47 +01:00
|
|
|
cl->i_state = HTTPD_CLIENT_RECEIVE_DONE;
|
2014-02-20 18:56:33 +01:00
|
|
|
} else for (;;) { /* we are reading a header -> char by char */
|
|
|
|
if (cl->i_buffer == cl->i_buffer_size) {
|
2018-06-27 11:06:42 +02:00
|
|
|
// Allocate an extra byte for the termination null byte
|
|
|
|
uint8_t *newbuf = realloc(cl->p_buffer, cl->i_buffer_size + 1025);
|
2014-02-20 18:56:33 +01:00
|
|
|
if (!newbuf) {
|
|
|
|
i_len = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
cl->p_buffer = newbuf;
|
|
|
|
cl->i_buffer_size += 1024;
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
2014-02-20 18:56:33 +01:00
|
|
|
|
|
|
|
i_len = httpd_NetRecv (cl, &cl->p_buffer[cl->i_buffer], 1);
|
|
|
|
if (i_len <= 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
cl->i_buffer++;
|
|
|
|
|
|
|
|
if ((cl->query.i_proto == HTTPD_PROTO_HTTP0)
|
|
|
|
&& (cl->p_buffer[cl->i_buffer - 1] == '\n'))
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
2014-02-20 18:56:33 +01:00
|
|
|
/* Request line is now complete */
|
|
|
|
const char *p = memchr(cl->p_buffer, ' ', cl->i_buffer);
|
|
|
|
size_t len;
|
2007-09-15 15:43:38 +02:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
assert(cl->query.i_type == HTTPD_MSG_NONE);
|
2007-09-15 15:43:38 +02:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (!p) { /* no URI: evil guy */
|
|
|
|
i_len = 0; /* drop connection */
|
2004-03-03 14:23:47 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
do
|
|
|
|
p++; /* skips extra spaces */
|
|
|
|
while (*p == ' ');
|
2007-09-24 19:07:42 +02:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
p = memchr(p, ' ', ((char *)cl->p_buffer) + cl->i_buffer - p);
|
|
|
|
if (!p) { /* no explicit protocol: HTTP/0.9 */
|
|
|
|
i_len = 0; /* not supported currently -> drop */
|
|
|
|
break;
|
|
|
|
}
|
2007-09-24 19:07:42 +02:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
do
|
|
|
|
p++; /* skips extra spaces ever again */
|
|
|
|
while (*p == ' ');
|
2007-09-24 19:07:42 +02:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
len = ((char *)cl->p_buffer) + cl->i_buffer - p;
|
|
|
|
if (len < 7) /* foreign protocol */
|
|
|
|
i_len = 0; /* I don't understand -> drop */
|
|
|
|
else if (!memcmp(p, "HTTP/1.", 7)) {
|
|
|
|
cl->query.i_proto = HTTPD_PROTO_HTTP;
|
|
|
|
cl->query.i_version = atoi(p + 7);
|
|
|
|
} else if (!memcmp(p, "RTSP/1.", 7)) {
|
|
|
|
cl->query.i_proto = HTTPD_PROTO_RTSP;
|
|
|
|
cl->query.i_version = atoi(p + 7);
|
|
|
|
} else if (!memcmp(p, "HTTP/", 5)) {
|
|
|
|
const uint8_t sorry[] =
|
|
|
|
"HTTP/1.1 505 Unknown HTTP version\r\n\r\n";
|
|
|
|
httpd_NetSend(cl, sorry, sizeof(sorry) - 1);
|
|
|
|
i_len = 0; /* drop */
|
|
|
|
} else if (!memcmp(p, "RTSP/", 5)) {
|
|
|
|
const uint8_t sorry[] =
|
|
|
|
"RTSP/1.0 505 Unknown RTSP version\r\n\r\n";
|
|
|
|
httpd_NetSend(cl, sorry, sizeof(sorry) - 1);
|
|
|
|
i_len = 0; /* drop */
|
|
|
|
} else /* yet another foreign protocol */
|
|
|
|
i_len = 0;
|
|
|
|
|
|
|
|
if (i_len == 0)
|
|
|
|
break;
|
|
|
|
}
|
2007-09-24 19:07:42 +02:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if ((cl->i_buffer >= 2 && !memcmp(&cl->p_buffer[cl->i_buffer-2], "\n\n", 2))||
|
|
|
|
(cl->i_buffer >= 4 && !memcmp(&cl->p_buffer[cl->i_buffer-4], "\r\n\r\n", 4)))
|
|
|
|
{
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
/* we have finished the header so parse it and set i_body */
|
|
|
|
cl->p_buffer[cl->i_buffer] = '\0';
|
|
|
|
|
|
|
|
if (cl->query.i_type == HTTPD_MSG_ANSWER) {
|
|
|
|
/* FIXME:
|
|
|
|
* assume strlen("HTTP/1.x") = 8
|
|
|
|
*/
|
|
|
|
cl->query.i_status =
|
|
|
|
strtol((char *)&cl->p_buffer[8],
|
|
|
|
&p, 0);
|
|
|
|
while (*p == ' ')
|
|
|
|
p++;
|
|
|
|
} else {
|
|
|
|
p = NULL;
|
|
|
|
cl->query.i_type = HTTPD_MSG_NONE;
|
|
|
|
|
|
|
|
for (unsigned i = 0; msg_type[i].name[0]; i++)
|
|
|
|
if (!strncmp((char *)cl->p_buffer, msg_type[i].name,
|
|
|
|
strlen(msg_type[i].name))) {
|
|
|
|
p = (char *)&cl->p_buffer[strlen(msg_type[i].name) + 1 ];
|
|
|
|
cl->query.i_type = msg_type[i].i_type;
|
|
|
|
if (cl->query.i_proto != msg_type[i].i_proto) {
|
|
|
|
p = NULL;
|
|
|
|
cl->query.i_proto = HTTPD_PROTO_NONE;
|
|
|
|
cl->query.i_type = HTTPD_MSG_NONE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2007-09-24 19:07:42 +02:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (!p) {
|
|
|
|
if (strstr((char *)cl->p_buffer, "HTTP/1."))
|
|
|
|
cl->query.i_proto = HTTPD_PROTO_HTTP;
|
|
|
|
else if (strstr((char *)cl->p_buffer, "RTSP/1."))
|
|
|
|
cl->query.i_proto = HTTPD_PROTO_RTSP;
|
|
|
|
} else {
|
|
|
|
char *p2;
|
|
|
|
char *p3;
|
2007-09-24 19:07:42 +02:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
while (*p == ' ')
|
2004-03-03 14:23:47 +01:00
|
|
|
p++;
|
2014-02-20 18:56:33 +01:00
|
|
|
|
|
|
|
p2 = strchr(p, ' ');
|
|
|
|
if (p2)
|
|
|
|
*p2++ = '\0';
|
|
|
|
|
|
|
|
if (!strncasecmp(p, (cl->query.i_proto == HTTPD_PROTO_HTTP) ? "http:" : "rtsp:", 5)) {
|
|
|
|
/* Skip hier-part of URL (if present) */
|
|
|
|
p += 5;
|
|
|
|
if (!strncmp(p, "//", 2)) { /* skip authority */
|
|
|
|
/* see RFC3986 §3.2 */
|
|
|
|
p += 2;
|
|
|
|
p += strcspn(p, "/?#");
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
|
|
|
}
|
2014-02-20 18:56:33 +01:00
|
|
|
else if (!strncasecmp(p, (cl->query.i_proto == HTTPD_PROTO_HTTP) ? "https:" : "rtsps:", 6)) {
|
|
|
|
/* Skip hier-part of URL (if present) */
|
|
|
|
p += 6;
|
|
|
|
if (!strncmp(p, "//", 2)) { /* skip authority */
|
|
|
|
/* see RFC3986 §3.2 */
|
|
|
|
p += 2;
|
|
|
|
p += strcspn(p, "/?#");
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-27 11:16:56 +02:00
|
|
|
if(cl->query.psz_url == NULL) {
|
|
|
|
cl->query.psz_url = strdup(p);
|
|
|
|
if ((p3 = strchr(cl->query.psz_url, '?')) ) {
|
|
|
|
*p3++ = '\0';
|
|
|
|
cl->query.psz_args = (uint8_t *)strdup(p3);
|
|
|
|
}
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
2014-02-20 18:56:33 +01:00
|
|
|
p = p2;
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
2014-02-20 18:56:33 +01:00
|
|
|
}
|
|
|
|
if (p)
|
|
|
|
p = strchr(p, '\n');
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (p) {
|
|
|
|
while (*p == '\n' || *p == '\r')
|
|
|
|
p++;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
while (p && *p) {
|
|
|
|
char *line = p;
|
|
|
|
char *eol = p = strchr(p, '\n');
|
|
|
|
char *colon;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
while (eol && eol >= line && (*eol == '\n' || *eol == '\r'))
|
|
|
|
*eol-- = '\0';
|
|
|
|
|
|
|
|
if ((colon = strchr(line, ':'))) {
|
|
|
|
*colon++ = '\0';
|
|
|
|
while (*colon == ' ')
|
|
|
|
colon++;
|
2014-02-25 11:39:43 +01:00
|
|
|
httpd_MsgAdd(&cl->query, line, "%s", colon);
|
2014-02-20 18:56:33 +01:00
|
|
|
|
|
|
|
if (!strcasecmp(line, "Content-Length"))
|
|
|
|
cl->query.i_body = atol(colon);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p) {
|
|
|
|
p++;
|
|
|
|
while (*p == '\n' || *p == '\r')
|
2004-03-03 14:23:47 +01:00
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
2014-02-20 18:56:33 +01:00
|
|
|
}
|
|
|
|
if (cl->query.i_body > 0) {
|
|
|
|
/* TODO Mhh, handle the case where the client only
|
|
|
|
* sends a request and closes the connection to
|
|
|
|
* mark the end of the body (probably only RTSP) */
|
2017-09-15 22:19:33 +02:00
|
|
|
if (cl->query.i_body < 65536)
|
2014-08-25 19:55:42 +02:00
|
|
|
cl->query.p_body = malloc(cl->query.i_body);
|
|
|
|
else
|
|
|
|
cl->query.p_body = NULL;
|
2014-02-20 18:56:33 +01:00
|
|
|
cl->i_buffer = 0;
|
|
|
|
if (!cl->query.p_body) {
|
|
|
|
switch (cl->query.i_proto) {
|
2014-02-16 11:02:02 +01:00
|
|
|
case HTTPD_PROTO_HTTP: {
|
2014-08-25 19:55:28 +02:00
|
|
|
const uint8_t sorry[] = "HTTP/1.1 413 Request Entity Too Large\r\n\r\n";
|
|
|
|
httpd_NetSend(cl, sorry, sizeof(sorry) - 1);
|
|
|
|
break;
|
|
|
|
}
|
2014-02-16 11:02:02 +01:00
|
|
|
case HTTPD_PROTO_RTSP: {
|
2014-08-25 19:55:28 +02:00
|
|
|
const uint8_t sorry[] = "RTSP/1.0 413 Request Entity Too Large\r\n\r\n";
|
|
|
|
httpd_NetSend(cl, sorry, sizeof(sorry) - 1);
|
|
|
|
break;
|
|
|
|
}
|
2014-02-16 11:02:02 +01:00
|
|
|
default:
|
2015-02-17 22:10:16 +01:00
|
|
|
vlc_assert_unreachable();
|
2010-12-11 00:58:18 +01:00
|
|
|
}
|
2014-02-20 18:56:33 +01:00
|
|
|
i_len = 0; /* drop */
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
2014-02-20 18:56:33 +01:00
|
|
|
break;
|
|
|
|
} else
|
|
|
|
cl->i_state = HTTPD_CLIENT_RECEIVE_DONE;
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check if the client is to be set to dead */
|
2014-02-20 18:56:33 +01:00
|
|
|
#if defined(_WIN32)
|
|
|
|
if ((i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK) || (i_len == 0))
|
2004-03-03 14:23:47 +01:00
|
|
|
#else
|
2014-02-20 18:56:33 +01:00
|
|
|
if ((i_len < 0 && errno != EAGAIN) || (i_len == 0))
|
2004-03-03 14:23:47 +01:00
|
|
|
#endif
|
|
|
|
{
|
2014-02-20 18:56:33 +01:00
|
|
|
if (cl->query.i_proto != HTTPD_PROTO_NONE && cl->query.i_type != HTTPD_MSG_NONE) {
|
2004-03-03 14:23:47 +01:00
|
|
|
/* connection closed -> end of data */
|
2014-02-20 18:56:33 +01:00
|
|
|
if (cl->query.i_body > 0)
|
2004-03-03 14:23:47 +01:00
|
|
|
cl->query.i_body = cl->i_buffer;
|
|
|
|
cl->i_state = HTTPD_CLIENT_RECEIVE_DONE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
cl->i_state = HTTPD_CLIENT_DEAD;
|
|
|
|
}
|
|
|
|
|
2004-11-02 16:23:18 +01:00
|
|
|
/* XXX: for QT I have to disable timeout. Try to find why */
|
2014-02-20 18:56:33 +01:00
|
|
|
if (cl->query.i_proto == HTTPD_PROTO_RTSP)
|
2004-11-02 16:23:18 +01:00
|
|
|
cl->i_activity_timeout = 0;
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
static void httpd_ClientSend(httpd_client_t *cl)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
|
|
|
int i_len;
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (cl->i_buffer < 0) {
|
2004-03-03 14:23:47 +01:00
|
|
|
/* We need to create the header */
|
|
|
|
int i_size = 0;
|
|
|
|
char *p;
|
2014-02-20 18:56:33 +01:00
|
|
|
const char *psz_status = httpd_ReasonFromCode(cl->answer.i_status);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
i_size = strlen("HTTP/1.") + 10 + 10 + strlen(psz_status) + 5;
|
|
|
|
for (size_t i = 0; i < cl->answer.i_headers; i++)
|
|
|
|
i_size += strlen(cl->answer.p_headers[i].name) + 2 +
|
|
|
|
strlen(cl->answer.p_headers[i].value) + 2;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (cl->i_buffer_size < i_size) {
|
2004-03-03 14:23:47 +01:00
|
|
|
cl->i_buffer_size = i_size;
|
2014-02-20 18:56:33 +01:00
|
|
|
free(cl->p_buffer);
|
|
|
|
cl->p_buffer = xmalloc(i_size);
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
2005-07-10 12:03:30 +02:00
|
|
|
p = (char *)cl->p_buffer;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2018-07-11 15:31:36 +02:00
|
|
|
p += sprintf(p, "%s.%" PRIu8 " %d %s\r\n",
|
2007-09-02 10:51:01 +02:00
|
|
|
cl->answer.i_proto == HTTPD_PROTO_HTTP ? "HTTP/1" : "RTSP/1",
|
2004-03-03 14:23:47 +01:00
|
|
|
cl->answer.i_version,
|
2014-02-20 18:56:33 +01:00
|
|
|
cl->answer.i_status, psz_status);
|
|
|
|
for (size_t i = 0; i < cl->answer.i_headers; i++)
|
|
|
|
p += sprintf(p, "%s: %s\r\n", cl->answer.p_headers[i].name,
|
|
|
|
cl->answer.p_headers[i].value);
|
|
|
|
p += sprintf(p, "\r\n");
|
2004-03-03 14:23:47 +01:00
|
|
|
|
|
|
|
cl->i_buffer = 0;
|
|
|
|
cl->i_buffer_size = (uint8_t*)p - cl->p_buffer;
|
|
|
|
}
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
i_len = httpd_NetSend(cl, &cl->p_buffer[cl->i_buffer],
|
|
|
|
cl->i_buffer_size - cl->i_buffer);
|
|
|
|
if (i_len >= 0) {
|
2004-03-03 14:23:47 +01:00
|
|
|
cl->i_buffer += i_len;
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (cl->i_buffer >= cl->i_buffer_size) {
|
|
|
|
if (cl->answer.i_body == 0 && cl->answer.i_body_offset > 0) {
|
2004-03-03 14:23:47 +01:00
|
|
|
/* catch more body data */
|
2004-03-10 18:23:16 +01:00
|
|
|
int i_msg = cl->query.i_type;
|
|
|
|
int64_t i_offset = cl->answer.i_body_offset;
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_MsgClean(&cl->answer);
|
2004-03-10 18:23:16 +01:00
|
|
|
cl->answer.i_body_offset = i_offset;
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
cl->url->catch[i_msg].cb(cl->url->catch[i_msg].p_sys, cl,
|
|
|
|
&cl->answer, &cl->query);
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (cl->answer.i_body > 0) {
|
2004-03-03 14:23:47 +01:00
|
|
|
/* send the body data */
|
2014-02-20 18:56:33 +01:00
|
|
|
free(cl->p_buffer);
|
2004-03-03 14:23:47 +01:00
|
|
|
cl->p_buffer = cl->answer.p_body;
|
|
|
|
cl->i_buffer_size = cl->answer.i_body;
|
|
|
|
cl->i_buffer = 0;
|
|
|
|
|
|
|
|
cl->answer.i_body = 0;
|
|
|
|
cl->answer.p_body = NULL;
|
2014-02-20 18:56:33 +01:00
|
|
|
} else /* send finished */
|
2004-03-03 14:23:47 +01:00
|
|
|
cl->i_state = HTTPD_CLIENT_SEND_DONE;
|
|
|
|
}
|
2014-02-20 18:56:33 +01:00
|
|
|
} else {
|
|
|
|
#if defined(_WIN32)
|
|
|
|
if ((i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK) || (i_len == 0))
|
2004-03-03 14:23:47 +01:00
|
|
|
#else
|
2014-02-20 18:56:33 +01:00
|
|
|
if ((i_len < 0 && errno != EAGAIN) || (i_len == 0))
|
2004-03-03 14:23:47 +01:00
|
|
|
#endif
|
|
|
|
{
|
|
|
|
/* error */
|
|
|
|
cl->i_state = HTTPD_CLIENT_DEAD;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-16 18:03:32 +01:00
|
|
|
static void httpd_ClientTlsHandshake(httpd_host_t *host, httpd_client_t *cl)
|
2004-12-25 22:35:09 +01:00
|
|
|
{
|
2017-02-26 14:37:52 +01:00
|
|
|
switch (vlc_tls_SessionHandshake(host->p_tls, cl->sock))
|
2014-08-23 22:10:50 +02:00
|
|
|
{
|
2014-02-16 11:02:02 +01:00
|
|
|
case -1: cl->i_state = HTTPD_CLIENT_DEAD; break;
|
|
|
|
case 0: cl->i_state = HTTPD_CLIENT_RECEIVING; break;
|
|
|
|
case 1: cl->i_state = HTTPD_CLIENT_TLS_HS_IN; break;
|
|
|
|
case 2: cl->i_state = HTTPD_CLIENT_TLS_HS_OUT; break;
|
2004-12-25 22:35:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-16 16:23:21 +01:00
|
|
|
static bool httpdAuthOk(const char *user, const char *pass, const char *b64)
|
2014-02-16 12:01:20 +01:00
|
|
|
{
|
|
|
|
if (!*user && !*pass)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (!b64)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (strncasecmp(b64, "BASIC", 5))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
b64 += 5;
|
|
|
|
while (*b64 == ' ')
|
|
|
|
b64++;
|
|
|
|
|
|
|
|
char *given_user = vlc_b64_decode(b64);
|
|
|
|
if (!given_user)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
char *given_pass = NULL;
|
|
|
|
given_pass = strchr (given_user, ':');
|
|
|
|
if (!given_pass)
|
|
|
|
goto auth_failed;
|
|
|
|
|
|
|
|
*given_pass++ = '\0';
|
|
|
|
|
|
|
|
if (strcmp (given_user, user))
|
|
|
|
goto auth_failed;
|
|
|
|
|
|
|
|
if (strcmp (given_pass, pass))
|
|
|
|
goto auth_failed;
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
free(given_user);
|
2014-02-16 12:01:20 +01:00
|
|
|
return true;
|
|
|
|
|
|
|
|
auth_failed:
|
2014-02-20 18:56:33 +01:00
|
|
|
free(given_user);
|
2014-02-16 12:01:20 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-02-16 11:29:30 +01:00
|
|
|
static void httpdLoop(httpd_host_t *host)
|
2004-03-03 14:23:47 +01:00
|
|
|
{
|
2018-06-17 20:03:46 +02:00
|
|
|
struct pollfd ufd[host->nfd + host->client_count];
|
2014-02-16 11:29:30 +01:00
|
|
|
unsigned nfd;
|
2014-02-20 18:56:33 +01:00
|
|
|
for (nfd = 0; nfd < host->nfd; nfd++) {
|
2014-02-16 11:29:30 +01:00
|
|
|
ufd[nfd].fd = host->fds[nfd];
|
|
|
|
ufd[nfd].events = POLLIN;
|
|
|
|
ufd[nfd].revents = 0;
|
|
|
|
}
|
2007-02-21 18:58:12 +01:00
|
|
|
|
2018-06-17 19:41:53 +02:00
|
|
|
vlc_mutex_lock(&host->lock);
|
2014-02-16 11:29:30 +01:00
|
|
|
/* add all socket that should be read/write and close dead connection */
|
2018-06-17 20:09:44 +02:00
|
|
|
while (vlc_list_is_empty(&host->urls)) {
|
2014-02-20 18:56:33 +01:00
|
|
|
mutex_cleanup_push(&host->lock);
|
|
|
|
vlc_cond_wait(&host->wait, &host->lock);
|
2014-02-16 11:29:30 +01:00
|
|
|
vlc_cleanup_pop();
|
|
|
|
}
|
2009-01-14 21:07:31 +01:00
|
|
|
|
2018-06-20 08:37:11 +02:00
|
|
|
vlc_tick_t now = vlc_tick_now();
|
2014-02-16 11:29:30 +01:00
|
|
|
bool b_low_delay = false;
|
2018-06-17 20:03:46 +02:00
|
|
|
httpd_client_t *cl;
|
2007-02-21 18:58:12 +01:00
|
|
|
|
2014-02-16 11:29:30 +01:00
|
|
|
int canc = vlc_savecancel();
|
2018-06-17 20:03:46 +02:00
|
|
|
vlc_list_foreach(cl, &host->clients, node) {
|
2014-02-16 11:29:30 +01:00
|
|
|
int64_t i_offset;
|
2018-06-17 20:03:46 +02:00
|
|
|
|
2018-06-17 20:04:58 +02:00
|
|
|
if (cl->i_state == HTTPD_CLIENT_DEAD
|
|
|
|
|| (cl->i_activity_timeout > 0
|
|
|
|
&& cl->i_activity_date + cl->i_activity_timeout < now)) {
|
2018-06-17 20:03:46 +02:00
|
|
|
host->client_count--;
|
2016-01-12 21:09:48 +01:00
|
|
|
httpd_ClientDestroy(cl);
|
2014-02-16 11:29:30 +01:00
|
|
|
continue;
|
|
|
|
}
|
2007-02-21 18:58:12 +01:00
|
|
|
|
2014-02-16 11:29:30 +01:00
|
|
|
struct pollfd *pufd = ufd + nfd;
|
|
|
|
assert (pufd < ufd + (sizeof (ufd) / sizeof (ufd[0])));
|
2007-02-21 18:58:12 +01:00
|
|
|
|
2014-02-16 11:29:30 +01:00
|
|
|
pufd->events = pufd->revents = 0;
|
2007-02-21 18:58:12 +01:00
|
|
|
|
2014-02-16 11:29:30 +01:00
|
|
|
switch (cl->i_state) {
|
2014-02-16 11:02:02 +01:00
|
|
|
case HTTPD_CLIENT_RECEIVING:
|
|
|
|
case HTTPD_CLIENT_TLS_HS_IN:
|
2007-02-21 18:58:12 +01:00
|
|
|
pufd->events = POLLIN;
|
2014-02-16 11:02:02 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case HTTPD_CLIENT_SENDING:
|
|
|
|
case HTTPD_CLIENT_TLS_HS_OUT:
|
2007-02-21 18:58:12 +01:00
|
|
|
pufd->events = POLLOUT;
|
2014-02-16 11:02:02 +01:00
|
|
|
break;
|
|
|
|
|
2014-02-16 11:29:30 +01:00
|
|
|
case HTTPD_CLIENT_RECEIVE_DONE: {
|
2004-03-03 14:23:47 +01:00
|
|
|
httpd_message_t *answer = &cl->answer;
|
|
|
|
httpd_message_t *query = &cl->query;
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_MsgInit(answer);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
|
|
|
/* Handle what we received */
|
2014-02-16 11:02:02 +01:00
|
|
|
switch (query->i_type) {
|
2014-02-16 11:29:30 +01:00
|
|
|
case HTTPD_MSG_ANSWER:
|
|
|
|
cl->url = NULL;
|
|
|
|
cl->i_state = HTTPD_CLIENT_DEAD;
|
2014-02-16 11:02:02 +01:00
|
|
|
break;
|
|
|
|
|
2014-02-16 11:29:30 +01:00
|
|
|
case HTTPD_MSG_OPTIONS:
|
|
|
|
answer->i_type = HTTPD_MSG_ANSWER;
|
|
|
|
answer->i_proto = query->i_proto;
|
|
|
|
answer->i_status = 200;
|
|
|
|
answer->i_body = 0;
|
|
|
|
answer->p_body = NULL;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_MsgAdd(answer, "Server", "VLC/%s", VERSION);
|
|
|
|
httpd_MsgAdd(answer, "Content-Length", "0");
|
2014-02-16 11:02:02 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
switch(query->i_proto) {
|
2014-02-16 12:02:22 +01:00
|
|
|
case HTTPD_PROTO_HTTP:
|
|
|
|
answer->i_version = 1;
|
|
|
|
httpd_MsgAdd(answer, "Allow", "GET,HEAD,POST,OPTIONS");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HTTPD_PROTO_RTSP:
|
|
|
|
answer->i_version = 0;
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
const char *p = httpd_MsgGet(query, "Cseq");
|
|
|
|
if (p)
|
|
|
|
httpd_MsgAdd(answer, "Cseq", "%s", p);
|
|
|
|
p = httpd_MsgGet(query, "Timestamp");
|
|
|
|
if (p)
|
|
|
|
httpd_MsgAdd(answer, "Timestamp", "%s", p);
|
2014-02-16 12:02:22 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
p = httpd_MsgGet(query, "Require");
|
|
|
|
if (p) {
|
2014-02-16 12:02:22 +01:00
|
|
|
answer->i_status = 551;
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_MsgAdd(query, "Unsupported", "%s", p);
|
2014-02-16 12:02:22 +01:00
|
|
|
}
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_MsgAdd(answer, "Public", "DESCRIBE,SETUP,"
|
|
|
|
"TEARDOWN,PLAY,PAUSE,GET_PARAMETER");
|
2014-02-16 12:02:22 +01:00
|
|
|
break;
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
2006-02-10 15:43:05 +01:00
|
|
|
|
2017-10-08 17:17:37 +02:00
|
|
|
if (httpd_MsgGet(&cl->query, "Connection") != NULL)
|
|
|
|
httpd_MsgAdd(answer, "Connection", "close");
|
|
|
|
|
2014-02-16 11:29:30 +01:00
|
|
|
cl->i_buffer = -1; /* Force the creation of the answer in
|
|
|
|
* httpd_ClientSend */
|
|
|
|
cl->i_state = HTTPD_CLIENT_SENDING;
|
|
|
|
break;
|
2006-11-24 16:46:43 +01:00
|
|
|
|
2014-02-16 11:29:30 +01:00
|
|
|
case HTTPD_MSG_NONE:
|
2014-02-20 18:56:33 +01:00
|
|
|
if (query->i_proto == HTTPD_PROTO_NONE) {
|
2014-02-16 11:29:30 +01:00
|
|
|
cl->url = NULL;
|
|
|
|
cl->i_state = HTTPD_CLIENT_DEAD;
|
2014-02-16 12:01:20 +01:00
|
|
|
} else {
|
2014-02-16 11:29:30 +01:00
|
|
|
/* unimplemented */
|
|
|
|
answer->i_proto = query->i_proto ;
|
|
|
|
answer->i_type = HTTPD_MSG_ANSWER;
|
|
|
|
answer->i_version= 0;
|
|
|
|
answer->i_status = 501;
|
|
|
|
|
|
|
|
char *p;
|
|
|
|
answer->i_body = httpd_HtmlError (&p, 501, NULL);
|
|
|
|
answer->p_body = (uint8_t *)p;
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_MsgAdd(answer, "Content-Length", "%d", answer->i_body);
|
2017-10-08 17:18:05 +02:00
|
|
|
httpd_MsgAdd(answer, "Connection", "close");
|
2014-02-16 11:29:30 +01:00
|
|
|
|
|
|
|
cl->i_buffer = -1; /* Force the creation of the answer in httpd_ClientSend */
|
|
|
|
cl->i_state = HTTPD_CLIENT_SENDING;
|
|
|
|
}
|
|
|
|
break;
|
2005-08-26 20:15:21 +02:00
|
|
|
|
2014-02-16 11:29:30 +01:00
|
|
|
default: {
|
2018-06-17 20:09:44 +02:00
|
|
|
httpd_url_t *url;
|
2014-02-16 12:02:22 +01:00
|
|
|
int i_msg = query->i_type;
|
|
|
|
bool b_auth_failed = false;
|
|
|
|
|
|
|
|
/* Search the url and trigger callbacks */
|
2018-06-17 20:09:44 +02:00
|
|
|
vlc_list_foreach(url, &host->urls, node) {
|
2014-02-16 12:02:22 +01:00
|
|
|
if (strcmp(url->psz_url, query->psz_url))
|
|
|
|
continue;
|
|
|
|
if (!url->catch[i_msg].cb)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (answer) {
|
|
|
|
b_auth_failed = !httpdAuthOk(url->psz_user,
|
|
|
|
url->psz_password,
|
|
|
|
httpd_MsgGet(query, "Authorization")); /* BASIC id */
|
|
|
|
if (b_auth_failed)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (url->catch[i_msg].cb(url->catch[i_msg].p_sys, cl, answer, query))
|
|
|
|
continue;
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (answer->i_proto == HTTPD_PROTO_NONE)
|
2014-02-16 12:02:22 +01:00
|
|
|
cl->i_buffer = cl->i_buffer_size; /* Raw answer from a CGI */
|
|
|
|
else
|
|
|
|
cl->i_buffer = -1;
|
|
|
|
|
|
|
|
/* only one url can answer */
|
|
|
|
answer = NULL;
|
2014-02-20 18:56:33 +01:00
|
|
|
if (!cl->url)
|
2014-02-16 12:02:22 +01:00
|
|
|
cl->url = url;
|
|
|
|
}
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (answer) {
|
2014-02-16 12:02:22 +01:00
|
|
|
answer->i_proto = query->i_proto;
|
|
|
|
answer->i_type = HTTPD_MSG_ANSWER;
|
|
|
|
answer->i_version= 0;
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (b_auth_failed) {
|
|
|
|
httpd_MsgAdd(answer, "WWW-Authenticate",
|
|
|
|
"Basic realm=\"VLC stream\"");
|
2014-02-16 12:02:22 +01:00
|
|
|
answer->i_status = 401;
|
|
|
|
} else
|
|
|
|
answer->i_status = 404; /* no url registered */
|
|
|
|
|
|
|
|
char *p;
|
|
|
|
answer->i_body = httpd_HtmlError (&p, answer->i_status,
|
|
|
|
query->psz_url);
|
|
|
|
answer->p_body = (uint8_t *)p;
|
|
|
|
|
|
|
|
cl->i_buffer = -1; /* Force the creation of the answer in httpd_ClientSend */
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_MsgAdd(answer, "Content-Length", "%d", answer->i_body);
|
|
|
|
httpd_MsgAdd(answer, "Content-Type", "%s", "text/html");
|
2017-10-08 17:18:19 +02:00
|
|
|
if (httpd_MsgGet(&cl->query, "Connection") != NULL)
|
|
|
|
httpd_MsgAdd(answer, "Connection", "close");
|
2014-02-16 12:02:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
cl->i_state = HTTPD_CLIENT_SENDING;
|
|
|
|
}
|
2014-02-16 11:02:02 +01:00
|
|
|
}
|
2014-02-16 11:29:30 +01:00
|
|
|
break;
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
2014-02-16 11:02:02 +01:00
|
|
|
|
|
|
|
case HTTPD_CLIENT_SEND_DONE:
|
2014-02-20 18:56:33 +01:00
|
|
|
if (!cl->b_stream_mode || cl->answer.i_body_offset == 0) {
|
2017-10-08 17:22:26 +02:00
|
|
|
bool do_close = false;
|
2006-03-22 22:01:09 +01:00
|
|
|
|
2004-03-03 14:23:47 +01:00
|
|
|
cl->url = NULL;
|
2006-03-22 22:01:09 +01:00
|
|
|
|
2017-10-08 17:22:26 +02:00
|
|
|
if (cl->query.i_proto != HTTPD_PROTO_HTTP
|
|
|
|
|| cl->query.i_version > 0)
|
|
|
|
{
|
|
|
|
const char *psz_connection = httpd_MsgGet(&cl->answer,
|
|
|
|
"Connection");
|
|
|
|
if (psz_connection != NULL)
|
|
|
|
do_close = !strcasecmp(psz_connection, "close");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
do_close = true;
|
2006-03-22 22:01:09 +01:00
|
|
|
|
2017-10-08 17:22:26 +02:00
|
|
|
if (!do_close) {
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_MsgClean(&cl->query);
|
|
|
|
httpd_MsgInit(&cl->query);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
|
|
|
cl->i_buffer = 0;
|
|
|
|
cl->i_buffer_size = 1000;
|
2014-02-20 18:56:33 +01:00
|
|
|
free(cl->p_buffer);
|
2018-06-27 11:06:42 +02:00
|
|
|
// Allocate an extra byte for the null terminating byte
|
|
|
|
cl->p_buffer = xmalloc(cl->i_buffer_size + 1);
|
2004-03-03 14:23:47 +01:00
|
|
|
cl->i_state = HTTPD_CLIENT_RECEIVING;
|
2014-02-20 18:56:33 +01:00
|
|
|
} else
|
2004-03-03 14:23:47 +01:00
|
|
|
cl->i_state = HTTPD_CLIENT_DEAD;
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_MsgClean(&cl->answer);
|
|
|
|
} else {
|
2014-02-16 11:02:02 +01:00
|
|
|
i_offset = cl->answer.i_body_offset;
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_MsgClean(&cl->answer);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
|
|
|
cl->answer.i_body_offset = i_offset;
|
2014-02-20 18:56:33 +01:00
|
|
|
free(cl->p_buffer);
|
2004-03-10 18:23:16 +01:00
|
|
|
cl->p_buffer = NULL;
|
|
|
|
cl->i_buffer = 0;
|
|
|
|
cl->i_buffer_size = 0;
|
|
|
|
|
2004-03-03 14:23:47 +01:00
|
|
|
cl->i_state = HTTPD_CLIENT_WAITING;
|
|
|
|
}
|
2014-02-16 11:02:02 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case HTTPD_CLIENT_WAITING:
|
|
|
|
i_offset = cl->answer.i_body_offset;
|
|
|
|
int i_msg = cl->query.i_type;
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
httpd_MsgInit(&cl->answer);
|
2004-03-03 14:23:47 +01:00
|
|
|
cl->answer.i_body_offset = i_offset;
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
cl->url->catch[i_msg].cb(cl->url->catch[i_msg].p_sys, cl,
|
|
|
|
&cl->answer, &cl->query);
|
|
|
|
if (cl->answer.i_type != HTTPD_MSG_NONE) {
|
2007-02-21 18:58:12 +01:00
|
|
|
/* we have new data, so re-enter send mode */
|
2004-03-03 14:23:47 +01:00
|
|
|
cl->i_buffer = 0;
|
|
|
|
cl->p_buffer = cl->answer.p_body;
|
|
|
|
cl->i_buffer_size = cl->answer.i_body;
|
|
|
|
cl->answer.p_body = NULL;
|
|
|
|
cl->answer.i_body = 0;
|
|
|
|
cl->i_state = HTTPD_CLIENT_SENDING;
|
|
|
|
}
|
2007-10-01 20:04:10 +02:00
|
|
|
}
|
2007-10-01 20:11:03 +02:00
|
|
|
|
2018-11-17 20:48:10 +01:00
|
|
|
pufd->fd = vlc_tls_GetPollFD(cl->sock, &pufd->events);
|
|
|
|
|
2014-02-16 11:29:30 +01:00
|
|
|
if (pufd->events != 0)
|
|
|
|
nfd++;
|
|
|
|
else
|
|
|
|
b_low_delay = true;
|
|
|
|
}
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_mutex_unlock(&host->lock);
|
|
|
|
vlc_restorecancel(canc);
|
2014-02-16 11:29:30 +01:00
|
|
|
|
|
|
|
/* we will wait 20ms (not too big) if HTTPD_CLIENT_WAITING */
|
2017-07-07 20:00:24 +02:00
|
|
|
while (poll(ufd, nfd, b_low_delay ? 20 : -1) < 0)
|
|
|
|
{
|
|
|
|
if (errno != EINTR)
|
|
|
|
msg_Err(host, "polling error: %s", vlc_strerror_c(errno));
|
|
|
|
}
|
2012-05-09 17:16:58 +02:00
|
|
|
|
2014-02-16 11:29:30 +01:00
|
|
|
canc = vlc_savecancel();
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_mutex_lock(&host->lock);
|
2004-03-03 14:23:47 +01:00
|
|
|
|
2014-02-16 11:29:30 +01:00
|
|
|
/* Handle client sockets */
|
2018-06-20 08:37:11 +02:00
|
|
|
now = vlc_tick_now();
|
2014-02-16 11:29:30 +01:00
|
|
|
nfd = host->nfd;
|
2012-05-09 17:16:58 +02:00
|
|
|
|
2018-06-17 20:03:46 +02:00
|
|
|
vlc_list_foreach(cl, &host->clients, node) {
|
2014-02-16 11:29:30 +01:00
|
|
|
const struct pollfd *pufd = &ufd[nfd];
|
2007-02-25 15:33:13 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
assert(pufd < &ufd[sizeof(ufd) / sizeof(ufd[0])]);
|
2007-02-25 15:33:13 +01:00
|
|
|
|
2017-02-26 14:37:52 +01:00
|
|
|
if (vlc_tls_GetFD(cl->sock) != pufd->fd)
|
2014-02-16 11:29:30 +01:00
|
|
|
continue; // we were not waiting for this client
|
|
|
|
++nfd;
|
2014-02-20 18:56:33 +01:00
|
|
|
if (pufd->revents == 0)
|
2014-02-16 11:29:30 +01:00
|
|
|
continue; // no event received
|
2007-02-25 15:33:13 +01:00
|
|
|
|
2014-02-16 11:29:30 +01:00
|
|
|
cl->i_activity_date = now;
|
2007-02-25 15:33:13 +01:00
|
|
|
|
2014-02-16 11:29:30 +01:00
|
|
|
switch (cl->i_state) {
|
2014-02-20 18:56:33 +01:00
|
|
|
case HTTPD_CLIENT_RECEIVING: httpd_ClientRecv(cl); break;
|
|
|
|
case HTTPD_CLIENT_SENDING: httpd_ClientSend(cl); break;
|
2014-02-16 11:02:02 +01:00
|
|
|
case HTTPD_CLIENT_TLS_HS_IN:
|
2015-12-16 18:03:32 +01:00
|
|
|
case HTTPD_CLIENT_TLS_HS_OUT:
|
|
|
|
httpd_ClientTlsHandshake(host, cl);
|
|
|
|
break;
|
2007-02-25 15:33:13 +01:00
|
|
|
}
|
2014-02-16 11:29:30 +01:00
|
|
|
}
|
2007-02-21 19:03:40 +01:00
|
|
|
|
2014-02-16 11:29:30 +01:00
|
|
|
/* Handle server sockets (accept new connections) */
|
2014-02-20 18:56:33 +01:00
|
|
|
for (nfd = 0; nfd < host->nfd; nfd++) {
|
2014-02-16 11:29:30 +01:00
|
|
|
int fd = ufd[nfd].fd;
|
2007-02-25 15:33:13 +01:00
|
|
|
|
2014-02-16 11:29:30 +01:00
|
|
|
assert (fd == host->fds[nfd]);
|
2006-02-04 20:48:38 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (ufd[nfd].revents == 0)
|
2014-02-16 11:29:30 +01:00
|
|
|
continue;
|
2006-04-27 22:18:14 +02:00
|
|
|
|
2014-02-16 11:29:30 +01:00
|
|
|
/* */
|
|
|
|
fd = vlc_accept (fd, NULL, NULL, true);
|
|
|
|
if (fd == -1)
|
|
|
|
continue;
|
|
|
|
setsockopt (fd, SOL_SOCKET, SO_REUSEADDR,
|
|
|
|
&(int){ 1 }, sizeof(int));
|
2006-02-04 20:48:38 +01:00
|
|
|
|
2017-02-26 14:37:52 +01:00
|
|
|
vlc_tls_t *sk = vlc_tls_SocketOpen(fd);
|
|
|
|
if (unlikely(sk == NULL))
|
|
|
|
{
|
|
|
|
vlc_close(fd);
|
|
|
|
continue;
|
|
|
|
}
|
2011-07-23 10:45:40 +02:00
|
|
|
|
2014-08-23 22:15:02 +02:00
|
|
|
if (host->p_tls != NULL)
|
|
|
|
{
|
|
|
|
const char *alpn[] = { "http/1.1", NULL };
|
2017-02-26 14:37:52 +01:00
|
|
|
vlc_tls_t *tls;
|
2014-08-23 22:15:02 +02:00
|
|
|
|
2017-02-26 14:37:52 +01:00
|
|
|
tls = vlc_tls_ServerSessionCreate(host->p_tls, sk, alpn);
|
|
|
|
if (tls == NULL)
|
|
|
|
{
|
|
|
|
vlc_tls_SessionDelete(sk);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
sk = tls;
|
2014-08-23 22:15:02 +02:00
|
|
|
}
|
2007-02-21 18:58:12 +01:00
|
|
|
|
2017-02-26 14:37:52 +01:00
|
|
|
cl = httpd_ClientNew(sk, now);
|
|
|
|
|
|
|
|
if (host->p_tls != NULL)
|
|
|
|
cl->i_state = HTTPD_CLIENT_TLS_HS_OUT;
|
2012-09-29 22:00:21 +02:00
|
|
|
|
2018-06-17 20:03:46 +02:00
|
|
|
host->client_count++;
|
|
|
|
vlc_list_append(&cl->node, &host->clients);
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
2014-02-16 11:29:30 +01:00
|
|
|
|
2018-06-17 19:41:53 +02:00
|
|
|
vlc_mutex_unlock(&host->lock);
|
2014-02-16 11:29:30 +01:00
|
|
|
vlc_restorecancel(canc);
|
|
|
|
}
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
static void* httpd_HostThread(void *data)
|
2014-02-16 11:29:30 +01:00
|
|
|
{
|
|
|
|
httpd_host_t *host = data;
|
|
|
|
|
2018-06-17 19:41:53 +02:00
|
|
|
while (atomic_load_explicit(&host->ref, memory_order_relaxed) > 0)
|
2014-02-16 11:29:30 +01:00
|
|
|
httpdLoop(host);
|
2008-08-07 20:34:04 +02:00
|
|
|
return NULL;
|
2004-03-03 14:23:47 +01:00
|
|
|
}
|
2014-02-15 23:15:49 +01:00
|
|
|
|
2017-07-23 16:18:57 +02:00
|
|
|
int httpd_StreamSetHTTPHeaders(httpd_stream_t * p_stream,
|
|
|
|
const httpd_header *p_headers, size_t i_headers)
|
2014-02-15 23:15:49 +01:00
|
|
|
{
|
2014-02-20 18:56:33 +01:00
|
|
|
if (!p_stream)
|
2014-02-15 23:15:49 +01:00
|
|
|
return VLC_EGENERIC;
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_mutex_lock(&p_stream->lock);
|
|
|
|
if (p_stream->p_http_headers) {
|
|
|
|
for (size_t i = 0; i < p_stream->i_http_headers; i++) {
|
|
|
|
free(p_stream->p_http_headers[i].name);
|
|
|
|
free(p_stream->p_http_headers[i].value);
|
2014-02-15 23:15:49 +01:00
|
|
|
}
|
2014-02-20 18:56:33 +01:00
|
|
|
free(p_stream->p_http_headers);
|
2014-02-15 23:15:49 +01:00
|
|
|
p_stream->p_http_headers = NULL;
|
|
|
|
p_stream->i_http_headers = 0;
|
|
|
|
}
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (!p_headers || !i_headers) {
|
|
|
|
vlc_mutex_unlock(&p_stream->lock);
|
2014-02-15 23:15:49 +01:00
|
|
|
return VLC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2017-11-12 11:42:13 +01:00
|
|
|
p_stream->p_http_headers = vlc_alloc(i_headers, sizeof(httpd_header));
|
2014-02-20 18:56:33 +01:00
|
|
|
if (!p_stream->p_http_headers) {
|
|
|
|
vlc_mutex_unlock(&p_stream->lock);
|
2014-02-15 23:15:49 +01:00
|
|
|
return VLC_ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t j = 0;
|
2014-02-20 18:56:33 +01:00
|
|
|
for (size_t i = 0; i < i_headers; i++) {
|
|
|
|
if (unlikely(!p_headers[i].name || !p_headers[i].value))
|
2014-02-15 23:15:49 +01:00
|
|
|
continue;
|
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
p_stream->p_http_headers[j].name = strdup(p_headers[i].name);
|
|
|
|
p_stream->p_http_headers[j].value = strdup(p_headers[i].value);
|
2014-02-15 23:15:49 +01:00
|
|
|
|
2014-02-20 18:56:33 +01:00
|
|
|
if (unlikely(!p_stream->p_http_headers[j].name ||
|
|
|
|
!p_stream->p_http_headers[j].value)) {
|
|
|
|
free(p_stream->p_http_headers[j].name);
|
|
|
|
free(p_stream->p_http_headers[j].value);
|
2014-02-15 23:15:49 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
p_stream->i_http_headers = j;
|
2014-02-20 18:56:33 +01:00
|
|
|
vlc_mutex_unlock(&p_stream->lock);
|
2014-02-15 23:15:49 +01:00
|
|
|
return VLC_SUCCESS;
|
|
|
|
}
|