screen: disable compilation for non-Mac Darwin flavors
[vlc/vlc-acra.git] / src / network / httpd.c
blobf76c47ce0594a65225ec419d849f66f82a9e97c5
1 /*****************************************************************************
2 * httpd.c
3 *****************************************************************************
4 * Copyright (C) 2004-2006 VLC authors and VideoLAN
5 * Copyright © 2004-2007 Rémi Denis-Courmont
6 * $Id$
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 it
12 * under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this program; if not, write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <vlc_common.h>
31 #include <vlc_httpd.h>
33 #include <assert.h>
35 #include <vlc_network.h>
36 #include <vlc_tls.h>
37 #include <vlc_strings.h>
38 #include <vlc_rand.h>
39 #include <vlc_charset.h>
40 #include <vlc_url.h>
41 #include <vlc_mime.h>
42 #include "../libvlc.h"
44 #include <string.h>
45 #include <errno.h>
47 #ifdef HAVE_UNISTD_H
48 # include <unistd.h>
49 #endif
51 #ifdef HAVE_POLL
52 # include <poll.h>
53 #endif
55 #if defined( WIN32 )
56 # include <winsock2.h>
57 #else
58 # include <sys/socket.h>
59 #endif
61 #if defined( WIN32 )
62 /* We need HUGE buffer otherwise TCP throughput is very limited */
63 #define HTTPD_CL_BUFSIZE 1000000
64 #else
65 #define HTTPD_CL_BUFSIZE 10000
66 #endif
68 static void httpd_ClientClean( httpd_client_t *cl );
70 /* each host run in his own thread */
71 struct httpd_host_t
73 VLC_COMMON_MEMBERS
75 /* ref count */
76 unsigned i_ref;
78 /* address/port and socket for listening at connections */
79 int *fds;
80 unsigned nfd;
81 unsigned port;
83 vlc_thread_t thread;
84 vlc_mutex_t lock;
85 vlc_cond_t wait;
87 /* all registered url (becarefull that 2 httpd_url_t could point at the same url)
88 * This will slow down the url research but make my live easier
89 * All url will have their cb trigger, but only the first one can answer
90 * */
91 int i_url;
92 httpd_url_t **url;
94 int i_client;
95 httpd_client_t **client;
97 /* TLS data */
98 vlc_tls_creds_t *p_tls;
102 struct httpd_url_t
104 httpd_host_t *host;
106 vlc_mutex_t lock;
108 char *psz_url;
109 char *psz_user;
110 char *psz_password;
112 struct
114 httpd_callback_t cb;
115 httpd_callback_sys_t *p_sys;
116 } catch[HTTPD_MSG_MAX];
119 /* status */
120 enum
122 HTTPD_CLIENT_RECEIVING,
123 HTTPD_CLIENT_RECEIVE_DONE,
125 HTTPD_CLIENT_SENDING,
126 HTTPD_CLIENT_SEND_DONE,
128 HTTPD_CLIENT_WAITING,
130 HTTPD_CLIENT_DEAD,
132 HTTPD_CLIENT_TLS_HS_IN,
133 HTTPD_CLIENT_TLS_HS_OUT
136 /* mode */
137 enum
139 HTTPD_CLIENT_FILE, /* default */
140 HTTPD_CLIENT_STREAM, /* regulary get data from cb */
143 struct httpd_client_t
145 httpd_url_t *url;
147 int i_ref;
149 int fd;
151 bool b_stream_mode;
152 uint8_t i_state;
154 mtime_t i_activity_date;
155 mtime_t i_activity_timeout;
157 /* buffer for reading header */
158 int i_buffer_size;
159 int i_buffer;
160 uint8_t *p_buffer;
162 /* */
163 httpd_message_t query; /* client -> httpd */
164 httpd_message_t answer; /* httpd -> client */
166 /* TLS data */
167 vlc_tls_t *p_tls;
171 /*****************************************************************************
172 * Various functions
173 *****************************************************************************/
174 typedef struct
176 unsigned i_code;
177 const char psz_reason[36];
178 } http_status_info;
180 static const http_status_info http_reason[] =
182 /*{ 100, "Continue" },
183 { 101, "Switching Protocols" },*/
184 { 200, "OK" },
185 /*{ 201, "Created" },
186 { 202, "Accepted" },
187 { 203, "Non-authoritative information" },
188 { 204, "No content" },
189 { 205, "Reset content" },
190 { 206, "Partial content" },
191 { 250, "Low on storage space" },
192 { 300, "Multiple choices" },*/
193 { 301, "Moved permanently" },
194 /*{ 302, "Moved temporarily" },
195 { 303, "See other" },
196 { 304, "Not modified" },
197 { 305, "Use proxy" },
198 { 307, "Temporary redirect" },
199 { 400, "Bad request" },*/
200 { 401, "Unauthorized" },
201 /*{ 402, "Payment Required" },*/
202 { 403, "Forbidden" },
203 { 404, "Not found" },
204 { 405, "Method not allowed" },
205 /*{ 406, "Not acceptable" },
206 { 407, "Proxy authentication required" },
207 { 408, "Request time-out" },
208 { 409, "Conflict" },
209 { 410, "Gone" },
210 { 411, "Length required" },
211 { 412, "Precondition failed" },
212 { 413, "Request entity too large" },
213 { 414, "Request-URI too large" },
214 { 415, "Unsupported media Type" },
215 { 416, "Requested range not satisfiable" },
216 { 417, "Expectation failed" },
217 { 451, "Parameter not understood" },
218 { 452, "Conference not found" },
219 { 453, "Not enough bandwidth" },*/
220 { 454, "Session not found" },
221 { 455, "Method not valid in this State" },
222 { 456, "Header field not valid for resource" },
223 { 457, "Invalid range" },
224 /*{ 458, "Read-only parameter" },*/
225 { 459, "Aggregate operation not allowed" },
226 { 460, "Non-aggregate operation not allowed" },
227 { 461, "Unsupported transport" },
228 /*{ 462, "Destination unreachable" },*/
229 { 500, "Internal server error" },
230 { 501, "Not implemented" },
231 /*{ 502, "Bad gateway" },*/
232 { 503, "Service unavailable" },
233 /*{ 504, "Gateway time-out" },*/
234 { 505, "Protocol version not supported" },
235 { 551, "Option not supported" },
236 { 999, "" }
239 static const char psz_fallback_reason[5][16] =
240 { "Continue", "OK", "Found", "Client error", "Server error" };
242 static const char *httpd_ReasonFromCode( unsigned i_code )
244 const http_status_info *p;
246 assert( ( i_code >= 100 ) && ( i_code <= 599 ) );
248 for (p = http_reason; i_code > p->i_code; p++);
250 if( p->i_code == i_code )
251 return p->psz_reason;
253 return psz_fallback_reason[(i_code / 100) - 1];
257 static size_t httpd_HtmlError (char **body, int code, const char *url)
259 const char *errname = httpd_ReasonFromCode (code);
260 assert (errname != NULL);
262 int res = asprintf (body,
263 "<?xml version=\"1.0\" encoding=\"ascii\" ?>\n"
264 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\""
265 " \"http://www.w3.org/TR/xhtml10/DTD/xhtml10strict.dtd\">\n"
266 "<html lang=\"en\">\n"
267 "<head>\n"
268 "<title>%s</title>\n"
269 "</head>\n"
270 "<body>\n"
271 "<h1>%d %s%s%s%s</h1>\n"
272 "<hr />\n"
273 "<a href=\"http://www.videolan.org\">VideoLAN</a>\n"
274 "</body>\n"
275 "</html>\n", errname, code, errname,
276 (url ? " (" : ""), (url ? url : ""), (url ? ")" : ""));
278 if (res == -1)
280 *body = NULL;
281 return 0;
284 return (size_t)res;
288 /*****************************************************************************
289 * High Level Functions: httpd_file_t
290 *****************************************************************************/
291 struct httpd_file_t
293 httpd_url_t *url;
295 char *psz_url;
296 char *psz_mime;
298 httpd_file_callback_t pf_fill;
299 httpd_file_sys_t *p_sys;
303 static int
304 httpd_FileCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *cl,
305 httpd_message_t *answer, const httpd_message_t *query )
307 httpd_file_t *file = (httpd_file_t*)p_sys;
308 uint8_t **pp_body, *p_body;
309 const char *psz_connection;
310 int *pi_body, i_body;
312 if( answer == NULL || query == NULL )
314 return VLC_SUCCESS;
316 answer->i_proto = HTTPD_PROTO_HTTP;
317 answer->i_version= 1;
318 answer->i_type = HTTPD_MSG_ANSWER;
320 answer->i_status = 200;
322 httpd_MsgAdd( answer, "Content-type", "%s", file->psz_mime );
323 httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" );
325 if( query->i_type != HTTPD_MSG_HEAD )
327 pp_body = &answer->p_body;
328 pi_body = &answer->i_body;
330 else
332 /* The file still needs to be executed. */
333 p_body = NULL;
334 i_body = 0;
335 pp_body = &p_body;
336 pi_body = &i_body;
339 if( query->i_type == HTTPD_MSG_POST )
341 /* msg_Warn not supported */
344 uint8_t *psz_args = query->psz_args;
345 file->pf_fill( file->p_sys, file, psz_args, pp_body, pi_body );
347 if( query->i_type == HTTPD_MSG_HEAD && p_body != NULL )
349 free( p_body );
352 /* We respect client request */
353 psz_connection = httpd_MsgGet( &cl->query, "Connection" );
354 if( psz_connection != NULL )
356 httpd_MsgAdd( answer, "Connection", "%s", psz_connection );
359 httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
361 return VLC_SUCCESS;
364 httpd_file_t *httpd_FileNew( httpd_host_t *host,
365 const char *psz_url, const char *psz_mime,
366 const char *psz_user, const char *psz_password,
367 httpd_file_callback_t pf_fill,
368 httpd_file_sys_t *p_sys )
370 httpd_file_t *file = xmalloc( sizeof( httpd_file_t ) );
372 file->url = httpd_UrlNew( host, psz_url, psz_user, psz_password );
373 if( file->url == NULL )
375 free( file );
376 return NULL;
379 file->psz_url = strdup( psz_url );
380 if( psz_mime && *psz_mime )
382 file->psz_mime = strdup( psz_mime );
384 else
386 file->psz_mime = strdup( vlc_mime_Ext2Mime( psz_url ) );
389 file->pf_fill = pf_fill;
390 file->p_sys = p_sys;
392 httpd_UrlCatch( file->url, HTTPD_MSG_HEAD, httpd_FileCallBack,
393 (httpd_callback_sys_t*)file );
394 httpd_UrlCatch( file->url, HTTPD_MSG_GET, httpd_FileCallBack,
395 (httpd_callback_sys_t*)file );
396 httpd_UrlCatch( file->url, HTTPD_MSG_POST, httpd_FileCallBack,
397 (httpd_callback_sys_t*)file );
399 return file;
402 httpd_file_sys_t *httpd_FileDelete( httpd_file_t *file )
404 httpd_file_sys_t *p_sys = file->p_sys;
406 httpd_UrlDelete( file->url );
408 free( file->psz_url );
409 free( file->psz_mime );
411 free( file );
413 return p_sys;
416 /*****************************************************************************
417 * High Level Functions: httpd_handler_t (for CGIs)
418 *****************************************************************************/
419 struct httpd_handler_t
421 httpd_url_t *url;
423 httpd_handler_callback_t pf_fill;
424 httpd_handler_sys_t *p_sys;
428 static int
429 httpd_HandlerCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *cl,
430 httpd_message_t *answer, const httpd_message_t *query )
432 httpd_handler_t *handler = (httpd_handler_t*)p_sys;
433 char psz_remote_addr[NI_MAXNUMERICHOST];
435 if( answer == NULL || query == NULL )
437 return VLC_SUCCESS;
439 answer->i_proto = HTTPD_PROTO_NONE;
440 answer->i_type = HTTPD_MSG_ANSWER;
442 /* We do it ourselves, thanks */
443 answer->i_status = 0;
445 if( httpd_ClientIP( cl, psz_remote_addr, NULL ) == NULL )
446 *psz_remote_addr = '\0';
448 uint8_t *psz_args = query->psz_args;
449 handler->pf_fill( handler->p_sys, handler, query->psz_url, psz_args,
450 query->i_type, query->p_body, query->i_body,
451 psz_remote_addr, NULL,
452 &answer->p_body, &answer->i_body );
454 if( query->i_type == HTTPD_MSG_HEAD )
456 char *p = (char *)answer->p_body;
458 /* Looks for end of header (i.e. one empty line) */
459 while ( (p = strchr( p, '\r' )) != NULL )
461 if( p[1] && p[1] == '\n' && p[2] && p[2] == '\r'
462 && p[3] && p[3] == '\n' )
464 break;
468 if( p != NULL )
470 p[4] = '\0';
471 answer->i_body = strlen((char*)answer->p_body) + 1;
472 answer->p_body = xrealloc( answer->p_body, answer->i_body );
476 if( strncmp( (char *)answer->p_body, "HTTP/1.", 7 ) )
478 int i_status, i_headers;
479 char *psz_headers, *psz_new;
480 const char *psz_status;
482 if( !strncmp( (char *)answer->p_body, "Status: ", 8 ) )
484 /* Apache-style */
485 i_status = strtol( (char *)&answer->p_body[8], &psz_headers, 0 );
486 if( *psz_headers == '\r' || *psz_headers == '\n' ) psz_headers++;
487 if( *psz_headers == '\n' ) psz_headers++;
488 i_headers = answer->i_body - (psz_headers - (char *)answer->p_body);
490 else
492 i_status = 200;
493 psz_headers = (char *)answer->p_body;
494 i_headers = answer->i_body;
497 psz_status = httpd_ReasonFromCode( i_status );
498 answer->i_body = sizeof("HTTP/1.0 xxx \r\n")
499 + strlen(psz_status) + i_headers - 1;
500 psz_new = (char *)xmalloc( answer->i_body + 1);
501 sprintf( psz_new, "HTTP/1.0 %03d %s\r\n", i_status, psz_status );
502 memcpy( &psz_new[strlen(psz_new)], psz_headers, i_headers );
503 free( answer->p_body );
504 answer->p_body = (uint8_t *)psz_new;
507 return VLC_SUCCESS;
510 httpd_handler_t *httpd_HandlerNew( httpd_host_t *host, const char *psz_url,
511 const char *psz_user,
512 const char *psz_password,
513 httpd_handler_callback_t pf_fill,
514 httpd_handler_sys_t *p_sys )
516 httpd_handler_t *handler = xmalloc( sizeof( httpd_handler_t ) );
518 handler->url = httpd_UrlNew( host, psz_url, psz_user, psz_password );
519 if( handler->url == NULL )
521 free( handler );
522 return NULL;
525 handler->pf_fill = pf_fill;
526 handler->p_sys = p_sys;
528 httpd_UrlCatch( handler->url, HTTPD_MSG_HEAD, httpd_HandlerCallBack,
529 (httpd_callback_sys_t*)handler );
530 httpd_UrlCatch( handler->url, HTTPD_MSG_GET, httpd_HandlerCallBack,
531 (httpd_callback_sys_t*)handler );
532 httpd_UrlCatch( handler->url, HTTPD_MSG_POST, httpd_HandlerCallBack,
533 (httpd_callback_sys_t*)handler );
535 return handler;
538 httpd_handler_sys_t *httpd_HandlerDelete( httpd_handler_t *handler )
540 httpd_handler_sys_t *p_sys = handler->p_sys;
541 httpd_UrlDelete( handler->url );
542 free( handler );
543 return p_sys;
546 /*****************************************************************************
547 * High Level Functions: httpd_redirect_t
548 *****************************************************************************/
549 struct httpd_redirect_t
551 httpd_url_t *url;
552 char *psz_dst;
555 static int httpd_RedirectCallBack( httpd_callback_sys_t *p_sys,
556 httpd_client_t *cl, httpd_message_t *answer,
557 const httpd_message_t *query )
559 httpd_redirect_t *rdir = (httpd_redirect_t*)p_sys;
560 char *p_body;
561 (void)cl;
563 if( answer == NULL || query == NULL )
565 return VLC_SUCCESS;
567 answer->i_proto = HTTPD_PROTO_HTTP;
568 answer->i_version= 1;
569 answer->i_type = HTTPD_MSG_ANSWER;
570 answer->i_status = 301;
572 answer->i_body = httpd_HtmlError (&p_body, 301, rdir->psz_dst);
573 answer->p_body = (unsigned char *)p_body;
575 /* XXX check if it's ok or we need to set an absolute url */
576 httpd_MsgAdd( answer, "Location", "%s", rdir->psz_dst );
578 httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
580 return VLC_SUCCESS;
583 httpd_redirect_t *httpd_RedirectNew( httpd_host_t *host, const char *psz_url_dst,
584 const char *psz_url_src )
586 httpd_redirect_t *rdir = xmalloc( sizeof( httpd_redirect_t ) );
588 rdir->url = httpd_UrlNew( host, psz_url_src, NULL, NULL );
589 if( rdir->url == NULL )
591 free( rdir );
592 return NULL;
594 rdir->psz_dst = strdup( psz_url_dst );
596 /* Redirect apply for all HTTP request and RTSP DESCRIBE resquest */
597 httpd_UrlCatch( rdir->url, HTTPD_MSG_HEAD, httpd_RedirectCallBack,
598 (httpd_callback_sys_t*)rdir );
599 httpd_UrlCatch( rdir->url, HTTPD_MSG_GET, httpd_RedirectCallBack,
600 (httpd_callback_sys_t*)rdir );
601 httpd_UrlCatch( rdir->url, HTTPD_MSG_POST, httpd_RedirectCallBack,
602 (httpd_callback_sys_t*)rdir );
603 httpd_UrlCatch( rdir->url, HTTPD_MSG_DESCRIBE, httpd_RedirectCallBack,
604 (httpd_callback_sys_t*)rdir );
606 return rdir;
608 void httpd_RedirectDelete( httpd_redirect_t *rdir )
610 httpd_UrlDelete( rdir->url );
611 free( rdir->psz_dst );
612 free( rdir );
615 /*****************************************************************************
616 * High Level Funtions: httpd_stream_t
617 *****************************************************************************/
618 struct httpd_stream_t
620 vlc_mutex_t lock;
621 httpd_url_t *url;
623 char *psz_mime;
625 /* Header to send as first packet */
626 uint8_t *p_header;
627 int i_header;
629 /* circular buffer */
630 int i_buffer_size; /* buffer size, can't be reallocated smaller */
631 uint8_t *p_buffer; /* buffer */
632 int64_t i_buffer_pos; /* absolute position from begining */
633 int64_t i_buffer_last_pos; /* a new connection will start with that */
636 static int httpd_StreamCallBack( httpd_callback_sys_t *p_sys,
637 httpd_client_t *cl, httpd_message_t *answer,
638 const httpd_message_t *query )
640 httpd_stream_t *stream = (httpd_stream_t*)p_sys;
642 if( answer == NULL || query == NULL || cl == NULL )
644 return VLC_SUCCESS;
647 if( answer->i_body_offset > 0 )
649 int64_t i_write;
650 int i_pos;
652 #if 0
653 fprintf( stderr, "httpd_StreamCallBack i_body_offset=%lld\n",
654 answer->i_body_offset );
655 #endif
657 if( answer->i_body_offset >= stream->i_buffer_pos )
659 /* fprintf( stderr, "httpd_StreamCallBack: no data\n" ); */
660 return VLC_EGENERIC; /* wait, no data available */
662 if( answer->i_body_offset + stream->i_buffer_size <
663 stream->i_buffer_pos )
665 /* this client isn't fast enough */
666 #if 0
667 fprintf( stderr, "fixing i_body_offset (old=%lld new=%lld)\n",
668 answer->i_body_offset, stream->i_buffer_last_pos );
669 #endif
670 answer->i_body_offset = stream->i_buffer_last_pos;
673 i_pos = answer->i_body_offset % stream->i_buffer_size;
674 i_write = stream->i_buffer_pos - answer->i_body_offset;
675 if( i_write > HTTPD_CL_BUFSIZE )
677 i_write = HTTPD_CL_BUFSIZE;
679 else if( i_write <= 0 )
681 return VLC_EGENERIC; /* wait, no data available */
684 /* Don't go past the end of the circular buffer */
685 i_write = __MIN( i_write, stream->i_buffer_size - i_pos );
687 /* using HTTPD_MSG_ANSWER -> data available */
688 answer->i_proto = HTTPD_PROTO_HTTP;
689 answer->i_version= 0;
690 answer->i_type = HTTPD_MSG_ANSWER;
692 answer->i_body = i_write;
693 answer->p_body = xmalloc( i_write );
694 memcpy( answer->p_body, &stream->p_buffer[i_pos], i_write );
696 answer->i_body_offset += i_write;
698 return VLC_SUCCESS;
700 else
702 answer->i_proto = HTTPD_PROTO_HTTP;
703 answer->i_version= 0;
704 answer->i_type = HTTPD_MSG_ANSWER;
706 answer->i_status = 200;
708 if( query->i_type != HTTPD_MSG_HEAD )
710 cl->b_stream_mode = true;
711 vlc_mutex_lock( &stream->lock );
712 /* Send the header */
713 if( stream->i_header > 0 )
715 answer->i_body = stream->i_header;
716 answer->p_body = xmalloc( stream->i_header );
717 memcpy( answer->p_body, stream->p_header, stream->i_header );
719 answer->i_body_offset = stream->i_buffer_last_pos;
720 vlc_mutex_unlock( &stream->lock );
722 else
724 httpd_MsgAdd( answer, "Content-Length", "%d", 0 );
725 answer->i_body_offset = 0;
728 if( !strcmp( stream->psz_mime, "video/x-ms-asf-stream" ) )
730 bool b_xplaystream = false;
731 int i;
733 httpd_MsgAdd( answer, "Content-type", "%s",
734 "application/octet-stream" );
735 httpd_MsgAdd( answer, "Server", "Cougar 4.1.0.3921" );
736 httpd_MsgAdd( answer, "Pragma", "no-cache" );
737 httpd_MsgAdd( answer, "Pragma", "client-id=%lu",
738 vlc_mrand48()&0x7fff );
739 httpd_MsgAdd( answer, "Pragma", "features=\"broadcast\"" );
741 /* Check if there is a xPlayStrm=1 */
742 for( i = 0; i < query->i_name; i++ )
744 if( !strcasecmp( query->name[i], "Pragma" ) &&
745 strstr( query->value[i], "xPlayStrm=1" ) )
747 b_xplaystream = true;
751 if( !b_xplaystream )
753 answer->i_body_offset = 0;
756 else
758 httpd_MsgAdd( answer, "Content-type", "%s", stream->psz_mime );
760 httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" );
761 return VLC_SUCCESS;
765 httpd_stream_t *httpd_StreamNew( httpd_host_t *host,
766 const char *psz_url, const char *psz_mime,
767 const char *psz_user, const char *psz_password )
769 httpd_stream_t *stream = xmalloc( sizeof( httpd_stream_t ) );
771 stream->url = httpd_UrlNew( host, psz_url, psz_user, psz_password );
772 if( stream->url == NULL )
774 free( stream );
775 return NULL;
777 vlc_mutex_init( &stream->lock );
778 if( psz_mime && *psz_mime )
780 stream->psz_mime = strdup( psz_mime );
782 else
784 stream->psz_mime = strdup( vlc_mime_Ext2Mime( psz_url ) );
786 stream->i_header = 0;
787 stream->p_header = NULL;
788 stream->i_buffer_size = 5000000; /* 5 Mo per stream */
789 stream->p_buffer = xmalloc( stream->i_buffer_size );
790 /* We set to 1 to make life simpler
791 * (this way i_body_offset can never be 0) */
792 stream->i_buffer_pos = 1;
793 stream->i_buffer_last_pos = 1;
795 httpd_UrlCatch( stream->url, HTTPD_MSG_HEAD, httpd_StreamCallBack,
796 (httpd_callback_sys_t*)stream );
797 httpd_UrlCatch( stream->url, HTTPD_MSG_GET, httpd_StreamCallBack,
798 (httpd_callback_sys_t*)stream );
799 httpd_UrlCatch( stream->url, HTTPD_MSG_POST, httpd_StreamCallBack,
800 (httpd_callback_sys_t*)stream );
802 return stream;
805 int httpd_StreamHeader( httpd_stream_t *stream, uint8_t *p_data, int i_data )
807 vlc_mutex_lock( &stream->lock );
808 free( stream->p_header );
809 stream->p_header = NULL;
811 stream->i_header = i_data;
812 if( i_data > 0 )
814 stream->p_header = xmalloc( i_data );
815 memcpy( stream->p_header, p_data, i_data );
817 vlc_mutex_unlock( &stream->lock );
819 return VLC_SUCCESS;
822 int httpd_StreamSend( httpd_stream_t *stream, uint8_t *p_data, int i_data )
824 int i_count;
825 int i_pos;
827 if( i_data < 0 || p_data == NULL )
829 return VLC_SUCCESS;
831 vlc_mutex_lock( &stream->lock );
833 /* save this pointer (to be used by new connection) */
834 stream->i_buffer_last_pos = stream->i_buffer_pos;
836 i_pos = stream->i_buffer_pos % stream->i_buffer_size;
837 i_count = i_data;
838 while( i_count > 0)
840 int i_copy;
842 i_copy = __MIN( i_count, stream->i_buffer_size - i_pos );
844 /* Ok, we can't go past the end of our buffer */
845 memcpy( &stream->p_buffer[i_pos], p_data, i_copy );
847 i_pos = ( i_pos + i_copy ) % stream->i_buffer_size;
848 i_count -= i_copy;
849 p_data += i_copy;
852 stream->i_buffer_pos += i_data;
854 vlc_mutex_unlock( &stream->lock );
855 return VLC_SUCCESS;
858 void httpd_StreamDelete( httpd_stream_t *stream )
860 httpd_UrlDelete( stream->url );
861 vlc_mutex_destroy( &stream->lock );
862 free( stream->psz_mime );
863 free( stream->p_header );
864 free( stream->p_buffer );
865 free( stream );
868 /*****************************************************************************
869 * Low level
870 *****************************************************************************/
871 static void* httpd_HostThread( void * );
872 static httpd_host_t *httpd_HostCreate( vlc_object_t *, const char *,
873 const char *, vlc_tls_creds_t * );
875 /* create a new host */
876 httpd_host_t *vlc_http_HostNew( vlc_object_t *p_this )
878 return httpd_HostCreate( p_this, "http-host", "http-port", NULL );
881 httpd_host_t *vlc_https_HostNew( vlc_object_t *obj )
883 char *cert = var_InheritString( obj, "http-cert" );
884 if( cert == NULL )
886 msg_Err( obj, "HTTP/TLS certificate not specified!" );
887 return NULL;
890 char *key = var_InheritString( obj, "http-key" );
891 vlc_tls_creds_t *tls = vlc_tls_ServerCreate( obj, cert, key );
893 if( tls == NULL )
895 msg_Err( obj, "HTTP/TLS certificate error (%s and %s)",
896 cert, (key != NULL) ? key : cert );
897 free( key );
898 free( cert );
899 return NULL;
901 free( key );
902 free( cert );
904 char *ca = var_InheritString( obj, "http-ca" );
905 if( ca != NULL )
907 if( vlc_tls_ServerAddCA( tls, ca ) )
909 msg_Err( obj, "HTTP/TLS CA error (%s)", ca );
910 free( ca );
911 goto error;
913 free( ca );
916 char *crl = var_InheritString( obj, "http-crl" );
917 if( crl != NULL )
919 if( vlc_tls_ServerAddCRL( tls, crl ) )
921 msg_Err( obj, "TLS CRL error (%s)", crl );
922 free( crl );
923 goto error;
925 free( crl );
928 return httpd_HostCreate( obj, "http-host", "https-port", tls );
930 error:
931 vlc_tls_Delete( tls );
932 return NULL;
935 httpd_host_t *vlc_rtsp_HostNew( vlc_object_t *p_this )
937 return httpd_HostCreate( p_this, "rtsp-host", "rtsp-port", NULL );
940 static struct httpd
942 vlc_mutex_t mutex;
944 httpd_host_t **host;
945 int i_host;
946 } httpd = { VLC_STATIC_MUTEX, NULL, 0 };
948 static httpd_host_t *httpd_HostCreate( vlc_object_t *p_this,
949 const char *hostvar,
950 const char *portvar,
951 vlc_tls_creds_t *p_tls )
953 httpd_host_t *host;
954 char *hostname = var_InheritString( p_this, hostvar );
955 unsigned port = var_InheritInteger( p_this, portvar );
957 vlc_url_t url;
958 vlc_UrlParse( &url, hostname, 0 );
959 free( hostname );
960 if( url.i_port != 0 )
962 msg_Err( p_this, "Ignoring port %d (using %d)", url.i_port, port );
963 msg_Info( p_this, "Specify port %d separately with the "
964 "%s option instead.", url.i_port, portvar );
967 /* to be sure to avoid multiple creation */
968 vlc_mutex_lock( &httpd.mutex );
970 /* verify if it already exist */
971 for( int i = 0; i < httpd.i_host; i++ )
973 host = httpd.host[i];
975 /* cannot mix TLS and non-TLS hosts */
976 if( host->port != port
977 || (host->p_tls != NULL) != (p_tls != NULL) )
978 continue;
980 /* Increase existing matching host reference count.
981 * The reference count is written under both the global httpd and the
982 * host lock. It is read with either or both locks held. The global
983 * lock is always acquired first. */
984 vlc_mutex_lock( &host->lock );
985 host->i_ref++;
986 vlc_mutex_unlock( &host->lock );
988 vlc_mutex_unlock( &httpd.mutex );
989 vlc_UrlClean( &url );
990 vlc_tls_Delete( p_tls );
991 return host;
994 /* create the new host */
995 host = (httpd_host_t *)vlc_custom_create( p_this, sizeof (*host),
996 "http host" );
997 if (host == NULL)
998 goto error;
1000 vlc_mutex_init( &host->lock );
1001 vlc_cond_init( &host->wait );
1002 host->i_ref = 1;
1004 host->fds = net_ListenTCP( p_this, url.psz_host, port );
1005 if( host->fds == NULL )
1007 msg_Err( p_this, "cannot create socket(s) for HTTP host" );
1008 goto error;
1010 for (host->nfd = 0; host->fds[host->nfd] != -1; host->nfd++);
1012 if( vlc_object_waitpipe( VLC_OBJECT( host ) ) == -1 )
1014 msg_Err( host, "signaling pipe error: %m" );
1015 goto error;
1018 host->port = port;
1019 host->i_url = 0;
1020 host->url = NULL;
1021 host->i_client = 0;
1022 host->client = NULL;
1023 host->p_tls = p_tls;
1025 /* create the thread */
1026 if( vlc_clone( &host->thread, httpd_HostThread, host,
1027 VLC_THREAD_PRIORITY_LOW ) )
1029 msg_Err( p_this, "cannot spawn http host thread" );
1030 goto error;
1033 /* now add it to httpd */
1034 TAB_APPEND( httpd.i_host, httpd.host, host );
1035 vlc_mutex_unlock( &httpd.mutex );
1037 vlc_UrlClean( &url );
1039 return host;
1041 error:
1042 vlc_mutex_unlock( &httpd.mutex );
1044 if( host != NULL )
1046 net_ListenClose( host->fds );
1047 vlc_cond_destroy( &host->wait );
1048 vlc_mutex_destroy( &host->lock );
1049 vlc_object_release( host );
1052 vlc_UrlClean( &url );
1053 vlc_tls_Delete( p_tls );
1054 return NULL;
1057 /* delete a host */
1058 void httpd_HostDelete( httpd_host_t *host )
1060 int i;
1061 bool delete = false;
1063 vlc_mutex_lock( &httpd.mutex );
1065 vlc_mutex_lock( &host->lock );
1066 host->i_ref--;
1067 if( host->i_ref == 0 )
1068 delete = true;
1069 vlc_mutex_unlock( &host->lock );
1070 if( !delete )
1072 /* still used */
1073 vlc_mutex_unlock( &httpd.mutex );
1074 msg_Dbg( host, "httpd_HostDelete: host still in use" );
1075 return;
1077 TAB_REMOVE( httpd.i_host, httpd.host, host );
1079 vlc_cancel( host->thread );
1080 vlc_join( host->thread, NULL );
1082 msg_Dbg( host, "HTTP host removed" );
1084 for( i = 0; i < host->i_url; i++ )
1086 msg_Err( host, "url still registered: %s", host->url[i]->psz_url );
1088 for( i = 0; i < host->i_client; i++ )
1090 httpd_client_t *cl = host->client[i];
1091 msg_Warn( host, "client still connected" );
1092 httpd_ClientClean( cl );
1093 TAB_REMOVE( host->i_client, host->client, cl );
1094 free( cl );
1095 i--;
1096 /* TODO */
1099 vlc_tls_Delete( host->p_tls );
1100 net_ListenClose( host->fds );
1101 vlc_cond_destroy( &host->wait );
1102 vlc_mutex_destroy( &host->lock );
1103 vlc_object_release( host );
1104 vlc_mutex_unlock( &httpd.mutex );
1107 /* register a new url */
1108 httpd_url_t *httpd_UrlNew( httpd_host_t *host, const char *psz_url,
1109 const char *psz_user, const char *psz_password )
1111 httpd_url_t *url;
1113 assert( psz_url != NULL );
1115 vlc_mutex_lock( &host->lock );
1116 for( int i = 0; i < host->i_url; i++ )
1118 if( !strcmp( psz_url, host->url[i]->psz_url ) )
1120 msg_Warn( host,
1121 "cannot add '%s' (url already defined)", psz_url );
1122 vlc_mutex_unlock( &host->lock );
1123 return NULL;
1127 url = xmalloc( sizeof( httpd_url_t ) );
1128 url->host = host;
1130 vlc_mutex_init( &url->lock );
1131 url->psz_url = strdup( psz_url );
1132 url->psz_user = strdup( psz_user ? psz_user : "" );
1133 url->psz_password = strdup( psz_password ? psz_password : "" );
1134 for( int i = 0; i < HTTPD_MSG_MAX; i++ )
1136 url->catch[i].cb = NULL;
1137 url->catch[i].p_sys = NULL;
1140 TAB_APPEND( host->i_url, host->url, url );
1141 vlc_cond_signal( &host->wait );
1142 vlc_mutex_unlock( &host->lock );
1144 return url;
1147 /* register callback on a url */
1148 int httpd_UrlCatch( httpd_url_t *url, int i_msg, httpd_callback_t cb,
1149 httpd_callback_sys_t *p_sys )
1151 vlc_mutex_lock( &url->lock );
1152 url->catch[i_msg].cb = cb;
1153 url->catch[i_msg].p_sys= p_sys;
1154 vlc_mutex_unlock( &url->lock );
1156 return VLC_SUCCESS;
1159 /* delete a url */
1160 void httpd_UrlDelete( httpd_url_t *url )
1162 httpd_host_t *host = url->host;
1163 int i;
1165 vlc_mutex_lock( &host->lock );
1166 TAB_REMOVE( host->i_url, host->url, url );
1168 vlc_mutex_destroy( &url->lock );
1169 free( url->psz_url );
1170 free( url->psz_user );
1171 free( url->psz_password );
1173 for( i = 0; i < host->i_client; i++ )
1175 httpd_client_t *client = host->client[i];
1177 if( client->url == url )
1179 /* TODO complete it */
1180 msg_Warn( host, "force closing connections" );
1181 httpd_ClientClean( client );
1182 TAB_REMOVE( host->i_client, host->client, client );
1183 free( client );
1184 i--;
1187 free( url );
1188 vlc_mutex_unlock( &host->lock );
1191 static void httpd_MsgInit( httpd_message_t *msg )
1193 msg->cl = NULL;
1194 msg->i_type = HTTPD_MSG_NONE;
1195 msg->i_proto = HTTPD_PROTO_NONE;
1196 msg->i_version = -1; /* FIXME */
1198 msg->i_status = 0;
1200 msg->psz_url = NULL;
1201 msg->psz_args = NULL;
1203 msg->i_name = 0;
1204 msg->name = NULL;
1205 msg->i_value = 0;
1206 msg->value = NULL;
1208 msg->i_body_offset = 0;
1209 msg->i_body = 0;
1210 msg->p_body = NULL;
1213 static void httpd_MsgClean( httpd_message_t *msg )
1215 int i;
1217 free( msg->psz_url );
1218 free( msg->psz_args );
1219 for( i = 0; i < msg->i_name; i++ )
1221 free( msg->name[i] );
1222 free( msg->value[i] );
1224 free( msg->name );
1225 free( msg->value );
1226 free( msg->p_body );
1227 httpd_MsgInit( msg );
1230 const char *httpd_MsgGet( const httpd_message_t *msg, const char *name )
1232 int i;
1234 for( i = 0; i < msg->i_name; i++ )
1236 if( !strcasecmp( msg->name[i], name ))
1238 return msg->value[i];
1241 return NULL;
1244 void httpd_MsgAdd( httpd_message_t *msg, const char *name, const char *psz_value, ... )
1246 va_list args;
1247 char *value = NULL;
1249 va_start( args, psz_value );
1250 if( us_vasprintf( &value, psz_value, args ) == -1 )
1251 value = NULL;
1252 va_end( args );
1254 if( value == NULL )
1255 return;
1257 name = strdup( name );
1258 if( name == NULL )
1260 free( value );
1261 return;
1264 TAB_APPEND( msg->i_name, msg->name, (char*)name );
1265 TAB_APPEND( msg->i_value, msg->value, value );
1268 static void httpd_ClientInit( httpd_client_t *cl, mtime_t now )
1270 cl->i_state = HTTPD_CLIENT_RECEIVING;
1271 cl->i_activity_date = now;
1272 cl->i_activity_timeout = INT64_C(10000000);
1273 cl->i_buffer_size = HTTPD_CL_BUFSIZE;
1274 cl->i_buffer = 0;
1275 cl->p_buffer = xmalloc( cl->i_buffer_size );
1276 cl->b_stream_mode = false;
1278 httpd_MsgInit( &cl->query );
1279 httpd_MsgInit( &cl->answer );
1282 char* httpd_ClientIP( const httpd_client_t *cl, char *ip, int *port )
1284 return net_GetPeerAddress( cl->fd, ip, port ) ? NULL : ip;
1287 char* httpd_ServerIP( const httpd_client_t *cl, char *ip, int *port )
1289 return net_GetSockAddress( cl->fd, ip, port ) ? NULL : ip;
1292 static void httpd_ClientClean( httpd_client_t *cl )
1294 if( cl->fd >= 0 )
1296 if( cl->p_tls != NULL )
1297 vlc_tls_SessionDelete( cl->p_tls );
1298 net_Close( cl->fd );
1299 cl->fd = -1;
1302 httpd_MsgClean( &cl->answer );
1303 httpd_MsgClean( &cl->query );
1305 free( cl->p_buffer );
1306 cl->p_buffer = NULL;
1309 static httpd_client_t *httpd_ClientNew( int fd, vlc_tls_t *p_tls, mtime_t now )
1311 httpd_client_t *cl = malloc( sizeof( httpd_client_t ) );
1313 if( !cl ) return NULL;
1315 cl->i_ref = 0;
1316 cl->fd = fd;
1317 cl->url = NULL;
1318 cl->p_tls = p_tls;
1320 httpd_ClientInit( cl, now );
1321 if( p_tls != NULL )
1322 cl->i_state = HTTPD_CLIENT_TLS_HS_OUT;
1324 return cl;
1327 static
1328 ssize_t httpd_NetRecv (httpd_client_t *cl, uint8_t *p, size_t i_len)
1330 vlc_tls_t *p_tls;
1331 ssize_t val;
1333 p_tls = cl->p_tls;
1335 val = p_tls ? tls_Recv (p_tls, p, i_len)
1336 : recv (cl->fd, p, i_len, 0);
1337 while (val == -1 && errno == EINTR);
1338 return val;
1341 static
1342 ssize_t httpd_NetSend (httpd_client_t *cl, const uint8_t *p, size_t i_len)
1344 vlc_tls_t *p_tls;
1345 ssize_t val;
1347 p_tls = cl->p_tls;
1349 val = p_tls ? tls_Send( p_tls, p, i_len )
1350 : send (cl->fd, p, i_len, 0);
1351 while (val == -1 && errno == EINTR);
1352 return val;
1356 static const struct
1358 const char name[16];
1359 int i_type;
1360 int i_proto;
1362 msg_type[] =
1364 { "OPTIONS", HTTPD_MSG_OPTIONS, HTTPD_PROTO_RTSP },
1365 { "DESCRIBE", HTTPD_MSG_DESCRIBE, HTTPD_PROTO_RTSP },
1366 { "SETUP", HTTPD_MSG_SETUP, HTTPD_PROTO_RTSP },
1367 { "PLAY", HTTPD_MSG_PLAY, HTTPD_PROTO_RTSP },
1368 { "PAUSE", HTTPD_MSG_PAUSE, HTTPD_PROTO_RTSP },
1369 { "GET_PARAMETER", HTTPD_MSG_GETPARAMETER, HTTPD_PROTO_RTSP },
1370 { "TEARDOWN", HTTPD_MSG_TEARDOWN, HTTPD_PROTO_RTSP },
1371 { "GET", HTTPD_MSG_GET, HTTPD_PROTO_HTTP },
1372 { "HEAD", HTTPD_MSG_HEAD, HTTPD_PROTO_HTTP },
1373 { "POST", HTTPD_MSG_POST, HTTPD_PROTO_HTTP },
1374 { "", HTTPD_MSG_NONE, HTTPD_PROTO_NONE }
1378 static void httpd_ClientRecv( httpd_client_t *cl )
1380 int i_len;
1382 /* ignore leading whites */
1383 if( ( cl->query.i_proto == HTTPD_PROTO_NONE ) &&
1384 ( cl->i_buffer == 0 ) )
1386 unsigned char c;
1388 i_len = httpd_NetRecv( cl, &c, 1 );
1390 if( ( i_len > 0 ) && ( strchr( "\r\n\t ", c ) == NULL ) )
1392 cl->p_buffer[0] = c;
1393 cl->i_buffer++;
1396 else
1397 if( cl->query.i_proto == HTTPD_PROTO_NONE )
1399 /* enough to see if it's Interleaved RTP over RTSP or RTSP/HTTP */
1400 i_len = httpd_NetRecv( cl, &cl->p_buffer[cl->i_buffer],
1401 7 - cl->i_buffer );
1402 if( i_len > 0 )
1404 cl->i_buffer += i_len;
1407 /* The smallest legal request is 7 bytes ("GET /\r\n"),
1408 * this is the maximum we can ask at this point. */
1409 if( cl->i_buffer >= 7 )
1411 if( !memcmp( cl->p_buffer, "HTTP/1.", 7 ) )
1413 cl->query.i_proto = HTTPD_PROTO_HTTP;
1414 cl->query.i_type = HTTPD_MSG_ANSWER;
1416 else if( !memcmp( cl->p_buffer, "RTSP/1.", 7 ) )
1418 cl->query.i_proto = HTTPD_PROTO_RTSP;
1419 cl->query.i_type = HTTPD_MSG_ANSWER;
1421 else
1423 /* We need the full request line to determine the protocol. */
1424 cl->query.i_proto = HTTPD_PROTO_HTTP0;
1425 cl->query.i_type = HTTPD_MSG_NONE;
1429 else if( cl->query.i_body > 0 )
1431 /* we are reading the body of a request or a channel */
1432 i_len = httpd_NetRecv( cl, &cl->query.p_body[cl->i_buffer],
1433 cl->query.i_body - cl->i_buffer );
1434 if( i_len > 0 )
1436 cl->i_buffer += i_len;
1438 if( cl->i_buffer >= cl->query.i_body )
1440 cl->i_state = HTTPD_CLIENT_RECEIVE_DONE;
1443 else
1445 /* we are reading a header -> char by char */
1446 for( ;; )
1448 if( cl->i_buffer == cl->i_buffer_size )
1450 uint8_t *newbuf = realloc( cl->p_buffer, cl->i_buffer_size + 1024 );
1451 if( newbuf == NULL )
1453 i_len = 0;
1454 break;
1457 cl->p_buffer = newbuf;
1458 cl->i_buffer_size += 1024;
1461 i_len = httpd_NetRecv (cl, &cl->p_buffer[cl->i_buffer], 1 );
1462 if( i_len <= 0 )
1464 break;
1466 cl->i_buffer++;
1468 if( ( cl->query.i_proto == HTTPD_PROTO_HTTP0 )
1469 && ( cl->p_buffer[cl->i_buffer - 1] == '\n' ) )
1471 /* Request line is now complete */
1472 const char *p = memchr( cl->p_buffer, ' ', cl->i_buffer );
1473 size_t len;
1475 assert( cl->query.i_type == HTTPD_MSG_NONE );
1477 if( p == NULL ) /* no URI: evil guy */
1479 i_len = 0; /* drop connection */
1480 break;
1484 p++; /* skips extra spaces */
1485 while( *p == ' ' );
1487 p = memchr( p, ' ', ((char *)cl->p_buffer) + cl->i_buffer - p );
1488 if( p == NULL ) /* no explicit protocol: HTTP/0.9 */
1490 i_len = 0; /* not supported currently -> drop */
1491 break;
1495 p++; /* skips extra spaces ever again */
1496 while( *p == ' ' );
1498 len = ((char *)cl->p_buffer) + cl->i_buffer - p;
1499 if( len < 7 ) /* foreign protocol */
1500 i_len = 0; /* I don't understand -> drop */
1501 else
1502 if( memcmp( p, "HTTP/1.", 7 ) == 0 )
1504 cl->query.i_proto = HTTPD_PROTO_HTTP;
1505 cl->query.i_version = atoi( p + 7 );
1507 else
1508 if( memcmp( p, "RTSP/1.", 7 ) == 0 )
1510 cl->query.i_proto = HTTPD_PROTO_RTSP;
1511 cl->query.i_version = atoi( p + 7 );
1513 else
1514 if( memcmp( p, "HTTP/", 5 ) == 0 )
1516 const uint8_t sorry[] =
1517 "HTTP/1.1 505 Unknown HTTP version\r\n\r\n";
1518 httpd_NetSend( cl, sorry, sizeof( sorry ) - 1 );
1519 i_len = 0; /* drop */
1521 else
1522 if( memcmp( p, "RTSP/", 5 ) == 0 )
1524 const uint8_t sorry[] =
1525 "RTSP/1.0 505 Unknown RTSP version\r\n\r\n";
1526 httpd_NetSend( cl, sorry, sizeof( sorry ) - 1 );
1527 i_len = 0; /* drop */
1529 else /* yet another foreign protocol */
1530 i_len = 0;
1532 if( i_len == 0 )
1533 break;
1536 if( ( cl->i_buffer >= 2 && !memcmp( &cl->p_buffer[cl->i_buffer-2], "\n\n", 2 ) )||
1537 ( cl->i_buffer >= 4 && !memcmp( &cl->p_buffer[cl->i_buffer-4], "\r\n\r\n", 4 ) ) )
1539 char *p;
1541 /* we have finished the header so parse it and set i_body */
1542 cl->p_buffer[cl->i_buffer] = '\0';
1544 if( cl->query.i_type == HTTPD_MSG_ANSWER )
1546 /* FIXME:
1547 * assume strlen( "HTTP/1.x" ) = 8
1549 cl->query.i_status =
1550 strtol( (char *)&cl->p_buffer[8],
1551 &p, 0 );
1552 while( *p == ' ' )
1553 p++;
1555 else
1557 unsigned i;
1559 p = NULL;
1560 cl->query.i_type = HTTPD_MSG_NONE;
1562 /*fprintf( stderr, "received new request=%s\n", cl->p_buffer);*/
1564 for( i = 0; msg_type[i].name[0]; i++ )
1566 if( !strncmp( (char *)cl->p_buffer, msg_type[i].name,
1567 strlen( msg_type[i].name ) ) )
1569 p = (char *)&cl->p_buffer[strlen(msg_type[i].name) + 1 ];
1570 cl->query.i_type = msg_type[i].i_type;
1571 if( cl->query.i_proto != msg_type[i].i_proto )
1573 p = NULL;
1574 cl->query.i_proto = HTTPD_PROTO_NONE;
1575 cl->query.i_type = HTTPD_MSG_NONE;
1577 break;
1580 if( p == NULL )
1582 if( strstr( (char *)cl->p_buffer, "HTTP/1." ) )
1584 cl->query.i_proto = HTTPD_PROTO_HTTP;
1586 else if( strstr( (char *)cl->p_buffer, "RTSP/1." ) )
1588 cl->query.i_proto = HTTPD_PROTO_RTSP;
1591 else
1593 char *p2;
1594 char *p3;
1596 while( *p == ' ' )
1598 p++;
1600 p2 = strchr( p, ' ' );
1601 if( p2 )
1603 *p2++ = '\0';
1605 if( !strncasecmp( p, ( cl->query.i_proto
1606 == HTTPD_PROTO_HTTP ) ? "http:" : "rtsp:", 5 ) )
1607 { /* Skip hier-part of URL (if present) */
1608 p += 5;
1609 if( !strncmp( p, "//", 2 ) ) /* skip authority */
1610 { /* see RFC3986 §3.2 */
1611 p += 2;
1612 p += strcspn( p, "/?#" );
1615 else
1616 if( !strncasecmp( p, ( cl->query.i_proto
1617 == HTTPD_PROTO_HTTP ) ? "https:" : "rtsps:", 6 ) )
1618 { /* Skip hier-part of URL (if present) */
1619 p += 6;
1620 if( !strncmp( p, "//", 2 ) ) /* skip authority */
1621 { /* see RFC3986 §3.2 */
1622 p += 2;
1623 p += strcspn( p, "/?#" );
1627 cl->query.psz_url = strdup( p );
1628 if( ( p3 = strchr( cl->query.psz_url, '?' ) ) )
1630 *p3++ = '\0';
1631 cl->query.psz_args = (uint8_t *)strdup( p3 );
1633 p = p2;
1636 if( p )
1638 p = strchr( p, '\n' );
1640 if( p )
1642 while( *p == '\n' || *p == '\r' )
1644 p++;
1646 while( p && *p != '\0' )
1648 char *line = p;
1649 char *eol = p = strchr( p, '\n' );
1650 char *colon;
1652 while( eol && eol >= line && ( *eol == '\n' || *eol == '\r' ) )
1654 *eol-- = '\0';
1657 if( ( colon = strchr( line, ':' ) ) )
1659 char *name;
1660 char *value;
1662 *colon++ = '\0';
1663 while( *colon == ' ' )
1665 colon++;
1667 name = strdup( line );
1668 value = strdup( colon );
1670 TAB_APPEND( cl->query.i_name, cl->query.name, name );
1671 TAB_APPEND( cl->query.i_value,cl->query.value,value);
1673 if( !strcasecmp( name, "Content-Length" ) )
1675 cl->query.i_body = atol( value );
1679 if( p )
1681 p++;
1682 while( *p == '\n' || *p == '\r' )
1684 p++;
1689 if( cl->query.i_body > 0 )
1691 /* TODO Mhh, handle the case where the client only
1692 * sends a request and closes the connection to
1693 * mark the end of the body (probably only RTSP) */
1694 cl->query.p_body = malloc( cl->query.i_body );
1695 cl->i_buffer = 0;
1696 if ( cl->query.p_body == NULL )
1698 switch (cl->query.i_proto)
1700 case HTTPD_PROTO_HTTP:
1702 const uint8_t sorry[] =
1703 "HTTP/1.1 413 Request Entity Too Large\r\n\r\n";
1704 httpd_NetSend( cl, sorry, sizeof( sorry ) - 1 );
1705 break;
1707 case HTTPD_PROTO_RTSP:
1709 const uint8_t sorry[] =
1710 "RTSP/1.0 413 Request Entity Too Large\r\n\r\n";
1711 httpd_NetSend( cl, sorry, sizeof( sorry ) - 1 );
1712 break;
1714 default:
1715 assert( 0 );
1717 i_len = 0; /* drop */
1719 break;
1721 else
1723 cl->i_state = HTTPD_CLIENT_RECEIVE_DONE;
1729 /* check if the client is to be set to dead */
1730 #if defined( WIN32 )
1731 if( ( i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK ) || ( i_len == 0 ) )
1732 #else
1733 if( ( i_len < 0 && errno != EAGAIN ) || ( i_len == 0 ) )
1734 #endif
1736 if( cl->query.i_proto != HTTPD_PROTO_NONE && cl->query.i_type != HTTPD_MSG_NONE )
1738 /* connection closed -> end of data */
1739 if( cl->query.i_body > 0 )
1741 cl->query.i_body = cl->i_buffer;
1743 cl->i_state = HTTPD_CLIENT_RECEIVE_DONE;
1745 else
1747 cl->i_state = HTTPD_CLIENT_DEAD;
1751 /* XXX: for QT I have to disable timeout. Try to find why */
1752 if( cl->query.i_proto == HTTPD_PROTO_RTSP )
1753 cl->i_activity_timeout = 0;
1755 #if 0 /* Debugging only */
1756 if( cl->i_state == HTTPD_CLIENT_RECEIVE_DONE )
1758 int i;
1760 fprintf( stderr, "received new request\n" );
1761 fprintf( stderr, " - proto=%s\n",
1762 cl->query.i_proto == HTTPD_PROTO_HTTP ? "HTTP" : "RTSP" );
1763 fprintf( stderr, " - version=%d\n", cl->query.i_version );
1764 fprintf( stderr, " - msg=%d\n", cl->query.i_type );
1765 if( cl->query.i_type == HTTPD_MSG_ANSWER )
1767 fprintf( stderr, " - answer=%d '%s'\n", cl->query.i_status,
1768 cl->query.psz_status );
1770 else if( cl->query.i_type != HTTPD_MSG_NONE )
1772 fprintf( stderr, " - url=%s\n", cl->query.psz_url );
1774 for( i = 0; i < cl->query.i_name; i++ )
1776 fprintf( stderr, " - option name='%s' value='%s'\n",
1777 cl->query.name[i], cl->query.value[i] );
1780 #endif
1783 static void httpd_ClientSend( httpd_client_t *cl )
1785 int i;
1786 int i_len;
1788 if( cl->i_buffer < 0 )
1790 /* We need to create the header */
1791 int i_size = 0;
1792 char *p;
1793 const char *psz_status = httpd_ReasonFromCode( cl->answer.i_status );
1795 i_size = strlen( "HTTP/1.") + 10 + 10 + strlen( psz_status ) + 5;
1796 for( i = 0; i < cl->answer.i_name; i++ )
1798 i_size += strlen( cl->answer.name[i] ) + 2 +
1799 strlen( cl->answer.value[i] ) + 2;
1802 if( cl->i_buffer_size < i_size )
1804 cl->i_buffer_size = i_size;
1805 free( cl->p_buffer );
1806 cl->p_buffer = xmalloc( i_size );
1808 p = (char *)cl->p_buffer;
1810 p += sprintf( p, "%s.%u %d %s\r\n",
1811 cl->answer.i_proto == HTTPD_PROTO_HTTP ? "HTTP/1" : "RTSP/1",
1812 cl->answer.i_version,
1813 cl->answer.i_status, psz_status );
1814 for( i = 0; i < cl->answer.i_name; i++ )
1816 p += sprintf( p, "%s: %s\r\n", cl->answer.name[i],
1817 cl->answer.value[i] );
1819 p += sprintf( p, "\r\n" );
1821 cl->i_buffer = 0;
1822 cl->i_buffer_size = (uint8_t*)p - cl->p_buffer;
1824 /*fprintf( stderr, "sending answer\n" );
1825 fprintf( stderr, "%s", cl->p_buffer );*/
1828 i_len = httpd_NetSend( cl, &cl->p_buffer[cl->i_buffer],
1829 cl->i_buffer_size - cl->i_buffer );
1830 if( i_len >= 0 )
1832 cl->i_buffer += i_len;
1834 if( cl->i_buffer >= cl->i_buffer_size )
1836 if( cl->answer.i_body == 0 && cl->answer.i_body_offset > 0 )
1838 /* catch more body data */
1839 int i_msg = cl->query.i_type;
1840 int64_t i_offset = cl->answer.i_body_offset;
1842 httpd_MsgClean( &cl->answer );
1843 cl->answer.i_body_offset = i_offset;
1845 cl->url->catch[i_msg].cb( cl->url->catch[i_msg].p_sys, cl,
1846 &cl->answer, &cl->query );
1849 if( cl->answer.i_body > 0 )
1851 /* send the body data */
1852 free( cl->p_buffer );
1853 cl->p_buffer = cl->answer.p_body;
1854 cl->i_buffer_size = cl->answer.i_body;
1855 cl->i_buffer = 0;
1857 cl->answer.i_body = 0;
1858 cl->answer.p_body = NULL;
1860 else
1862 /* send finished */
1863 cl->i_state = HTTPD_CLIENT_SEND_DONE;
1867 else
1869 #if defined( WIN32 )
1870 if( ( i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK ) || ( i_len == 0 ) )
1871 #else
1872 if( ( i_len < 0 && errno != EAGAIN ) || ( i_len == 0 ) )
1873 #endif
1875 /* error */
1876 cl->i_state = HTTPD_CLIENT_DEAD;
1881 static void httpd_ClientTlsHandshake( httpd_client_t *cl )
1883 switch( vlc_tls_SessionHandshake( cl->p_tls, NULL, NULL ) )
1885 case 0:
1886 cl->i_state = HTTPD_CLIENT_RECEIVING;
1887 break;
1889 case -1:
1890 cl->i_state = HTTPD_CLIENT_DEAD;
1891 break;
1893 case 1:
1894 cl->i_state = HTTPD_CLIENT_TLS_HS_IN;
1895 break;
1897 case 2:
1898 cl->i_state = HTTPD_CLIENT_TLS_HS_OUT;
1899 break;
1903 static void* httpd_HostThread( void *data )
1905 httpd_host_t *host = data;
1906 int canc = vlc_savecancel();
1908 vlc_mutex_lock( &host->lock );
1909 while( host->i_ref > 0 )
1911 struct pollfd ufd[host->nfd + host->i_client];
1912 unsigned nfd;
1913 for( nfd = 0; nfd < host->nfd; nfd++ )
1915 ufd[nfd].fd = host->fds[nfd];
1916 ufd[nfd].events = POLLIN;
1917 ufd[nfd].revents = 0;
1920 /* add all socket that should be read/write and close dead connection */
1921 while( host->i_url <= 0 )
1923 mutex_cleanup_push( &host->lock );
1924 vlc_restorecancel( canc );
1925 vlc_cond_wait( &host->wait, &host->lock );
1926 canc = vlc_savecancel();
1927 vlc_cleanup_pop();
1930 mtime_t now = mdate();
1931 bool b_low_delay = false;
1933 for(int i_client = 0; i_client < host->i_client; i_client++ )
1935 httpd_client_t *cl = host->client[i_client];
1936 if( cl->i_ref < 0 || ( cl->i_ref == 0 &&
1937 ( cl->i_state == HTTPD_CLIENT_DEAD ||
1938 ( cl->i_activity_timeout > 0 &&
1939 cl->i_activity_date+cl->i_activity_timeout < now) ) ) )
1941 httpd_ClientClean( cl );
1942 TAB_REMOVE( host->i_client, host->client, cl );
1943 free( cl );
1944 i_client--;
1945 continue;
1948 struct pollfd *pufd = ufd + nfd;
1949 assert (pufd < ufd + (sizeof (ufd) / sizeof (ufd[0])));
1951 pufd->fd = cl->fd;
1952 pufd->events = pufd->revents = 0;
1954 if( ( cl->i_state == HTTPD_CLIENT_RECEIVING )
1955 || ( cl->i_state == HTTPD_CLIENT_TLS_HS_IN ) )
1957 pufd->events = POLLIN;
1959 else if( ( cl->i_state == HTTPD_CLIENT_SENDING )
1960 || ( cl->i_state == HTTPD_CLIENT_TLS_HS_OUT ) )
1962 pufd->events = POLLOUT;
1964 else if( cl->i_state == HTTPD_CLIENT_RECEIVE_DONE )
1966 httpd_message_t *answer = &cl->answer;
1967 httpd_message_t *query = &cl->query;
1968 int i_msg = query->i_type;
1970 httpd_MsgInit( answer );
1972 /* Handle what we received */
1973 if( i_msg == HTTPD_MSG_ANSWER )
1975 cl->url = NULL;
1976 cl->i_state = HTTPD_CLIENT_DEAD;
1978 else if( i_msg == HTTPD_MSG_OPTIONS )
1981 answer->i_type = HTTPD_MSG_ANSWER;
1982 answer->i_proto = query->i_proto;
1983 answer->i_status = 200;
1984 answer->i_body = 0;
1985 answer->p_body = NULL;
1987 httpd_MsgAdd( answer, "Server", "VLC/%s", VERSION );
1988 httpd_MsgAdd( answer, "Content-Length", "0" );
1990 switch( query->i_proto )
1992 case HTTPD_PROTO_HTTP:
1993 answer->i_version = 1;
1994 httpd_MsgAdd( answer, "Allow",
1995 "GET,HEAD,POST,OPTIONS" );
1996 break;
1998 case HTTPD_PROTO_RTSP:
2000 const char *p;
2001 answer->i_version = 0;
2003 p = httpd_MsgGet( query, "Cseq" );
2004 if( p != NULL )
2005 httpd_MsgAdd( answer, "Cseq", "%s", p );
2006 p = httpd_MsgGet( query, "Timestamp" );
2007 if( p != NULL )
2008 httpd_MsgAdd( answer, "Timestamp", "%s", p );
2010 p = httpd_MsgGet( query, "Require" );
2011 if( p != NULL )
2013 answer->i_status = 551;
2014 httpd_MsgAdd( query, "Unsupported", "%s", p );
2017 httpd_MsgAdd( answer, "Public", "DESCRIBE,SETUP,"
2018 "TEARDOWN,PLAY,PAUSE,GET_PARAMETER" );
2019 break;
2023 cl->i_buffer = -1; /* Force the creation of the answer in
2024 * httpd_ClientSend */
2025 cl->i_state = HTTPD_CLIENT_SENDING;
2027 else if( i_msg == HTTPD_MSG_NONE )
2029 if( query->i_proto == HTTPD_PROTO_NONE )
2031 cl->url = NULL;
2032 cl->i_state = HTTPD_CLIENT_DEAD;
2034 else
2036 char *p;
2038 /* unimplemented */
2039 answer->i_proto = query->i_proto ;
2040 answer->i_type = HTTPD_MSG_ANSWER;
2041 answer->i_version= 0;
2042 answer->i_status = 501;
2044 answer->i_body = httpd_HtmlError (&p, 501, NULL);
2045 answer->p_body = (uint8_t *)p;
2046 httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
2048 cl->i_buffer = -1; /* Force the creation of the answer in httpd_ClientSend */
2049 cl->i_state = HTTPD_CLIENT_SENDING;
2052 else
2054 bool b_auth_failed = false;
2056 /* Search the url and trigger callbacks */
2057 for(int i = 0; i < host->i_url; i++ )
2059 httpd_url_t *url = host->url[i];
2061 if( !strcmp( url->psz_url, query->psz_url ) )
2063 if( url->catch[i_msg].cb )
2065 if( answer && ( *url->psz_user || *url->psz_password ) )
2067 /* create the headers */
2068 const char *b64 = httpd_MsgGet( query, "Authorization" ); /* BASIC id */
2069 char *user = NULL, *pass = NULL;
2071 if( b64 != NULL
2072 && !strncasecmp( b64, "BASIC", 5 ) )
2074 b64 += 5;
2075 while( *b64 == ' ' )
2076 b64++;
2078 user = vlc_b64_decode( b64 );
2079 if (user != NULL)
2081 pass = strchr (user, ':');
2082 if (pass != NULL)
2083 *pass++ = '\0';
2087 if ((user == NULL) || (pass == NULL)
2088 || strcmp (user, url->psz_user)
2089 || strcmp (pass, url->psz_password))
2091 httpd_MsgAdd( answer,
2092 "WWW-Authenticate",
2093 "Basic realm=\"VLC stream\"" );
2094 /* We fail for all url */
2095 b_auth_failed = true;
2096 free( user );
2097 break;
2100 free( user );
2103 if( !url->catch[i_msg].cb( url->catch[i_msg].p_sys, cl, answer, query ) )
2105 if( answer->i_proto == HTTPD_PROTO_NONE )
2107 /* Raw answer from a CGI */
2108 cl->i_buffer = cl->i_buffer_size;
2110 else
2111 cl->i_buffer = -1;
2113 /* only one url can answer */
2114 answer = NULL;
2115 if( cl->url == NULL )
2117 cl->url = url;
2124 if( answer )
2126 char *p;
2128 answer->i_proto = query->i_proto;
2129 answer->i_type = HTTPD_MSG_ANSWER;
2130 answer->i_version= 0;
2132 if( b_auth_failed )
2134 answer->i_status = 401;
2136 else
2138 /* no url registered */
2139 answer->i_status = 404;
2142 answer->i_body = httpd_HtmlError (&p,
2143 answer->i_status,
2144 query->psz_url);
2145 answer->p_body = (uint8_t *)p;
2147 cl->i_buffer = -1; /* Force the creation of the answer in httpd_ClientSend */
2148 httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
2149 httpd_MsgAdd( answer, "Content-Type", "%s", "text/html" );
2152 cl->i_state = HTTPD_CLIENT_SENDING;
2155 else if( cl->i_state == HTTPD_CLIENT_SEND_DONE )
2157 if( !cl->b_stream_mode || cl->answer.i_body_offset == 0 )
2159 const char *psz_connection = httpd_MsgGet( &cl->answer, "Connection" );
2160 const char *psz_query = httpd_MsgGet( &cl->query, "Connection" );
2161 bool b_connection = false;
2162 bool b_keepalive = false;
2163 bool b_query = false;
2165 cl->url = NULL;
2166 if( psz_connection )
2168 b_connection = ( strcasecmp( psz_connection, "Close" ) == 0 );
2169 b_keepalive = ( strcasecmp( psz_connection, "Keep-Alive" ) == 0 );
2172 if( psz_query )
2174 b_query = ( strcasecmp( psz_query, "Close" ) == 0 );
2177 if( ( ( cl->query.i_proto == HTTPD_PROTO_HTTP ) &&
2178 ( ( cl->query.i_version == 0 && b_keepalive ) ||
2179 ( cl->query.i_version == 1 && !b_connection ) ) ) ||
2180 ( ( cl->query.i_proto == HTTPD_PROTO_RTSP ) &&
2181 !b_query && !b_connection ) )
2183 httpd_MsgClean( &cl->query );
2184 httpd_MsgInit( &cl->query );
2186 cl->i_buffer = 0;
2187 cl->i_buffer_size = 1000;
2188 free( cl->p_buffer );
2189 cl->p_buffer = xmalloc( cl->i_buffer_size );
2190 cl->i_state = HTTPD_CLIENT_RECEIVING;
2192 else
2194 cl->i_state = HTTPD_CLIENT_DEAD;
2196 httpd_MsgClean( &cl->answer );
2198 else
2200 int64_t i_offset = cl->answer.i_body_offset;
2201 httpd_MsgClean( &cl->answer );
2203 cl->answer.i_body_offset = i_offset;
2204 free( cl->p_buffer );
2205 cl->p_buffer = NULL;
2206 cl->i_buffer = 0;
2207 cl->i_buffer_size = 0;
2209 cl->i_state = HTTPD_CLIENT_WAITING;
2212 else if( cl->i_state == HTTPD_CLIENT_WAITING )
2214 int64_t i_offset = cl->answer.i_body_offset;
2215 int i_msg = cl->query.i_type;
2217 httpd_MsgInit( &cl->answer );
2218 cl->answer.i_body_offset = i_offset;
2220 cl->url->catch[i_msg].cb( cl->url->catch[i_msg].p_sys, cl,
2221 &cl->answer, &cl->query );
2222 if( cl->answer.i_type != HTTPD_MSG_NONE )
2224 /* we have new data, so re-enter send mode */
2225 cl->i_buffer = 0;
2226 cl->p_buffer = cl->answer.p_body;
2227 cl->i_buffer_size = cl->answer.i_body;
2228 cl->answer.p_body = NULL;
2229 cl->answer.i_body = 0;
2230 cl->i_state = HTTPD_CLIENT_SENDING;
2234 if (pufd->events != 0)
2235 nfd++;
2236 else
2237 b_low_delay = true;
2239 vlc_mutex_unlock( &host->lock );
2240 vlc_restorecancel( canc );
2242 /* we will wait 20ms (not too big) if HTTPD_CLIENT_WAITING */
2243 int ret = poll( ufd, nfd, b_low_delay ? 20 : -1 );
2245 canc = vlc_savecancel();
2246 vlc_mutex_lock( &host->lock );
2247 switch( ret )
2249 case -1:
2250 if (errno != EINTR)
2252 /* Kernel on low memory or a bug: pace */
2253 msg_Err( host, "polling error: %m" );
2254 msleep( 100000 );
2256 case 0:
2257 continue;
2260 /* Handle client sockets */
2261 now = mdate();
2262 nfd = host->nfd;
2264 for( int i_client = 0; i_client < host->i_client; i_client++ )
2266 httpd_client_t *cl = host->client[i_client];
2267 const struct pollfd *pufd = &ufd[nfd];
2269 assert( pufd < &ufd[sizeof(ufd) / sizeof(ufd[0])] );
2271 if( cl->fd != pufd->fd )
2272 continue; // we were not waiting for this client
2273 ++nfd;
2274 if( pufd->revents == 0 )
2275 continue; // no event received
2277 cl->i_activity_date = now;
2279 if( cl->i_state == HTTPD_CLIENT_RECEIVING )
2281 httpd_ClientRecv( cl );
2283 else if( cl->i_state == HTTPD_CLIENT_SENDING )
2285 httpd_ClientSend( cl );
2287 else if( cl->i_state == HTTPD_CLIENT_TLS_HS_IN
2288 || cl->i_state == HTTPD_CLIENT_TLS_HS_OUT )
2290 httpd_ClientTlsHandshake( cl );
2294 /* Handle server sockets (accept new connections) */
2295 for( nfd = 0; nfd < host->nfd; nfd++ )
2297 httpd_client_t *cl;
2298 int fd = ufd[nfd].fd;
2300 assert (fd == host->fds[nfd]);
2302 if( ufd[nfd].revents == 0 )
2303 continue;
2305 /* */
2306 fd = vlc_accept (fd, NULL, NULL, true);
2307 if (fd == -1)
2308 continue;
2309 setsockopt (fd, SOL_SOCKET, SO_REUSEADDR,
2310 &(int){ 1 }, sizeof(int));
2312 vlc_tls_t *p_tls;
2314 if( host->p_tls != NULL )
2315 p_tls = vlc_tls_SessionCreate( host->p_tls, fd, NULL );
2316 else
2317 p_tls = NULL;
2319 cl = httpd_ClientNew( fd, p_tls, now );
2321 TAB_APPEND( host->i_client, host->client, cl );
2324 vlc_mutex_unlock( &host->lock );
2325 return NULL;