1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2004-2006 the VideoLAN team
5 * Copyright © 2004-2007 Rémi Denis-Courmont
8 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
9 * Rémi Denis-Courmont <rem # videolan.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
30 #include <vlc_common.h>
31 #include <vlc_httpd.h>
35 #include <vlc_network.h>
38 #include <vlc_strings.h>
40 #include "../libvlc.h"
53 #if defined( UNDER_CE )
55 #elif defined( WIN32 )
56 # include <winsock2.h>
58 # include <sys/socket.h>
62 /* We need HUGE buffer otherwise TCP throughput is very limited */
63 #define HTTPD_CL_BUFSIZE 1000000
65 #define HTTPD_CL_BUFSIZE 10000
68 static void httpd_ClientClean( httpd_client_t
*cl
);
79 /* each host run in his own thread */
89 /* address/port and socket for listening at connections */
99 /* all registered url (becarefull that 2 httpd_url_t could point at the same url)
100 * This will slow down the url research but make my live easier
101 * All url will have their cb trigger, but only the first one can answer
107 httpd_client_t
**client
;
128 httpd_callback_sys_t
*p_sys
;
129 } catch[HTTPD_MSG_MAX
];
135 HTTPD_CLIENT_RECEIVING
,
136 HTTPD_CLIENT_RECEIVE_DONE
,
138 HTTPD_CLIENT_SENDING
,
139 HTTPD_CLIENT_SEND_DONE
,
141 HTTPD_CLIENT_WAITING
,
145 HTTPD_CLIENT_TLS_HS_IN
,
146 HTTPD_CLIENT_TLS_HS_OUT
152 HTTPD_CLIENT_FILE
, /* default */
153 HTTPD_CLIENT_STREAM
, /* regulary get data from cb */
154 HTTPD_CLIENT_BIDIR
, /* check for reading and get data from cb */
157 struct httpd_client_t
167 int b_read_waiting
; /* stop as soon as possible sending */
169 mtime_t i_activity_date
;
170 mtime_t i_activity_timeout
;
172 /* buffer for reading header */
178 httpd_message_t query
; /* client -> httpd */
179 httpd_message_t answer
; /* httpd -> client */
182 tls_session_t
*p_tls
;
186 /*****************************************************************************
188 *****************************************************************************/
191 const char psz_ext
[8];
192 const char *psz_mime
;
195 { ".htm", "text/html" },
196 { ".html", "text/html" },
197 { ".txt", "text/plain" },
198 { ".xml", "text/xml" },
199 { ".dtd", "text/dtd" },
201 { ".css", "text/css" },
204 { ".gif", "image/gif" },
205 { ".jpe", "image/jpeg" },
206 { ".jpg", "image/jpeg" },
207 { ".jpeg", "image/jpeg" },
208 { ".png", "image/png" },
209 /* same as modules/mux/mpjpeg.c here: */
210 { ".mpjpeg","multipart/x-mixed-replace; boundary=7b3cc56e5f51db803f790dad720ed50a" },
213 { ".avi", "video/avi" },
214 { ".asf", "video/x-ms-asf" },
215 { ".m1a", "audio/mpeg" },
216 { ".m2a", "audio/mpeg" },
217 { ".m1v", "video/mpeg" },
218 { ".m2v", "video/mpeg" },
219 { ".mp2", "audio/mpeg" },
220 { ".mp3", "audio/mpeg" },
221 { ".mpa", "audio/mpeg" },
222 { ".mpg", "video/mpeg" },
223 { ".mpeg", "video/mpeg" },
224 { ".mpe", "video/mpeg" },
225 { ".mov", "video/quicktime" },
226 { ".moov", "video/quicktime" },
227 { ".oga", "audio/ogg" },
228 { ".ogg", "application/ogg" },
229 { ".ogm", "application/ogg" },
230 { ".ogv", "video/ogg" },
231 { ".ogx", "application/ogg" },
232 { ".spx", "audio/ogg" },
233 { ".wav", "audio/wav" },
234 { ".wma", "audio/x-ms-wma" },
235 { ".wmv", "video/x-ms-wmv" },
242 static const char *httpd_MimeFromUrl( const char *psz_url
)
247 psz_ext
= strrchr( psz_url
, '.' );
252 for( i
= 0; http_mime
[i
].psz_ext
[0] ; i
++ )
254 if( !strcasecmp( http_mime
[i
].psz_ext
, psz_ext
) )
256 return http_mime
[i
].psz_mime
;
260 return "application/octet-stream";
267 const char psz_reason
[36];
270 static const http_status_info http_reason
[] =
272 /*{ 100, "Continue" },
273 { 101, "Switching Protocols" },*/
275 /*{ 201, "Created" },
277 { 203, "Non-authoritative information" },
278 { 204, "No content" },
279 { 205, "Reset content" },
280 { 206, "Partial content" },
281 { 250, "Low on storage space" },
282 { 300, "Multiple choices" },*/
283 { 301, "Moved permanently" },
284 /*{ 302, "Moved temporarily" },
285 { 303, "See other" },
286 { 304, "Not modified" },
287 { 305, "Use proxy" },
288 { 307, "Temporary redirect" },
289 { 400, "Bad request" },*/
290 { 401, "Unauthorized" },
291 /*{ 402, "Payment Required" },*/
292 { 403, "Forbidden" },
293 { 404, "Not found" },
294 { 405, "Method not allowed" },
295 /*{ 406, "Not acceptable" },
296 { 407, "Proxy authentication required" },
297 { 408, "Request time-out" },
300 { 411, "Length required" },
301 { 412, "Precondition failed" },
302 { 413, "Request entity too large" },
303 { 414, "Request-URI too large" },
304 { 415, "Unsupported media Type" },
305 { 416, "Requested range not satisfiable" },
306 { 417, "Expectation failed" },
307 { 451, "Parameter not understood" },
308 { 452, "Conference not found" },
309 { 453, "Not enough bandwidth" },*/
310 { 454, "Session not found" },
311 /*{ 455, "Method not valid in this State" },*/
312 { 456, "Header field not valid for resource" },
313 /*{ 457, "Invalid range" },
314 { 458, "Read-only parameter" },*/
315 { 459, "Aggregate operation not allowed" },
316 { 460, "Non-aggregate operation not allowed" },
317 { 461, "Unsupported transport" },
318 /*{ 462, "Destination unreachable" },*/
319 { 500, "Internal server error" },
320 { 501, "Not implemented" },
321 /*{ 502, "Bad gateway" },*/
322 { 503, "Service unavailable" },
323 /*{ 504, "Gateway time-out" },*/
324 { 505, "Protocol version not supported" },
325 { 551, "Option not supported" },
329 static const char psz_fallback_reason
[5][16] =
330 { "Continue", "OK", "Found", "Client error", "Server error" };
332 static const char *httpd_ReasonFromCode( unsigned i_code
)
334 const http_status_info
*p
;
336 assert( ( i_code
>= 100 ) && ( i_code
<= 599 ) );
338 for (p
= http_reason
; i_code
> p
->i_code
; p
++);
340 if( p
->i_code
== i_code
)
341 return p
->psz_reason
;
343 return psz_fallback_reason
[(i_code
/ 100) - 1];
347 static size_t httpd_HtmlError (char **body
, int code
, const char *url
)
349 const char *errname
= httpd_ReasonFromCode (code
);
350 assert (errname
!= NULL
);
352 int res
= asprintf (body
,
353 "<?xml version=\"1.0\" encoding=\"ascii\" ?>\n"
354 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\""
355 " \"http://www.w3.org/TR/xhtml10/DTD/xhtml10strict.dtd\">\n"
356 "<html lang=\"en\">\n"
358 "<title>%s</title>\n"
361 "<h1>%d %s%s%s%s</h1>\n"
363 "<a href=\"http://www.videolan.org\">VideoLAN</a>\n"
365 "</html>\n", errname
, code
, errname
,
366 (url
? " (" : ""), (url
? url
: ""), (url
? ")" : ""));
378 /*****************************************************************************
379 * High Level Functions: httpd_file_t
380 *****************************************************************************/
388 httpd_file_callback_t pf_fill
;
389 httpd_file_sys_t
*p_sys
;
394 httpd_FileCallBack( httpd_callback_sys_t
*p_sys
, httpd_client_t
*cl
,
395 httpd_message_t
*answer
, const httpd_message_t
*query
)
397 httpd_file_t
*file
= (httpd_file_t
*)p_sys
;
398 uint8_t **pp_body
, *p_body
;
399 const char *psz_connection
;
400 int *pi_body
, i_body
;
402 if( answer
== NULL
|| query
== NULL
)
406 answer
->i_proto
= HTTPD_PROTO_HTTP
;
407 answer
->i_version
= 1;
408 answer
->i_type
= HTTPD_MSG_ANSWER
;
410 answer
->i_status
= 200;
412 httpd_MsgAdd( answer
, "Content-type", "%s", file
->psz_mime
);
413 httpd_MsgAdd( answer
, "Cache-Control", "%s", "no-cache" );
415 if( query
->i_type
!= HTTPD_MSG_HEAD
)
417 pp_body
= &answer
->p_body
;
418 pi_body
= &answer
->i_body
;
422 /* The file still needs to be executed. */
429 if( query
->i_type
== HTTPD_MSG_POST
)
431 /* msg_Warn not supported */
434 uint8_t *psz_args
= query
->psz_args
;
435 file
->pf_fill( file
->p_sys
, file
, psz_args
, pp_body
, pi_body
);
437 if( query
->i_type
== HTTPD_MSG_HEAD
&& p_body
!= NULL
)
442 /* We respect client request */
443 psz_connection
= httpd_MsgGet( &cl
->query
, "Connection" );
444 if( psz_connection
!= NULL
)
446 httpd_MsgAdd( answer
, "Connection", "%s", psz_connection
);
449 httpd_MsgAdd( answer
, "Content-Length", "%d", answer
->i_body
);
454 httpd_file_t
*httpd_FileNew( httpd_host_t
*host
,
455 const char *psz_url
, const char *psz_mime
,
456 const char *psz_user
, const char *psz_password
,
457 const vlc_acl_t
*p_acl
, httpd_file_callback_t pf_fill
,
458 httpd_file_sys_t
*p_sys
)
460 httpd_file_t
*file
= xmalloc( sizeof( httpd_file_t
) );
462 if( ( file
->url
= httpd_UrlNewUnique( host
, psz_url
, psz_user
,
463 psz_password
, p_acl
)
470 file
->psz_url
= strdup( psz_url
);
471 if( psz_mime
&& *psz_mime
)
473 file
->psz_mime
= strdup( psz_mime
);
477 file
->psz_mime
= strdup( httpd_MimeFromUrl( psz_url
) );
480 file
->pf_fill
= pf_fill
;
483 httpd_UrlCatch( file
->url
, HTTPD_MSG_HEAD
, httpd_FileCallBack
,
484 (httpd_callback_sys_t
*)file
);
485 httpd_UrlCatch( file
->url
, HTTPD_MSG_GET
, httpd_FileCallBack
,
486 (httpd_callback_sys_t
*)file
);
487 httpd_UrlCatch( file
->url
, HTTPD_MSG_POST
, httpd_FileCallBack
,
488 (httpd_callback_sys_t
*)file
);
493 httpd_file_sys_t
*httpd_FileDelete( httpd_file_t
*file
)
495 httpd_file_sys_t
*p_sys
= file
->p_sys
;
497 httpd_UrlDelete( file
->url
);
499 free( file
->psz_url
);
500 free( file
->psz_mime
);
507 /*****************************************************************************
508 * High Level Functions: httpd_handler_t (for CGIs)
509 *****************************************************************************/
510 struct httpd_handler_t
514 httpd_handler_callback_t pf_fill
;
515 httpd_handler_sys_t
*p_sys
;
520 httpd_HandlerCallBack( httpd_callback_sys_t
*p_sys
, httpd_client_t
*cl
,
521 httpd_message_t
*answer
, const httpd_message_t
*query
)
523 httpd_handler_t
*handler
= (httpd_handler_t
*)p_sys
;
524 char psz_remote_addr
[NI_MAXNUMERICHOST
];
526 if( answer
== NULL
|| query
== NULL
)
530 answer
->i_proto
= HTTPD_PROTO_NONE
;
531 answer
->i_type
= HTTPD_MSG_ANSWER
;
533 /* We do it ourselves, thanks */
534 answer
->i_status
= 0;
536 if( httpd_ClientIP( cl
, psz_remote_addr
) == NULL
)
537 *psz_remote_addr
= '\0';
539 uint8_t *psz_args
= query
->psz_args
;
540 handler
->pf_fill( handler
->p_sys
, handler
, query
->psz_url
, psz_args
,
541 query
->i_type
, query
->p_body
, query
->i_body
,
542 psz_remote_addr
, NULL
,
543 &answer
->p_body
, &answer
->i_body
);
545 if( query
->i_type
== HTTPD_MSG_HEAD
)
547 char *p
= (char *)answer
->p_body
;
549 /* Looks for end of header (i.e. one empty line) */
550 while ( (p
= strchr( p
, '\r' )) != NULL
)
552 if( p
[1] && p
[1] == '\n' && p
[2] && p
[2] == '\r'
553 && p
[3] && p
[3] == '\n' )
562 answer
->i_body
= strlen((char*)answer
->p_body
) + 1;
563 answer
->p_body
= xrealloc( answer
->p_body
, answer
->i_body
);
567 if( strncmp( (char *)answer
->p_body
, "HTTP/1.", 7 ) )
569 int i_status
, i_headers
;
570 char *psz_headers
, *psz_new
;
571 const char *psz_status
;
573 if( !strncmp( (char *)answer
->p_body
, "Status: ", 8 ) )
576 i_status
= strtol( (char *)&answer
->p_body
[8], &psz_headers
, 0 );
577 if( *psz_headers
== '\r' || *psz_headers
== '\n' ) psz_headers
++;
578 if( *psz_headers
== '\n' ) psz_headers
++;
579 i_headers
= answer
->i_body
- (psz_headers
- (char *)answer
->p_body
);
584 psz_headers
= (char *)answer
->p_body
;
585 i_headers
= answer
->i_body
;
588 psz_status
= httpd_ReasonFromCode( i_status
);
589 answer
->i_body
= sizeof("HTTP/1.0 xxx \r\n")
590 + strlen(psz_status
) + i_headers
- 1;
591 psz_new
= (char *)xmalloc( answer
->i_body
+ 1);
592 sprintf( psz_new
, "HTTP/1.0 %03d %s\r\n", i_status
, psz_status
);
593 memcpy( &psz_new
[strlen(psz_new
)], psz_headers
, i_headers
);
594 free( answer
->p_body
);
595 answer
->p_body
= (uint8_t *)psz_new
;
601 httpd_handler_t
*httpd_HandlerNew( httpd_host_t
*host
, const char *psz_url
,
602 const char *psz_user
,
603 const char *psz_password
,
604 const vlc_acl_t
*p_acl
,
605 httpd_handler_callback_t pf_fill
,
606 httpd_handler_sys_t
*p_sys
)
608 httpd_handler_t
*handler
= xmalloc( sizeof( httpd_handler_t
) );
610 if( ( handler
->url
= httpd_UrlNewUnique( host
, psz_url
, psz_user
,
611 psz_password
, p_acl
)
618 handler
->pf_fill
= pf_fill
;
619 handler
->p_sys
= p_sys
;
621 httpd_UrlCatch( handler
->url
, HTTPD_MSG_HEAD
, httpd_HandlerCallBack
,
622 (httpd_callback_sys_t
*)handler
);
623 httpd_UrlCatch( handler
->url
, HTTPD_MSG_GET
, httpd_HandlerCallBack
,
624 (httpd_callback_sys_t
*)handler
);
625 httpd_UrlCatch( handler
->url
, HTTPD_MSG_POST
, httpd_HandlerCallBack
,
626 (httpd_callback_sys_t
*)handler
);
631 httpd_handler_sys_t
*httpd_HandlerDelete( httpd_handler_t
*handler
)
633 httpd_handler_sys_t
*p_sys
= handler
->p_sys
;
634 httpd_UrlDelete( handler
->url
);
639 /*****************************************************************************
640 * High Level Functions: httpd_redirect_t
641 *****************************************************************************/
642 struct httpd_redirect_t
648 static int httpd_RedirectCallBack( httpd_callback_sys_t
*p_sys
,
649 httpd_client_t
*cl
, httpd_message_t
*answer
,
650 const httpd_message_t
*query
)
652 httpd_redirect_t
*rdir
= (httpd_redirect_t
*)p_sys
;
656 if( answer
== NULL
|| query
== NULL
)
660 answer
->i_proto
= HTTPD_PROTO_HTTP
;
661 answer
->i_version
= 1;
662 answer
->i_type
= HTTPD_MSG_ANSWER
;
663 answer
->i_status
= 301;
665 answer
->i_body
= httpd_HtmlError (&p_body
, 301, rdir
->psz_dst
);
666 answer
->p_body
= (unsigned char *)p_body
;
668 /* XXX check if it's ok or we need to set an absolute url */
669 httpd_MsgAdd( answer
, "Location", "%s", rdir
->psz_dst
);
671 httpd_MsgAdd( answer
, "Content-Length", "%d", answer
->i_body
);
676 httpd_redirect_t
*httpd_RedirectNew( httpd_host_t
*host
, const char *psz_url_dst
,
677 const char *psz_url_src
)
679 httpd_redirect_t
*rdir
= xmalloc( sizeof( httpd_redirect_t
) );
681 if( !( rdir
->url
= httpd_UrlNewUnique( host
, psz_url_src
, NULL
, NULL
, NULL
) ) )
686 rdir
->psz_dst
= strdup( psz_url_dst
);
688 /* Redirect apply for all HTTP request and RTSP DESCRIBE resquest */
689 httpd_UrlCatch( rdir
->url
, HTTPD_MSG_HEAD
, httpd_RedirectCallBack
,
690 (httpd_callback_sys_t
*)rdir
);
691 httpd_UrlCatch( rdir
->url
, HTTPD_MSG_GET
, httpd_RedirectCallBack
,
692 (httpd_callback_sys_t
*)rdir
);
693 httpd_UrlCatch( rdir
->url
, HTTPD_MSG_POST
, httpd_RedirectCallBack
,
694 (httpd_callback_sys_t
*)rdir
);
695 httpd_UrlCatch( rdir
->url
, HTTPD_MSG_DESCRIBE
, httpd_RedirectCallBack
,
696 (httpd_callback_sys_t
*)rdir
);
700 void httpd_RedirectDelete( httpd_redirect_t
*rdir
)
702 httpd_UrlDelete( rdir
->url
);
703 free( rdir
->psz_dst
);
707 /*****************************************************************************
708 * High Level Funtions: httpd_stream_t
709 *****************************************************************************/
710 struct httpd_stream_t
717 /* Header to send as first packet */
721 /* circular buffer */
722 int i_buffer_size
; /* buffer size, can't be reallocated smaller */
723 uint8_t *p_buffer
; /* buffer */
724 int64_t i_buffer_pos
; /* absolute position from begining */
725 int64_t i_buffer_last_pos
; /* a new connection will start with that */
728 static int httpd_StreamCallBack( httpd_callback_sys_t
*p_sys
,
729 httpd_client_t
*cl
, httpd_message_t
*answer
,
730 const httpd_message_t
*query
)
732 httpd_stream_t
*stream
= (httpd_stream_t
*)p_sys
;
734 if( answer
== NULL
|| query
== NULL
|| cl
== NULL
)
739 if( answer
->i_body_offset
> 0 )
745 fprintf( stderr
, "httpd_StreamCallBack i_body_offset=%lld\n",
746 answer
->i_body_offset
);
749 if( answer
->i_body_offset
>= stream
->i_buffer_pos
)
751 /* fprintf( stderr, "httpd_StreamCallBack: no data\n" ); */
752 return VLC_EGENERIC
; /* wait, no data available */
754 if( answer
->i_body_offset
+ stream
->i_buffer_size
<
755 stream
->i_buffer_pos
)
757 /* this client isn't fast enough */
759 fprintf( stderr
, "fixing i_body_offset (old=%lld new=%lld)\n",
760 answer
->i_body_offset
, stream
->i_buffer_last_pos
);
762 answer
->i_body_offset
= stream
->i_buffer_last_pos
;
765 i_pos
= answer
->i_body_offset
% stream
->i_buffer_size
;
766 i_write
= stream
->i_buffer_pos
- answer
->i_body_offset
;
767 if( i_write
> HTTPD_CL_BUFSIZE
)
769 i_write
= HTTPD_CL_BUFSIZE
;
771 else if( i_write
<= 0 )
773 return VLC_EGENERIC
; /* wait, no data available */
776 /* Don't go past the end of the circular buffer */
777 i_write
= __MIN( i_write
, stream
->i_buffer_size
- i_pos
);
779 /* using HTTPD_MSG_ANSWER -> data available */
780 answer
->i_proto
= HTTPD_PROTO_HTTP
;
781 answer
->i_version
= 0;
782 answer
->i_type
= HTTPD_MSG_ANSWER
;
784 answer
->i_body
= i_write
;
785 answer
->p_body
= xmalloc( i_write
);
786 memcpy( answer
->p_body
, &stream
->p_buffer
[i_pos
], i_write
);
788 answer
->i_body_offset
+= i_write
;
794 answer
->i_proto
= HTTPD_PROTO_HTTP
;
795 answer
->i_version
= 0;
796 answer
->i_type
= HTTPD_MSG_ANSWER
;
798 answer
->i_status
= 200;
800 if( query
->i_type
!= HTTPD_MSG_HEAD
)
802 httpd_ClientModeStream( cl
);
803 vlc_mutex_lock( &stream
->lock
);
804 /* Send the header */
805 if( stream
->i_header
> 0 )
807 answer
->i_body
= stream
->i_header
;
808 answer
->p_body
= xmalloc( stream
->i_header
);
809 memcpy( answer
->p_body
, stream
->p_header
, stream
->i_header
);
811 answer
->i_body_offset
= stream
->i_buffer_last_pos
;
812 vlc_mutex_unlock( &stream
->lock
);
816 httpd_MsgAdd( answer
, "Content-Length", "%d", 0 );
817 answer
->i_body_offset
= 0;
820 if( !strcmp( stream
->psz_mime
, "video/x-ms-asf-stream" ) )
822 bool b_xplaystream
= false;
825 httpd_MsgAdd( answer
, "Content-type", "%s",
826 "application/octet-stream" );
827 httpd_MsgAdd( answer
, "Server", "Cougar 4.1.0.3921" );
828 httpd_MsgAdd( answer
, "Pragma", "no-cache" );
829 httpd_MsgAdd( answer
, "Pragma", "client-id=%lu",
830 vlc_mrand48()&0x7fff );
831 httpd_MsgAdd( answer
, "Pragma", "features=\"broadcast\"" );
833 /* Check if there is a xPlayStrm=1 */
834 for( i
= 0; i
< query
->i_name
; i
++ )
836 if( !strcasecmp( query
->name
[i
], "Pragma" ) &&
837 strstr( query
->value
[i
], "xPlayStrm=1" ) )
839 b_xplaystream
= true;
845 answer
->i_body_offset
= 0;
850 httpd_MsgAdd( answer
, "Content-type", "%s", stream
->psz_mime
);
852 httpd_MsgAdd( answer
, "Cache-Control", "%s", "no-cache" );
857 httpd_stream_t
*httpd_StreamNew( httpd_host_t
*host
,
858 const char *psz_url
, const char *psz_mime
,
859 const char *psz_user
, const char *psz_password
,
860 const vlc_acl_t
*p_acl
)
862 httpd_stream_t
*stream
= xmalloc( sizeof( httpd_stream_t
) );
864 if( ( stream
->url
= httpd_UrlNewUnique( host
, psz_url
, psz_user
,
865 psz_password
, p_acl
)
871 vlc_mutex_init( &stream
->lock
);
872 if( psz_mime
&& *psz_mime
)
874 stream
->psz_mime
= strdup( psz_mime
);
878 stream
->psz_mime
= strdup( httpd_MimeFromUrl( psz_url
) );
880 stream
->i_header
= 0;
881 stream
->p_header
= NULL
;
882 stream
->i_buffer_size
= 5000000; /* 5 Mo per stream */
883 stream
->p_buffer
= xmalloc( stream
->i_buffer_size
);
884 /* We set to 1 to make life simpler
885 * (this way i_body_offset can never be 0) */
886 stream
->i_buffer_pos
= 1;
887 stream
->i_buffer_last_pos
= 1;
889 httpd_UrlCatch( stream
->url
, HTTPD_MSG_HEAD
, httpd_StreamCallBack
,
890 (httpd_callback_sys_t
*)stream
);
891 httpd_UrlCatch( stream
->url
, HTTPD_MSG_GET
, httpd_StreamCallBack
,
892 (httpd_callback_sys_t
*)stream
);
893 httpd_UrlCatch( stream
->url
, HTTPD_MSG_POST
, httpd_StreamCallBack
,
894 (httpd_callback_sys_t
*)stream
);
899 int httpd_StreamHeader( httpd_stream_t
*stream
, uint8_t *p_data
, int i_data
)
901 vlc_mutex_lock( &stream
->lock
);
902 free( stream
->p_header
);
903 stream
->p_header
= NULL
;
905 stream
->i_header
= i_data
;
908 stream
->p_header
= xmalloc( i_data
);
909 memcpy( stream
->p_header
, p_data
, i_data
);
911 vlc_mutex_unlock( &stream
->lock
);
916 int httpd_StreamSend( httpd_stream_t
*stream
, uint8_t *p_data
, int i_data
)
921 if( i_data
< 0 || p_data
== NULL
)
925 vlc_mutex_lock( &stream
->lock
);
927 /* save this pointer (to be used by new connection) */
928 stream
->i_buffer_last_pos
= stream
->i_buffer_pos
;
930 i_pos
= stream
->i_buffer_pos
% stream
->i_buffer_size
;
936 i_copy
= __MIN( i_count
, stream
->i_buffer_size
- i_pos
);
938 /* Ok, we can't go past the end of our buffer */
939 memcpy( &stream
->p_buffer
[i_pos
], p_data
, i_copy
);
941 i_pos
= ( i_pos
+ i_copy
) % stream
->i_buffer_size
;
946 stream
->i_buffer_pos
+= i_data
;
948 vlc_mutex_unlock( &stream
->lock
);
952 void httpd_StreamDelete( httpd_stream_t
*stream
)
954 httpd_UrlDelete( stream
->url
);
955 vlc_mutex_destroy( &stream
->lock
);
956 free( stream
->psz_mime
);
957 free( stream
->p_header
);
958 free( stream
->p_buffer
);
962 /*****************************************************************************
964 *****************************************************************************/
965 static void* httpd_HostThread( void * );
967 /* create a new host */
968 httpd_host_t
*httpd_HostNew( vlc_object_t
*p_this
, const char *psz_host
,
971 return httpd_TLSHostNew( p_this
, psz_host
, i_port
, NULL
, NULL
, NULL
, NULL
975 static const char psz_object_type
[] = "http server";
976 static vlc_mutex_t httpd_mutex
= VLC_STATIC_MUTEX
;
978 httpd_host_t
*httpd_TLSHostNew( vlc_object_t
*p_this
, const char *psz_hostname
,
980 const char *psz_cert
, const char *psz_key
,
981 const char *psz_ca
, const char *psz_crl
)
990 if( psz_hostname
== NULL
)
993 psz_host
= strdup( psz_hostname
);
994 if( psz_host
== NULL
)
997 /* to be sure to avoid multiple creation */
998 vlc_mutex_lock( &httpd_mutex
);
999 httpd
= libvlc_priv (p_this
->p_libvlc
)->p_httpd
;
1003 msg_Info( p_this
, "creating httpd" );
1004 httpd
= (httpd_t
*)vlc_custom_create( p_this
, sizeof (*httpd
),
1009 vlc_mutex_unlock( &httpd_mutex
);
1017 ptrval
.p_address
= httpd
;
1018 libvlc_priv (p_this
->p_libvlc
)->p_httpd
= httpd
;
1019 vlc_object_attach( httpd
, p_this
->p_libvlc
);
1022 /* verify if it already exist */
1023 for( i
= httpd
->i_host
- 1; i
>= 0; i
-- )
1025 host
= httpd
->host
[i
];
1027 /* cannot mix TLS and non-TLS hosts */
1028 if( ( ( httpd
->host
[i
]->p_tls
!= NULL
) != ( psz_cert
!= NULL
) )
1029 || ( host
->i_port
!= i_port
)
1030 || strcmp( host
->psz_hostname
, psz_hostname
) )
1033 /* Increase existing matching host reference count.
1034 * The reference count is written under both the global httpd and the
1035 * host lock. It is read with either or both locks held. The global
1036 * lock is always acquired first. */
1037 vlc_mutex_lock( &host
->lock
);
1039 vlc_mutex_unlock( &host
->lock
);
1041 vlc_mutex_unlock( &httpd_mutex
);
1047 /* determine TLS configuration */
1048 if ( psz_cert
!= NULL
)
1050 p_tls
= tls_ServerCreate( p_this
, psz_cert
, psz_key
);
1051 if ( p_tls
== NULL
)
1053 msg_Err( p_this
, "TLS initialization error" );
1057 if ( ( psz_ca
!= NULL
) && tls_ServerAddCA( p_tls
, psz_ca
) )
1059 msg_Err( p_this
, "TLS CA error" );
1063 if ( ( psz_crl
!= NULL
) && tls_ServerAddCRL( p_tls
, psz_crl
) )
1065 msg_Err( p_this
, "TLS CRL error" );
1072 /* create the new host */
1073 host
= (httpd_host_t
*)vlc_custom_create( p_this
, sizeof (*host
),
1079 host
->httpd
= httpd
;
1080 vlc_mutex_init( &host
->lock
);
1081 vlc_cond_init( &host
->wait
);
1084 vlc_object_attach( host
, p_this
);
1086 host
->fds
= net_ListenTCP( p_this
, psz_host
, i_port
);
1087 if( host
->fds
== NULL
)
1089 msg_Err( p_this
, "cannot create socket(s) for HTTP host" );
1092 for (host
->nfd
= 0; host
->fds
[host
->nfd
] != -1; host
->nfd
++);
1094 if( vlc_object_waitpipe( VLC_OBJECT( host
) ) == -1 )
1096 msg_Err( host
, "signaling pipe error: %m" );
1100 host
->i_port
= i_port
;
1101 host
->psz_hostname
= psz_host
;
1106 host
->client
= NULL
;
1108 host
->p_tls
= p_tls
;
1110 /* create the thread */
1111 if( vlc_clone( &host
->thread
, httpd_HostThread
, host
,
1112 VLC_THREAD_PRIORITY_LOW
) )
1114 msg_Err( p_this
, "cannot spawn http host thread" );
1118 /* now add it to httpd */
1119 TAB_APPEND( httpd
->i_host
, httpd
->host
, host
);
1120 vlc_mutex_unlock( &httpd_mutex
);
1126 if( httpd
->i_host
<= 0 )
1128 libvlc_priv (httpd
->p_libvlc
)->p_httpd
= NULL
;
1129 vlc_object_release( httpd
);
1131 vlc_mutex_unlock( &httpd_mutex
);
1135 net_ListenClose( host
->fds
);
1136 vlc_cond_destroy( &host
->wait
);
1137 vlc_mutex_destroy( &host
->lock
);
1138 vlc_object_release( host
);
1142 tls_ServerDelete( p_tls
);
1148 void httpd_HostDelete( httpd_host_t
*host
)
1150 httpd_t
*httpd
= host
->httpd
;
1152 bool delete = false;
1154 vlc_mutex_lock( &httpd_mutex
);
1156 vlc_mutex_lock( &host
->lock
);
1158 if( host
->i_ref
== 0 )
1160 vlc_cond_signal( &host
->wait
);
1163 vlc_mutex_unlock( &host
->lock
);
1167 vlc_mutex_unlock( &httpd_mutex
);
1168 msg_Dbg( host
, "httpd_HostDelete: host still in use" );
1171 TAB_REMOVE( httpd
->i_host
, httpd
->host
, host
);
1173 vlc_object_kill( host
);
1174 vlc_join( host
->thread
, NULL
);
1176 msg_Dbg( host
, "HTTP host removed" );
1178 for( i
= 0; i
< host
->i_url
; i
++ )
1180 msg_Err( host
, "url still registered: %s", host
->url
[i
]->psz_url
);
1182 for( i
= 0; i
< host
->i_client
; i
++ )
1184 httpd_client_t
*cl
= host
->client
[i
];
1185 msg_Warn( host
, "client still connected" );
1186 httpd_ClientClean( cl
);
1187 TAB_REMOVE( host
->i_client
, host
->client
, cl
);
1193 if( host
->p_tls
!= NULL
)
1194 tls_ServerDelete( host
->p_tls
);
1196 net_ListenClose( host
->fds
);
1197 free( host
->psz_hostname
);
1199 vlc_cond_destroy( &host
->wait
);
1200 vlc_mutex_destroy( &host
->lock
);
1201 vlc_object_release( host
);
1203 if( httpd
->i_host
<= 0 )
1205 msg_Dbg( httpd
, "no hosts left, stopping httpd" );
1207 libvlc_priv (httpd
->p_libvlc
)->p_httpd
= NULL
;
1208 vlc_object_release( httpd
);
1210 vlc_mutex_unlock( &httpd_mutex
);
1213 /* register a new url */
1214 static httpd_url_t
*httpd_UrlNewPrivate( httpd_host_t
*host
, const char *psz_url
,
1215 const char *psz_user
, const char *psz_password
,
1216 const vlc_acl_t
*p_acl
, bool b_check
)
1221 assert( psz_url
!= NULL
);
1223 vlc_mutex_lock( &host
->lock
);
1226 for( i
= 0; i
< host
->i_url
; i
++ )
1228 if( !strcmp( psz_url
, host
->url
[i
]->psz_url
) )
1230 msg_Warn( host
->httpd
,
1231 "cannot add '%s' (url already defined)", psz_url
);
1232 vlc_mutex_unlock( &host
->lock
);
1238 url
= xmalloc( sizeof( httpd_url_t
) );
1241 vlc_mutex_init( &url
->lock
);
1242 url
->psz_url
= strdup( psz_url
);
1243 url
->psz_user
= strdup( psz_user
? psz_user
: "" );
1244 url
->psz_password
= strdup( psz_password
? psz_password
: "" );
1245 url
->p_acl
= ACL_Duplicate( host
, p_acl
);
1246 for( i
= 0; i
< HTTPD_MSG_MAX
; i
++ )
1248 url
->catch[i
].cb
= NULL
;
1249 url
->catch[i
].p_sys
= NULL
;
1252 TAB_APPEND( host
->i_url
, host
->url
, url
);
1253 vlc_cond_signal( &host
->wait
);
1254 vlc_mutex_unlock( &host
->lock
);
1259 httpd_url_t
*httpd_UrlNew( httpd_host_t
*host
, const char *psz_url
,
1260 const char *psz_user
, const char *psz_password
,
1261 const vlc_acl_t
*p_acl
)
1263 return httpd_UrlNewPrivate( host
, psz_url
, psz_user
,
1264 psz_password
, p_acl
, false );
1267 httpd_url_t
*httpd_UrlNewUnique( httpd_host_t
*host
, const char *psz_url
,
1268 const char *psz_user
, const char *psz_password
,
1269 const vlc_acl_t
*p_acl
)
1271 return httpd_UrlNewPrivate( host
, psz_url
, psz_user
,
1272 psz_password
, p_acl
, true );
1275 /* register callback on a url */
1276 int httpd_UrlCatch( httpd_url_t
*url
, int i_msg
, httpd_callback_t cb
,
1277 httpd_callback_sys_t
*p_sys
)
1279 vlc_mutex_lock( &url
->lock
);
1280 url
->catch[i_msg
].cb
= cb
;
1281 url
->catch[i_msg
].p_sys
= p_sys
;
1282 vlc_mutex_unlock( &url
->lock
);
1288 void httpd_UrlDelete( httpd_url_t
*url
)
1290 httpd_host_t
*host
= url
->host
;
1293 vlc_mutex_lock( &host
->lock
);
1294 TAB_REMOVE( host
->i_url
, host
->url
, url
);
1296 vlc_mutex_destroy( &url
->lock
);
1297 free( url
->psz_url
);
1298 free( url
->psz_user
);
1299 free( url
->psz_password
);
1300 ACL_Destroy( url
->p_acl
);
1302 for( i
= 0; i
< host
->i_client
; i
++ )
1304 httpd_client_t
*client
= host
->client
[i
];
1306 if( client
->url
== url
)
1308 /* TODO complete it */
1309 msg_Warn( host
, "force closing connections" );
1310 httpd_ClientClean( client
);
1311 TAB_REMOVE( host
->i_client
, host
->client
, client
);
1317 vlc_mutex_unlock( &host
->lock
);
1320 static void httpd_MsgInit( httpd_message_t
*msg
)
1323 msg
->i_type
= HTTPD_MSG_NONE
;
1324 msg
->i_proto
= HTTPD_PROTO_NONE
;
1325 msg
->i_version
= -1; /* FIXME */
1329 msg
->psz_url
= NULL
;
1330 msg
->psz_args
= NULL
;
1332 msg
->i_channel
= -1;
1339 msg
->i_body_offset
= 0;
1344 static void httpd_MsgClean( httpd_message_t
*msg
)
1348 free( msg
->psz_url
);
1349 free( msg
->psz_args
);
1350 for( i
= 0; i
< msg
->i_name
; i
++ )
1352 free( msg
->name
[i
] );
1353 free( msg
->value
[i
] );
1357 free( msg
->p_body
);
1358 httpd_MsgInit( msg
);
1361 const char *httpd_MsgGet( const httpd_message_t
*msg
, const char *name
)
1365 for( i
= 0; i
< msg
->i_name
; i
++ )
1367 if( !strcasecmp( msg
->name
[i
], name
))
1369 return msg
->value
[i
];
1375 void httpd_MsgAdd( httpd_message_t
*msg
, const char *name
, const char *psz_value
, ... )
1380 va_start( args
, psz_value
);
1381 if( vasprintf( &value
, psz_value
, args
) == -1 )
1388 name
= strdup( name
);
1395 TAB_APPEND( msg
->i_name
, msg
->name
, (char*)name
);
1396 TAB_APPEND( msg
->i_value
, msg
->value
, value
);
1399 static void httpd_ClientInit( httpd_client_t
*cl
, mtime_t now
)
1401 cl
->i_state
= HTTPD_CLIENT_RECEIVING
;
1402 cl
->i_activity_date
= now
;
1403 cl
->i_activity_timeout
= INT64_C(10000000);
1404 cl
->i_buffer_size
= HTTPD_CL_BUFSIZE
;
1406 cl
->p_buffer
= xmalloc( cl
->i_buffer_size
);
1407 cl
->i_mode
= HTTPD_CLIENT_FILE
;
1408 cl
->b_read_waiting
= false;
1410 httpd_MsgInit( &cl
->query
);
1411 httpd_MsgInit( &cl
->answer
);
1414 void httpd_ClientModeStream( httpd_client_t
*cl
)
1416 cl
->i_mode
= HTTPD_CLIENT_STREAM
;
1419 void httpd_ClientModeBidir( httpd_client_t
*cl
)
1421 cl
->i_mode
= HTTPD_CLIENT_BIDIR
;
1424 char* httpd_ClientIP( const httpd_client_t
*cl
, char *psz_ip
)
1426 return net_GetPeerAddress( cl
->fd
, psz_ip
, NULL
) ? NULL
: psz_ip
;
1429 char* httpd_ServerIP( const httpd_client_t
*cl
, char *psz_ip
)
1431 return net_GetSockAddress( cl
->fd
, psz_ip
, NULL
) ? NULL
: psz_ip
;
1434 static void httpd_ClientClean( httpd_client_t
*cl
)
1438 if( cl
->p_tls
!= NULL
)
1439 tls_ServerSessionClose( cl
->p_tls
);
1440 net_Close( cl
->fd
);
1444 httpd_MsgClean( &cl
->answer
);
1445 httpd_MsgClean( &cl
->query
);
1447 free( cl
->p_buffer
);
1448 cl
->p_buffer
= NULL
;
1451 static httpd_client_t
*httpd_ClientNew( int fd
, tls_session_t
*p_tls
, mtime_t now
)
1453 httpd_client_t
*cl
= malloc( sizeof( httpd_client_t
) );
1455 if( !cl
) return NULL
;
1462 httpd_ClientInit( cl
, now
);
1468 ssize_t
httpd_NetRecv (httpd_client_t
*cl
, uint8_t *p
, size_t i_len
)
1470 tls_session_t
*p_tls
;
1475 val
= p_tls
? tls_Recv (p_tls
, p
, i_len
)
1476 : recv (cl
->fd
, p
, i_len
, 0);
1477 while (val
== -1 && errno
== EINTR
);
1482 ssize_t
httpd_NetSend (httpd_client_t
*cl
, const uint8_t *p
, size_t i_len
)
1484 tls_session_t
*p_tls
;
1489 val
= p_tls
? tls_Send( p_tls
, p
, i_len
)
1490 : send (cl
->fd
, p
, i_len
, 0);
1491 while (val
== -1 && errno
== EINTR
);
1498 const char name
[16];
1504 { "OPTIONS", HTTPD_MSG_OPTIONS
, HTTPD_PROTO_RTSP
},
1505 { "DESCRIBE", HTTPD_MSG_DESCRIBE
, HTTPD_PROTO_RTSP
},
1506 { "SETUP", HTTPD_MSG_SETUP
, HTTPD_PROTO_RTSP
},
1507 { "PLAY", HTTPD_MSG_PLAY
, HTTPD_PROTO_RTSP
},
1508 { "PAUSE", HTTPD_MSG_PAUSE
, HTTPD_PROTO_RTSP
},
1509 { "GET_PARAMETER", HTTPD_MSG_GETPARAMETER
, HTTPD_PROTO_RTSP
},
1510 { "TEARDOWN", HTTPD_MSG_TEARDOWN
, HTTPD_PROTO_RTSP
},
1511 { "GET", HTTPD_MSG_GET
, HTTPD_PROTO_HTTP
},
1512 { "HEAD", HTTPD_MSG_HEAD
, HTTPD_PROTO_HTTP
},
1513 { "POST", HTTPD_MSG_POST
, HTTPD_PROTO_HTTP
},
1514 { "", HTTPD_MSG_NONE
, HTTPD_PROTO_NONE
}
1518 static void httpd_ClientRecv( httpd_client_t
*cl
)
1522 /* ignore leading whites */
1523 if( ( cl
->query
.i_proto
== HTTPD_PROTO_NONE
) &&
1524 ( cl
->i_buffer
== 0 ) )
1528 i_len
= httpd_NetRecv( cl
, &c
, 1 );
1530 if( ( i_len
> 0 ) && ( strchr( "\r\n\t ", c
) == NULL
) )
1532 cl
->p_buffer
[0] = c
;
1537 if( cl
->query
.i_proto
== HTTPD_PROTO_NONE
)
1539 /* enough to see if it's Interleaved RTP over RTSP or RTSP/HTTP */
1540 i_len
= httpd_NetRecv( cl
, &cl
->p_buffer
[cl
->i_buffer
],
1544 cl
->i_buffer
+= i_len
;
1547 if( ( cl
->i_buffer
>= 4 ) && ( cl
->p_buffer
[0] == '$' ) )
1549 /* Interleaved RTP over RTSP */
1550 cl
->query
.i_proto
= HTTPD_PROTO_RTSP
;
1551 cl
->query
.i_type
= HTTPD_MSG_CHANNEL
;
1552 cl
->query
.i_channel
= cl
->p_buffer
[1];
1553 cl
->query
.i_body
= (cl
->p_buffer
[2] << 8)|cl
->p_buffer
[3];
1554 cl
->query
.p_body
= xmalloc( cl
->query
.i_body
);
1556 memcpy( cl
->query
.p_body
, cl
->p_buffer
+ 4, cl
->i_buffer
);
1559 /* The smallest legal request is 7 bytes ("GET /\r\n"),
1560 * this is the maximum we can ask at this point. */
1561 if( cl
->i_buffer
>= 7 )
1563 if( !memcmp( cl
->p_buffer
, "HTTP/1.", 7 ) )
1565 cl
->query
.i_proto
= HTTPD_PROTO_HTTP
;
1566 cl
->query
.i_type
= HTTPD_MSG_ANSWER
;
1568 else if( !memcmp( cl
->p_buffer
, "RTSP/1.", 7 ) )
1570 cl
->query
.i_proto
= HTTPD_PROTO_RTSP
;
1571 cl
->query
.i_type
= HTTPD_MSG_ANSWER
;
1575 /* We need the full request line to determine the protocol. */
1576 cl
->query
.i_proto
= HTTPD_PROTO_HTTP0
;
1577 cl
->query
.i_type
= HTTPD_MSG_NONE
;
1581 else if( cl
->query
.i_body
> 0 )
1583 /* we are reading the body of a request or a channel */
1584 i_len
= httpd_NetRecv( cl
, &cl
->query
.p_body
[cl
->i_buffer
],
1585 cl
->query
.i_body
- cl
->i_buffer
);
1588 cl
->i_buffer
+= i_len
;
1590 if( cl
->i_buffer
>= cl
->query
.i_body
)
1592 cl
->i_state
= HTTPD_CLIENT_RECEIVE_DONE
;
1597 /* we are reading a header -> char by char */
1600 if( cl
->i_buffer
== cl
->i_buffer_size
)
1602 uint8_t *newbuf
= realloc( cl
->p_buffer
, cl
->i_buffer_size
+ 1024 );
1603 if( newbuf
== NULL
)
1609 cl
->p_buffer
= newbuf
;
1610 cl
->i_buffer_size
+= 1024;
1613 i_len
= httpd_NetRecv (cl
, &cl
->p_buffer
[cl
->i_buffer
], 1 );
1620 if( ( cl
->query
.i_proto
== HTTPD_PROTO_HTTP0
)
1621 && ( cl
->p_buffer
[cl
->i_buffer
- 1] == '\n' ) )
1623 /* Request line is now complete */
1624 const char *p
= memchr( cl
->p_buffer
, ' ', cl
->i_buffer
);
1627 assert( cl
->query
.i_type
== HTTPD_MSG_NONE
);
1629 if( p
== NULL
) /* no URI: evil guy */
1631 i_len
= 0; /* drop connection */
1636 p
++; /* skips extra spaces */
1639 p
= memchr( p
, ' ', ((char *)cl
->p_buffer
) + cl
->i_buffer
- p
);
1640 if( p
== NULL
) /* no explicit protocol: HTTP/0.9 */
1642 i_len
= 0; /* not supported currently -> drop */
1647 p
++; /* skips extra spaces ever again */
1650 len
= ((char *)cl
->p_buffer
) + cl
->i_buffer
- p
;
1651 if( len
< 7 ) /* foreign protocol */
1652 i_len
= 0; /* I don't understand -> drop */
1654 if( memcmp( p
, "HTTP/1.", 7 ) == 0 )
1656 cl
->query
.i_proto
= HTTPD_PROTO_HTTP
;
1657 cl
->query
.i_version
= atoi( p
+ 7 );
1660 if( memcmp( p
, "RTSP/1.", 7 ) == 0 )
1662 cl
->query
.i_proto
= HTTPD_PROTO_RTSP
;
1663 cl
->query
.i_version
= atoi( p
+ 7 );
1666 if( memcmp( p
, "HTTP/", 5 ) == 0 )
1668 const uint8_t sorry
[] =
1669 "HTTP/1.1 505 Unknown HTTP version\r\n\r\n";
1670 httpd_NetSend( cl
, sorry
, sizeof( sorry
) - 1 );
1671 i_len
= 0; /* drop */
1674 if( memcmp( p
, "RTSP/", 5 ) == 0 )
1676 const uint8_t sorry
[] =
1677 "RTSP/1.0 505 Unknown RTSP version\r\n\r\n";
1678 httpd_NetSend( cl
, sorry
, sizeof( sorry
) - 1 );
1679 i_len
= 0; /* drop */
1681 else /* yet another foreign protocol */
1688 if( ( cl
->i_buffer
>= 2 && !memcmp( &cl
->p_buffer
[cl
->i_buffer
-2], "\n\n", 2 ) )||
1689 ( cl
->i_buffer
>= 4 && !memcmp( &cl
->p_buffer
[cl
->i_buffer
-4], "\r\n\r\n", 4 ) ) )
1693 /* we have finished the header so parse it and set i_body */
1694 cl
->p_buffer
[cl
->i_buffer
] = '\0';
1696 if( cl
->query
.i_type
== HTTPD_MSG_ANSWER
)
1699 * assume strlen( "HTTP/1.x" ) = 8
1701 cl
->query
.i_status
=
1702 strtol( (char *)&cl
->p_buffer
[8],
1712 cl
->query
.i_type
= HTTPD_MSG_NONE
;
1714 /*fprintf( stderr, "received new request=%s\n", cl->p_buffer);*/
1716 for( i
= 0; msg_type
[i
].name
[0]; i
++ )
1718 if( !strncmp( (char *)cl
->p_buffer
, msg_type
[i
].name
,
1719 strlen( msg_type
[i
].name
) ) )
1721 p
= (char *)&cl
->p_buffer
[strlen(msg_type
[i
].name
) + 1 ];
1722 cl
->query
.i_type
= msg_type
[i
].i_type
;
1723 if( cl
->query
.i_proto
!= msg_type
[i
].i_proto
)
1726 cl
->query
.i_proto
= HTTPD_PROTO_NONE
;
1727 cl
->query
.i_type
= HTTPD_MSG_NONE
;
1734 if( strstr( (char *)cl
->p_buffer
, "HTTP/1." ) )
1736 cl
->query
.i_proto
= HTTPD_PROTO_HTTP
;
1738 else if( strstr( (char *)cl
->p_buffer
, "RTSP/1." ) )
1740 cl
->query
.i_proto
= HTTPD_PROTO_RTSP
;
1752 p2
= strchr( p
, ' ' );
1757 if( !strncasecmp( p
, ( cl
->query
.i_proto
1758 == HTTPD_PROTO_HTTP
) ? "http" : "rtsp", 4 )
1759 && p
[4 + !!strchr( "sS", p
[4] )] == ':' )
1760 { /* Skip hier-part of URL (if present) */
1761 p
= strchr( p
, ':' ) + 1; /* skip URI scheme */
1762 if( !strncmp( p
, "//", 2 ) ) /* skip authority */
1763 { /* see RFC3986 §3.2 */
1765 while( *p
&& !strchr( "/?#", *p
) ) p
++;
1768 cl
->query
.psz_url
= strdup( p
);
1769 if( ( p3
= strchr( cl
->query
.psz_url
, '?' ) ) )
1772 cl
->query
.psz_args
= (uint8_t *)strdup( p3
);
1779 p
= strchr( p
, '\n' );
1783 while( *p
== '\n' || *p
== '\r' )
1787 while( p
&& *p
!= '\0' )
1790 char *eol
= p
= strchr( p
, '\n' );
1793 while( eol
&& eol
>= line
&& ( *eol
== '\n' || *eol
== '\r' ) )
1798 if( ( colon
= strchr( line
, ':' ) ) )
1804 while( *colon
== ' ' )
1808 name
= strdup( line
);
1809 value
= strdup( colon
);
1811 TAB_APPEND( cl
->query
.i_name
, cl
->query
.name
, name
);
1812 TAB_APPEND( cl
->query
.i_value
,cl
->query
.value
,value
);
1814 if( !strcasecmp( name
, "Content-Length" ) )
1816 cl
->query
.i_body
= atol( value
);
1823 while( *p
== '\n' || *p
== '\r' )
1830 if( cl
->query
.i_body
> 0 )
1832 /* TODO Mhh, handle the case client will only send a
1833 * request and close the connection
1834 * to mark and of body (probably only RTSP) */
1835 cl
->query
.p_body
= xmalloc( cl
->query
.i_body
);
1840 cl
->i_state
= HTTPD_CLIENT_RECEIVE_DONE
;
1846 /* check if the client is to be set to dead */
1847 #if defined( WIN32 ) || defined( UNDER_CE )
1848 if( ( i_len
< 0 && WSAGetLastError() != WSAEWOULDBLOCK
) || ( i_len
== 0 ) )
1850 if( ( i_len
< 0 && errno
!= EAGAIN
) || ( i_len
== 0 ) )
1853 if( cl
->query
.i_proto
!= HTTPD_PROTO_NONE
&& cl
->query
.i_type
!= HTTPD_MSG_NONE
)
1855 /* connection closed -> end of data */
1856 if( cl
->query
.i_body
> 0 )
1858 cl
->query
.i_body
= cl
->i_buffer
;
1860 cl
->i_state
= HTTPD_CLIENT_RECEIVE_DONE
;
1864 cl
->i_state
= HTTPD_CLIENT_DEAD
;
1868 /* XXX: for QT I have to disable timeout. Try to find why */
1869 if( cl
->query
.i_proto
== HTTPD_PROTO_RTSP
)
1870 cl
->i_activity_timeout
= 0;
1872 #if 0 /* Debugging only */
1873 if( cl
->i_state
== HTTPD_CLIENT_RECEIVE_DONE
)
1877 fprintf( stderr
, "received new request\n" );
1878 fprintf( stderr
, " - proto=%s\n",
1879 cl
->query
.i_proto
== HTTPD_PROTO_HTTP
? "HTTP" : "RTSP" );
1880 fprintf( stderr
, " - version=%d\n", cl
->query
.i_version
);
1881 fprintf( stderr
, " - msg=%d\n", cl
->query
.i_type
);
1882 if( cl
->query
.i_type
== HTTPD_MSG_ANSWER
)
1884 fprintf( stderr
, " - answer=%d '%s'\n", cl
->query
.i_status
,
1885 cl
->query
.psz_status
);
1887 else if( cl
->query
.i_type
!= HTTPD_MSG_NONE
)
1889 fprintf( stderr
, " - url=%s\n", cl
->query
.psz_url
);
1891 for( i
= 0; i
< cl
->query
.i_name
; i
++ )
1893 fprintf( stderr
, " - option name='%s' value='%s'\n",
1894 cl
->query
.name
[i
], cl
->query
.value
[i
] );
1900 static void httpd_ClientSend( httpd_client_t
*cl
)
1905 if( cl
->i_buffer
< 0 )
1907 /* We need to create the header */
1910 const char *psz_status
= httpd_ReasonFromCode( cl
->answer
.i_status
);
1912 i_size
= strlen( "HTTP/1.") + 10 + 10 + strlen( psz_status
) + 5;
1913 for( i
= 0; i
< cl
->answer
.i_name
; i
++ )
1915 i_size
+= strlen( cl
->answer
.name
[i
] ) + 2 +
1916 strlen( cl
->answer
.value
[i
] ) + 2;
1919 if( cl
->i_buffer_size
< i_size
)
1921 cl
->i_buffer_size
= i_size
;
1922 free( cl
->p_buffer
);
1923 cl
->p_buffer
= xmalloc( i_size
);
1925 p
= (char *)cl
->p_buffer
;
1927 p
+= sprintf( p
, "%s.%u %d %s\r\n",
1928 cl
->answer
.i_proto
== HTTPD_PROTO_HTTP
? "HTTP/1" : "RTSP/1",
1929 cl
->answer
.i_version
,
1930 cl
->answer
.i_status
, psz_status
);
1931 for( i
= 0; i
< cl
->answer
.i_name
; i
++ )
1933 p
+= sprintf( p
, "%s: %s\r\n", cl
->answer
.name
[i
],
1934 cl
->answer
.value
[i
] );
1936 p
+= sprintf( p
, "\r\n" );
1939 cl
->i_buffer_size
= (uint8_t*)p
- cl
->p_buffer
;
1941 /*fprintf( stderr, "sending answer\n" );
1942 fprintf( stderr, "%s", cl->p_buffer );*/
1945 i_len
= httpd_NetSend( cl
, &cl
->p_buffer
[cl
->i_buffer
],
1946 cl
->i_buffer_size
- cl
->i_buffer
);
1949 cl
->i_buffer
+= i_len
;
1951 if( cl
->i_buffer
>= cl
->i_buffer_size
)
1953 if( cl
->answer
.i_body
== 0 && cl
->answer
.i_body_offset
> 0 &&
1954 !cl
->b_read_waiting
)
1956 /* catch more body data */
1957 int i_msg
= cl
->query
.i_type
;
1958 int64_t i_offset
= cl
->answer
.i_body_offset
;
1960 httpd_MsgClean( &cl
->answer
);
1961 cl
->answer
.i_body_offset
= i_offset
;
1963 cl
->url
->catch[i_msg
].cb( cl
->url
->catch[i_msg
].p_sys
, cl
,
1964 &cl
->answer
, &cl
->query
);
1967 if( cl
->answer
.i_body
> 0 )
1969 /* send the body data */
1970 free( cl
->p_buffer
);
1971 cl
->p_buffer
= cl
->answer
.p_body
;
1972 cl
->i_buffer_size
= cl
->answer
.i_body
;
1975 cl
->answer
.i_body
= 0;
1976 cl
->answer
.p_body
= NULL
;
1981 cl
->i_state
= HTTPD_CLIENT_SEND_DONE
;
1987 #if defined( WIN32 ) || defined( UNDER_CE )
1988 if( ( i_len
< 0 && WSAGetLastError() != WSAEWOULDBLOCK
) || ( i_len
== 0 ) )
1990 if( ( i_len
< 0 && errno
!= EAGAIN
) || ( i_len
== 0 ) )
1994 cl
->i_state
= HTTPD_CLIENT_DEAD
;
1999 static void httpd_ClientTlsHsIn( httpd_client_t
*cl
)
2001 switch( tls_SessionContinueHandshake( cl
->p_tls
) )
2004 cl
->i_state
= HTTPD_CLIENT_RECEIVING
;
2008 cl
->i_state
= HTTPD_CLIENT_DEAD
;
2013 cl
->i_state
= HTTPD_CLIENT_TLS_HS_OUT
;
2017 static void httpd_ClientTlsHsOut( httpd_client_t
*cl
)
2019 switch( tls_SessionContinueHandshake( cl
->p_tls
) )
2022 cl
->i_state
= HTTPD_CLIENT_RECEIVING
;
2026 cl
->i_state
= HTTPD_CLIENT_DEAD
;
2031 cl
->i_state
= HTTPD_CLIENT_TLS_HS_IN
;
2036 static void* httpd_HostThread( void *data
)
2038 httpd_host_t
*host
= data
;
2039 tls_session_t
*p_tls
= NULL
;
2040 counter_t
*p_total_counter
= stats_CounterCreate( host
, VLC_VAR_INTEGER
, STATS_COUNTER
);
2041 counter_t
*p_active_counter
= stats_CounterCreate( host
, VLC_VAR_INTEGER
, STATS_COUNTER
);
2042 int evfd
= vlc_object_waitpipe( VLC_OBJECT( host
) );
2046 /* prepare a new TLS session */
2047 if( ( p_tls
== NULL
) && ( host
->p_tls
!= NULL
) )
2048 p_tls
= tls_ServerSessionPrepare( host
->p_tls
);
2050 struct pollfd ufd
[host
->nfd
+ host
->i_client
+ 1];
2052 for( nfd
= 0; nfd
< host
->nfd
; nfd
++ )
2054 ufd
[nfd
].fd
= host
->fds
[nfd
];
2055 ufd
[nfd
].events
= POLLIN
;
2056 ufd
[nfd
].revents
= 0;
2059 /* add all socket that should be read/write and close dead connection */
2060 vlc_mutex_lock( &host
->lock
);
2061 while( host
->i_url
<= 0 && host
->i_ref
> 0 )
2062 vlc_cond_wait( &host
->wait
, &host
->lock
);
2064 mtime_t now
= mdate();
2065 bool b_low_delay
= false;
2067 for(int i_client
= 0; i_client
< host
->i_client
; i_client
++ )
2069 httpd_client_t
*cl
= host
->client
[i_client
];
2070 if( cl
->i_ref
< 0 || ( cl
->i_ref
== 0 &&
2071 ( cl
->i_state
== HTTPD_CLIENT_DEAD
||
2072 ( cl
->i_activity_timeout
> 0 &&
2073 cl
->i_activity_date
+cl
->i_activity_timeout
< now
) ) ) )
2075 httpd_ClientClean( cl
);
2076 stats_UpdateInteger( host
, p_active_counter
, -1, NULL
);
2077 TAB_REMOVE( host
->i_client
, host
->client
, cl
);
2083 struct pollfd
*pufd
= ufd
+ nfd
;
2084 assert (pufd
< ufd
+ (sizeof (ufd
) / sizeof (ufd
[0])));
2087 pufd
->events
= pufd
->revents
= 0;
2089 if( ( cl
->i_state
== HTTPD_CLIENT_RECEIVING
)
2090 || ( cl
->i_state
== HTTPD_CLIENT_TLS_HS_IN
) )
2092 pufd
->events
= POLLIN
;
2094 else if( ( cl
->i_state
== HTTPD_CLIENT_SENDING
)
2095 || ( cl
->i_state
== HTTPD_CLIENT_TLS_HS_OUT
) )
2097 pufd
->events
= POLLOUT
;
2099 else if( cl
->i_state
== HTTPD_CLIENT_RECEIVE_DONE
)
2101 httpd_message_t
*answer
= &cl
->answer
;
2102 httpd_message_t
*query
= &cl
->query
;
2103 int i_msg
= query
->i_type
;
2105 httpd_MsgInit( answer
);
2107 /* Handle what we received */
2108 if( (cl
->i_mode
!= HTTPD_CLIENT_BIDIR
) &&
2109 (i_msg
== HTTPD_MSG_ANSWER
|| i_msg
== HTTPD_MSG_CHANNEL
) )
2111 /* we can only receive request from client when not
2114 cl
->i_state
= HTTPD_CLIENT_DEAD
;
2116 else if( i_msg
== HTTPD_MSG_ANSWER
)
2118 /* We are in BIDIR mode, trigger the callback and then
2119 * check for new data */
2120 if( cl
->url
&& cl
->url
->catch[i_msg
].cb
)
2122 cl
->url
->catch[i_msg
].cb( cl
->url
->catch[i_msg
].p_sys
,
2125 cl
->i_state
= HTTPD_CLIENT_WAITING
;
2127 else if( i_msg
== HTTPD_MSG_CHANNEL
)
2129 /* We are in BIDIR mode, trigger the callback and then
2130 * check for new data */
2131 if( cl
->url
&& cl
->url
->catch[i_msg
].cb
)
2133 cl
->url
->catch[i_msg
].cb( cl
->url
->catch[i_msg
].p_sys
,
2136 cl
->i_state
= HTTPD_CLIENT_WAITING
;
2138 else if( i_msg
== HTTPD_MSG_OPTIONS
)
2141 answer
->i_type
= HTTPD_MSG_ANSWER
;
2142 answer
->i_proto
= query
->i_proto
;
2143 answer
->i_status
= 200;
2145 answer
->p_body
= NULL
;
2147 httpd_MsgAdd( answer
, "Server", "VLC/%s", VERSION
);
2148 httpd_MsgAdd( answer
, "Content-Length", "0" );
2150 switch( query
->i_proto
)
2152 case HTTPD_PROTO_HTTP
:
2153 answer
->i_version
= 1;
2154 httpd_MsgAdd( answer
, "Allow",
2155 "GET,HEAD,POST,OPTIONS" );
2158 case HTTPD_PROTO_RTSP
:
2161 answer
->i_version
= 0;
2163 p
= httpd_MsgGet( query
, "Cseq" );
2165 httpd_MsgAdd( answer
, "Cseq", "%s", p
);
2166 p
= httpd_MsgGet( query
, "Timestamp" );
2168 httpd_MsgAdd( answer
, "Timestamp", "%s", p
);
2170 p
= httpd_MsgGet( query
, "Require" );
2173 answer
->i_status
= 551;
2174 httpd_MsgAdd( query
, "Unsupported", "%s", p
);
2177 httpd_MsgAdd( answer
, "Public", "DESCRIBE,SETUP,"
2178 "TEARDOWN,PLAY,PAUSE,GET_PARAMETER" );
2183 cl
->i_buffer
= -1; /* Force the creation of the answer in
2184 * httpd_ClientSend */
2185 cl
->i_state
= HTTPD_CLIENT_SENDING
;
2187 else if( i_msg
== HTTPD_MSG_NONE
)
2189 if( query
->i_proto
== HTTPD_PROTO_NONE
)
2192 cl
->i_state
= HTTPD_CLIENT_DEAD
;
2199 answer
->i_proto
= query
->i_proto
;
2200 answer
->i_type
= HTTPD_MSG_ANSWER
;
2201 answer
->i_version
= 0;
2202 answer
->i_status
= 501;
2204 answer
->i_body
= httpd_HtmlError (&p
, 501, NULL
);
2205 answer
->p_body
= (uint8_t *)p
;
2206 httpd_MsgAdd( answer
, "Content-Length", "%d", answer
->i_body
);
2208 cl
->i_buffer
= -1; /* Force the creation of the answer in httpd_ClientSend */
2209 cl
->i_state
= HTTPD_CLIENT_SENDING
;
2214 bool b_auth_failed
= false;
2215 bool b_hosts_failed
= false;
2217 /* Search the url and trigger callbacks */
2218 for(int i
= 0; i
< host
->i_url
; i
++ )
2220 httpd_url_t
*url
= host
->url
[i
];
2222 if( !strcmp( url
->psz_url
, query
->psz_url
) )
2224 if( url
->catch[i_msg
].cb
)
2226 if( answer
&& ( url
->p_acl
!= NULL
) )
2228 char ip
[NI_MAXNUMERICHOST
];
2230 if( ( httpd_ClientIP( cl
, ip
) == NULL
)
2231 || ACL_Check( url
->p_acl
, ip
) )
2233 b_hosts_failed
= true;
2238 if( answer
&& ( *url
->psz_user
|| *url
->psz_password
) )
2240 /* create the headers */
2241 const char *b64
= httpd_MsgGet( query
, "Authorization" ); /* BASIC id */
2242 char *user
= NULL
, *pass
= NULL
;
2245 && !strncasecmp( b64
, "BASIC", 5 ) )
2248 while( *b64
== ' ' )
2251 user
= vlc_b64_decode( b64
);
2254 pass
= strchr (user
, ':');
2260 if ((user
== NULL
) || (pass
== NULL
)
2261 || strcmp (user
, url
->psz_user
)
2262 || strcmp (pass
, url
->psz_password
))
2264 httpd_MsgAdd( answer
,
2266 "Basic realm=\"VLC stream\"" );
2267 /* We fail for all url */
2268 b_auth_failed
= true;
2276 if( !url
->catch[i_msg
].cb( url
->catch[i_msg
].p_sys
, cl
, answer
, query
) )
2278 if( answer
->i_proto
== HTTPD_PROTO_NONE
)
2280 /* Raw answer from a CGI */
2281 cl
->i_buffer
= cl
->i_buffer_size
;
2286 /* only one url can answer */
2288 if( cl
->url
== NULL
)
2301 answer
->i_proto
= query
->i_proto
;
2302 answer
->i_type
= HTTPD_MSG_ANSWER
;
2303 answer
->i_version
= 0;
2305 if( b_hosts_failed
)
2307 answer
->i_status
= 403;
2309 else if( b_auth_failed
)
2311 answer
->i_status
= 401;
2315 /* no url registered */
2316 answer
->i_status
= 404;
2319 answer
->i_body
= httpd_HtmlError (&p
,
2322 answer
->p_body
= (uint8_t *)p
;
2324 cl
->i_buffer
= -1; /* Force the creation of the answer in httpd_ClientSend */
2325 httpd_MsgAdd( answer
, "Content-Length", "%d", answer
->i_body
);
2326 httpd_MsgAdd( answer
, "Content-Type", "%s", "text/html" );
2329 cl
->i_state
= HTTPD_CLIENT_SENDING
;
2332 else if( cl
->i_state
== HTTPD_CLIENT_SEND_DONE
)
2334 if( cl
->i_mode
== HTTPD_CLIENT_FILE
|| cl
->answer
.i_body_offset
== 0 )
2336 const char *psz_connection
= httpd_MsgGet( &cl
->answer
, "Connection" );
2337 const char *psz_query
= httpd_MsgGet( &cl
->query
, "Connection" );
2338 bool b_connection
= false;
2339 bool b_keepalive
= false;
2340 bool b_query
= false;
2343 if( psz_connection
)
2345 b_connection
= ( strcasecmp( psz_connection
, "Close" ) == 0 );
2346 b_keepalive
= ( strcasecmp( psz_connection
, "Keep-Alive" ) == 0 );
2351 b_query
= ( strcasecmp( psz_query
, "Close" ) == 0 );
2354 if( ( ( cl
->query
.i_proto
== HTTPD_PROTO_HTTP
) &&
2355 ( ( cl
->query
.i_version
== 0 && b_keepalive
) ||
2356 ( cl
->query
.i_version
== 1 && !b_connection
) ) ) ||
2357 ( ( cl
->query
.i_proto
== HTTPD_PROTO_RTSP
) &&
2358 !b_query
&& !b_connection
) )
2360 httpd_MsgClean( &cl
->query
);
2361 httpd_MsgInit( &cl
->query
);
2364 cl
->i_buffer_size
= 1000;
2365 free( cl
->p_buffer
);
2366 cl
->p_buffer
= xmalloc( cl
->i_buffer_size
);
2367 cl
->i_state
= HTTPD_CLIENT_RECEIVING
;
2371 cl
->i_state
= HTTPD_CLIENT_DEAD
;
2373 httpd_MsgClean( &cl
->answer
);
2375 else if( cl
->b_read_waiting
)
2377 /* we have a message waiting for us to read it */
2378 httpd_MsgClean( &cl
->answer
);
2379 httpd_MsgClean( &cl
->query
);
2382 cl
->i_buffer_size
= 1000;
2383 free( cl
->p_buffer
);
2384 cl
->p_buffer
= xmalloc( cl
->i_buffer_size
);
2385 cl
->i_state
= HTTPD_CLIENT_RECEIVING
;
2386 cl
->b_read_waiting
= false;
2390 int64_t i_offset
= cl
->answer
.i_body_offset
;
2391 httpd_MsgClean( &cl
->answer
);
2393 cl
->answer
.i_body_offset
= i_offset
;
2394 free( cl
->p_buffer
);
2395 cl
->p_buffer
= NULL
;
2397 cl
->i_buffer_size
= 0;
2399 cl
->i_state
= HTTPD_CLIENT_WAITING
;
2402 else if( cl
->i_state
== HTTPD_CLIENT_WAITING
)
2404 int64_t i_offset
= cl
->answer
.i_body_offset
;
2405 int i_msg
= cl
->query
.i_type
;
2407 httpd_MsgInit( &cl
->answer
);
2408 cl
->answer
.i_body_offset
= i_offset
;
2410 cl
->url
->catch[i_msg
].cb( cl
->url
->catch[i_msg
].p_sys
, cl
,
2411 &cl
->answer
, &cl
->query
);
2412 if( cl
->answer
.i_type
!= HTTPD_MSG_NONE
)
2414 /* we have new data, so re-enter send mode */
2416 cl
->p_buffer
= cl
->answer
.p_body
;
2417 cl
->i_buffer_size
= cl
->answer
.i_body
;
2418 cl
->answer
.p_body
= NULL
;
2419 cl
->answer
.i_body
= 0;
2420 cl
->i_state
= HTTPD_CLIENT_SENDING
;
2424 /* Special for BIDIR mode we also check reading */
2425 if( cl
->i_mode
== HTTPD_CLIENT_BIDIR
&&
2426 cl
->i_state
== HTTPD_CLIENT_SENDING
)
2428 pufd
->events
|= POLLIN
;
2431 if (pufd
->events
!= 0)
2436 vlc_mutex_unlock( &host
->lock
);
2439 ufd
[nfd
].events
= POLLIN
;
2440 ufd
[nfd
].revents
= 0;
2443 /* we will wait 20ms (not too big) if HTTPD_CLIENT_WAITING */
2444 switch( poll( ufd
, nfd
, b_low_delay
? 20 : -1) )
2449 /* Kernel on low memory or a bug: pace */
2450 msg_Err( host
, "polling error: %m" );
2457 if( ufd
[nfd
- 1].revents
)
2460 /* Handle client sockets */
2461 vlc_mutex_lock( &host
->lock
);
2464 for( int i_client
= 0; i_client
< host
->i_client
; i_client
++ )
2466 httpd_client_t
*cl
= host
->client
[i_client
];
2467 const struct pollfd
*pufd
= &ufd
[nfd
];
2469 assert( pufd
< &ufd
[sizeof(ufd
) / sizeof(ufd
[0])] );
2471 if( cl
->fd
!= pufd
->fd
)
2472 continue; // we were not waiting for this client
2474 if( pufd
->revents
== 0 )
2475 continue; // no event received
2477 cl
->i_activity_date
= now
;
2479 if( cl
->i_state
== HTTPD_CLIENT_RECEIVING
)
2481 httpd_ClientRecv( cl
);
2483 else if( cl
->i_state
== HTTPD_CLIENT_SENDING
)
2485 httpd_ClientSend( cl
);
2487 else if( cl
->i_state
== HTTPD_CLIENT_TLS_HS_IN
)
2489 httpd_ClientTlsHsIn( cl
);
2491 else if( cl
->i_state
== HTTPD_CLIENT_TLS_HS_OUT
)
2493 httpd_ClientTlsHsOut( cl
);
2496 if( cl
->i_mode
== HTTPD_CLIENT_BIDIR
&&
2497 cl
->i_state
== HTTPD_CLIENT_SENDING
&&
2498 (pufd
->revents
& POLLIN
) )
2500 cl
->b_read_waiting
= true;
2503 vlc_mutex_unlock( &host
->lock
);
2505 /* Handle server sockets (accept new connections) */
2506 for( nfd
= 0; nfd
< host
->nfd
; nfd
++ )
2510 int fd
= ufd
[nfd
].fd
;
2512 assert (fd
== host
->fds
[nfd
]);
2514 if( ufd
[nfd
].revents
== 0 )
2518 fd
= vlc_accept (fd
, NULL
, NULL
, true);
2521 setsockopt (fd
, SOL_SOCKET
, SO_REUSEADDR
,
2522 &(int){ 1 }, sizeof(int));
2526 switch( tls_ServerSessionHandshake( p_tls
, fd
) )
2529 msg_Err( host
, "Rejecting TLS connection" );
2535 case 1: /* missing input - most likely */
2536 i_state
= HTTPD_CLIENT_TLS_HS_IN
;
2539 case 2: /* missing output */
2540 i_state
= HTTPD_CLIENT_TLS_HS_OUT
;
2544 if( (p_tls
== NULL
) != (host
->p_tls
== NULL
) )
2545 break; // wasted TLS session, cannot accept() anymore
2548 stats_UpdateInteger( host
, p_total_counter
, 1, NULL
);
2549 stats_UpdateInteger( host
, p_active_counter
, 1, NULL
);
2550 cl
= httpd_ClientNew( fd
, p_tls
, now
);
2552 vlc_mutex_lock( &host
->lock
);
2553 TAB_APPEND( host
->i_client
, host
->client
, cl
);
2554 vlc_mutex_unlock( &host
->lock
);
2556 cl
->i_state
= i_state
; // override state for TLS
2558 if (host
->p_tls
!= NULL
)
2559 break; // cannot accept further without new TLS session
2565 tls_ServerSessionClose( p_tls
);
2566 if( p_total_counter
)
2567 stats_CounterClean( p_total_counter
);
2568 if( p_active_counter
)
2569 stats_CounterClean( p_active_counter
);