1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2001-2003 VideoLAN
5 * $Id: httpd.c,v 1.25 2003/08/11 20:18:02 fenrir Exp $
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 /*****************************************************************************
28 * - make that two distinct host:port use different daemon
29 *****************************************************************************/
40 #ifdef HAVE_SYS_TIME_H
41 # include <sys/time.h>
48 #if defined( UNDER_CE )
50 #elif defined( WIN32 )
51 # include <winsock2.h>
52 # include <ws2tcpip.h>
54 # define IN_MULTICAST(a) IN_CLASSD(a)
57 # include <netdb.h> /* hostent ... */
58 # include <sys/socket.h>
59 # include <netinet/in.h>
60 # ifdef HAVE_ARPA_INET_H
61 # include <arpa/inet.h> /* inet_ntoa(), inet_aton() */
68 # define INADDR_ANY 0x00000000
71 # define INADDR_NONE 0xFFFFFFFF
74 #define LISTEN_BACKLOG 100
75 #define HTTPD_MAX_CONNECTION 512
76 #define HTTPD_CONNECTION_MAX_UNUSED 10000000
79 #define FREE( p ) if( p ) { free( p); (p) = NULL; }
81 #if defined( WIN32 ) || defined( UNDER_CE )
82 #define SOCKET_CLOSE(a) closesocket(a)
84 #define SOCKET_CLOSE(a) close(a)
87 /*****************************************************************************
89 *****************************************************************************/
90 static int Open ( vlc_object_t * );
91 static void Close ( vlc_object_t * );
93 /*****************************************************************************
95 *****************************************************************************/
97 set_description( _("HTTP 1.0 daemon") );
98 set_capability( "httpd", 42 );
99 set_callbacks( Open, Close );
100 var_Create( p_module->p_libvlc, "httpd", VLC_VAR_MUTEX );
103 /*****************************************************************************
105 *****************************************************************************/
106 static httpd_host_t *RegisterHost ( httpd_t *, char *, int );
107 static void UnregisterHost ( httpd_t *, httpd_host_t * );
109 static httpd_file_t *RegisterFile ( httpd_t *,
110 char *psz_file, char *psz_mime,
111 char *psz_user, char *psz_password,
112 httpd_file_callback pf_get,
113 httpd_file_callback pf_post,
114 httpd_file_callback_args_t *p_args );
115 static void UnregisterFile ( httpd_t *, httpd_file_t * );
117 //#define httpd_stream_t httpd_file_t
118 static httpd_stream_t *RegisterStream ( httpd_t *,
119 char *psz_file, char *psz_mime,
120 char *psz_user, char *psz_password );
121 static int SendStream ( httpd_t *, httpd_stream_t *, uint8_t *, int );
122 static int HeaderStream ( httpd_t *, httpd_stream_t *, uint8_t *, int );
123 static void UnregisterStream( httpd_t *, httpd_stream_t* );
124 static int Control ( httpd_t *, int , void*, void* );
125 /*****************************************************************************
126 * Internal definitions
127 *****************************************************************************/
135 struct sockaddr_in sock;
140 enum httpd_authenticate_e
142 HTTPD_AUTHENTICATE_NONE = 0,
143 HTTPD_AUTHENTICATE_BASIC = 1
146 //typedef httpd_file_t httpd_stream_t;
156 int i_authenticate_method;
157 char *psz_user; /* NULL if no auth */
158 char *psz_password; /* NULL if no auth */
160 vlc_bool_t b_stream; /* if false: httpd will retreive data by a callback
161 true: it's up to the program to give data to httpd */
162 void *p_sys; /* provided for user */
163 httpd_file_callback pf_get; /* it should allocate and fill *pp_data and *pi_data */
164 httpd_file_callback pf_post; /* it should allocate and fill *pp_data and *pi_data */
168 /* circular buffer for stream only */
169 int i_buffer_size; /* buffer size, can't be reallocated smaller */
170 uint8_t *p_buffer; /* buffer */
171 int64_t i_buffer_pos; /* absolute position from begining */
172 int i_buffer_last_pos; /* a new connection will start with that */
174 /* data to be send at connection time (if any) */
180 enum httpd_connection_state_e
182 HTTPD_CONNECTION_RECEIVING_REQUEST = 1,
183 HTTPD_CONNECTION_SENDING_HEADER = 2,
184 HTTPD_CONNECTION_SENDING_FILE = 3,
185 HTTPD_CONNECTION_SENDING_STREAM = 4,
186 HTTPD_CONNECTION_TO_BE_CLOSED = 5
189 enum httpd_connection_method_e
191 HTTPD_CONNECTION_METHOD_GET = 1,
192 HTTPD_CONNECTION_METHOD_POST = 2,
193 HTTPD_CONNECTION_METHOD_HEAD =3
196 typedef struct httpd_connection_s
198 struct httpd_connection_s *p_next;
199 struct httpd_connection_s *p_prev;
201 struct sockaddr_in sock;
203 mtime_t i_last_activity_date;
206 int i_method; /* get/post */
208 char *psz_file; // file to be send
209 int i_http_error; // error to be send with the file
210 char *psz_user; // if Authorization in the request header
213 uint8_t *p_request; // whith get: ?<*>, with post: main data
216 httpd_file_t *p_file;
218 /* used while sending header and file */
221 int i_buffer; /* private */
223 /* used for stream */
224 int64_t i_stream_pos; /* absolute pos in stream */
225 } httpd_connection_t;
227 /* Linked List of banned IP */
228 typedef struct httpd_banned_ip_s
230 struct httpd_banned_ip_s *p_next;
231 struct httpd_banned_ip_s *p_prev;
243 vlc_mutex_t host_lock;
244 volatile int i_host_count;
247 vlc_mutex_t file_lock;
251 vlc_mutex_t connection_lock;
252 int i_connection_count;
253 httpd_connection_t *p_first_connection;
255 vlc_mutex_t ban_lock;
256 int i_banned_ip_count;
257 httpd_banned_ip_t *p_first_banned_ip;
260 static void httpd_Thread( httpd_sys_t *p_httpt );
261 static void httpd_ConnnectionNew( httpd_sys_t *, int , struct sockaddr_in * );
262 static void httpd_ConnnectionClose( httpd_sys_t *, httpd_connection_t * );
263 static int httpd_UnbanIP( httpd_sys_t *, httpd_banned_ip_t *);
265 static int httpd_BanIP( httpd_sys_t *, char *);
267 static httpd_banned_ip_t *httpd_GetbannedIP( httpd_sys_t *, char * );
269 /*****************************************************************************
271 *****************************************************************************/
273 static int Open( vlc_object_t *p_this )
275 httpd_t *p_httpd = (httpd_t*)p_this;
276 httpd_sys_t *p_httpt;
278 /* Launch httpt thread */
279 if( !( p_httpt = vlc_object_create( p_this, sizeof( httpd_sys_t ) ) ) )
281 msg_Err( p_this, "out of memory" );
282 return( VLC_EGENERIC );
288 /* init httpt_t structure */
289 vlc_mutex_init( p_httpd, &p_httpt->host_lock );
290 p_httpt->i_host_count = 0;
291 p_httpt->host = NULL;
293 vlc_mutex_init( p_httpd, &p_httpt->file_lock );
294 p_httpt->i_file_count = 0;
295 p_httpt->file = NULL;
297 vlc_mutex_init( p_httpd, &p_httpt->connection_lock );
298 p_httpt->i_connection_count = 0;
299 p_httpt->p_first_connection = NULL;
301 vlc_mutex_init( p_httpd, &p_httpt->ban_lock );
302 p_httpt->i_banned_ip_count = 0;
303 p_httpt->p_first_banned_ip = NULL;
305 /* start the thread */
306 if( vlc_thread_create( p_httpt, "httpd thread",
307 httpd_Thread, VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
309 msg_Err( p_this, "cannot spawn http thread" );
311 vlc_mutex_destroy( &p_httpt->host_lock );
312 vlc_mutex_destroy( &p_httpt->file_lock );
313 vlc_mutex_destroy( &p_httpt->connection_lock );
314 vlc_mutex_destroy( &p_httpt->ban_lock );
316 vlc_object_destroy( p_httpt );
317 return( VLC_EGENERIC );
320 msg_Info( p_httpd, "http thread launched" );
322 p_httpd->p_sys = p_httpt;
323 p_httpd->pf_register_host = RegisterHost;
324 p_httpd->pf_unregister_host = UnregisterHost;
325 p_httpd->pf_register_file = RegisterFile;
326 p_httpd->pf_unregister_file = UnregisterFile;
327 p_httpd->pf_register_stream = RegisterStream;
328 p_httpd->pf_header_stream = HeaderStream;
329 p_httpd->pf_send_stream = SendStream;
330 p_httpd->pf_unregister_stream=UnregisterStream;
331 p_httpd->pf_control = Control;
333 return( VLC_SUCCESS );
336 /*****************************************************************************
337 * Close: close the target
338 *****************************************************************************/
339 static void Close( vlc_object_t * p_this )
341 httpd_t *p_httpd = (httpd_t*)p_this;
342 httpd_sys_t *p_httpt = p_httpd->p_sys;
344 httpd_connection_t *p_con;
345 httpd_banned_ip_t *p_banned_ip;
350 vlc_thread_join( p_httpt );
352 /* first close all host */
353 vlc_mutex_destroy( &p_httpt->host_lock );
354 if( p_httpt->i_host_count )
356 msg_Err( p_httpd, "still have %d hosts registered !", p_httpt->i_host_count );
358 for( i = 0; i < p_httpt->i_host_count; i++ )
360 #define p_host p_httpt->host[i]
361 FREE( p_host->psz_host_addr );
362 SOCKET_CLOSE( p_host->fd );
367 FREE( p_httpt->host );
370 vlc_mutex_destroy( &p_httpt->file_lock );
371 if( p_httpt->i_file_count )
373 msg_Err( p_httpd, "still have %d files registered !", p_httpt->i_file_count );
375 for( i = 0; i < p_httpt->i_file_count; i++ )
377 #define p_file p_httpt->file[i]
378 FREE( p_file->psz_file );
379 FREE( p_file->psz_mime );
380 if( p_file->i_authenticate_method != HTTPD_AUTHENTICATE_NONE )
382 FREE( p_file->psz_user );
383 FREE( p_file->psz_password );
385 FREE( p_file->p_buffer );
390 FREE( p_httpt->file );
392 /* andd close all connection */
393 vlc_mutex_destroy( &p_httpt->connection_lock );
394 if( p_httpt->i_connection_count )
396 msg_Warn( p_httpd, "%d connections still in use", p_httpt->i_connection_count );
398 while( ( p_con = p_httpt->p_first_connection ) )
400 httpd_ConnnectionClose( p_httpt, p_con );
403 /* Free all banned IP */
404 vlc_mutex_destroy( &p_httpt->ban_lock );
405 while( ( p_banned_ip = p_httpt->p_first_banned_ip))
407 httpd_UnbanIP(p_httpt,p_banned_ip);
410 msg_Info( p_httpd, "httpd instance closed" );
411 vlc_object_destroy( p_httpt );
415 /****************************************************************************
416 ****************************************************************************
419 ****************************************************************************
420 ****************************************************************************/
421 static int BuildAddr( struct sockaddr_in * p_socket,
422 const char * psz_address, int i_port )
425 memset( p_socket, 0, sizeof( struct sockaddr_in ) );
426 p_socket->sin_family = AF_INET; /* family */
427 p_socket->sin_port = htons( (uint16_t)i_port );
430 p_socket->sin_addr.s_addr = INADDR_ANY;
434 struct hostent * p_hostent;
436 /* Try to convert address directly from in_addr - this will work if
437 * psz_address is dotted decimal. */
438 #ifdef HAVE_ARPA_INET_H
439 if( !inet_aton( psz_address, &p_socket->sin_addr ) )
441 p_socket->sin_addr.s_addr = inet_addr( psz_address );
442 if( p_socket->sin_addr.s_addr == INADDR_NONE )
445 /* We have a fqdn, try to find its address */
446 if ( (p_hostent = gethostbyname( psz_address )) == NULL )
451 /* Copy the first address of the host in the socket address */
452 memcpy( &p_socket->sin_addr, p_hostent->h_addr_list[0],
453 p_hostent->h_length );
461 * listen on a host for a httpd instance
464 static httpd_host_t *_RegisterHost( httpd_sys_t *p_httpt, char *psz_host_addr, int i_port )
466 httpd_host_t *p_host;
467 struct sockaddr_in sock;
471 #if !defined( WIN32 ) && !defined( UNDER_CE )
475 if( BuildAddr( &sock, psz_host_addr, i_port ) )
477 msg_Err( p_httpt, "cannot build address for %s:%d", psz_host_addr, i_port );
481 /* is it already declared ? */
482 vlc_mutex_lock( &p_httpt->host_lock );
483 for( i = 0; i < p_httpt->i_host_count; i++ )
485 if( p_httpt->host[i]->sock.sin_port == sock.sin_port &&
486 ( p_httpt->host[i]->sock.sin_addr.s_addr == INADDR_ANY ||
487 p_httpt->host[i]->sock.sin_addr.s_addr == sock.sin_addr.s_addr ) )
493 if( i < p_httpt->i_host_count )
495 /* yes, increment ref count and succed */
496 p_httpt->host[i]->i_ref++;
497 vlc_mutex_unlock( &p_httpt->host_lock );
498 return( p_httpt->host[i] );
501 /* need to add a new listening socket */
504 fd = socket( AF_INET, SOCK_STREAM, 0 );
507 msg_Err( p_httpt, "cannot open socket" );
512 if( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR,
513 (void *) &i_opt, sizeof( i_opt ) ) < 0 )
515 msg_Warn( p_httpt, "cannot configure socket (SO_REUSEADDR)" );
518 if( bind( fd, (struct sockaddr *)&sock, sizeof( struct sockaddr_in ) ) < 0 )
520 msg_Err( p_httpt, "cannot bind socket" );
523 /* set to non-blocking */
524 #if defined( WIN32 ) || defined( UNDER_CE )
526 unsigned long i_dummy = 1;
527 if( ioctlsocket( fd, FIONBIO, &i_dummy ) != 0 )
529 msg_Err( p_httpt, "cannot set socket to non-blocking mode" );
534 if( ( i_flags = fcntl( fd, F_GETFL, 0 ) ) < 0 )
536 msg_Err( p_httpt, "cannot F_GETFL socket" );
539 if( fcntl( fd, F_SETFL, i_flags | O_NONBLOCK ) < 0 )
541 msg_Err( p_httpt, "cannot F_SETFL O_NONBLOCK" );
546 if( listen( fd, LISTEN_BACKLOG ) < 0 )
548 msg_Err( p_httpt, "cannot listen socket" );
554 p_httpt->host = realloc( p_httpt->host, sizeof( httpd_host_t *) * ( p_httpt->i_host_count + 1 ) );
558 p_httpt->host = malloc( sizeof( httpd_host_t *) );
560 p_host = malloc( sizeof( httpd_host_t ) );
562 p_host->psz_host_addr = strdup( psz_host_addr );
563 p_host->i_port = i_port;
567 p_httpt->host[p_httpt->i_host_count++] = p_host;
568 vlc_mutex_unlock( &p_httpt->host_lock );
573 vlc_mutex_unlock( &p_httpt->host_lock );
580 static httpd_host_t *RegisterHost( httpd_t *p_httpd, char *psz_host_addr, int i_port )
582 return( _RegisterHost( p_httpd->p_sys, psz_host_addr, i_port ) );
586 * remove a listening host for an httpd instance
588 static void _UnregisterHost( httpd_sys_t *p_httpt, httpd_host_t *p_host )
592 vlc_mutex_lock( &p_httpt->host_lock );
593 for( i = 0; i < p_httpt->i_host_count; i++ )
595 if( p_httpt->host[i] == p_host )
600 if( i >= p_httpt->i_host_count )
602 vlc_mutex_unlock( &p_httpt->host_lock );
603 msg_Err( p_httpt, "cannot unregister host" );
609 if( p_host->i_ref > 0 )
612 vlc_mutex_unlock( &p_httpt->host_lock );
617 FREE( p_host->psz_host_addr );
618 SOCKET_CLOSE( p_host->fd );
622 if( p_httpt->i_host_count <= 1 )
624 FREE( p_httpt->host );
625 p_httpt->i_host_count = 0;
631 i_move = p_httpt->i_host_count - i - 1;
635 memmove( &p_httpt->host[i],
637 i_move * sizeof( httpd_host_t * ) );
640 p_httpt->i_host_count--;
641 p_httpt->host = realloc( p_httpt->host,
642 p_httpt->i_host_count * sizeof( httpd_host_t * ) );
645 vlc_mutex_unlock( &p_httpt->host_lock );
647 static void UnregisterHost( httpd_t *p_httpd, httpd_host_t *p_host )
649 _UnregisterHost( p_httpd->p_sys, p_host );
653 static void __RegisterFile( httpd_sys_t *p_httpt, httpd_file_t *p_file )
656 if( p_httpt->i_file_count )
658 p_httpt->file = realloc( p_httpt->file, sizeof( httpd_file_t *) * ( p_httpt->i_file_count + 1 ) );
662 p_httpt->file = malloc( sizeof( httpd_file_t *) );
665 p_httpt->file[p_httpt->i_file_count++] = p_file;
668 static httpd_file_t *_RegisterFile( httpd_sys_t *p_httpt,
669 char *psz_file, char *psz_mime,
670 char *psz_user, char *psz_password,
671 httpd_file_callback pf_get,
672 httpd_file_callback pf_post,
673 httpd_file_callback_args_t *p_args )
675 httpd_file_t *p_file;
678 vlc_mutex_lock( &p_httpt->file_lock );
679 for( i = 0; i < p_httpt->i_file_count; i++ )
681 if( !strcmp( psz_file, p_httpt->file[i]->psz_file ) )
686 if( i < p_httpt->i_file_count )
688 vlc_mutex_unlock( &p_httpt->file_lock );
689 msg_Err( p_httpt, "%s already registered", psz_file );
693 p_file = malloc( sizeof( httpd_file_t ) );
695 p_file->psz_file = strdup( psz_file );
696 p_file->psz_mime = strdup( psz_mime );
697 if( psz_user && *psz_user )
699 p_file->i_authenticate_method = HTTPD_AUTHENTICATE_BASIC;
700 p_file->psz_user = strdup( psz_user );
701 p_file->psz_password = strdup( psz_password );
705 p_file->i_authenticate_method = HTTPD_AUTHENTICATE_NONE;
706 p_file->psz_user = NULL;
707 p_file->psz_password = NULL;
710 p_file->b_stream = VLC_FALSE;
711 p_file->p_sys = p_args;
712 p_file->pf_get = pf_get;
713 p_file->pf_post = pf_post;
715 p_file->i_buffer_size = 0;
716 p_file->i_buffer_last_pos = 0;
717 p_file->i_buffer_pos = 0;
718 p_file->p_buffer = NULL;
720 p_file->i_header_size = 0;
721 p_file->p_header = NULL;
723 __RegisterFile( p_httpt, p_file );
725 vlc_mutex_unlock( &p_httpt->file_lock );
729 static httpd_file_t *RegisterFile( httpd_t *p_httpd,
730 char *psz_file, char *psz_mime,
731 char *psz_user, char *psz_password,
732 httpd_file_callback pf_get,
733 httpd_file_callback pf_post,
734 httpd_file_callback_args_t *p_args )
736 return( _RegisterFile( p_httpd->p_sys,
737 psz_file, psz_mime, psz_user, psz_password,
738 pf_get, pf_post, p_args ) );
741 static httpd_stream_t *_RegisterStream( httpd_sys_t *p_httpt,
742 char *psz_file, char *psz_mime,
743 char *psz_user, char *psz_password )
745 httpd_stream_t *p_stream;
748 vlc_mutex_lock( &p_httpt->file_lock );
749 for( i = 0; i < p_httpt->i_file_count; i++ )
751 if( !strcmp( psz_file, p_httpt->file[i]->psz_file ) )
756 if( i < p_httpt->i_file_count )
758 vlc_mutex_unlock( &p_httpt->file_lock );
759 msg_Err( p_httpt, "%s already registered", psz_file );
763 p_stream = malloc( sizeof( httpd_stream_t ) );
765 p_stream->psz_file = strdup( psz_file );
766 p_stream->psz_mime = strdup( psz_mime );
767 if( psz_user && *psz_user )
769 p_stream->i_authenticate_method = HTTPD_AUTHENTICATE_BASIC;
770 p_stream->psz_user = strdup( psz_user );
771 p_stream->psz_password = strdup( psz_password );
775 p_stream->i_authenticate_method = HTTPD_AUTHENTICATE_NONE;
776 p_stream->psz_user = NULL;
777 p_stream->psz_password = NULL;
780 p_stream->b_stream = VLC_TRUE;
781 p_stream->p_sys = NULL;
782 p_stream->pf_get = NULL;
783 p_stream->pf_post = NULL;
785 p_stream->i_buffer_size = 5*1024*1024;
786 p_stream->i_buffer_pos = 0;
787 p_stream->i_buffer_last_pos = 0;
788 p_stream->p_buffer = malloc( p_stream->i_buffer_size );
790 p_stream->i_header_size = 0;
791 p_stream->p_header = NULL;
793 __RegisterFile( p_httpt, p_stream );
795 vlc_mutex_unlock( &p_httpt->file_lock );
799 static httpd_stream_t *RegisterStream( httpd_t *p_httpd,
800 char *psz_file, char *psz_mime,
801 char *psz_user, char *psz_password )
803 return( _RegisterStream( p_httpd->p_sys,
804 psz_file, psz_mime, psz_user, psz_password ) );
807 static void _UnregisterFile( httpd_sys_t *p_httpt, httpd_file_t *p_file )
811 vlc_mutex_lock( &p_httpt->file_lock );
812 for( i = 0; i < p_httpt->i_file_count; i++ )
814 if( !strcmp( p_file->psz_file, p_httpt->file[i]->psz_file ) )
819 if( i >= p_httpt->i_file_count )
821 vlc_mutex_unlock( &p_httpt->file_lock );
822 msg_Err( p_httpt, "cannot unregister file" );
826 if( p_file->i_ref > 0 )
828 httpd_connection_t *p_con;
829 /* force closing all connection for this file */
830 msg_Err( p_httpt, "closing all client connection" );
832 vlc_mutex_lock( &p_httpt->connection_lock );
833 for( p_con = p_httpt->p_first_connection; p_con != NULL; )
835 httpd_connection_t *p_next;
837 p_next = p_con->p_next;
838 if( p_con->p_file == p_file )
840 httpd_ConnnectionClose( p_httpt, p_con );
844 vlc_mutex_unlock( &p_httpt->connection_lock );
847 FREE( p_file->psz_file );
848 FREE( p_file->psz_mime );
849 if( p_file->i_authenticate_method != HTTPD_AUTHENTICATE_NONE )
851 FREE( p_file->psz_user );
852 FREE( p_file->psz_password );
854 FREE( p_file->p_buffer );
855 FREE( p_file->p_header );
860 if( p_httpt->i_file_count == 1 )
862 FREE( p_httpt->file );
863 p_httpt->i_file_count = 0;
869 i_move = p_httpt->i_file_count - i - 1;
872 memmove( &p_httpt->file[i], &p_httpt->file[i + 1], sizeof( httpd_file_t *) * i_move );
874 p_httpt->i_file_count--;
875 p_httpt->file = realloc( p_httpt->file, sizeof( httpd_file_t *) * p_httpt->i_file_count );
878 vlc_mutex_unlock( &p_httpt->file_lock );
880 static void UnregisterFile( httpd_t *p_httpd, httpd_file_t *p_file )
882 _UnregisterFile( p_httpd->p_sys, p_file );
885 static void UnregisterStream( httpd_t *p_httpd, httpd_stream_t *p_stream )
887 _UnregisterFile( p_httpd->p_sys, p_stream );
892 static int _SendStream( httpd_sys_t *p_httpt, httpd_stream_t *p_stream, uint8_t *p_data, int i_data )
897 if( i_data <= 0 || p_data == NULL )
899 return( VLC_SUCCESS );
901 //fprintf( stderr, "## i_data=%d pos=%lld\n", i_data, p_stream->i_buffer_pos );
903 vlc_mutex_lock( &p_httpt->file_lock );
905 /* save this pointer (to be used by new connection) */
906 p_stream->i_buffer_last_pos = p_stream->i_buffer_pos;
908 i_pos = p_stream->i_buffer_pos % p_stream->i_buffer_size;
914 i_copy = __MIN( i_count, p_stream->i_buffer_size - i_pos );
916 memcpy( &p_stream->p_buffer[i_pos],
920 i_pos = ( i_pos + i_copy ) % p_stream->i_buffer_size;
925 p_stream->i_buffer_pos += i_data;
926 vlc_mutex_unlock( &p_httpt->file_lock );
928 return( VLC_SUCCESS );
930 static int SendStream( httpd_t *p_httpd, httpd_stream_t *p_stream, uint8_t *p_data, int i_data )
932 return( _SendStream( p_httpd->p_sys, p_stream, p_data, i_data ) );
935 static int HeaderStream( httpd_t *p_httpd, httpd_stream_t *p_stream, uint8_t *p_data, int i_data )
937 httpd_sys_t *p_httpt = p_httpd->p_sys;
939 vlc_mutex_lock( &p_httpt->file_lock );
941 FREE( p_stream->p_header );
942 if( p_data == NULL || i_data <= 0 )
944 p_stream->i_header_size = 0;
948 p_stream->i_header_size = i_data;
949 p_stream->p_header = malloc( i_data );
950 memcpy( p_stream->p_header,
954 vlc_mutex_unlock( &p_httpt->file_lock );
956 return( VLC_SUCCESS );
959 static void httpd_info_add_ss( httpd_info_t *p_info, char *name, char *value )
961 if( p_info->i_count == 0 )
963 p_info->info = malloc( sizeof( httpd_val_t ) );
968 realloc( p_info->info,
969 sizeof( httpd_val_t ) * ( p_info->i_count + 1 ) );
971 p_info->info[p_info->i_count].psz_name = strdup( name );
972 p_info->info[p_info->i_count].psz_value = strdup( value ? value : "(null)");
976 static void httpd_info_add_si( httpd_info_t *p_info, char *name, int i_value )
980 sprintf( v, "%d", i_value );
981 httpd_info_add_ss( p_info, name, v );
984 static void httpd_info_add_sp( httpd_info_t *p_info, char *name, void *value )
988 sprintf( v, "%p", value );
989 httpd_info_add_ss( p_info, name, v );
993 static int Control( httpd_t *p_httpd,
994 int i_query, void *arg1, void *arg2 )
996 httpd_sys_t *p_httpt = p_httpd->p_sys;
997 httpd_info_t *p_info;
998 httpd_connection_t *p_con;
1004 case HTTPD_GET_HOSTS:
1006 p_info->i_count = 0;
1007 vlc_mutex_lock( &p_httpt->host_lock );
1008 for( i = 0; i < p_httpt->i_host_count; i++ )
1010 httpd_info_add_sp( p_info,
1011 "id", p_httpt->host[i] );
1012 httpd_info_add_ss( p_info,
1013 "host", p_httpt->host[i]->psz_host_addr );
1014 httpd_info_add_ss( p_info,
1016 inet_ntoa(p_httpt->host[i]->sock.sin_addr));
1017 httpd_info_add_si( p_info,
1018 "port", p_httpt->host[i]->i_port );
1020 vlc_mutex_unlock( &p_httpt->host_lock );
1022 case HTTPD_GET_URLS:
1024 p_info->i_count = 0;
1025 /* we can't take file_lock */
1026 for( i = 0; i < p_httpt->i_file_count; i++ )
1028 httpd_info_add_sp( p_info,
1029 "id", p_httpt->file[i] );
1030 httpd_info_add_si( p_info,
1031 "stream", p_httpt->file[i]->b_stream ? 1 : 0 );
1032 httpd_info_add_ss( p_info,
1033 "url", p_httpt->file[i]->psz_file );
1034 httpd_info_add_ss( p_info,
1035 "mime", p_httpt->file[i]->psz_mime );
1036 httpd_info_add_si( p_info,
1037 "protected", p_httpt->file[i]->psz_user ? 1 : 0 );
1038 httpd_info_add_si( p_info,
1039 "used", p_httpt->file[i]->i_ref );
1042 case HTTPD_GET_CONNECTIONS:
1044 p_info->i_count = 0;
1045 /* we can't take lock */
1046 for( p_con = p_httpt->p_first_connection;p_con != NULL; p_con = p_con->p_next )
1048 if( p_con->i_state != HTTPD_CONNECTION_TO_BE_CLOSED )
1050 httpd_info_add_sp( p_info,
1052 httpd_info_add_ss( p_info,
1053 "ip", inet_ntoa( p_con->sock.sin_addr ) );
1054 httpd_info_add_ss( p_info,
1055 "url", p_con->psz_file );
1056 httpd_info_add_si( p_info,
1057 "status", p_con->i_http_error );
1063 p_info->i_count = 0;
1064 return VLC_EGENERIC;
1066 case HTTPD_SET_CLOSE:
1067 sscanf( arg1, "%p", &id );
1068 fprintf( stderr, "Control: HTTPD_SET_CLOSE: id=%p", id );
1070 for( p_con = p_httpt->p_first_connection;p_con != NULL; p_con = p_con->p_next )
1072 if( (void*)p_con == id )
1074 /* XXX don't free p_con as it could be the one that it is sending ... */
1075 p_con->i_state = HTTPD_CONNECTION_TO_BE_CLOSED;
1079 return VLC_EGENERIC;
1082 sscanf( arg1, "%p", &id );
1083 fprintf( stderr, "Control: %p", id );
1085 return VLC_EGENERIC;
1089 /****************************************************************************/
1090 /****************************************************************************/
1091 /****************************************************************************/
1092 /****************************************************************************/
1093 /****************************************************************************/
1095 static int httpd_page_401_get( httpd_file_callback_args_t *p_args,
1096 uint8_t *p_request, int i_request,
1097 uint8_t **pp_data, int *pi_data )
1101 p = *pp_data = malloc( 1024 );
1103 p += sprintf( p, "<html>\n" );
1104 p += sprintf( p, "<head>\n" );
1105 p += sprintf( p, "<title>Error 401</title>\n" );
1106 p += sprintf( p, "</head>\n" );
1107 p += sprintf( p, "<body>\n" );
1108 p += sprintf( p, "<h1><center> 401 authentification needed</center></h1>\n" );
1109 p += sprintf( p, "<hr />\n" );
1110 p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
1111 p += sprintf( p, "</body>\n" );
1112 p += sprintf( p, "</html>\n" );
1114 *pi_data = strlen( *pp_data ) + 1;
1118 static int httpd_page_404_get( httpd_file_callback_args_t *p_args,
1119 uint8_t *p_request, int i_request,
1120 uint8_t **pp_data, int *pi_data )
1124 p = *pp_data = malloc( 1024 );
1126 p += sprintf( p, "<html>\n" );
1127 p += sprintf( p, "<head>\n" );
1128 p += sprintf( p, "<title>Error 404</title>\n" );
1129 p += sprintf( p, "</head>\n" );
1130 p += sprintf( p, "<body>\n" );
1131 p += sprintf( p, "<h1><center> 404 Ressource not found</center></h1>\n" );
1132 p += sprintf( p, "<hr />\n" );
1133 p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
1134 p += sprintf( p, "</body>\n" );
1135 p += sprintf( p, "</html>\n" );
1137 *pi_data = strlen( *pp_data ) + 1;
1143 static int httpd_BanIP( httpd_sys_t *p_httpt, char * psz_new_banned_ip)
1145 httpd_banned_ip_t *p_new_banned_ip ;
1147 p_new_banned_ip = malloc( sizeof( httpd_banned_ip_t ) );
1148 if( !p_new_banned_ip )
1152 p_new_banned_ip->p_next=NULL;
1153 p_new_banned_ip->psz_ip = malloc( strlen( psz_new_banned_ip ) + 1 );
1154 if( !p_new_banned_ip->psz_ip )
1159 strcpy( p_new_banned_ip->psz_ip, psz_new_banned_ip );
1161 msg_Dbg( p_httpt, "Banning IP %s", psz_new_banned_ip );
1163 if( p_httpt->p_first_banned_ip )
1165 httpd_banned_ip_t *p_last;
1167 p_last = p_httpt->p_first_banned_ip;
1168 while( p_last->p_next )
1170 p_last = p_last->p_next;
1173 p_last->p_next = p_new_banned_ip;
1174 p_new_banned_ip->p_prev = p_last;
1178 p_new_banned_ip->p_prev = NULL;
1180 p_httpt->p_first_banned_ip = p_new_banned_ip;
1183 p_httpt->i_banned_ip_count++;
1187 static httpd_banned_ip_t *httpd_GetbannedIP( httpd_sys_t *p_httpt, char *psz_ip )
1189 httpd_banned_ip_t *p_ip;
1191 p_ip = p_httpt->p_first_banned_ip;
1195 if( strcmp( psz_ip, p_ip->psz_ip ) == 0 )
1199 p_ip = p_ip->p_next;
1205 static int httpd_UnbanIP( httpd_sys_t *p_httpt, httpd_banned_ip_t *p_banned_ip )
1212 msg_Dbg( p_httpt, "Unbanning IP %s",p_banned_ip->psz_ip);
1214 /* first cut out from list */
1215 if( p_banned_ip->p_prev )
1217 p_banned_ip->p_prev->p_next = p_banned_ip->p_next;
1221 p_httpt->p_first_banned_ip = p_banned_ip->p_next;
1224 if( p_banned_ip->p_next )
1226 p_banned_ip->p_next->p_prev = p_banned_ip->p_prev;
1229 FREE( p_banned_ip->psz_ip );
1230 FREE( p_banned_ip );
1232 p_httpt->i_banned_ip_count--;
1237 static void httpd_ConnnectionNew( httpd_sys_t *p_httpt, int fd, struct sockaddr_in *p_sock )
1239 httpd_connection_t *p_con;
1241 msg_Dbg( p_httpt, "new connection from %s", inet_ntoa( p_sock->sin_addr ) );
1243 /* verify if it's a banned ip */
1244 if(httpd_GetbannedIP( p_httpt,inet_ntoa( p_sock->sin_addr ) ) )
1246 msg_Dbg( p_httpt, "Ip %s banned : closing connection", inet_ntoa( p_sock->sin_addr ) );
1251 /* create a new connection and link it */
1252 p_con = malloc( sizeof( httpd_connection_t ) );
1253 p_con->i_state = HTTPD_CONNECTION_RECEIVING_REQUEST;
1255 p_con->i_last_activity_date = mdate();
1257 p_con->sock = *p_sock;
1258 p_con->psz_file = NULL;
1259 p_con->i_http_error = 0;
1260 p_con->psz_user = NULL;
1261 p_con->psz_password = NULL;
1262 p_con->p_file = NULL;
1264 p_con->i_request_size = 0;
1265 p_con->p_request = NULL;
1267 p_con->i_buffer = 0;
1268 p_con->i_buffer_size = 8096;
1269 p_con->p_buffer = malloc( p_con->i_buffer_size );
1271 p_con->i_stream_pos = 0; // updated by httpd_thread */
1272 p_con->p_next = NULL;
1274 if( p_httpt->p_first_connection )
1276 httpd_connection_t *p_last;
1278 p_last = p_httpt->p_first_connection;
1279 while( p_last->p_next )
1281 p_last = p_last->p_next;
1284 p_last->p_next = p_con;
1285 p_con->p_prev = p_last;
1289 p_con->p_prev = NULL;
1291 p_httpt->p_first_connection = p_con;
1294 p_httpt->i_connection_count++;
1297 static void httpd_ConnnectionClose( httpd_sys_t *p_httpt, httpd_connection_t *p_con )
1299 msg_Dbg( p_httpt, "close connection from %s", inet_ntoa( p_con->sock.sin_addr ) );
1301 p_httpt->i_connection_count--;
1302 /* first cut out from list */
1305 p_con->p_prev->p_next = p_con->p_next;
1309 p_httpt->p_first_connection = p_con->p_next;
1314 p_con->p_next->p_prev = p_con->p_prev;
1317 if( p_con->p_file ) p_con->p_file->i_ref--;
1318 FREE( p_con->psz_file );
1320 FREE( p_con->p_buffer );
1321 SOCKET_CLOSE( p_con->fd );
1323 FREE( p_con->psz_user );
1324 FREE( p_con->psz_password );
1326 FREE( p_con->p_request );
1330 static void httpd_RequestGetWord( char *word, int i_word_max, char **pp_buffer, char *p_end )
1332 char *p = *pp_buffer;
1335 while( p < p_end && *p && ( *p == ' ' || *p == '\t' ) )
1341 for( i = 0; i < i_word_max && p < p_end && *p && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r'; i++,p++)
1346 word[__MIN( i, i_word_max -1 )] = '\0';
1351 static int httpd_RequestNextLine( char **pp_buffer, char *p_end )
1355 for( p = *pp_buffer; p < p_end; p++ )
1357 if( p + 1 < p_end && *p == '\n' )
1362 if( p + 2 < p_end && p[0] == '\r' && p[1] == '\n' )
1369 return VLC_EGENERIC;
1372 //char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1373 static void b64_decode( char *dest, char *src )
1378 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
1379 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
1380 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
1381 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
1382 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
1383 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
1384 -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
1385 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
1386 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
1387 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
1388 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
1389 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
1390 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
1391 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
1392 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
1393 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
1396 for( i_level = 0; *src != '\0'; src++ )
1400 c = b64[(unsigned int)*src];
1413 *dest++ = ( last << 2 ) | ( ( c >> 4)&0x03 );
1417 *dest++ = ( ( last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f );
1421 *dest++ = ( ( last &0x03 ) << 6 ) | c;
1430 static void httpd_ConnectionParseRequest( httpd_sys_t *p_httpt, httpd_connection_t *p_con )
1439 char user[512] = "";
1440 char password[512] = "";
1442 //msg_Dbg( p_httpt, "new request=\n%s", p_con->p_buffer );
1445 p = p_con->p_buffer;
1446 p_end = p + strlen( p ) + 1;
1448 httpd_RequestGetWord( command, 32, &p, p_end );
1449 httpd_RequestGetWord( url, 1024, &p, p_end );
1450 httpd_RequestGetWord( version, 32, &p, p_end );
1451 //msg_Dbg( p_httpt, "ask =%s= =%s= =%s=", command, url, version );
1453 p_con->p_request = NULL;
1454 p_con->i_request_size = 0;
1455 if( !strcmp( command, "GET" ) )
1457 p_con->i_method = HTTPD_CONNECTION_METHOD_GET;
1459 else if( !strcmp( command, "POST" ))
1461 p_con->i_method = HTTPD_CONNECTION_METHOD_POST;
1463 else if( !strcmp( command, "HEAD" ))
1465 p_con->i_method = HTTPD_CONNECTION_METHOD_HEAD;
1470 p_con->psz_file = strdup( "/501.html" );
1471 p_con->i_method = HTTPD_CONNECTION_METHOD_GET;
1472 p_con->i_http_error = 501;
1476 if( strcmp( version, "HTTP/1.0" ) && strcmp( version, "HTTP/1.1" ) )
1478 p_con->psz_file = strdup( "/505.html" );
1479 p_con->i_http_error = 505;
1489 if( httpd_RequestNextLine( &p, p_end ) )
1491 //msg_Dbg( p_httpt, "failled new line" );
1494 //msg_Dbg( p_httpt, "new line=%s", p );
1496 httpd_RequestGetWord( header, 1024, &p, p_end );
1497 if( !strcmp( header, "\r\n" ) || !strcmp( header, "\n" ) )
1502 if( !strcmp( header, "Authorization:" ) )
1506 httpd_RequestGetWord( method, 32, &p, p_end );
1507 if( !strcasecmp( method, "BASIC" ) )
1512 httpd_RequestGetWord( basic, 1024, &p, p_end );
1513 // msg_Dbg( p_httpt, "Authorization: basic:%s", basic );
1514 b64_decode( decoded, basic );
1515 // msg_Dbg( p_httpt, "Authorization: decoded:%s", decoded );
1516 if( strchr( decoded, ':' ) )
1518 char *p = strchr( decoded, ':' );
1521 strcpy( user, decoded );
1522 strcpy( password, p );
1528 if( strchr( url, '?' ) )
1530 char *p_request = strchr( url, '?' );
1531 *p_request++ = '\0';
1532 p_con->psz_file = strdup( url );
1533 p_con->p_request = strdup( p_request );
1534 p_con->i_request_size = strlen( p_con->p_request );
1538 p_con->psz_file = strdup( url );
1542 if( p_con->i_method == HTTPD_CONNECTION_METHOD_POST )
1545 if( strstr( p_con->p_buffer, "\r\n\r\n" ) )
1547 p_request = strstr( p_con->p_buffer, "\r\n\r\n" ) + 4;
1549 else if( strstr( p_con->p_buffer, "\n\n" ) )
1551 p_request = strstr( p_con->p_buffer, "\n\n" ) + 2;
1557 if( p_request && p_request < p_end )
1559 p_con->i_request_size = p_end - p_request;
1560 p_con->p_request = malloc( p_con->i_request_size + 1);
1562 memcpy( p_con->p_request,
1564 p_con->i_request_size );
1566 p_con->p_request[p_con->i_request_size] = '\0';
1569 p_con->i_http_error = 200;
1572 //msg_Dbg( p_httpt, "ask %s %s %d", command, p_con->psz_file, p_con->i_http_error );
1573 FREE( p_con->p_buffer );
1574 p_con->i_buffer = 0;
1575 p_con->i_buffer_size = 0;
1577 //vlc_mutex_lock( &p_httpt->file_lock );
1580 p_con->p_file = NULL;
1581 for( i = 0; i < p_httpt->i_file_count; i++ )
1583 if( !strcmp( p_httpt->file[i]->psz_file, p_con->psz_file ) )
1585 if( p_httpt->file[i]->b_stream ||
1586 p_con->i_method == HTTPD_CONNECTION_METHOD_HEAD ||
1587 ( p_con->i_method == HTTPD_CONNECTION_METHOD_GET && p_httpt->file[i]->pf_get ) ||
1588 ( p_con->i_method == HTTPD_CONNECTION_METHOD_POST && p_httpt->file[i]->pf_post ) )
1590 p_con->p_file = p_httpt->file[i];
1596 if( !p_con->p_file )
1598 p_con->psz_file = strdup( "/404.html" );
1599 p_con->i_http_error = 404;
1601 /* XXX be sure that "/404.html" exist else ... */
1605 if( p_con->p_file->i_authenticate_method == HTTPD_AUTHENTICATE_BASIC )
1607 if( strcmp( user, p_con->p_file->psz_user ) || strcmp( password, p_con->p_file->psz_password ) )
1609 p_con->psz_file = strdup( "/401.html" );
1610 strcpy( user, p_con->p_file->psz_user );
1611 p_con->i_http_error = 401;
1613 /* XXX do not put password on 404 else ... */
1618 p_con->p_file->i_ref++;
1619 // vlc_mutex_unlock( &p_httpt->file_lock );
1621 switch( p_con->i_http_error )
1628 psz_status = "Authorization Required";
1631 psz_status = "Unknown";
1635 p_con->i_state = HTTPD_CONNECTION_SENDING_HEADER;
1637 p_con->i_buffer_size = 4096;
1638 p_con->i_buffer = 0;
1640 /* we send stream header with this one */
1641 if( p_con->i_http_error == 200 && p_con->p_file->b_stream )
1643 p_con->i_buffer_size += p_con->p_file->i_header_size;
1646 p = p_con->p_buffer = malloc( p_con->i_buffer_size );
1648 p += sprintf( p, "HTTP/1.0 %d %s\r\n", p_con->i_http_error, psz_status );
1649 p += sprintf( p, "Content-type: %s\r\n", p_con->p_file->psz_mime );
1650 if( p_con->i_http_error == 401 )
1652 p += sprintf( p, "WWW-Authenticate: Basic realm=\"%s\"\r\n", user );
1654 p += sprintf( p, "Cache-Control: no-cache\r\n" );
1655 p += sprintf( p, "\r\n" );
1657 p_con->i_buffer_size = strlen( p_con->p_buffer );// + 1;
1659 if( p_con->i_http_error == 200 && p_con->p_file->b_stream && p_con->p_file->i_header_size > 0 )
1661 /* add stream header */
1662 memcpy( &p_con->p_buffer[p_con->i_buffer_size],
1663 p_con->p_file->p_header,
1664 p_con->p_file->i_header_size );
1665 p_con->i_buffer_size += p_con->p_file->i_header_size;
1668 //msg_Dbg( p_httpt, "answer=\n%s", p_con->p_buffer );
1670 #define HTTPD_STREAM_PACKET 10000
1671 static void httpd_Thread( httpd_sys_t *p_httpt )
1673 httpd_file_t *p_page_401;
1674 httpd_file_t *p_page_404;
1676 httpd_connection_t *p_con;
1678 msg_Info( p_httpt, "httpd started" );
1680 p_page_401 = _RegisterFile( p_httpt,
1681 "/401.html", "text/html",
1685 (httpd_file_callback_args_t*)NULL );
1686 p_page_404 = _RegisterFile( p_httpt,
1687 "/404.html", "text/html",
1691 (httpd_file_callback_args_t*)NULL );
1693 while( !p_httpt->b_die )
1695 struct timeval timeout;
1698 int i_handle_max = 0;
1701 if( p_httpt->i_host_count <= 0 )
1703 msleep( 100 * 1000 );
1707 /* we will create a socket set with host and connection */
1708 FD_ZERO( &fds_read );
1709 FD_ZERO( &fds_write );
1711 vlc_mutex_lock( &p_httpt->host_lock );
1712 vlc_mutex_lock( &p_httpt->connection_lock );
1713 for( i = 0; i < p_httpt->i_host_count; i++ )
1715 FD_SET( p_httpt->host[i]->fd, &fds_read );
1716 i_handle_max = __MAX( i_handle_max, p_httpt->host[i]->fd );
1718 for( p_con = p_httpt->p_first_connection; p_con != NULL; )
1720 /* no more than 10s of inactivity */
1721 if( p_con->i_last_activity_date + (mtime_t)HTTPD_CONNECTION_MAX_UNUSED < mdate() ||
1722 p_con->i_state == HTTPD_CONNECTION_TO_BE_CLOSED)
1724 httpd_connection_t *p_next = p_con->p_next;
1726 msg_Dbg( p_httpt, "close unused connection" );
1727 httpd_ConnnectionClose( p_httpt, p_con );
1732 if( p_con->i_state == HTTPD_CONNECTION_SENDING_STREAM && p_con->i_stream_pos + HTTPD_STREAM_PACKET >= p_con->p_file->i_buffer_pos )
1734 p_con = p_con->p_next;
1738 if( p_con->i_state == HTTPD_CONNECTION_RECEIVING_REQUEST )
1740 FD_SET( p_con->fd, &fds_read );
1744 FD_SET( p_con->fd, &fds_write );
1746 i_handle_max = __MAX( i_handle_max, p_con->fd );
1748 p_con = p_con->p_next;
1750 vlc_mutex_unlock( &p_httpt->host_lock );
1751 vlc_mutex_unlock( &p_httpt->connection_lock );
1753 /* we will wait 0.5s */
1755 timeout.tv_usec = 500*1000;
1757 i_ret = select( i_handle_max + 1,
1762 if( i_ret == -1 && errno != EINTR )
1764 msg_Warn( p_httpt, "cannot select sockets" );
1774 vlc_mutex_lock( &p_httpt->host_lock );
1775 /* accept/refuse new connection */
1776 for( i = 0; i < p_httpt->i_host_count; i++ )
1778 int i_sock_size = sizeof( struct sockaddr_in );
1779 struct sockaddr_in sock;
1782 fd = accept( p_httpt->host[i]->fd, (struct sockaddr *)&sock,
1786 #if defined( WIN32 ) || defined( UNDER_CE )
1788 unsigned long i_dummy = 1;
1789 ioctlsocket( fd, FIONBIO, &i_dummy );
1792 fcntl( fd, F_SETFL, O_NONBLOCK );
1795 if( p_httpt->i_connection_count >= HTTPD_MAX_CONNECTION )
1797 msg_Warn( p_httpt, "max connection reached" );
1801 /* create a new connection and link it */
1802 httpd_ConnnectionNew( p_httpt, fd, &sock );
1806 vlc_mutex_unlock( &p_httpt->host_lock );
1808 vlc_mutex_lock( &p_httpt->file_lock );
1809 /* now do work for all connections */
1810 for( p_con = p_httpt->p_first_connection; p_con != NULL; )
1812 if( p_con->i_state == HTTPD_CONNECTION_RECEIVING_REQUEST )
1816 i_len = recv( p_con->fd,
1817 p_con->p_buffer + p_con->i_buffer,
1818 p_con->i_buffer_size - p_con->i_buffer, 0 );
1821 #if defined( WIN32 ) || defined( UNDER_CE )
1822 if( ( i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK ) || ( i_len == 0 ) )
1824 if( ( i_len < 0 && errno != EAGAIN && errno != EINTR ) || ( i_len == 0 ) )
1827 httpd_connection_t *p_next = p_con->p_next;
1829 httpd_ConnnectionClose( p_httpt, p_con );
1832 else if( i_len > 0 )
1835 p_con->i_last_activity_date = mdate();
1836 p_con->i_buffer += i_len;
1838 ptr = p_con->p_buffer + p_con->i_buffer;
1840 if( ( p_con->i_buffer >= 2 && !strncmp( ptr - 2, "\n\n", 2 ) )||
1841 ( p_con->i_buffer >= 4 && !strncmp( ptr - 4, "\r\n\r\n", 4 ) ) ||
1842 p_con->i_buffer >= p_con->i_buffer_size )
1844 p_con->p_buffer[__MIN( p_con->i_buffer, p_con->i_buffer_size - 1 )] = '\0';
1845 httpd_ConnectionParseRequest( p_httpt, p_con );
1848 p_con = p_con->p_next;
1852 p_con = p_con->p_next;
1854 continue; /* just for clarity */
1856 else if( p_con->i_state == HTTPD_CONNECTION_SENDING_HEADER || p_con->i_state == HTTPD_CONNECTION_SENDING_FILE )
1861 if( p_con->i_buffer_size - p_con->i_buffer > 0 )
1863 i_len = send( p_con->fd, p_con->p_buffer + p_con->i_buffer, p_con->i_buffer_size - p_con->i_buffer, 0 );
1869 // msg_Warn( p_httpt, "on %d send %d bytes %s", p_con->i_buffer_size, i_len, p_con->p_buffer + p_con->i_buffer );
1871 #if defined( WIN32 ) || defined( UNDER_CE )
1872 if( ( i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK ) || ( i_len == 0 ) )
1874 if( ( i_len < 0 && errno != EAGAIN && errno != EINTR ) || ( i_len == 0 ) )
1877 httpd_connection_t *p_next = p_con->p_next;
1879 httpd_ConnnectionClose( p_httpt, p_con );
1882 else if( i_len > 0 )
1884 p_con->i_last_activity_date = mdate();
1885 p_con->i_buffer += i_len;
1887 if( p_con->i_buffer >= p_con->i_buffer_size )
1889 if( p_con->i_state == HTTPD_CONNECTION_SENDING_HEADER )
1891 p_con->i_buffer_size = 0;
1892 p_con->i_buffer = 0;
1893 FREE( p_con->p_buffer );
1895 if( !p_con->p_file->b_stream || p_con->i_method == HTTPD_CONNECTION_METHOD_HEAD )
1897 p_con->i_state = HTTPD_CONNECTION_SENDING_FILE; // be sure to out from HTTPD_CONNECTION_SENDING_HEADER
1898 if( p_con->i_method == HTTPD_CONNECTION_METHOD_GET )
1900 p_con->p_file->pf_get( p_con->p_file->p_sys,
1901 p_con->p_request, p_con->i_request_size,
1902 &p_con->p_buffer, &p_con->i_buffer_size );
1904 else if( p_con->i_method == HTTPD_CONNECTION_METHOD_POST )
1906 p_con->p_file->pf_post( p_con->p_file->p_sys,
1907 p_con->p_request, p_con->i_request_size,
1908 &p_con->p_buffer, &p_con->i_buffer_size );
1912 /* HTTPD_CONNECTION_METHOD_HEAD for example */
1913 p_con->p_buffer = NULL;
1914 p_con->i_buffer_size = 0;
1919 p_con->i_state = HTTPD_CONNECTION_SENDING_STREAM;
1920 p_con->i_stream_pos = p_con->p_file->i_buffer_last_pos;
1922 p_con = p_con->p_next;
1926 httpd_connection_t *p_next = p_con->p_next;
1928 httpd_ConnnectionClose( p_httpt, p_con );
1934 p_con = p_con->p_next;
1939 p_con = p_con->p_next;
1941 continue; /* just for clarity */
1943 else if( p_con->i_state == HTTPD_CONNECTION_SENDING_STREAM )
1945 httpd_stream_t *p_stream = p_con->p_file;
1949 if( p_con->i_stream_pos < p_stream->i_buffer_pos )
1952 /* check if this p_con aren't to late */
1953 if( p_con->i_stream_pos + p_stream->i_buffer_size < p_stream->i_buffer_pos )
1955 fprintf( stderr, "fixing i_stream_pos (old=%lld i_buffer_pos=%lld\n",
1956 p_con->i_stream_pos, p_stream->i_buffer_pos );
1957 p_con->i_stream_pos = p_stream->i_buffer_last_pos;
1960 i_pos = p_con->i_stream_pos % p_stream->i_buffer_size;
1961 /* size until end of buffer */
1962 i_write = p_stream->i_buffer_size - i_pos;
1963 /* is it more than valid data */
1964 if( i_write >= p_stream->i_buffer_pos - p_con->i_stream_pos )
1966 i_write = p_stream->i_buffer_pos - p_con->i_stream_pos;
1968 /* limit to HTTPD_STREAM_PACKET */
1969 if( i_write > HTTPD_STREAM_PACKET )
1971 i_write = HTTPD_STREAM_PACKET;
1973 i_send = send( p_con->fd, &p_stream->p_buffer[i_pos], i_write, 0 );
1975 #if defined( WIN32 ) || defined( UNDER_CE )
1976 if( ( i_send < 0 && WSAGetLastError() != WSAEWOULDBLOCK )|| ( i_send == 0 ) )
1978 if( ( i_send < 0 && errno != EAGAIN && errno != EINTR )|| ( i_send == 0 ) )
1981 httpd_connection_t *p_next = p_con->p_next;
1983 httpd_ConnnectionClose( p_httpt, p_con );
1987 else if( i_send > 0 )
1989 p_con->i_last_activity_date = mdate();
1990 p_con->i_stream_pos += i_send;
1993 p_con = p_con->p_next;
1994 continue; /* just for clarity */
1996 else if( p_con->i_state != HTTPD_CONNECTION_TO_BE_CLOSED )
1998 msg_Warn( p_httpt, "cannot occur (Invalid p_con->i_state)" );
1999 p_con = p_con->p_next;
2001 } /* for over connection */
2003 vlc_mutex_unlock( &p_httpt->file_lock );
2006 msg_Info( p_httpt, "httpd stopped" );
2008 _UnregisterFile( p_httpt, p_page_401 );
2009 _UnregisterFile( p_httpt, p_page_404 );