httpd: reduce variable scope
[vlc.git] / src / network / httpd.c
1 /*****************************************************************************
2  * httpd.c
3  *****************************************************************************
4  * Copyright (C) 2004-2006 VLC authors and VideoLAN
5  * Copyright © 2004-2007 Rémi Denis-Courmont
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *          Rémi Denis-Courmont
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation; either version 2.1 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <stdatomic.h>
30
31 #include <vlc_common.h>
32 #include <vlc_httpd.h>
33
34 #include <assert.h>
35
36 #include <vlc_list.h>
37 #include <vlc_network.h>
38 #include <vlc_tls.h>
39 #include <vlc_strings.h>
40 #include <vlc_rand.h>
41 #include <vlc_charset.h>
42 #include <vlc_url.h>
43 #include <vlc_mime.h>
44 #include <vlc_block.h>
45 #include "../libvlc.h"
46
47 #include <string.h>
48 #include <errno.h>
49 #include <unistd.h>
50 #ifdef HAVE_SYS_UIO_H
51 # include <sys/uio.h>
52 #endif
53
54 #ifdef HAVE_POLL
55 # include <poll.h>
56 #endif
57
58 #if defined(_WIN32)
59 #   include <winsock2.h>
60 #elif defined(HAVE_SYS_SOCKET_H)
61 #   include <sys/socket.h>
62 #endif
63
64 #if defined(_WIN32)
65 /* We need HUGE buffer otherwise TCP throughput is very limited */
66 #define HTTPD_CL_BUFSIZE 1000000
67 #else
68 #define HTTPD_CL_BUFSIZE 10000
69 #endif
70
71 static void httpd_ClientDestroy(httpd_client_t *cl);
72 static void httpd_AppendData(httpd_stream_t *stream, uint8_t *p_data, int i_data);
73
74 /* each host run in his own thread */
75 struct httpd_host_t
76 {
77     struct vlc_object_t obj;
78     struct vlc_list node;
79
80     /* ref count */
81     atomic_uint ref;
82
83     /* address/port and socket for listening at connections */
84     int         *fds;
85     unsigned     nfd;
86     unsigned     port;
87
88     vlc_thread_t thread;
89     vlc_mutex_t lock;
90
91     /* all registered url (becarefull that 2 httpd_url_t could point at the same url)
92      * This will slow down the url research but make my live easier
93      * All url will have their cb trigger, but only the first one can answer
94      * */
95     struct vlc_list urls;
96
97     size_t client_count;
98     struct vlc_list clients;
99
100     /* TLS data */
101     vlc_tls_server_t *p_tls;
102 };
103
104
105 struct httpd_url_t
106 {
107     httpd_host_t *host;
108     struct vlc_list node;
109     vlc_mutex_t lock;
110
111     char      *psz_url;
112     char      *psz_user;
113     char      *psz_password;
114
115     struct
116     {
117         httpd_callback_t     cb;
118         httpd_callback_sys_t *p_sys;
119     } catch[HTTPD_MSG_MAX];
120 };
121
122 /* status */
123 enum
124 {
125     HTTPD_CLIENT_RECEIVING,
126     HTTPD_CLIENT_RECEIVE_DONE,
127
128     HTTPD_CLIENT_SENDING,
129     HTTPD_CLIENT_SEND_DONE,
130
131     HTTPD_CLIENT_WAITING,
132
133     HTTPD_CLIENT_DEAD,
134
135     HTTPD_CLIENT_TLS_HS_IN,
136     HTTPD_CLIENT_TLS_HS_OUT
137 };
138
139 struct httpd_client_t
140 {
141     httpd_url_t *url;
142     vlc_tls_t   *sock;
143
144     struct vlc_list node;
145
146     bool    b_stream_mode;
147     uint8_t i_state;
148
149     vlc_tick_t i_activity_date;
150     vlc_tick_t i_activity_timeout;
151
152     /* buffer for reading header */
153     int     i_buffer_size;
154     int     i_buffer;
155     uint8_t *p_buffer;
156
157     /*
158      * If waiting for a keyframe, this is the position (in bytes) of the
159      * last keyframe the stream saw before this client connected.
160      * Otherwise, -1.
161      */
162     int64_t i_keyframe_wait_to_pass;
163
164     /* */
165     httpd_message_t query;  /* client -> httpd */
166     httpd_message_t answer; /* httpd -> client */
167 };
168
169
170 /*****************************************************************************
171  * Various functions
172  *****************************************************************************/
173 static const char *httpd_ReasonFromCode(unsigned i_code)
174 {
175     typedef struct
176     {
177         unsigned   i_code;
178         const char psz_reason[36];
179     } http_status_info;
180
181     static const http_status_info http_reason[] =
182     {
183         /*{ 100, "Continue" },
184           { 101, "Switching Protocols" },*/
185         { 200, "OK" },
186         /*{ 201, "Created" },
187           { 202, "Accepted" },
188           { 203, "Non-authoritative information" },
189           { 204, "No content" },
190           { 205, "Reset content" },
191           { 206, "Partial content" },
192           { 250, "Low on storage space" },
193           { 300, "Multiple choices" },*/
194         { 301, "Moved permanently" },
195         /*{ 302, "Moved temporarily" },
196           { 303, "See other" },
197           { 304, "Not modified" },
198           { 305, "Use proxy" },
199           { 307, "Temporary redirect" },
200           { 400, "Bad request" },*/
201         { 401, "Unauthorized" },
202         /*{ 402, "Payment Required" },*/
203         { 403, "Forbidden" },
204         { 404, "Not found" },
205         { 405, "Method not allowed" },
206         /*{ 406, "Not acceptable" },
207           { 407, "Proxy authentication required" },
208           { 408, "Request time-out" },
209           { 409, "Conflict" },
210           { 410, "Gone" },
211           { 411, "Length required" },
212           { 412, "Precondition failed" },
213           { 413, "Request entity too large" },
214           { 414, "Request-URI too large" },
215           { 415, "Unsupported media Type" },
216           { 416, "Requested range not satisfiable" },
217           { 417, "Expectation failed" },
218           { 451, "Parameter not understood" },
219           { 452, "Conference not found" },
220           { 453, "Not enough bandwidth" },*/
221         { 454, "Session not found" },
222         { 455, "Method not valid in this State" },
223         { 456, "Header field not valid for resource" },
224         { 457, "Invalid range" },
225         /*{ 458, "Read-only parameter" },*/
226         { 459, "Aggregate operation not allowed" },
227         { 460, "Non-aggregate operation not allowed" },
228         { 461, "Unsupported transport" },
229         /*{ 462, "Destination unreachable" },*/
230         { 500, "Internal server error" },
231         { 501, "Not implemented" },
232         /*{ 502, "Bad gateway" },*/
233         { 503, "Service unavailable" },
234         /*{ 504, "Gateway time-out" },*/
235         { 505, "Protocol version not supported" },
236         { 551, "Option not supported" },
237         { 999, "" }
238     };
239
240     static const char psz_fallback_reason[5][16] = {
241         "Continue", "OK", "Found", "Client error", "Server error"
242     };
243
244     assert((i_code >= 100) && (i_code <= 599));
245
246     const http_status_info *p = http_reason;
247     while (i_code < p->i_code)
248         p++;
249
250     if (p->i_code == i_code)
251         return p->psz_reason;
252
253     return psz_fallback_reason[(i_code / 100) - 1];
254 }
255
256 static size_t httpd_HtmlError (char **body, int code, const char *url)
257 {
258     const char *errname = httpd_ReasonFromCode (code);
259     assert (errname);
260
261     char *url_Encoded = vlc_xml_encode (url ? url : "");
262
263     int res = asprintf (body,
264         "<?xml version=\"1.0\" encoding=\"ascii\" ?>\n"
265         "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\""
266         " \"http://www.w3.org/TR/xhtml10/DTD/xhtml10strict.dtd\">\n"
267         "<html lang=\"en\">\n"
268         "<head>\n"
269         "<title>%s</title>\n"
270         "</head>\n"
271         "<body>\n"
272         "<h1>%d %s%s%s%s</h1>\n"
273         "<hr />\n"
274         "<a href=\"http://www.videolan.org\">VideoLAN</a>\n"
275         "</body>\n"
276         "</html>\n", errname, code, errname,
277         (url_Encoded ? " (" : ""), (url_Encoded ? url_Encoded : ""), (url_Encoded ? ")" : ""));
278
279     free (url_Encoded);
280
281     if (res == -1) {
282         *body = NULL;
283         return 0;
284     }
285
286     return (size_t)res;
287 }
288
289
290 /*****************************************************************************
291  * High Level Functions: httpd_file_t
292  *****************************************************************************/
293 struct httpd_file_t
294 {
295     httpd_url_t *url;
296     httpd_file_callback_t pf_fill;
297     httpd_file_sys_t      *p_sys;
298     char mime[1];
299 };
300
301 static int
302 httpd_FileCallBack(httpd_callback_sys_t *p_sys, httpd_client_t *cl,
303                     httpd_message_t *answer, const httpd_message_t *query)
304 {
305     httpd_file_t *file = (httpd_file_t*)p_sys;
306     uint8_t **pp_body, *p_body;
307     int *pi_body, i_body;
308
309     if (!answer || !query )
310         return VLC_SUCCESS;
311
312     answer->i_proto  = HTTPD_PROTO_HTTP;
313     answer->i_version= 1;
314     answer->i_type   = HTTPD_MSG_ANSWER;
315
316     answer->i_status = 200;
317
318     httpd_MsgAdd(answer, "Content-type",  "%s", file->mime);
319     httpd_MsgAdd(answer, "Cache-Control", "%s", "no-cache");
320
321     if (query->i_type != HTTPD_MSG_HEAD) {
322         pp_body = &answer->p_body;
323         pi_body = &answer->i_body;
324     } else {
325         /* The file still needs to be executed. */
326         p_body = NULL;
327         i_body = 0;
328         pp_body = &p_body;
329         pi_body = &i_body;
330     }
331
332     if (query->i_type == HTTPD_MSG_POST) {
333         /* msg_Warn not supported */
334     }
335
336     uint8_t *psz_args = query->psz_args;
337     file->pf_fill(file->p_sys, file, psz_args, pp_body, pi_body);
338
339     if (query->i_type == HTTPD_MSG_HEAD)
340         free(p_body);
341
342     /* We respect client request */
343     if (httpd_MsgGet(&cl->query, "Connection") != NULL)
344         httpd_MsgAdd(answer, "Connection", "close");
345
346     httpd_MsgAdd(answer, "Content-Length", "%d", answer->i_body);
347
348     return VLC_SUCCESS;
349 }
350
351 httpd_file_t *httpd_FileNew(httpd_host_t *host,
352                              const char *psz_url, const char *psz_mime,
353                              const char *psz_user, const char *psz_password,
354                              httpd_file_callback_t pf_fill,
355                              httpd_file_sys_t *p_sys)
356 {
357     const char *mime = psz_mime;
358     if (mime == NULL || mime[0] == '\0')
359         mime = vlc_mime_Ext2Mime(psz_url);
360
361     size_t mimelen = strlen(mime);
362     httpd_file_t *file = malloc(sizeof(*file) + mimelen);
363     if (unlikely(file == NULL))
364         return NULL;
365
366     file->url = httpd_UrlNew(host, psz_url, psz_user, psz_password);
367     if (!file->url) {
368         free(file);
369         return NULL;
370     }
371
372     file->pf_fill = pf_fill;
373     file->p_sys   = p_sys;
374     memcpy(file->mime, mime, mimelen + 1);
375
376     httpd_UrlCatch(file->url, HTTPD_MSG_HEAD, httpd_FileCallBack,
377                     (httpd_callback_sys_t*)file);
378     httpd_UrlCatch(file->url, HTTPD_MSG_GET,  httpd_FileCallBack,
379                     (httpd_callback_sys_t*)file);
380     httpd_UrlCatch(file->url, HTTPD_MSG_POST, httpd_FileCallBack,
381                     (httpd_callback_sys_t*)file);
382
383     return file;
384 }
385
386 httpd_file_sys_t *httpd_FileDelete(httpd_file_t *file)
387 {
388     httpd_file_sys_t *p_sys = file->p_sys;
389
390     httpd_UrlDelete(file->url);
391     free(file);
392     return p_sys;
393 }
394
395 /*****************************************************************************
396  * High Level Functions: httpd_handler_t (for CGIs)
397  *****************************************************************************/
398 struct httpd_handler_t
399 {
400     httpd_url_t *url;
401
402     httpd_handler_callback_t pf_fill;
403     void                    *p_sys;
404
405 };
406
407 static int
408 httpd_HandlerCallBack(httpd_callback_sys_t *p_sys, httpd_client_t *cl,
409                        httpd_message_t *answer, const httpd_message_t *query)
410 {
411     httpd_handler_t *handler = (httpd_handler_t*)p_sys;
412     char psz_remote_addr[NI_MAXNUMERICHOST];
413
414     if (!answer || !query)
415         return VLC_SUCCESS;
416
417     answer->i_proto  = HTTPD_PROTO_NONE;
418     answer->i_type   = HTTPD_MSG_ANSWER;
419
420     /* We do it ourselves, thanks */
421     answer->i_status = 0;
422
423     if (!httpd_ClientIP(cl, psz_remote_addr, NULL))
424         *psz_remote_addr = '\0';
425
426     uint8_t *psz_args = query->psz_args;
427     handler->pf_fill(handler->p_sys, handler, query->psz_url, psz_args,
428                       query->i_type, query->p_body, query->i_body,
429                       psz_remote_addr, NULL,
430                       &answer->p_body, &answer->i_body);
431
432     if (query->i_type == HTTPD_MSG_HEAD) {
433         char *p = (char *)answer->p_body;
434
435         /* Looks for end of header (i.e. one empty line) */
436         while ((p = strchr(p, '\r')))
437             if (p[1] == '\n' && p[2] == '\r' && p[3] == '\n')
438                 break;
439
440         if (p) {
441             p[4] = '\0';
442             answer->i_body = strlen((char*)answer->p_body) + 1;
443             answer->p_body = xrealloc(answer->p_body, answer->i_body);
444         }
445     }
446
447     if (strncmp((char *)answer->p_body, "HTTP/1.", 7)) {
448         int i_status, i_headers;
449         char *psz_headers, *psz_new;
450         const char *psz_status;
451
452         if (!strncmp((char *)answer->p_body, "Status: ", 8)) {
453             /* Apache-style */
454             i_status = strtol((char *)&answer->p_body[8], &psz_headers, 0);
455             if (*psz_headers == '\r' || *psz_headers == '\n') psz_headers++;
456             if (*psz_headers == '\n') psz_headers++;
457             i_headers = answer->i_body - (psz_headers - (char *)answer->p_body);
458         } else {
459             i_status = 200;
460             psz_headers = (char *)answer->p_body;
461             i_headers = answer->i_body;
462         }
463
464         psz_status = httpd_ReasonFromCode(i_status);
465         answer->i_body = sizeof("HTTP/1.0 xxx \r\n")
466                         + strlen(psz_status) + i_headers - 1;
467         psz_new = (char *)xmalloc(answer->i_body + 1);
468         sprintf(psz_new, "HTTP/1.0 %03d %s\r\n", i_status, psz_status);
469         memcpy(&psz_new[strlen(psz_new)], psz_headers, i_headers);
470         free(answer->p_body);
471         answer->p_body = (uint8_t *)psz_new;
472     }
473
474     return VLC_SUCCESS;
475 }
476
477 httpd_handler_t *httpd_HandlerNew(httpd_host_t *host, const char *psz_url,
478                                   const char *psz_user,
479                                   const char *psz_password,
480                                   httpd_handler_callback_t pf_fill,
481                                   void *p_sys)
482 {
483     httpd_handler_t *handler = malloc(sizeof(*handler));
484     if (!handler)
485         return NULL;
486
487     handler->url = httpd_UrlNew(host, psz_url, psz_user, psz_password);
488     if (!handler->url) {
489         free(handler);
490         return NULL;
491     }
492
493     handler->pf_fill = pf_fill;
494     handler->p_sys   = p_sys;
495
496     httpd_UrlCatch(handler->url, HTTPD_MSG_HEAD, httpd_HandlerCallBack,
497                     (httpd_callback_sys_t*)handler);
498     httpd_UrlCatch(handler->url, HTTPD_MSG_GET,  httpd_HandlerCallBack,
499                     (httpd_callback_sys_t*)handler);
500     httpd_UrlCatch(handler->url, HTTPD_MSG_POST, httpd_HandlerCallBack,
501                     (httpd_callback_sys_t*)handler);
502
503     return handler;
504 }
505
506 void *httpd_HandlerDelete(httpd_handler_t *handler)
507 {
508     void *p_sys = handler->p_sys;
509     httpd_UrlDelete(handler->url);
510     free(handler);
511     return p_sys;
512 }
513
514 /*****************************************************************************
515  * High Level Functions: httpd_redirect_t
516  *****************************************************************************/
517 struct httpd_redirect_t
518 {
519     httpd_url_t *url;
520     char         dst[1];
521 };
522
523 static int httpd_RedirectCallBack(httpd_callback_sys_t *p_sys,
524                                    httpd_client_t *cl, httpd_message_t *answer,
525                                    const httpd_message_t *query)
526 {
527     httpd_redirect_t *rdir = (httpd_redirect_t*)p_sys;
528     char *p_body;
529     (void)cl;
530
531     if (!answer || !query)
532         return VLC_SUCCESS;
533
534     answer->i_proto  = HTTPD_PROTO_HTTP;
535     answer->i_version= 1;
536     answer->i_type   = HTTPD_MSG_ANSWER;
537     answer->i_status = 301;
538
539     answer->i_body = httpd_HtmlError (&p_body, 301, rdir->dst);
540     answer->p_body = (unsigned char *)p_body;
541
542     /* XXX check if it's ok or we need to set an absolute url */
543     httpd_MsgAdd(answer, "Location",  "%s", rdir->dst);
544
545     httpd_MsgAdd(answer, "Content-Length", "%d", answer->i_body);
546
547     if (httpd_MsgGet(&cl->query, "Connection") != NULL)
548         httpd_MsgAdd(answer, "Connection", "close");
549
550     return VLC_SUCCESS;
551 }
552
553 httpd_redirect_t *httpd_RedirectNew(httpd_host_t *host, const char *psz_url_dst,
554                                      const char *psz_url_src)
555 {
556     size_t dstlen = strlen(psz_url_dst);
557
558     httpd_redirect_t *rdir = malloc(sizeof(*rdir) + dstlen);
559     if (unlikely(rdir == NULL))
560         return NULL;
561
562     rdir->url = httpd_UrlNew(host, psz_url_src, NULL, NULL);
563     if (!rdir->url) {
564         free(rdir);
565         return NULL;
566     }
567     memcpy(rdir->dst, psz_url_dst, dstlen + 1);
568
569     /* Redirect apply for all HTTP request and RTSP DESCRIBE resquest */
570     httpd_UrlCatch(rdir->url, HTTPD_MSG_HEAD, httpd_RedirectCallBack,
571                     (httpd_callback_sys_t*)rdir);
572     httpd_UrlCatch(rdir->url, HTTPD_MSG_GET, httpd_RedirectCallBack,
573                     (httpd_callback_sys_t*)rdir);
574     httpd_UrlCatch(rdir->url, HTTPD_MSG_POST, httpd_RedirectCallBack,
575                     (httpd_callback_sys_t*)rdir);
576     httpd_UrlCatch(rdir->url, HTTPD_MSG_DESCRIBE, httpd_RedirectCallBack,
577                     (httpd_callback_sys_t*)rdir);
578
579     return rdir;
580 }
581 void httpd_RedirectDelete(httpd_redirect_t *rdir)
582 {
583     httpd_UrlDelete(rdir->url);
584     free(rdir);
585 }
586
587 /*****************************************************************************
588  * High Level Funtions: httpd_stream_t
589  *****************************************************************************/
590 struct httpd_stream_t
591 {
592     vlc_mutex_t lock;
593     httpd_url_t *url;
594
595     char    *psz_mime;
596
597     /* Header to send as first packet */
598     uint8_t *p_header;
599     int     i_header;
600
601     /* Some muxes, in particular the avformat mux, can mark given blocks
602      * as keyframes, to ensure that the stream starts with one.
603      * (This is particularly important for WebM streaming to certain
604      * browsers.) Store if we've ever seen any such keyframe blocks,
605      * and if so, the byte position of the start of the last one. */
606     bool        b_has_keyframes;
607     int64_t     i_last_keyframe_seen_pos;
608
609     /* circular buffer */
610     int         i_buffer_size;      /* buffer size, can't be reallocated smaller */
611     uint8_t     *p_buffer;          /* buffer */
612     int64_t     i_buffer_pos;       /* absolute position from beginning */
613     int64_t     i_buffer_last_pos;  /* a new connection will start with that */
614
615     /* custom headers */
616     size_t        i_http_headers;
617     httpd_header * p_http_headers;
618 };
619
620 static int httpd_StreamCallBack(httpd_callback_sys_t *p_sys,
621                                  httpd_client_t *cl, httpd_message_t *answer,
622                                  const httpd_message_t *query)
623 {
624     httpd_stream_t *stream = (httpd_stream_t*)p_sys;
625
626     if (!answer || !query || !cl)
627         return VLC_SUCCESS;
628
629     if (answer->i_body_offset > 0) {
630         int     i_pos;
631
632         if (answer->i_body_offset >= stream->i_buffer_pos)
633             return VLC_EGENERIC;    /* wait, no data available */
634
635         if (cl->i_keyframe_wait_to_pass >= 0) {
636             if (stream->i_last_keyframe_seen_pos <= cl->i_keyframe_wait_to_pass)
637                 /* still waiting for the next keyframe */
638                 return VLC_EGENERIC;
639
640             /* seek to the new keyframe */
641             answer->i_body_offset = stream->i_last_keyframe_seen_pos;
642             cl->i_keyframe_wait_to_pass = -1;
643         }
644
645         if (answer->i_body_offset + stream->i_buffer_size < stream->i_buffer_pos)
646             answer->i_body_offset = stream->i_buffer_last_pos; /* this client isn't fast enough */
647
648         i_pos   = answer->i_body_offset % stream->i_buffer_size;
649         int64_t i_write = stream->i_buffer_pos - answer->i_body_offset;
650
651         if (i_write > HTTPD_CL_BUFSIZE)
652             i_write = HTTPD_CL_BUFSIZE;
653         else if (i_write <= 0)
654             return VLC_EGENERIC;    /* wait, no data available */
655
656         /* Don't go past the end of the circular buffer */
657         i_write = __MIN(i_write, stream->i_buffer_size - i_pos);
658
659         /* using HTTPD_MSG_ANSWER -> data available */
660         answer->i_proto  = HTTPD_PROTO_HTTP;
661         answer->i_version= 0;
662         answer->i_type   = HTTPD_MSG_ANSWER;
663
664         answer->i_body = i_write;
665         answer->p_body = xmalloc(i_write);
666         memcpy(answer->p_body, &stream->p_buffer[i_pos], i_write);
667
668         answer->i_body_offset += i_write;
669
670         return VLC_SUCCESS;
671     } else {
672         answer->i_proto  = HTTPD_PROTO_HTTP;
673         answer->i_version= 0;
674         answer->i_type   = HTTPD_MSG_ANSWER;
675
676         answer->i_status = 200;
677
678         bool b_has_content_type = false;
679         bool b_has_cache_control = false;
680
681         vlc_mutex_lock(&stream->lock);
682         for (size_t i = 0; i < stream->i_http_headers; i++)
683             if (strncasecmp(stream->p_http_headers[i].name, "Content-Length", 14)) {
684                 httpd_MsgAdd(answer, stream->p_http_headers[i].name, "%s",
685                               stream->p_http_headers[i].value);
686
687                 if (!strncasecmp(stream->p_http_headers[i].name, "Content-Type", 12))
688                     b_has_content_type = true;
689                 else if (!strncasecmp(stream->p_http_headers[i].name, "Cache-Control", 13))
690                     b_has_cache_control = true;
691             }
692         vlc_mutex_unlock(&stream->lock);
693
694         if (query->i_type != HTTPD_MSG_HEAD) {
695             cl->b_stream_mode = true;
696             vlc_mutex_lock(&stream->lock);
697             /* Send the header */
698             if (stream->i_header > 0) {
699                 answer->i_body = stream->i_header;
700                 answer->p_body = xmalloc(stream->i_header);
701                 memcpy(answer->p_body, stream->p_header, stream->i_header);
702             }
703             answer->i_body_offset = stream->i_buffer_last_pos;
704             if (stream->b_has_keyframes)
705                 cl->i_keyframe_wait_to_pass = stream->i_last_keyframe_seen_pos;
706             else
707                 cl->i_keyframe_wait_to_pass = -1;
708             vlc_mutex_unlock(&stream->lock);
709         } else {
710             httpd_MsgAdd(answer, "Content-Length", "0");
711             answer->i_body_offset = 0;
712         }
713
714         /* FIXME: move to http access_output */
715         if (!strcmp(stream->psz_mime, "video/x-ms-asf-stream")) {
716             bool b_xplaystream = false;
717
718             httpd_MsgAdd(answer, "Content-type", "application/octet-stream");
719             httpd_MsgAdd(answer, "Server", "Cougar 4.1.0.3921");
720             httpd_MsgAdd(answer, "Pragma", "no-cache");
721             httpd_MsgAdd(answer, "Pragma", "client-id=%lu",
722                           vlc_mrand48()&0x7fff);
723             httpd_MsgAdd(answer, "Pragma", "features=\"broadcast\"");
724
725             /* Check if there is a xPlayStrm=1 */
726             for (size_t i = 0; i < query->i_headers; i++)
727                 if (!strcasecmp(query->p_headers[i].name,  "Pragma") &&
728                     strstr(query->p_headers[i].value, "xPlayStrm=1"))
729                     b_xplaystream = true;
730
731             if (!b_xplaystream)
732                 answer->i_body_offset = 0;
733         } else if (!b_has_content_type)
734             httpd_MsgAdd(answer, "Content-type", "%s", stream->psz_mime);
735
736         if (!b_has_cache_control)
737             httpd_MsgAdd(answer, "Cache-Control", "no-cache");
738
739         httpd_MsgAdd(answer, "Connection", "close");
740
741         return VLC_SUCCESS;
742     }
743 }
744
745 httpd_stream_t *httpd_StreamNew(httpd_host_t *host,
746                                  const char *psz_url, const char *psz_mime,
747                                  const char *psz_user, const char *psz_password)
748 {
749     httpd_stream_t *stream = malloc(sizeof(*stream));
750     if (!stream)
751         return NULL;
752
753     stream->psz_mime = NULL;
754     stream->p_buffer = NULL;
755
756     stream->url = httpd_UrlNew(host, psz_url, psz_user, psz_password);
757     if (!stream->url)
758         goto error;
759
760     vlc_mutex_init(&stream->lock);
761     if (psz_mime == NULL || psz_mime[0] == '\0')
762         psz_mime = vlc_mime_Ext2Mime(psz_url);
763
764     stream->psz_mime = strdup(psz_mime);
765     if (stream->psz_mime == NULL)
766         goto error;
767
768     stream->i_header = 0;
769     stream->p_header = NULL;
770     stream->i_buffer_size = 5000000;    /* 5 Mo per stream */
771
772     stream->p_buffer = malloc(stream->i_buffer_size);
773     if (stream->p_buffer == NULL)
774         goto error;
775
776     /* We set to 1 to make life simpler
777      * (this way i_body_offset can never be 0) */
778     stream->i_buffer_pos = 1;
779     stream->i_buffer_last_pos = 1;
780     stream->b_has_keyframes = false;
781     stream->i_last_keyframe_seen_pos = 0;
782     stream->i_http_headers = 0;
783     stream->p_http_headers = NULL;
784
785     httpd_UrlCatch(stream->url, HTTPD_MSG_HEAD, httpd_StreamCallBack,
786                     (httpd_callback_sys_t*)stream);
787     httpd_UrlCatch(stream->url, HTTPD_MSG_GET, httpd_StreamCallBack,
788                     (httpd_callback_sys_t*)stream);
789     httpd_UrlCatch(stream->url, HTTPD_MSG_POST, httpd_StreamCallBack,
790                     (httpd_callback_sys_t*)stream);
791
792     return stream;
793
794 error:
795     free(stream->psz_mime);
796
797     if (stream->url)
798         httpd_UrlDelete(stream->url);
799
800     free(stream);
801
802     return NULL;
803 }
804
805 int httpd_StreamHeader(httpd_stream_t *stream, uint8_t *p_data, int i_data)
806 {
807     vlc_mutex_lock(&stream->lock);
808     free(stream->p_header);
809     stream->p_header = NULL;
810
811     stream->i_header = i_data;
812     if (i_data > 0) {
813         stream->p_header = xmalloc(i_data);
814         memcpy(stream->p_header, p_data, i_data);
815     }
816     vlc_mutex_unlock(&stream->lock);
817
818     return VLC_SUCCESS;
819 }
820
821 static void httpd_AppendData(httpd_stream_t *stream, uint8_t *p_data, int i_data)
822 {
823     int i_pos = stream->i_buffer_pos % stream->i_buffer_size;
824     int i_count = i_data;
825     while (i_count > 0) {
826         int i_copy = __MIN(i_count, stream->i_buffer_size - i_pos);
827
828         /* Ok, we can't go past the end of our buffer */
829         memcpy(&stream->p_buffer[i_pos], p_data, i_copy);
830
831         i_pos = (i_pos + i_copy) % stream->i_buffer_size;
832         i_count -= i_copy;
833         p_data  += i_copy;
834     }
835
836     stream->i_buffer_pos += i_data;
837 }
838
839 int httpd_StreamSend(httpd_stream_t *stream, const block_t *p_block)
840 {
841     if (!p_block || !p_block->p_buffer)
842         return VLC_SUCCESS;
843
844     vlc_mutex_lock(&stream->lock);
845
846     /* save this pointer (to be used by new connection) */
847     stream->i_buffer_last_pos = stream->i_buffer_pos;
848
849     if (p_block->i_flags & BLOCK_FLAG_TYPE_I) {
850         stream->b_has_keyframes = true;
851         stream->i_last_keyframe_seen_pos = stream->i_buffer_pos;
852     }
853
854     httpd_AppendData(stream, p_block->p_buffer, p_block->i_buffer);
855
856     vlc_mutex_unlock(&stream->lock);
857     return VLC_SUCCESS;
858 }
859
860 void httpd_StreamDelete(httpd_stream_t *stream)
861 {
862     httpd_UrlDelete(stream->url);
863     for (size_t i = 0; i < stream->i_http_headers; i++) {
864         free(stream->p_http_headers[i].name);
865         free(stream->p_http_headers[i].value);
866     }
867     free(stream->p_http_headers);
868     free(stream->psz_mime);
869     free(stream->p_header);
870     free(stream->p_buffer);
871     free(stream);
872 }
873
874 /*****************************************************************************
875  * Low level
876  *****************************************************************************/
877 static void* httpd_HostThread(void *);
878 static httpd_host_t *httpd_HostCreate(vlc_object_t *, const char *,
879                                        const char *, vlc_tls_server_t *);
880
881 /* create a new host */
882 httpd_host_t *vlc_http_HostNew(vlc_object_t *p_this)
883 {
884     return httpd_HostCreate(p_this, "http-host", "http-port", NULL);
885 }
886
887 httpd_host_t *vlc_https_HostNew(vlc_object_t *obj)
888 {
889     char *cert = var_InheritString(obj, "http-cert");
890     if (!cert) {
891         msg_Err(obj, "HTTP/TLS certificate not specified!");
892         return NULL;
893     }
894
895     char *key = var_InheritString(obj, "http-key");
896     vlc_tls_server_t *tls = vlc_tls_ServerCreate(obj, cert, key);
897
898     if (!tls) {
899         msg_Err(obj, "HTTP/TLS certificate error (%s and %s)",
900                  cert, key ? key : cert);
901         free(key);
902         free(cert);
903         return NULL;
904     }
905     free(key);
906     free(cert);
907
908     return httpd_HostCreate(obj, "http-host", "https-port", tls);
909 }
910
911 httpd_host_t *vlc_rtsp_HostNew(vlc_object_t *p_this)
912 {
913     return httpd_HostCreate(p_this, "rtsp-host", "rtsp-port", NULL);
914 }
915
916 static struct httpd
917 {
918     vlc_mutex_t  mutex;
919     struct vlc_list hosts;
920 } httpd = { VLC_STATIC_MUTEX, VLC_LIST_INITIALIZER(&httpd.hosts) };
921
922 static httpd_host_t *httpd_HostCreate(vlc_object_t *p_this,
923                                        const char *hostvar,
924                                        const char *portvar,
925                                        vlc_tls_server_t *p_tls)
926 {
927     httpd_host_t *host;
928     unsigned port = var_InheritInteger(p_this, portvar);
929
930     /* to be sure to avoid multiple creation */
931     vlc_mutex_lock(&httpd.mutex);
932
933     /* verify if it already exist */
934     vlc_list_foreach(host, &httpd.hosts, node) {
935         /* cannot mix TLS and non-TLS hosts */
936         if (host->port != port
937          || (host->p_tls != NULL) != (p_tls != NULL))
938             continue;
939
940         /* Increase existing matching host reference count. */
941         atomic_fetch_add_explicit(&host->ref, 1, memory_order_relaxed);
942
943         vlc_mutex_unlock(&httpd.mutex);
944         vlc_tls_ServerDelete(p_tls);
945         return host;
946     }
947
948     /* create the new host */
949     host = (httpd_host_t *)vlc_custom_create(p_this, sizeof (*host),
950                                               "http host");
951     if (!host)
952         goto error;
953
954     vlc_mutex_init(&host->lock);
955     atomic_init(&host->ref, 1);
956
957     char *hostname = var_InheritString(p_this, hostvar);
958
959     host->fds = net_ListenTCP(p_this, hostname, port);
960     free(hostname);
961
962     if (!host->fds) {
963         msg_Err(p_this, "cannot create socket(s) for HTTP host");
964         goto error;
965     }
966     for (host->nfd = 0; host->fds[host->nfd] != -1; host->nfd++);
967
968     host->port     = port;
969     vlc_list_init(&host->urls);
970     host->client_count = 0;
971     vlc_list_init(&host->clients);
972     host->p_tls    = p_tls;
973
974     /* create the thread */
975     if (vlc_clone(&host->thread, httpd_HostThread, host,
976                    VLC_THREAD_PRIORITY_LOW)) {
977         msg_Err(p_this, "cannot spawn http host thread");
978         goto error;
979     }
980
981     /* now add it to httpd */
982     vlc_list_append(&host->node, &httpd.hosts);
983     vlc_mutex_unlock(&httpd.mutex);
984
985     return host;
986
987 error:
988     vlc_mutex_unlock(&httpd.mutex);
989
990     if (host) {
991         net_ListenClose(host->fds);
992         vlc_object_delete(host);
993     }
994
995     vlc_tls_ServerDelete(p_tls);
996     return NULL;
997 }
998
999 /* delete a host */
1000 void httpd_HostDelete(httpd_host_t *host)
1001 {
1002     httpd_client_t *client;
1003
1004     vlc_mutex_lock(&httpd.mutex);
1005
1006     if (atomic_fetch_sub_explicit(&host->ref, 1, memory_order_relaxed) > 1) {
1007         /* still used */
1008         vlc_mutex_unlock(&httpd.mutex);
1009         msg_Dbg(host, "httpd_HostDelete: host still in use");
1010         return;
1011     }
1012
1013     vlc_list_remove(&host->node);
1014     vlc_cancel(host->thread);
1015     vlc_join(host->thread, NULL);
1016
1017     msg_Dbg(host, "HTTP host removed");
1018
1019     vlc_list_foreach(client, &host->clients, node) {
1020         msg_Warn(host, "client still connected");
1021         httpd_ClientDestroy(client);
1022     }
1023
1024     assert(vlc_list_is_empty(&host->urls));
1025     vlc_tls_ServerDelete(host->p_tls);
1026     net_ListenClose(host->fds);
1027     vlc_object_delete(host);
1028     vlc_mutex_unlock(&httpd.mutex);
1029 }
1030
1031 /* register a new url */
1032 httpd_url_t *httpd_UrlNew(httpd_host_t *host, const char *psz_url,
1033                            const char *psz_user, const char *psz_password)
1034 {
1035     httpd_url_t *url;
1036
1037     assert(psz_url);
1038
1039     vlc_mutex_lock(&host->lock);
1040     vlc_list_foreach(url, &host->urls, node)
1041         if (!strcmp(psz_url, url->psz_url)) {
1042             msg_Warn(host, "cannot add '%s' (url already defined)", psz_url);
1043             vlc_mutex_unlock(&host->lock);
1044             return NULL;
1045         }
1046
1047     url = malloc(sizeof (*url));
1048     if (unlikely(url == NULL)) {
1049         vlc_mutex_unlock(&host->lock);
1050         return NULL;
1051     }
1052     url->psz_url = NULL;
1053     url->psz_user = NULL;
1054     url->psz_password = NULL;
1055
1056     url->host = host;
1057
1058     vlc_mutex_init(&url->lock);
1059
1060     url->psz_url = strdup(psz_url);
1061     if (url->psz_url == NULL)
1062         goto error;
1063
1064     url->psz_user = strdup(psz_user ? psz_user : "");
1065     if (url->psz_user == NULL)
1066         goto error;
1067
1068     url->psz_password = strdup(psz_password ? psz_password : "");
1069     if (url->psz_password == NULL)
1070         goto error;
1071
1072     for (int i = 0; i < HTTPD_MSG_MAX; i++) {
1073         url->catch[i].cb = NULL;
1074         url->catch[i].p_sys = NULL;
1075     }
1076
1077     vlc_list_append(&url->node, &host->urls);
1078     vlc_mutex_unlock(&host->lock);
1079
1080     return url;
1081
1082 error:
1083     free(url->psz_password);
1084     free(url->psz_user);
1085     free(url->psz_url);
1086
1087     free(url);
1088     return NULL;
1089 }
1090
1091 /* register callback on a url */
1092 int httpd_UrlCatch(httpd_url_t *url, int i_msg, httpd_callback_t cb,
1093                     httpd_callback_sys_t *p_sys)
1094 {
1095     vlc_mutex_lock(&url->lock);
1096     url->catch[i_msg].cb   = cb;
1097     url->catch[i_msg].p_sys= p_sys;
1098     vlc_mutex_unlock(&url->lock);
1099
1100     return VLC_SUCCESS;
1101 }
1102
1103 /* delete a url */
1104 void httpd_UrlDelete(httpd_url_t *url)
1105 {
1106     httpd_host_t *host = url->host;
1107     httpd_client_t *client;
1108
1109     vlc_mutex_lock(&host->lock);
1110     vlc_list_remove(&url->node);
1111
1112     free(url->psz_url);
1113     free(url->psz_user);
1114     free(url->psz_password);
1115
1116     vlc_list_foreach(client, &host->clients, node) {
1117         if (client->url != url)
1118             continue;
1119
1120         /* TODO complete it */
1121         msg_Warn(host, "force closing connections");
1122         host->client_count--;
1123         httpd_ClientDestroy(client);
1124     }
1125     free(url);
1126     vlc_mutex_unlock(&host->lock);
1127 }
1128
1129 static void httpd_MsgInit(httpd_message_t *msg)
1130 {
1131     msg->cl         = NULL;
1132     msg->i_type     = HTTPD_MSG_NONE;
1133     msg->i_proto    = HTTPD_PROTO_NONE;
1134     msg->i_version  = -1; /* FIXME */
1135
1136     msg->i_status   = 0;
1137
1138     msg->psz_url    = NULL;
1139     msg->psz_args   = NULL;
1140
1141     msg->i_headers  = 0;
1142     msg->p_headers  = NULL;
1143
1144     msg->i_body_offset = 0;
1145     msg->i_body        = 0;
1146     msg->p_body        = NULL;
1147 }
1148
1149 static void httpd_MsgClean(httpd_message_t *msg)
1150 {
1151     free(msg->psz_url);
1152     free(msg->psz_args);
1153     for (size_t i = 0; i < msg->i_headers; i++) {
1154         free(msg->p_headers[i].name);
1155         free(msg->p_headers[i].value);
1156     }
1157     free(msg->p_headers);
1158     free(msg->p_body);
1159     httpd_MsgInit(msg);
1160 }
1161
1162 const char *httpd_MsgGet(const httpd_message_t *msg, const char *name)
1163 {
1164     for (size_t i = 0; i < msg->i_headers; i++)
1165         if (!strcasecmp(msg->p_headers[i].name, name))
1166             return msg->p_headers[i].value;
1167     return NULL;
1168 }
1169
1170 void httpd_MsgAdd(httpd_message_t *msg, const char *name, const char *psz_value, ...)
1171 {
1172     httpd_header *p_tmp = realloc(msg->p_headers, sizeof(httpd_header) * (msg->i_headers + 1));
1173     if (!p_tmp)
1174         return;
1175
1176     msg->p_headers = p_tmp;
1177
1178     httpd_header *h = &msg->p_headers[msg->i_headers];
1179     h->name = strdup(name);
1180     if (!h->name)
1181         return;
1182
1183     h->value = NULL;
1184
1185     va_list args;
1186     va_start(args, psz_value);
1187     int ret = us_vasprintf(&h->value, psz_value, args);
1188     va_end(args);
1189
1190     if (ret == -1) {
1191         free(h->name);
1192         return;
1193     }
1194
1195     msg->i_headers++;
1196 }
1197
1198 static void httpd_ClientInit(httpd_client_t *cl, vlc_tick_t now)
1199 {
1200     cl->i_state = HTTPD_CLIENT_RECEIVING;
1201     cl->i_activity_date = now;
1202     cl->i_activity_timeout = VLC_TICK_FROM_SEC(10);
1203     cl->i_buffer_size = HTTPD_CL_BUFSIZE;
1204     cl->i_buffer = 0;
1205     cl->p_buffer = xmalloc(cl->i_buffer_size);
1206     cl->i_keyframe_wait_to_pass = -1;
1207     cl->b_stream_mode = false;
1208
1209     httpd_MsgInit(&cl->query);
1210     httpd_MsgInit(&cl->answer);
1211 }
1212
1213 char* httpd_ClientIP(const httpd_client_t *cl, char *ip, int *port)
1214 {
1215     return net_GetPeerAddress(vlc_tls_GetFD(cl->sock), ip, port) ? NULL : ip;
1216 }
1217
1218 char* httpd_ServerIP(const httpd_client_t *cl, char *ip, int *port)
1219 {
1220     return net_GetSockAddress(vlc_tls_GetFD(cl->sock), ip, port) ? NULL : ip;
1221 }
1222
1223 static void httpd_ClientDestroy(httpd_client_t *cl)
1224 {
1225     vlc_list_remove(&cl->node);
1226     vlc_tls_Close(cl->sock);
1227     httpd_MsgClean(&cl->answer);
1228     httpd_MsgClean(&cl->query);
1229
1230     free(cl->p_buffer);
1231     free(cl);
1232 }
1233
1234 static httpd_client_t *httpd_ClientNew(vlc_tls_t *sock, vlc_tick_t now)
1235 {
1236     httpd_client_t *cl = malloc(sizeof(httpd_client_t));
1237
1238     if (!cl) return NULL;
1239
1240     cl->sock    = sock;
1241     cl->url     = NULL;
1242
1243     httpd_ClientInit(cl, now);
1244     return cl;
1245 }
1246
1247 static
1248 ssize_t httpd_NetRecv (httpd_client_t *cl, uint8_t *p, size_t i_len)
1249 {
1250     vlc_tls_t *sock = cl->sock;
1251     struct iovec iov = { .iov_base = p, .iov_len = i_len };
1252     return sock->ops->readv(sock, &iov, 1);
1253 }
1254
1255 static
1256 ssize_t httpd_NetSend (httpd_client_t *cl, const uint8_t *p, size_t i_len)
1257 {
1258     vlc_tls_t *sock = cl->sock;
1259     const struct iovec iov = { .iov_base = (void *)p, .iov_len = i_len };
1260     return sock->ops->writev(sock, &iov, 1);
1261 }
1262
1263
1264 static const struct
1265 {
1266     const char name[16];
1267     int  i_type;
1268     int  i_proto;
1269 }
1270 msg_type[] =
1271 {
1272     { "OPTIONS",       HTTPD_MSG_OPTIONS,      HTTPD_PROTO_RTSP },
1273     { "DESCRIBE",      HTTPD_MSG_DESCRIBE,     HTTPD_PROTO_RTSP },
1274     { "SETUP",         HTTPD_MSG_SETUP,        HTTPD_PROTO_RTSP },
1275     { "PLAY",          HTTPD_MSG_PLAY,         HTTPD_PROTO_RTSP },
1276     { "PAUSE",         HTTPD_MSG_PAUSE,        HTTPD_PROTO_RTSP },
1277     { "GET_PARAMETER", HTTPD_MSG_GETPARAMETER, HTTPD_PROTO_RTSP },
1278     { "TEARDOWN",      HTTPD_MSG_TEARDOWN,     HTTPD_PROTO_RTSP },
1279     { "GET",           HTTPD_MSG_GET,          HTTPD_PROTO_HTTP },
1280     { "HEAD",          HTTPD_MSG_HEAD,         HTTPD_PROTO_HTTP },
1281     { "POST",          HTTPD_MSG_POST,         HTTPD_PROTO_HTTP },
1282 };
1283
1284
1285 static void httpd_ClientRecv(httpd_client_t *cl)
1286 {
1287     int i_len;
1288
1289     /* ignore leading whites */
1290     if (cl->query.i_proto == HTTPD_PROTO_NONE && cl->i_buffer == 0) {
1291         unsigned char c;
1292
1293         i_len = httpd_NetRecv(cl, &c, 1);
1294
1295         if (i_len > 0 && !strchr("\r\n\t ", c)) {
1296             cl->p_buffer[0] = c;
1297             cl->i_buffer++;
1298         }
1299     } else if (cl->query.i_proto == HTTPD_PROTO_NONE) {
1300         /* enough to see if it's Interleaved RTP over RTSP or RTSP/HTTP */
1301         i_len = httpd_NetRecv(cl, &cl->p_buffer[cl->i_buffer],
1302                                7 - cl->i_buffer);
1303         if (i_len > 0)
1304             cl->i_buffer += i_len;
1305
1306         /* The smallest legal request is 7 bytes ("GET /\r\n"),
1307          * this is the maximum we can ask at this point. */
1308         if (cl->i_buffer >= 7) {
1309             if (!memcmp(cl->p_buffer, "HTTP/1.", 7)) {
1310                 cl->query.i_proto = HTTPD_PROTO_HTTP;
1311                 cl->query.i_type  = HTTPD_MSG_ANSWER;
1312             } else if (!memcmp(cl->p_buffer, "RTSP/1.", 7)) {
1313                 cl->query.i_proto = HTTPD_PROTO_RTSP;
1314                 cl->query.i_type  = HTTPD_MSG_ANSWER;
1315             } else {
1316                 /* We need the full request line to determine the protocol. */
1317                 cl->query.i_proto = HTTPD_PROTO_HTTP0;
1318                 cl->query.i_type  = HTTPD_MSG_NONE;
1319             }
1320         }
1321     } else if (cl->query.i_body > 0) {
1322         /* we are reading the body of a request or a channel */
1323         assert (cl->query.p_body != NULL);
1324         i_len = httpd_NetRecv(cl, &cl->query.p_body[cl->i_buffer],
1325                                cl->query.i_body - cl->i_buffer);
1326         if (i_len > 0)
1327             cl->i_buffer += i_len;
1328
1329         if (cl->i_buffer >= cl->query.i_body)
1330             cl->i_state = HTTPD_CLIENT_RECEIVE_DONE;
1331     } else for (;;) { /* we are reading a header -> char by char */
1332         if (cl->i_buffer == cl->i_buffer_size) {
1333             // Allocate an extra byte for the termination null byte
1334             uint8_t *newbuf = realloc(cl->p_buffer, cl->i_buffer_size + 1025);
1335             if (!newbuf) {
1336                 i_len = 0;
1337                 break;
1338             }
1339
1340             cl->p_buffer = newbuf;
1341             cl->i_buffer_size += 1024;
1342         }
1343
1344         i_len = httpd_NetRecv (cl, &cl->p_buffer[cl->i_buffer], 1);
1345         if (i_len <= 0)
1346             break;
1347
1348         cl->i_buffer++;
1349
1350         if ((cl->query.i_proto == HTTPD_PROTO_HTTP0)
1351                 && (cl->p_buffer[cl->i_buffer - 1] == '\n'))
1352         {
1353             /* Request line is now complete */
1354             const char *p = memchr(cl->p_buffer, ' ', cl->i_buffer);
1355             size_t len;
1356
1357             assert(cl->query.i_type == HTTPD_MSG_NONE);
1358
1359             if (!p) { /* no URI: evil guy */
1360                 i_len = 0; /* drop connection */
1361                 break;
1362             }
1363
1364             do
1365                 p++; /* skips extra spaces */
1366             while (*p == ' ');
1367
1368             p = memchr(p, ' ', ((char *)cl->p_buffer) + cl->i_buffer - p);
1369             if (!p) { /* no explicit protocol: HTTP/0.9 */
1370                 i_len = 0; /* not supported currently -> drop */
1371                 break;
1372             }
1373
1374             do
1375                 p++; /* skips extra spaces ever again */
1376             while (*p == ' ');
1377
1378             len = ((char *)cl->p_buffer) + cl->i_buffer - p;
1379             if (len < 7) /* foreign protocol */
1380                 i_len = 0; /* I don't understand -> drop */
1381             else if (!memcmp(p, "HTTP/1.", 7)) {
1382                 cl->query.i_proto = HTTPD_PROTO_HTTP;
1383                 cl->query.i_version = atoi(p + 7);
1384             } else if (!memcmp(p, "RTSP/1.", 7)) {
1385                 cl->query.i_proto = HTTPD_PROTO_RTSP;
1386                 cl->query.i_version = atoi(p + 7);
1387             } else if (!memcmp(p, "HTTP/", 5)) {
1388                 const uint8_t sorry[] =
1389                     "HTTP/1.1 505 Unknown HTTP version\r\n\r\n";
1390                 httpd_NetSend(cl, sorry, sizeof(sorry) - 1);
1391                 i_len = 0; /* drop */
1392             } else if (!memcmp(p, "RTSP/", 5)) {
1393                 const uint8_t sorry[] =
1394                     "RTSP/1.0 505 Unknown RTSP version\r\n\r\n";
1395                 httpd_NetSend(cl, sorry, sizeof(sorry) - 1);
1396                 i_len = 0; /* drop */
1397             } else /* yet another foreign protocol */
1398                 i_len = 0;
1399
1400             if (i_len == 0)
1401                 break;
1402         }
1403
1404         if ((cl->i_buffer >= 2 && !memcmp(&cl->p_buffer[cl->i_buffer-2], "\n\n", 2))||
1405                 (cl->i_buffer >= 4 && !memcmp(&cl->p_buffer[cl->i_buffer-4], "\r\n\r\n", 4)))
1406         {
1407             char *p;
1408
1409             /* we have finished the header so parse it and set i_body */
1410             cl->p_buffer[cl->i_buffer] = '\0';
1411
1412             if (cl->query.i_type == HTTPD_MSG_ANSWER) {
1413                 /* FIXME:
1414                  * assume strlen("HTTP/1.x") = 8
1415                  */
1416                 cl->query.i_status =
1417                     strtol((char *)&cl->p_buffer[8],
1418                             &p, 0);
1419                 while (*p == ' ')
1420                     p++;
1421             } else {
1422                 p = NULL;
1423                 cl->query.i_type = HTTPD_MSG_NONE;
1424
1425                 for (unsigned i = 0; i < ARRAY_SIZE(msg_type); i++)
1426                     if (cl->query.i_proto == msg_type[i].i_proto
1427                      && strncmp((char *)cl->p_buffer, msg_type[i].name,
1428                                 strlen(msg_type[i].name)) == 0) {
1429                         p = (char *)&cl->p_buffer[strlen(msg_type[i].name) + 1 ];
1430                         cl->query.i_type = msg_type[i].i_type;
1431                         break;
1432                     }
1433
1434                 if (!p) {
1435                     if (strstr((char *)cl->p_buffer, "HTTP/1."))
1436                         cl->query.i_proto = HTTPD_PROTO_HTTP;
1437                     else if (strstr((char *)cl->p_buffer, "RTSP/1."))
1438                         cl->query.i_proto = HTTPD_PROTO_RTSP;
1439                 } else {
1440                     char *p2;
1441                     char *p3;
1442
1443                     while (*p == ' ')
1444                         p++;
1445
1446                     p2 = strchr(p, ' ');
1447                     if (p2)
1448                         *p2++ = '\0';
1449
1450                     if (!strncasecmp(p, (cl->query.i_proto == HTTPD_PROTO_HTTP) ? "http:" : "rtsp:", 5)) {
1451                         /* Skip hier-part of URL (if present) */
1452                         p += 5;
1453                         if (!strncmp(p, "//", 2)) { /* skip authority */
1454                             /* see RFC3986 §3.2 */
1455                             p += 2;
1456                             p += strcspn(p, "/?#");
1457                         }
1458                     }
1459                     else if (!strncasecmp(p, (cl->query.i_proto == HTTPD_PROTO_HTTP) ? "https:" : "rtsps:", 6)) {
1460                         /* Skip hier-part of URL (if present) */
1461                         p += 6;
1462                         if (!strncmp(p, "//", 2)) { /* skip authority */
1463                             /* see RFC3986 §3.2 */
1464                             p += 2;
1465                             p += strcspn(p, "/?#");
1466                         }
1467                     }
1468
1469                     if(cl->query.psz_url == NULL) {
1470                         cl->query.psz_url = strdup(p);
1471                         if ((p3 = strchr(cl->query.psz_url, '?')) ) {
1472                             *p3++ = '\0';
1473                             cl->query.psz_args = (uint8_t *)strdup(p3);
1474                         }
1475                     }
1476                     p = p2;
1477                 }
1478             }
1479             if (p)
1480                 p = strchr(p, '\n');
1481
1482             if (p) {
1483                 while (*p == '\n' || *p == '\r')
1484                     p++;
1485
1486                 while (p && *p) {
1487                     char *line = p;
1488                     char *eol = p = strchr(p, '\n');
1489                     char *colon;
1490
1491                     while (eol && eol >= line && (*eol == '\n' || *eol == '\r'))
1492                         *eol-- = '\0';
1493
1494                     if ((colon = strchr(line, ':'))) {
1495                         *colon++ = '\0';
1496                         while (*colon == ' ')
1497                             colon++;
1498                         httpd_MsgAdd(&cl->query, line, "%s", colon);
1499
1500                         if (!strcasecmp(line, "Content-Length"))
1501                             cl->query.i_body = atol(colon);
1502                     }
1503
1504                     if (p) {
1505                         p++;
1506                         while (*p == '\n' || *p == '\r')
1507                             p++;
1508                     }
1509                 }
1510             }
1511             if (cl->query.i_body > 0) {
1512                 /* TODO Mhh, handle the case where the client only
1513                  * sends a request and closes the connection to
1514                  * mark the end of the body (probably only RTSP) */
1515                 if (cl->query.i_body < 65536)
1516                     cl->query.p_body = malloc(cl->query.i_body);
1517                 else
1518                     cl->query.p_body = NULL;
1519                 cl->i_buffer = 0;
1520                 if (!cl->query.p_body) {
1521                     switch (cl->query.i_proto) {
1522                         case HTTPD_PROTO_HTTP: {
1523                             const uint8_t sorry[] = "HTTP/1.1 413 Request Entity Too Large\r\n\r\n";
1524                             httpd_NetSend(cl, sorry, sizeof(sorry) - 1);
1525                             break;
1526                         }
1527                         case HTTPD_PROTO_RTSP: {
1528                             const uint8_t sorry[] = "RTSP/1.0 413 Request Entity Too Large\r\n\r\n";
1529                             httpd_NetSend(cl, sorry, sizeof(sorry) - 1);
1530                             break;
1531                         }
1532                         default:
1533                             vlc_assert_unreachable();
1534                     }
1535                     i_len = 0; /* drop */
1536                 }
1537                 break;
1538             } else
1539                 cl->i_state = HTTPD_CLIENT_RECEIVE_DONE;
1540         }
1541     }
1542
1543     if (i_len == 0) {
1544         if (cl->query.i_proto != HTTPD_PROTO_NONE && cl->query.i_type != HTTPD_MSG_NONE) {
1545             /* connection closed -> end of data */
1546             if (cl->query.i_body > 0)
1547                 cl->query.i_body = cl->i_buffer;
1548             cl->i_state = HTTPD_CLIENT_RECEIVE_DONE;
1549         }
1550         else
1551             cl->i_state = HTTPD_CLIENT_DEAD; /* connection failed */
1552         return;
1553     }
1554
1555     /* check if the client is to be set to dead */
1556     if (i_len < 0) {
1557 #if defined(_WIN32)
1558         if (WSAGetLastError() != WSAEWOULDBLOCK)
1559 #else
1560         if (errno != EAGAIN)
1561 #endif
1562             cl->i_state = HTTPD_CLIENT_DEAD; /* connection failed */
1563         return;
1564     }
1565
1566     /* XXX: for QT I have to disable timeout. Try to find why */
1567     if (cl->query.i_proto == HTTPD_PROTO_RTSP)
1568         cl->i_activity_timeout = 0;
1569 }
1570
1571 static void httpd_ClientSend(httpd_client_t *cl)
1572 {
1573     int i_len;
1574
1575     if (cl->i_buffer < 0) {
1576         /* We need to create the header */
1577         int i_size = 0;
1578         char *p;
1579         const char *psz_status = httpd_ReasonFromCode(cl->answer.i_status);
1580
1581         i_size = strlen("HTTP/1.") + 10 + 10 + strlen(psz_status) + 5;
1582         for (size_t i = 0; i < cl->answer.i_headers; i++)
1583             i_size += strlen(cl->answer.p_headers[i].name) + 2 +
1584                       strlen(cl->answer.p_headers[i].value) + 2;
1585
1586         if (cl->i_buffer_size < i_size) {
1587             cl->i_buffer_size = i_size;
1588             free(cl->p_buffer);
1589             cl->p_buffer = xmalloc(i_size);
1590         }
1591         p = (char *)cl->p_buffer;
1592
1593         p += sprintf(p, "%s.%" PRIu8 " %d %s\r\n",
1594                       cl->answer.i_proto ==  HTTPD_PROTO_HTTP ? "HTTP/1" : "RTSP/1",
1595                       cl->answer.i_version,
1596                       cl->answer.i_status, psz_status);
1597         for (size_t i = 0; i < cl->answer.i_headers; i++)
1598             p += sprintf(p, "%s: %s\r\n", cl->answer.p_headers[i].name,
1599                           cl->answer.p_headers[i].value);
1600         p += sprintf(p, "\r\n");
1601
1602         cl->i_buffer = 0;
1603         cl->i_buffer_size = (uint8_t*)p - cl->p_buffer;
1604     }
1605
1606     i_len = httpd_NetSend(cl, &cl->p_buffer[cl->i_buffer],
1607                            cl->i_buffer_size - cl->i_buffer);
1608
1609     if (i_len == 0) {
1610         cl->i_state = HTTPD_CLIENT_DEAD; /* connection closed */
1611         return;
1612     }
1613
1614     if (i_len < 0) {
1615 #if defined(_WIN32)
1616         if (WSAGetLastError() != WSAEWOULDBLOCK)
1617 #else
1618         if (errno != EAGAIN)
1619 #endif
1620             /* Connection failed, or hung up (EPIPE) */
1621             cl->i_state = HTTPD_CLIENT_DEAD;
1622         return;
1623     }
1624
1625     cl->i_buffer += i_len;
1626
1627     if (cl->i_buffer >= cl->i_buffer_size) {
1628         if (cl->answer.i_body == 0  && cl->answer.i_body_offset > 0) {
1629             /* catch more body data */
1630             int     i_msg = cl->query.i_type;
1631             int64_t i_offset = cl->answer.i_body_offset;
1632
1633             httpd_MsgClean(&cl->answer);
1634             cl->answer.i_body_offset = i_offset;
1635
1636             cl->url->catch[i_msg].cb(cl->url->catch[i_msg].p_sys, cl,
1637                                      &cl->answer, &cl->query);
1638         }
1639
1640         if (cl->answer.i_body > 0) {
1641             /* send the body data */
1642             free(cl->p_buffer);
1643             cl->p_buffer = cl->answer.p_body;
1644             cl->i_buffer_size = cl->answer.i_body;
1645             cl->i_buffer = 0;
1646
1647             cl->answer.i_body = 0;
1648             cl->answer.p_body = NULL;
1649         } else /* send finished */
1650             cl->i_state = HTTPD_CLIENT_SEND_DONE;
1651     }
1652 }
1653
1654 static void httpd_ClientTlsHandshake(httpd_host_t *host, httpd_client_t *cl)
1655 {
1656     switch (vlc_tls_SessionHandshake(host->p_tls, cl->sock))
1657     {
1658         case -1: cl->i_state = HTTPD_CLIENT_DEAD;       break;
1659         case 0:  cl->i_state = HTTPD_CLIENT_RECEIVING;  break;
1660         case 1:  cl->i_state = HTTPD_CLIENT_TLS_HS_IN;  break;
1661         case 2:  cl->i_state = HTTPD_CLIENT_TLS_HS_OUT; break;
1662     }
1663 }
1664
1665 static bool httpdAuthOk(const char *user, const char *pass, const char *b64)
1666 {
1667     if (!*user && !*pass)
1668         return true;
1669
1670     if (!b64)
1671         return false;
1672
1673     if (strncasecmp(b64, "BASIC", 5))
1674         return false;
1675
1676     b64 += 5;
1677     while (*b64 == ' ')
1678         b64++;
1679
1680     char *given_user = vlc_b64_decode(b64);
1681     if (!given_user)
1682         return false;
1683
1684     char *given_pass = NULL;
1685     given_pass = strchr (given_user, ':');
1686     if (!given_pass)
1687         goto auth_failed;
1688
1689     *given_pass++ = '\0';
1690
1691     if (strcmp (given_user, user))
1692         goto auth_failed;
1693
1694     if (strcmp (given_pass, pass))
1695         goto auth_failed;
1696
1697     free(given_user);
1698     return true;
1699
1700 auth_failed:
1701     free(given_user);
1702     return false;
1703 }
1704
1705 static void httpdLoop(httpd_host_t *host)
1706 {
1707     struct pollfd ufd[host->nfd + host->client_count];
1708     unsigned nfd;
1709     for (nfd = 0; nfd < host->nfd; nfd++) {
1710         ufd[nfd].fd = host->fds[nfd];
1711         ufd[nfd].events = POLLIN;
1712         ufd[nfd].revents = 0;
1713     }
1714
1715     vlc_mutex_lock(&host->lock);
1716     /* add all socket that should be read/write and close dead connection */
1717     vlc_tick_t now = vlc_tick_now();
1718     bool b_low_delay = false;
1719     httpd_client_t *cl;
1720
1721     int canc = vlc_savecancel();
1722     vlc_list_foreach(cl, &host->clients, node) {
1723         if (cl->i_state == HTTPD_CLIENT_DEAD
1724          || (cl->i_activity_timeout > 0
1725           && cl->i_activity_date + cl->i_activity_timeout < now)) {
1726             host->client_count--;
1727             httpd_ClientDestroy(cl);
1728             continue;
1729         }
1730
1731         struct pollfd *pufd = ufd + nfd;
1732         assert (pufd < ufd + ARRAY_SIZE (ufd));
1733
1734         pufd->events = pufd->revents = 0;
1735
1736         switch (cl->i_state) {
1737             case HTTPD_CLIENT_RECEIVING:
1738             case HTTPD_CLIENT_TLS_HS_IN:
1739                 pufd->events = POLLIN;
1740                 break;
1741
1742             case HTTPD_CLIENT_SENDING:
1743             case HTTPD_CLIENT_TLS_HS_OUT:
1744                 pufd->events = POLLOUT;
1745                 break;
1746
1747             case HTTPD_CLIENT_RECEIVE_DONE: {
1748                 httpd_message_t *answer = &cl->answer;
1749                 httpd_message_t *query  = &cl->query;
1750
1751                 httpd_MsgInit(answer);
1752
1753                 /* Handle what we received */
1754                 switch (query->i_type) {
1755                     case HTTPD_MSG_ANSWER:
1756                         cl->url     = NULL;
1757                         cl->i_state = HTTPD_CLIENT_DEAD;
1758                         break;
1759
1760                     case HTTPD_MSG_OPTIONS:
1761                         answer->i_type   = HTTPD_MSG_ANSWER;
1762                         answer->i_proto  = query->i_proto;
1763                         answer->i_status = 200;
1764                         answer->i_body = 0;
1765                         answer->p_body = NULL;
1766
1767                         httpd_MsgAdd(answer, "Server", "VLC/%s", VERSION);
1768                         httpd_MsgAdd(answer, "Content-Length", "0");
1769
1770                         switch(query->i_proto) {
1771                         case HTTPD_PROTO_HTTP:
1772                             answer->i_version = 1;
1773                             httpd_MsgAdd(answer, "Allow", "GET,HEAD,POST,OPTIONS");
1774                             break;
1775
1776                         case HTTPD_PROTO_RTSP:
1777                             answer->i_version = 0;
1778
1779                             const char *p = httpd_MsgGet(query, "Cseq");
1780                             if (p)
1781                                 httpd_MsgAdd(answer, "Cseq", "%s", p);
1782                             p = httpd_MsgGet(query, "Timestamp");
1783                             if (p)
1784                                 httpd_MsgAdd(answer, "Timestamp", "%s", p);
1785
1786                             p = httpd_MsgGet(query, "Require");
1787                             if (p) {
1788                                 answer->i_status = 551;
1789                                 httpd_MsgAdd(query, "Unsupported", "%s", p);
1790                             }
1791
1792                             httpd_MsgAdd(answer, "Public", "DESCRIBE,SETUP,"
1793                                     "TEARDOWN,PLAY,PAUSE,GET_PARAMETER");
1794                             break;
1795                         }
1796
1797                         if (httpd_MsgGet(&cl->query, "Connection") != NULL)
1798                             httpd_MsgAdd(answer, "Connection", "close");
1799
1800                         cl->i_buffer = -1;  /* Force the creation of the answer in
1801                                              * httpd_ClientSend */
1802                         cl->i_state = HTTPD_CLIENT_SENDING;
1803                         break;
1804
1805                     case HTTPD_MSG_NONE:
1806                         if (query->i_proto == HTTPD_PROTO_NONE) {
1807                             cl->url = NULL;
1808                             cl->i_state = HTTPD_CLIENT_DEAD;
1809                         } else {
1810                             /* unimplemented */
1811                             answer->i_proto  = query->i_proto ;
1812                             answer->i_type   = HTTPD_MSG_ANSWER;
1813                             answer->i_version= 0;
1814                             answer->i_status = 501;
1815
1816                             char *p;
1817                             answer->i_body = httpd_HtmlError (&p, 501, NULL);
1818                             answer->p_body = (uint8_t *)p;
1819                             httpd_MsgAdd(answer, "Content-Length", "%d", answer->i_body);
1820                             httpd_MsgAdd(answer, "Connection", "close");
1821
1822                             cl->i_buffer = -1;  /* Force the creation of the answer in httpd_ClientSend */
1823                             cl->i_state = HTTPD_CLIENT_SENDING;
1824                         }
1825                         break;
1826
1827                     default: {
1828                         httpd_url_t *url;
1829                         int i_msg = query->i_type;
1830                         bool b_auth_failed = false;
1831
1832                         /* Search the url and trigger callbacks */
1833                         vlc_list_foreach(url, &host->urls, node) {
1834                             if (strcmp(url->psz_url, query->psz_url))
1835                                 continue;
1836                             if (!url->catch[i_msg].cb)
1837                                 continue;
1838
1839                             if (answer) {
1840                                 b_auth_failed = !httpdAuthOk(url->psz_user,
1841                                    url->psz_password,
1842                                    httpd_MsgGet(query, "Authorization")); /* BASIC id */
1843                                 if (b_auth_failed)
1844                                    break;
1845                             }
1846
1847                             if (url->catch[i_msg].cb(url->catch[i_msg].p_sys, cl, answer, query))
1848                                 continue;
1849
1850                             if (answer->i_proto == HTTPD_PROTO_NONE)
1851                                 cl->i_buffer = cl->i_buffer_size; /* Raw answer from a CGI */
1852                             else
1853                                 cl->i_buffer = -1;
1854
1855                             /* only one url can answer */
1856                             answer = NULL;
1857                             if (!cl->url)
1858                                 cl->url = url;
1859                         }
1860
1861                         if (answer) {
1862                             answer->i_proto  = query->i_proto;
1863                             answer->i_type   = HTTPD_MSG_ANSWER;
1864                             answer->i_version= 0;
1865
1866                            if (b_auth_failed) {
1867                                 httpd_MsgAdd(answer, "WWW-Authenticate",
1868                                         "Basic realm=\"VLC stream\"");
1869                                 answer->i_status = 401;
1870                             } else
1871                                 answer->i_status = 404; /* no url registered */
1872
1873                             char *p;
1874                             answer->i_body = httpd_HtmlError (&p, answer->i_status,
1875                                     query->psz_url);
1876                             answer->p_body = (uint8_t *)p;
1877
1878                             cl->i_buffer = -1;  /* Force the creation of the answer in httpd_ClientSend */
1879                             httpd_MsgAdd(answer, "Content-Length", "%d", answer->i_body);
1880                             httpd_MsgAdd(answer, "Content-Type", "%s", "text/html");
1881                             if (httpd_MsgGet(&cl->query, "Connection") != NULL)
1882                                 httpd_MsgAdd(answer, "Connection", "close");
1883                         }
1884
1885                         cl->i_state = HTTPD_CLIENT_SENDING;
1886                     }
1887                 }
1888                 break;
1889             }
1890
1891             case HTTPD_CLIENT_SEND_DONE:
1892                 if (!cl->b_stream_mode || cl->answer.i_body_offset == 0) {
1893                     bool do_close = false;
1894
1895                     cl->url = NULL;
1896
1897                     if (cl->query.i_proto != HTTPD_PROTO_HTTP
1898                      || cl->query.i_version > 0)
1899                     {
1900                         const char *psz_connection = httpd_MsgGet(&cl->answer,
1901                                                                  "Connection");
1902                         if (psz_connection != NULL)
1903                             do_close = !strcasecmp(psz_connection, "close");
1904                     }
1905                     else
1906                         do_close = true;
1907
1908                     if (!do_close) {
1909                         httpd_MsgClean(&cl->query);
1910                         httpd_MsgInit(&cl->query);
1911
1912                         cl->i_buffer = 0;
1913                         cl->i_buffer_size = 1000;
1914                         free(cl->p_buffer);
1915                         // Allocate an extra byte for the null terminating byte
1916                         cl->p_buffer = xmalloc(cl->i_buffer_size + 1);
1917                         cl->i_state = HTTPD_CLIENT_RECEIVING;
1918                     } else
1919                         cl->i_state = HTTPD_CLIENT_DEAD;
1920                     httpd_MsgClean(&cl->answer);
1921                 } else {
1922                     int64_t i_offset = cl->answer.i_body_offset;
1923                     httpd_MsgClean(&cl->answer);
1924
1925                     cl->answer.i_body_offset = i_offset;
1926                     free(cl->p_buffer);
1927                     cl->p_buffer = NULL;
1928                     cl->i_buffer = 0;
1929                     cl->i_buffer_size = 0;
1930
1931                     cl->i_state = HTTPD_CLIENT_WAITING;
1932                 }
1933                 break;
1934
1935             case HTTPD_CLIENT_WAITING: {
1936                 int64_t i_offset = cl->answer.i_body_offset;
1937                 int i_msg = cl->query.i_type;
1938
1939                 httpd_MsgInit(&cl->answer);
1940                 cl->answer.i_body_offset = i_offset;
1941
1942                 cl->url->catch[i_msg].cb(cl->url->catch[i_msg].p_sys, cl,
1943                         &cl->answer, &cl->query);
1944                 if (cl->answer.i_type != HTTPD_MSG_NONE) {
1945                     /* we have new data, so re-enter send mode */
1946                     cl->i_buffer      = 0;
1947                     cl->p_buffer      = cl->answer.p_body;
1948                     cl->i_buffer_size = cl->answer.i_body;
1949                     cl->answer.p_body = NULL;
1950                     cl->answer.i_body = 0;
1951                     cl->i_state = HTTPD_CLIENT_SENDING;
1952                 }
1953             }
1954         }
1955
1956         pufd->fd = vlc_tls_GetPollFD(cl->sock, &pufd->events);
1957
1958         if (pufd->events != 0)
1959             nfd++;
1960         else
1961             b_low_delay = true;
1962     }
1963     vlc_mutex_unlock(&host->lock);
1964     vlc_restorecancel(canc);
1965
1966     /* we will wait 20ms (not too big) if HTTPD_CLIENT_WAITING */
1967     while (poll(ufd, nfd, b_low_delay ? 20 : -1) < 0)
1968     {
1969         if (errno != EINTR)
1970             msg_Err(host, "polling error: %s", vlc_strerror_c(errno));
1971     }
1972
1973     canc = vlc_savecancel();
1974     vlc_mutex_lock(&host->lock);
1975
1976     /* Handle client sockets */
1977     now = vlc_tick_now();
1978     nfd = host->nfd;
1979
1980     vlc_list_foreach(cl, &host->clients, node) {
1981         const struct pollfd *pufd = &ufd[nfd];
1982
1983         assert(pufd < &ufd[ARRAY_SIZE(ufd)]);
1984
1985         if (vlc_tls_GetFD(cl->sock) != pufd->fd)
1986             continue; // we were not waiting for this client
1987         ++nfd;
1988         if (pufd->revents == 0)
1989             continue; // no event received
1990
1991         cl->i_activity_date = now;
1992
1993         switch (cl->i_state) {
1994             case HTTPD_CLIENT_RECEIVING: httpd_ClientRecv(cl); break;
1995             case HTTPD_CLIENT_SENDING:   httpd_ClientSend(cl); break;
1996             case HTTPD_CLIENT_TLS_HS_IN:
1997             case HTTPD_CLIENT_TLS_HS_OUT:
1998                 httpd_ClientTlsHandshake(host, cl);
1999                 break;
2000         }
2001     }
2002
2003     /* Handle server sockets (accept new connections) */
2004     for (nfd = 0; nfd < host->nfd; nfd++) {
2005         int fd = ufd[nfd].fd;
2006
2007         assert (fd == host->fds[nfd]);
2008
2009         if (ufd[nfd].revents == 0)
2010             continue;
2011
2012         /* */
2013         fd = vlc_accept (fd, NULL, NULL, true);
2014         if (fd == -1)
2015             continue;
2016         setsockopt (fd, SOL_SOCKET, SO_REUSEADDR,
2017                 &(int){ 1 }, sizeof(int));
2018
2019         vlc_tls_t *sk = vlc_tls_SocketOpen(fd);
2020         if (unlikely(sk == NULL))
2021         {
2022             vlc_close(fd);
2023             continue;
2024         }
2025
2026         if (host->p_tls != NULL)
2027         {
2028             const char *alpn[] = { "http/1.1", NULL };
2029             vlc_tls_t *tls;
2030
2031             tls = vlc_tls_ServerSessionCreate(host->p_tls, sk, alpn);
2032             if (tls == NULL)
2033             {
2034                 vlc_tls_SessionDelete(sk);
2035                 continue;
2036             }
2037             sk = tls;
2038         }
2039
2040         cl = httpd_ClientNew(sk, now);
2041
2042         if (host->p_tls != NULL)
2043             cl->i_state = HTTPD_CLIENT_TLS_HS_OUT;
2044
2045         host->client_count++;
2046         vlc_list_append(&cl->node, &host->clients);
2047     }
2048
2049     vlc_mutex_unlock(&host->lock);
2050     vlc_restorecancel(canc);
2051 }
2052
2053 static void* httpd_HostThread(void *data)
2054 {
2055     httpd_host_t *host = data;
2056
2057     while (atomic_load_explicit(&host->ref, memory_order_relaxed) > 0)
2058         httpdLoop(host);
2059     return NULL;
2060 }
2061
2062 int httpd_StreamSetHTTPHeaders(httpd_stream_t * p_stream,
2063                                const httpd_header *p_headers, size_t i_headers)
2064 {
2065     if (!p_stream)
2066         return VLC_EGENERIC;
2067
2068     vlc_mutex_lock(&p_stream->lock);
2069     if (p_stream->p_http_headers) {
2070         for (size_t i = 0; i < p_stream->i_http_headers; i++) {
2071             free(p_stream->p_http_headers[i].name);
2072             free(p_stream->p_http_headers[i].value);
2073         }
2074         free(p_stream->p_http_headers);
2075         p_stream->p_http_headers = NULL;
2076         p_stream->i_http_headers = 0;
2077     }
2078
2079     if (!p_headers || !i_headers) {
2080         vlc_mutex_unlock(&p_stream->lock);
2081         return VLC_SUCCESS;
2082     }
2083
2084     p_stream->p_http_headers = vlc_alloc(i_headers, sizeof(httpd_header));
2085     if (!p_stream->p_http_headers) {
2086         vlc_mutex_unlock(&p_stream->lock);
2087         return VLC_ENOMEM;
2088     }
2089
2090     size_t j = 0;
2091     for (size_t i = 0; i < i_headers; i++) {
2092         if (unlikely(!p_headers[i].name || !p_headers[i].value))
2093             continue;
2094
2095         p_stream->p_http_headers[j].name = strdup(p_headers[i].name);
2096         p_stream->p_http_headers[j].value = strdup(p_headers[i].value);
2097
2098         if (unlikely(!p_stream->p_http_headers[j].name ||
2099                       !p_stream->p_http_headers[j].value)) {
2100             free(p_stream->p_http_headers[j].name);
2101             free(p_stream->p_http_headers[j].value);
2102             break;
2103         }
2104         j++;
2105     }
2106     p_stream->i_http_headers = j;
2107     vlc_mutex_unlock(&p_stream->lock);
2108     return VLC_SUCCESS;
2109 }