http: Add support for reusing the http socket for subsequent requests
[ffmpeg.git] / libavformat / http.c
1 /*
2  * HTTP protocol for avconv client
3  * Copyright (c) 2000, 2001 Fabrice Bellard
4  *
5  * This file is part of Libav.
6  *
7  * Libav is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * Libav is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with Libav; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21
22 #include "libavutil/avstring.h"
23 #include "avformat.h"
24 #include <unistd.h>
25 #include "internal.h"
26 #include "network.h"
27 #include "http.h"
28 #include "os_support.h"
29 #include "httpauth.h"
30 #include "url.h"
31 #include "libavutil/opt.h"
32
33 /* XXX: POST protocol is not completely implemented because avconv uses
34    only a subset of it. */
35
36 /* used for protocol handling */
37 #define BUFFER_SIZE 1024
38 #define MAX_REDIRECTS 8
39
40 typedef struct {
41     const AVClass *class;
42     URLContext *hd;
43     unsigned char buffer[BUFFER_SIZE], *buf_ptr, *buf_end;
44     int line_count;
45     int http_code;
46     int64_t chunksize;      /**< Used if "Transfer-Encoding: chunked" otherwise -1. */
47     int64_t off, filesize;
48     char location[MAX_URL_SIZE];
49     HTTPAuthState auth_state;
50     HTTPAuthState proxy_auth_state;
51     char *headers;
52     int willclose;          /**< Set if the server correctly handles Connection: close and will close the connection after feeding us the content. */
53     int chunked_post;
54     int end_chunked_post;   /**< A flag which indicates if the end of chunked encoding has been sent. */
55     int end_header;         /**< A flag which indicates we have finished to read POST reply. */
56     int multiple_requests;  /**< A flag which indicates if we use persistent connections. */
57 } HTTPContext;
58
59 #define OFFSET(x) offsetof(HTTPContext, x)
60 #define D AV_OPT_FLAG_DECODING_PARAM
61 #define E AV_OPT_FLAG_ENCODING_PARAM
62 static const AVOption options[] = {
63 {"chunked_post", "use chunked transfer-encoding for posts", OFFSET(chunked_post), AV_OPT_TYPE_INT, {.dbl = 1}, 0, 1, E },
64 {"headers", "custom HTTP headers, can override built in default headers", OFFSET(headers), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E },
65 {"multiple_requests", "use persistent connections", OFFSET(multiple_requests), AV_OPT_TYPE_INT, {.dbl = 0}, 0, 1, D|E },
66 {NULL}
67 };
68 #define HTTP_CLASS(flavor)\
69 static const AVClass flavor ## _context_class = {\
70     .class_name     = #flavor,\
71     .item_name      = av_default_item_name,\
72     .option         = options,\
73     .version        = LIBAVUTIL_VERSION_INT,\
74 }
75
76 HTTP_CLASS(http);
77 HTTP_CLASS(https);
78
79 static int http_connect(URLContext *h, const char *path, const char *local_path,
80                         const char *hoststr, const char *auth,
81                         const char *proxyauth, int *new_location);
82
83 void ff_http_init_auth_state(URLContext *dest, const URLContext *src)
84 {
85     memcpy(&((HTTPContext*)dest->priv_data)->auth_state,
86            &((HTTPContext*)src->priv_data)->auth_state, sizeof(HTTPAuthState));
87     memcpy(&((HTTPContext*)dest->priv_data)->proxy_auth_state,
88            &((HTTPContext*)src->priv_data)->proxy_auth_state,
89            sizeof(HTTPAuthState));
90 }
91
92 /* return non zero if error */
93 static int http_open_cnx(URLContext *h)
94 {
95     const char *path, *proxy_path, *lower_proto = "tcp", *local_path;
96     char hostname[1024], hoststr[1024], proto[10];
97     char auth[1024], proxyauth[1024] = "";
98     char path1[1024];
99     char buf[1024], urlbuf[1024];
100     int port, use_proxy, err, location_changed = 0, redirects = 0, attempts = 0;
101     HTTPAuthType cur_auth_type, cur_proxy_auth_type;
102     HTTPContext *s = h->priv_data;
103     URLContext *hd = NULL;
104
105     proxy_path = getenv("http_proxy");
106     use_proxy = (proxy_path != NULL) && !getenv("no_proxy") &&
107         av_strstart(proxy_path, "http://", NULL);
108
109     /* fill the dest addr */
110  redo:
111     /* needed in any case to build the host string */
112     av_url_split(proto, sizeof(proto), auth, sizeof(auth),
113                  hostname, sizeof(hostname), &port,
114                  path1, sizeof(path1), s->location);
115     ff_url_join(hoststr, sizeof(hoststr), NULL, NULL, hostname, port, NULL);
116
117     if (!strcmp(proto, "https")) {
118         lower_proto = "tls";
119         use_proxy = 0;
120         if (port < 0)
121             port = 443;
122     }
123     if (port < 0)
124         port = 80;
125
126     if (path1[0] == '\0')
127         path = "/";
128     else
129         path = path1;
130     local_path = path;
131     if (use_proxy) {
132         /* Reassemble the request URL without auth string - we don't
133          * want to leak the auth to the proxy. */
134         ff_url_join(urlbuf, sizeof(urlbuf), proto, NULL, hostname, port, "%s",
135                     path1);
136         path = urlbuf;
137         av_url_split(NULL, 0, proxyauth, sizeof(proxyauth),
138                      hostname, sizeof(hostname), &port, NULL, 0, proxy_path);
139     }
140
141     ff_url_join(buf, sizeof(buf), lower_proto, NULL, hostname, port, NULL);
142
143     if (!s->hd) {
144         err = ffurl_open(&hd, buf, AVIO_FLAG_READ_WRITE,
145                          &h->interrupt_callback, NULL);
146         if (err < 0)
147             goto fail;
148
149         s->hd = hd;
150     }
151
152     cur_auth_type = s->auth_state.auth_type;
153     cur_proxy_auth_type = s->auth_state.auth_type;
154     if (http_connect(h, path, local_path, hoststr, auth, proxyauth, &location_changed) < 0)
155         goto fail;
156     attempts++;
157     if (s->http_code == 401) {
158         if ((cur_auth_type == HTTP_AUTH_NONE || s->auth_state.stale) &&
159             s->auth_state.auth_type != HTTP_AUTH_NONE && attempts < 4) {
160             ffurl_close(hd);
161             goto redo;
162         } else
163             goto fail;
164     }
165     if (s->http_code == 407) {
166         if ((cur_proxy_auth_type == HTTP_AUTH_NONE || s->proxy_auth_state.stale) &&
167             s->proxy_auth_state.auth_type != HTTP_AUTH_NONE && attempts < 4) {
168             ffurl_close(hd);
169             goto redo;
170         } else
171             goto fail;
172     }
173     if ((s->http_code == 301 || s->http_code == 302 || s->http_code == 303 || s->http_code == 307)
174         && location_changed == 1) {
175         /* url moved, get next */
176         ffurl_close(hd);
177         if (redirects++ >= MAX_REDIRECTS)
178             return AVERROR(EIO);
179         /* Restart the authentication process with the new target, which
180          * might use a different auth mechanism. */
181         memset(&s->auth_state, 0, sizeof(s->auth_state));
182         attempts = 0;
183         location_changed = 0;
184         goto redo;
185     }
186     return 0;
187  fail:
188     if (hd)
189         ffurl_close(hd);
190     s->hd = NULL;
191     return AVERROR(EIO);
192 }
193
194 int ff_http_do_new_request(URLContext *h, const char *uri)
195 {
196     HTTPContext *s = h->priv_data;
197
198     s->off = 0;
199     av_strlcpy(s->location, uri, sizeof(s->location));
200
201     return http_open_cnx(h);
202 }
203
204 static int http_open(URLContext *h, const char *uri, int flags)
205 {
206     HTTPContext *s = h->priv_data;
207
208     h->is_streamed = 1;
209
210     s->filesize = -1;
211     av_strlcpy(s->location, uri, sizeof(s->location));
212
213     if (s->headers) {
214         int len = strlen(s->headers);
215         if (len < 2 || strcmp("\r\n", s->headers + len - 2))
216             av_log(h, AV_LOG_WARNING, "No trailing CRLF found in HTTP header.\n");
217     }
218
219     return http_open_cnx(h);
220 }
221 static int http_getc(HTTPContext *s)
222 {
223     int len;
224     if (s->buf_ptr >= s->buf_end) {
225         len = ffurl_read(s->hd, s->buffer, BUFFER_SIZE);
226         if (len < 0) {
227             return AVERROR(EIO);
228         } else if (len == 0) {
229             return -1;
230         } else {
231             s->buf_ptr = s->buffer;
232             s->buf_end = s->buffer + len;
233         }
234     }
235     return *s->buf_ptr++;
236 }
237
238 static int http_get_line(HTTPContext *s, char *line, int line_size)
239 {
240     int ch;
241     char *q;
242
243     q = line;
244     for(;;) {
245         ch = http_getc(s);
246         if (ch < 0)
247             return AVERROR(EIO);
248         if (ch == '\n') {
249             /* process line */
250             if (q > line && q[-1] == '\r')
251                 q--;
252             *q = '\0';
253
254             return 0;
255         } else {
256             if ((q - line) < line_size - 1)
257                 *q++ = ch;
258         }
259     }
260 }
261
262 static int process_line(URLContext *h, char *line, int line_count,
263                         int *new_location)
264 {
265     HTTPContext *s = h->priv_data;
266     char *tag, *p, *end;
267
268     /* end of header */
269     if (line[0] == '\0') {
270         s->end_header = 1;
271         return 0;
272     }
273
274     p = line;
275     if (line_count == 0) {
276         while (!isspace(*p) && *p != '\0')
277             p++;
278         while (isspace(*p))
279             p++;
280         s->http_code = strtol(p, &end, 10);
281
282         av_dlog(NULL, "http_code=%d\n", s->http_code);
283
284         /* error codes are 4xx and 5xx, but regard 401 as a success, so we
285          * don't abort until all headers have been parsed. */
286         if (s->http_code >= 400 && s->http_code < 600 && (s->http_code != 401
287             || s->auth_state.auth_type != HTTP_AUTH_NONE) &&
288             (s->http_code != 407 || s->proxy_auth_state.auth_type != HTTP_AUTH_NONE)) {
289             end += strspn(end, SPACE_CHARS);
290             av_log(h, AV_LOG_WARNING, "HTTP error %d %s\n",
291                    s->http_code, end);
292             return -1;
293         }
294     } else {
295         while (*p != '\0' && *p != ':')
296             p++;
297         if (*p != ':')
298             return 1;
299
300         *p = '\0';
301         tag = line;
302         p++;
303         while (isspace(*p))
304             p++;
305         if (!av_strcasecmp(tag, "Location")) {
306             strcpy(s->location, p);
307             *new_location = 1;
308         } else if (!av_strcasecmp (tag, "Content-Length") && s->filesize == -1) {
309             s->filesize = atoll(p);
310         } else if (!av_strcasecmp (tag, "Content-Range")) {
311             /* "bytes $from-$to/$document_size" */
312             const char *slash;
313             if (!strncmp (p, "bytes ", 6)) {
314                 p += 6;
315                 s->off = atoll(p);
316                 if ((slash = strchr(p, '/')) && strlen(slash) > 0)
317                     s->filesize = atoll(slash+1);
318             }
319             h->is_streamed = 0; /* we _can_ in fact seek */
320         } else if (!av_strcasecmp(tag, "Accept-Ranges") && !strncmp(p, "bytes", 5)) {
321             h->is_streamed = 0;
322         } else if (!av_strcasecmp (tag, "Transfer-Encoding") && !av_strncasecmp(p, "chunked", 7)) {
323             s->filesize = -1;
324             s->chunksize = 0;
325         } else if (!av_strcasecmp (tag, "WWW-Authenticate")) {
326             ff_http_auth_handle_header(&s->auth_state, tag, p);
327         } else if (!av_strcasecmp (tag, "Authentication-Info")) {
328             ff_http_auth_handle_header(&s->auth_state, tag, p);
329         } else if (!av_strcasecmp (tag, "Proxy-Authenticate")) {
330             ff_http_auth_handle_header(&s->proxy_auth_state, tag, p);
331         } else if (!av_strcasecmp (tag, "Connection")) {
332             if (!strcmp(p, "close"))
333                 s->willclose = 1;
334         }
335     }
336     return 1;
337 }
338
339 static inline int has_header(const char *str, const char *header)
340 {
341     /* header + 2 to skip over CRLF prefix. (make sure you have one!) */
342     if (!str)
343         return 0;
344     return av_stristart(str, header + 2, NULL) || av_stristr(str, header);
345 }
346
347 static int http_read_header(URLContext *h, int *new_location)
348 {
349     HTTPContext *s = h->priv_data;
350     char line[1024];
351     int err = 0;
352
353     for (;;) {
354         if (http_get_line(s, line, sizeof(line)) < 0)
355             return AVERROR(EIO);
356
357         av_dlog(NULL, "header='%s'\n", line);
358
359         err = process_line(h, line, s->line_count, new_location);
360         if (err < 0)
361             return err;
362         if (err == 0)
363             break;
364         s->line_count++;
365     }
366
367     return err;
368 }
369
370 static int http_connect(URLContext *h, const char *path, const char *local_path,
371                         const char *hoststr, const char *auth,
372                         const char *proxyauth, int *new_location)
373 {
374     HTTPContext *s = h->priv_data;
375     int post, err;
376     char headers[1024] = "";
377     char *authstr = NULL, *proxyauthstr = NULL;
378     int64_t off = s->off;
379     int len = 0;
380     const char *method;
381
382
383     /* send http header */
384     post = h->flags & AVIO_FLAG_WRITE;
385     method = post ? "POST" : "GET";
386     authstr = ff_http_auth_create_response(&s->auth_state, auth, local_path,
387                                            method);
388     proxyauthstr = ff_http_auth_create_response(&s->proxy_auth_state, proxyauth,
389                                                 local_path, method);
390
391     /* set default headers if needed */
392     if (!has_header(s->headers, "\r\nUser-Agent: "))
393        len += av_strlcatf(headers + len, sizeof(headers) - len,
394                           "User-Agent: %s\r\n", LIBAVFORMAT_IDENT);
395     if (!has_header(s->headers, "\r\nAccept: "))
396         len += av_strlcpy(headers + len, "Accept: */*\r\n",
397                           sizeof(headers) - len);
398     if (!has_header(s->headers, "\r\nRange: ") && !post)
399         len += av_strlcatf(headers + len, sizeof(headers) - len,
400                            "Range: bytes=%"PRId64"-\r\n", s->off);
401
402     if (!has_header(s->headers, "\r\nConnection: ")) {
403         if (s->multiple_requests) {
404             len += av_strlcpy(headers + len, "Connection: keep-alive\r\n",
405                               sizeof(headers) - len);
406         } else {
407             len += av_strlcpy(headers + len, "Connection: close\r\n",
408                               sizeof(headers) - len);
409         }
410     }
411
412     if (!has_header(s->headers, "\r\nHost: "))
413         len += av_strlcatf(headers + len, sizeof(headers) - len,
414                            "Host: %s\r\n", hoststr);
415
416     /* now add in custom headers */
417     if (s->headers)
418         av_strlcpy(headers + len, s->headers, sizeof(headers) - len);
419
420     snprintf(s->buffer, sizeof(s->buffer),
421              "%s %s HTTP/1.1\r\n"
422              "%s"
423              "%s"
424              "%s"
425              "%s%s"
426              "\r\n",
427              method,
428              path,
429              post && s->chunked_post ? "Transfer-Encoding: chunked\r\n" : "",
430              headers,
431              authstr ? authstr : "",
432              proxyauthstr ? "Proxy-" : "", proxyauthstr ? proxyauthstr : "");
433
434     av_freep(&authstr);
435     av_freep(&proxyauthstr);
436     if (ffurl_write(s->hd, s->buffer, strlen(s->buffer)) < 0)
437         return AVERROR(EIO);
438
439     /* init input buffer */
440     s->buf_ptr = s->buffer;
441     s->buf_end = s->buffer;
442     s->line_count = 0;
443     s->off = 0;
444     s->filesize = -1;
445     s->willclose = 0;
446     s->end_chunked_post = 0;
447     s->end_header = 0;
448     if (post) {
449         /* Pretend that it did work. We didn't read any header yet, since
450          * we've still to send the POST data, but the code calling this
451          * function will check http_code after we return. */
452         s->http_code = 200;
453         return 0;
454     }
455     s->chunksize = -1;
456
457     /* wait for header */
458     err = http_read_header(h, new_location);
459     if (err < 0)
460         return err;
461
462     return (off == s->off) ? 0 : -1;
463 }
464
465
466 static int http_buf_read(URLContext *h, uint8_t *buf, int size)
467 {
468     HTTPContext *s = h->priv_data;
469     int len;
470     /* read bytes from input buffer first */
471     len = s->buf_end - s->buf_ptr;
472     if (len > 0) {
473         if (len > size)
474             len = size;
475         memcpy(buf, s->buf_ptr, len);
476         s->buf_ptr += len;
477     } else {
478         if (!s->willclose && s->filesize >= 0 && s->off >= s->filesize)
479             return AVERROR_EOF;
480         len = ffurl_read(s->hd, buf, size);
481     }
482     if (len > 0) {
483         s->off += len;
484         if (s->chunksize > 0)
485             s->chunksize -= len;
486     }
487     return len;
488 }
489
490 static int http_read(URLContext *h, uint8_t *buf, int size)
491 {
492     HTTPContext *s = h->priv_data;
493     int err, new_location;
494
495     if (s->end_chunked_post) {
496         if (!s->end_header) {
497             err = http_read_header(h, &new_location);
498             if (err < 0)
499                 return err;
500         }
501
502         return http_buf_read(h, buf, size);
503     }
504
505     if (s->chunksize >= 0) {
506         if (!s->chunksize) {
507             char line[32];
508
509             for(;;) {
510                 do {
511                     if (http_get_line(s, line, sizeof(line)) < 0)
512                         return AVERROR(EIO);
513                 } while (!*line);    /* skip CR LF from last chunk */
514
515                 s->chunksize = strtoll(line, NULL, 16);
516
517                 av_dlog(NULL, "Chunked encoding data size: %"PRId64"'\n", s->chunksize);
518
519                 if (!s->chunksize)
520                     return 0;
521                 break;
522             }
523         }
524         size = FFMIN(size, s->chunksize);
525     }
526     return http_buf_read(h, buf, size);
527 }
528
529 /* used only when posting data */
530 static int http_write(URLContext *h, const uint8_t *buf, int size)
531 {
532     char temp[11] = "";  /* 32-bit hex + CRLF + nul */
533     int ret;
534     char crlf[] = "\r\n";
535     HTTPContext *s = h->priv_data;
536
537     if (!s->chunked_post) {
538         /* non-chunked data is sent without any special encoding */
539         return ffurl_write(s->hd, buf, size);
540     }
541
542     /* silently ignore zero-size data since chunk encoding that would
543      * signal EOF */
544     if (size > 0) {
545         /* upload data using chunked encoding */
546         snprintf(temp, sizeof(temp), "%x\r\n", size);
547
548         if ((ret = ffurl_write(s->hd, temp, strlen(temp))) < 0 ||
549             (ret = ffurl_write(s->hd, buf, size)) < 0 ||
550             (ret = ffurl_write(s->hd, crlf, sizeof(crlf) - 1)) < 0)
551             return ret;
552     }
553     return size;
554 }
555
556 static int http_shutdown(URLContext *h, int flags)
557 {
558     int ret = 0;
559     char footer[] = "0\r\n\r\n";
560     HTTPContext *s = h->priv_data;
561
562     /* signal end of chunked encoding if used */
563     if ((flags & AVIO_FLAG_WRITE) && s->chunked_post) {
564         ret = ffurl_write(s->hd, footer, sizeof(footer) - 1);
565         ret = ret > 0 ? 0 : ret;
566         s->end_chunked_post = 1;
567     }
568
569     return ret;
570 }
571
572 static int http_close(URLContext *h)
573 {
574     int ret = 0;
575     HTTPContext *s = h->priv_data;
576
577     if (!s->end_chunked_post) {
578         /* Close the write direction by sending the end of chunked encoding. */
579         ret = http_shutdown(h, h->flags);
580     }
581
582     if (s->hd)
583         ffurl_close(s->hd);
584     return ret;
585 }
586
587 static int64_t http_seek(URLContext *h, int64_t off, int whence)
588 {
589     HTTPContext *s = h->priv_data;
590     URLContext *old_hd = s->hd;
591     int64_t old_off = s->off;
592     uint8_t old_buf[BUFFER_SIZE];
593     int old_buf_size;
594
595     if (whence == AVSEEK_SIZE)
596         return s->filesize;
597     else if ((s->filesize == -1 && whence == SEEK_END) || h->is_streamed)
598         return -1;
599
600     /* we save the old context in case the seek fails */
601     old_buf_size = s->buf_end - s->buf_ptr;
602     memcpy(old_buf, s->buf_ptr, old_buf_size);
603     s->hd = NULL;
604     if (whence == SEEK_CUR)
605         off += s->off;
606     else if (whence == SEEK_END)
607         off += s->filesize;
608     s->off = off;
609
610     /* if it fails, continue on old connection */
611     if (http_open_cnx(h) < 0) {
612         memcpy(s->buffer, old_buf, old_buf_size);
613         s->buf_ptr = s->buffer;
614         s->buf_end = s->buffer + old_buf_size;
615         s->hd = old_hd;
616         s->off = old_off;
617         return -1;
618     }
619     ffurl_close(old_hd);
620     return off;
621 }
622
623 static int
624 http_get_file_handle(URLContext *h)
625 {
626     HTTPContext *s = h->priv_data;
627     return ffurl_get_file_handle(s->hd);
628 }
629
630 #if CONFIG_HTTP_PROTOCOL
631 URLProtocol ff_http_protocol = {
632     .name                = "http",
633     .url_open            = http_open,
634     .url_read            = http_read,
635     .url_write           = http_write,
636     .url_seek            = http_seek,
637     .url_close           = http_close,
638     .url_get_file_handle = http_get_file_handle,
639     .url_shutdown        = http_shutdown,
640     .priv_data_size      = sizeof(HTTPContext),
641     .priv_data_class     = &http_context_class,
642     .flags               = URL_PROTOCOL_FLAG_NETWORK,
643 };
644 #endif
645 #if CONFIG_HTTPS_PROTOCOL
646 URLProtocol ff_https_protocol = {
647     .name                = "https",
648     .url_open            = http_open,
649     .url_read            = http_read,
650     .url_write           = http_write,
651     .url_seek            = http_seek,
652     .url_close           = http_close,
653     .url_get_file_handle = http_get_file_handle,
654     .priv_data_size      = sizeof(HTTPContext),
655     .priv_data_class     = &https_context_class,
656     .flags               = URL_PROTOCOL_FLAG_NETWORK,
657 };
658 #endif
659
660 #if CONFIG_HTTPPROXY_PROTOCOL
661 static int http_proxy_close(URLContext *h)
662 {
663     HTTPContext *s = h->priv_data;
664     if (s->hd)
665         ffurl_close(s->hd);
666     return 0;
667 }
668
669 static int http_proxy_open(URLContext *h, const char *uri, int flags)
670 {
671     HTTPContext *s = h->priv_data;
672     char hostname[1024], hoststr[1024];
673     char auth[1024], pathbuf[1024], *path;
674     char lower_url[100];
675     int port, ret = 0, attempts = 0;
676     HTTPAuthType cur_auth_type;
677     char *authstr;
678     int new_loc;
679
680     h->is_streamed = 1;
681
682     av_url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port,
683                  pathbuf, sizeof(pathbuf), uri);
684     ff_url_join(hoststr, sizeof(hoststr), NULL, NULL, hostname, port, NULL);
685     path = pathbuf;
686     if (*path == '/')
687         path++;
688
689     ff_url_join(lower_url, sizeof(lower_url), "tcp", NULL, hostname, port,
690                 NULL);
691 redo:
692     ret = ffurl_open(&s->hd, lower_url, AVIO_FLAG_READ_WRITE,
693                      &h->interrupt_callback, NULL);
694     if (ret < 0)
695         return ret;
696
697     authstr = ff_http_auth_create_response(&s->proxy_auth_state, auth,
698                                            path, "CONNECT");
699     snprintf(s->buffer, sizeof(s->buffer),
700              "CONNECT %s HTTP/1.1\r\n"
701              "Host: %s\r\n"
702              "Connection: close\r\n"
703              "%s%s"
704              "\r\n",
705              path,
706              hoststr,
707              authstr ? "Proxy-" : "", authstr ? authstr : "");
708     av_freep(&authstr);
709
710     if ((ret = ffurl_write(s->hd, s->buffer, strlen(s->buffer))) < 0)
711         goto fail;
712
713     s->buf_ptr = s->buffer;
714     s->buf_end = s->buffer;
715     s->line_count = 0;
716     s->filesize = -1;
717     cur_auth_type = s->proxy_auth_state.auth_type;
718
719     /* Note: This uses buffering, potentially reading more than the
720      * HTTP header. If tunneling a protocol where the server starts
721      * the conversation, we might buffer part of that here, too.
722      * Reading that requires using the proper ffurl_read() function
723      * on this URLContext, not using the fd directly (as the tls
724      * protocol does). This shouldn't be an issue for tls though,
725      * since the client starts the conversation there, so there
726      * is no extra data that we might buffer up here.
727      */
728     ret = http_read_header(h, &new_loc);
729     if (ret < 0)
730         goto fail;
731
732     attempts++;
733     if (s->http_code == 407 &&
734         (cur_auth_type == HTTP_AUTH_NONE || s->proxy_auth_state.stale) &&
735         s->proxy_auth_state.auth_type != HTTP_AUTH_NONE && attempts < 2) {
736         ffurl_close(s->hd);
737         s->hd = NULL;
738         goto redo;
739     }
740
741     if (s->http_code < 400)
742         return 0;
743     ret = AVERROR(EIO);
744
745 fail:
746     http_proxy_close(h);
747     return ret;
748 }
749
750 static int http_proxy_write(URLContext *h, const uint8_t *buf, int size)
751 {
752     HTTPContext *s = h->priv_data;
753     return ffurl_write(s->hd, buf, size);
754 }
755
756 URLProtocol ff_httpproxy_protocol = {
757     .name                = "httpproxy",
758     .url_open            = http_proxy_open,
759     .url_read            = http_buf_read,
760     .url_write           = http_proxy_write,
761     .url_close           = http_proxy_close,
762     .url_get_file_handle = http_get_file_handle,
763     .priv_data_size      = sizeof(HTTPContext),
764     .flags               = URL_PROTOCOL_FLAG_NETWORK,
765 };
766 #endif