Modify the behaviour of http_open to implicitly delay connection establishment
[libav.git] / libavformat / http.c
CommitLineData
558b86a5
LB
1/*
2 * HTTP protocol for ffmpeg client
406792e7 3 * Copyright (c) 2000, 2001 Fabrice Bellard
558b86a5
LB
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg 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 * FFmpeg 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 FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
245976da 21
245976da 22#include "libavutil/avstring.h"
558b86a5
LB
23#include "avformat.h"
24#include <unistd.h>
7995ed8e 25#include <strings.h>
e4a9e3cc 26#include "internal.h"
558b86a5 27#include "network.h"
a5e979f4 28#include "os_support.h"
9405f733 29#include "httpauth.h"
558b86a5 30
e42dba48
DB
31/* XXX: POST protocol is not completely implemented because ffmpeg uses
32 only a subset of it. */
558b86a5 33
558b86a5
LB
34/* used for protocol handling */
35#define BUFFER_SIZE 1024
36#define URL_SIZE 4096
37#define MAX_REDIRECTS 8
38
39typedef struct {
40 URLContext *hd;
41 unsigned char buffer[BUFFER_SIZE], *buf_ptr, *buf_end;
42 int line_count;
43 int http_code;
7995ed8e 44 int64_t chunksize; /**< Used if "Transfer-Encoding: chunked" otherwise -1. */
bc5c918e 45 int64_t off, filesize;
558b86a5 46 char location[URL_SIZE];
9405f733 47 HTTPAuthState auth_state;
0f3254b8 48 int init;
558b86a5
LB
49} HTTPContext;
50
51static int http_connect(URLContext *h, const char *path, const char *hoststr,
52 const char *auth, int *new_location);
27241cbf 53static int http_write(URLContext *h, const uint8_t *buf, int size);
558b86a5
LB
54
55
56/* return non zero if error */
57static int http_open_cnx(URLContext *h)
58{
59 const char *path, *proxy_path;
60 char hostname[1024], hoststr[1024];
61 char auth[1024];
62 char path1[1024];
63 char buf[1024];
64 int port, use_proxy, err, location_changed = 0, redirects = 0;
9405f733 65 HTTPAuthType cur_auth_type;
558b86a5
LB
66 HTTPContext *s = h->priv_data;
67 URLContext *hd = NULL;
68
0f3254b8 69 s->init = 1;
558b86a5
LB
70 proxy_path = getenv("http_proxy");
71 use_proxy = (proxy_path != NULL) && !getenv("no_proxy") &&
f7d78f36 72 av_strstart(proxy_path, "http://", NULL);
558b86a5
LB
73
74 /* fill the dest addr */
75 redo:
76 /* needed in any case to build the host string */
c5c6e67c 77 ff_url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port,
f984dcf6 78 path1, sizeof(path1), s->location);
57b5555c 79 ff_url_join(hoststr, sizeof(hoststr), NULL, NULL, hostname, port, NULL);
558b86a5
LB
80
81 if (use_proxy) {
c5c6e67c 82 ff_url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port,
f984dcf6 83 NULL, 0, proxy_path);
558b86a5
LB
84 path = s->location;
85 } else {
86 if (path1[0] == '\0')
87 path = "/";
88 else
89 path = path1;
90 }
91 if (port < 0)
92 port = 80;
93
57b5555c 94 ff_url_join(buf, sizeof(buf), "tcp", NULL, hostname, port, NULL);
558b86a5
LB
95 err = url_open(&hd, buf, URL_RDWR);
96 if (err < 0)
97 goto fail;
98
99 s->hd = hd;
9405f733 100 cur_auth_type = s->auth_state.auth_type;
558b86a5
LB
101 if (http_connect(h, path, hoststr, auth, &location_changed) < 0)
102 goto fail;
9405f733
MS
103 if (s->http_code == 401) {
104 if (cur_auth_type == HTTP_AUTH_NONE && s->auth_state.auth_type != HTTP_AUTH_NONE) {
105 url_close(hd);
106 goto redo;
107 } else
108 goto fail;
109 }
a3fd2bd8 110 if ((s->http_code == 302 || s->http_code == 303) && location_changed == 1) {
558b86a5
LB
111 /* url moved, get next */
112 url_close(hd);
113 if (redirects++ >= MAX_REDIRECTS)
6f3e0b21 114 return AVERROR(EIO);
558b86a5
LB
115 location_changed = 0;
116 goto redo;
117 }
118 return 0;
119 fail:
120 if (hd)
121 url_close(hd);
6f3e0b21 122 return AVERROR(EIO);
558b86a5
LB
123}
124
125static int http_open(URLContext *h, const char *uri, int flags)
126{
127 HTTPContext *s;
558b86a5
LB
128
129 h->is_streamed = 1;
130
131 s = av_malloc(sizeof(HTTPContext));
132 if (!s) {
133 return AVERROR(ENOMEM);
134 }
135 h->priv_data = s;
136 s->filesize = -1;
7995ed8e 137 s->chunksize = -1;
558b86a5 138 s->off = 0;
0f3254b8 139 s->init = 0;
9405f733 140 memset(&s->auth_state, 0, sizeof(s->auth_state));
f7d78f36 141 av_strlcpy(s->location, uri, URL_SIZE);
558b86a5 142
0f3254b8 143 return 0;
558b86a5
LB
144}
145static int http_getc(HTTPContext *s)
146{
147 int len;
148 if (s->buf_ptr >= s->buf_end) {
149 len = url_read(s->hd, s->buffer, BUFFER_SIZE);
150 if (len < 0) {
6f3e0b21 151 return AVERROR(EIO);
558b86a5
LB
152 } else if (len == 0) {
153 return -1;
154 } else {
155 s->buf_ptr = s->buffer;
156 s->buf_end = s->buffer + len;
157 }
158 }
159 return *s->buf_ptr++;
160}
161
682d49f4
PH
162static int http_get_line(HTTPContext *s, char *line, int line_size)
163{
164 int ch;
165 char *q;
166
167 q = line;
168 for(;;) {
169 ch = http_getc(s);
170 if (ch < 0)
171 return AVERROR(EIO);
172 if (ch == '\n') {
173 /* process line */
174 if (q > line && q[-1] == '\r')
175 q--;
176 *q = '\0';
177
178 return 0;
179 } else {
180 if ((q - line) < line_size - 1)
181 *q++ = ch;
182 }
183 }
184}
185
558b86a5
LB
186static int process_line(URLContext *h, char *line, int line_count,
187 int *new_location)
188{
189 HTTPContext *s = h->priv_data;
190 char *tag, *p;
191
192 /* end of header */
193 if (line[0] == '\0')
194 return 0;
195
196 p = line;
197 if (line_count == 0) {
198 while (!isspace(*p) && *p != '\0')
199 p++;
200 while (isspace(*p))
201 p++;
202 s->http_code = strtol(p, NULL, 10);
a52dc730
PH
203
204 dprintf(NULL, "http_code=%d\n", s->http_code);
205
9405f733
MS
206 /* error codes are 4xx and 5xx, but regard 401 as a success, so we
207 * don't abort until all headers have been parsed. */
208 if (s->http_code >= 400 && s->http_code < 600 && s->http_code != 401)
7b19aa64 209 return -1;
558b86a5
LB
210 } else {
211 while (*p != '\0' && *p != ':')
212 p++;
213 if (*p != ':')
214 return 1;
215
216 *p = '\0';
217 tag = line;
218 p++;
219 while (isspace(*p))
220 p++;
221 if (!strcmp(tag, "Location")) {
222 strcpy(s->location, p);
223 *new_location = 1;
224 } else if (!strcmp (tag, "Content-Length") && s->filesize == -1) {
225 s->filesize = atoll(p);
226 } else if (!strcmp (tag, "Content-Range")) {
227 /* "bytes $from-$to/$document_size" */
228 const char *slash;
229 if (!strncmp (p, "bytes ", 6)) {
230 p += 6;
231 s->off = atoll(p);
232 if ((slash = strchr(p, '/')) && strlen(slash) > 0)
233 s->filesize = atoll(slash+1);
234 }
235 h->is_streamed = 0; /* we _can_ in fact seek */
7995ed8e
PH
236 } else if (!strcmp (tag, "Transfer-Encoding") && !strncasecmp(p, "chunked", 7)) {
237 s->filesize = -1;
238 s->chunksize = 0;
9405f733
MS
239 } else if (!strcmp (tag, "WWW-Authenticate")) {
240 ff_http_auth_handle_header(&s->auth_state, tag, p);
241 } else if (!strcmp (tag, "Authentication-Info")) {
242 ff_http_auth_handle_header(&s->auth_state, tag, p);
558b86a5
LB
243 }
244 }
245 return 1;
246}
247
248static int http_connect(URLContext *h, const char *path, const char *hoststr,
249 const char *auth, int *new_location)
250{
251 HTTPContext *s = h->priv_data;
682d49f4
PH
252 int post, err;
253 char line[1024];
9405f733 254 char *authstr = NULL;
bc5c918e 255 int64_t off = s->off;
558b86a5
LB
256
257
258 /* send http header */
259 post = h->flags & URL_WRONLY;
9405f733
MS
260 authstr = ff_http_auth_create_response(&s->auth_state, auth, path,
261 post ? "POST" : "GET");
558b86a5
LB
262 snprintf(s->buffer, sizeof(s->buffer),
263 "%s %s HTTP/1.1\r\n"
264 "User-Agent: %s\r\n"
265 "Accept: */*\r\n"
266 "Range: bytes=%"PRId64"-\r\n"
267 "Host: %s\r\n"
9405f733 268 "%s"
167c5531 269 "Connection: close\r\n"
08f7a8ac 270 "%s"
558b86a5
LB
271 "\r\n",
272 post ? "POST" : "GET",
273 path,
274 LIBAVFORMAT_IDENT,
275 s->off,
276 hoststr,
9405f733 277 authstr ? authstr : "",
08f7a8ac 278 post ? "Transfer-Encoding: chunked\r\n" : "");
558b86a5 279
9405f733 280 av_freep(&authstr);
558b86a5 281 if (http_write(h, s->buffer, strlen(s->buffer)) < 0)
6f3e0b21 282 return AVERROR(EIO);
558b86a5
LB
283
284 /* init input buffer */
285 s->buf_ptr = s->buffer;
286 s->buf_end = s->buffer;
287 s->line_count = 0;
288 s->off = 0;
ecb4a895 289 s->filesize = -1;
558b86a5 290 if (post) {
08f7a8ac
TH
291 /* always use chunked encoding for upload data */
292 s->chunksize = 0;
dd798dcd
MS
293 /* Pretend that it did work. We didn't read any header yet, since
294 * we've still to send the POST data, but the code calling this
295 * function will check http_code after we return. */
296 s->http_code = 200;
558b86a5
LB
297 return 0;
298 }
299
300 /* wait for header */
558b86a5 301 for(;;) {
682d49f4 302 if (http_get_line(s, line, sizeof(line)) < 0)
6f3e0b21 303 return AVERROR(EIO);
a52dc730
PH
304
305 dprintf(NULL, "header='%s'\n", line);
306
2b01a520
JM
307 err = process_line(h, line, s->line_count, new_location);
308 if (err < 0)
309 return err;
310 if (err == 0)
311 break;
312 s->line_count++;
558b86a5
LB
313 }
314
315 return (off == s->off) ? 0 : -1;
316}
317
318
319static int http_read(URLContext *h, uint8_t *buf, int size)
320{
321 HTTPContext *s = h->priv_data;
322 int len;
323
0f3254b8
JA
324 if (!s->init) {
325 int ret = http_open_cnx(h);
326 if (ret != 0)
327 return ret;
328 }
329
330 /* A size of zero can be used to force
331 * initializaton of the connection. */
332 if (!size)
333 return 0;
334
7995ed8e
PH
335 if (s->chunksize >= 0) {
336 if (!s->chunksize) {
337 char line[32];
338
339 for(;;) {
340 do {
341 if (http_get_line(s, line, sizeof(line)) < 0)
342 return AVERROR(EIO);
343 } while (!*line); /* skip CR LF from last chunk */
344
345 s->chunksize = strtoll(line, NULL, 16);
346
347 dprintf(NULL, "Chunked encoding data size: %"PRId64"'\n", s->chunksize);
348
349 if (!s->chunksize)
350 return 0;
351 break;
352 }
353 }
354 size = FFMIN(size, s->chunksize);
355 }
558b86a5
LB
356 /* read bytes from input buffer first */
357 len = s->buf_end - s->buf_ptr;
358 if (len > 0) {
359 if (len > size)
360 len = size;
361 memcpy(buf, s->buf_ptr, len);
362 s->buf_ptr += len;
363 } else {
364 len = url_read(s->hd, buf, size);
365 }
7995ed8e 366 if (len > 0) {
558b86a5 367 s->off += len;
7995ed8e
PH
368 if (s->chunksize > 0)
369 s->chunksize -= len;
370 }
558b86a5
LB
371 return len;
372}
373
374/* used only when posting data */
27241cbf 375static int http_write(URLContext *h, const uint8_t *buf, int size)
558b86a5 376{
08f7a8ac
TH
377 char temp[11]; /* 32-bit hex + CRLF + nul */
378 int ret;
379 char crlf[] = "\r\n";
558b86a5 380 HTTPContext *s = h->priv_data;
08f7a8ac 381
0f3254b8
JA
382 if (!s->init) {
383 int ret = http_open_cnx(h);
384 if (ret != 0)
385 return ret;
386 }
387
08f7a8ac
TH
388 if (s->chunksize == -1) {
389 /* headers are sent without any special encoding */
2edabfdf 390 return url_write(s->hd, buf, size);
08f7a8ac
TH
391 }
392
393 /* silently ignore zero-size data since chunk encoding that would
394 * signal EOF */
395 if (size > 0) {
396 /* upload data using chunked encoding */
397 snprintf(temp, sizeof(temp), "%x\r\n", size);
398
399 if ((ret = url_write(s->hd, temp, strlen(temp))) < 0 ||
400 (ret = url_write(s->hd, buf, size)) < 0 ||
401 (ret = url_write(s->hd, crlf, sizeof(crlf) - 1)) < 0)
402 return ret;
403 }
404 return size;
558b86a5
LB
405}
406
407static int http_close(URLContext *h)
408{
08f7a8ac
TH
409 int ret = 0;
410 char footer[] = "0\r\n\r\n";
558b86a5 411 HTTPContext *s = h->priv_data;
08f7a8ac
TH
412
413 /* signal end of chunked encoding if used */
414 if ((h->flags & URL_WRONLY) && s->chunksize != -1) {
415 ret = url_write(s->hd, footer, sizeof(footer) - 1);
416 ret = ret > 0 ? 0 : ret;
417 }
418
558b86a5
LB
419 url_close(s->hd);
420 av_free(s);
08f7a8ac 421 return ret;
558b86a5
LB
422}
423
bc5c918e 424static int64_t http_seek(URLContext *h, int64_t off, int whence)
558b86a5
LB
425{
426 HTTPContext *s = h->priv_data;
427 URLContext *old_hd = s->hd;
bc5c918e 428 int64_t old_off = s->off;
08c8e66a
BC
429 uint8_t old_buf[BUFFER_SIZE];
430 int old_buf_size;
558b86a5
LB
431
432 if (whence == AVSEEK_SIZE)
433 return s->filesize;
434 else if ((s->filesize == -1 && whence == SEEK_END) || h->is_streamed)
435 return -1;
436
437 /* we save the old context in case the seek fails */
08c8e66a
BC
438 old_buf_size = s->buf_end - s->buf_ptr;
439 memcpy(old_buf, s->buf_ptr, old_buf_size);
558b86a5
LB
440 s->hd = NULL;
441 if (whence == SEEK_CUR)
442 off += s->off;
443 else if (whence == SEEK_END)
444 off += s->filesize;
445 s->off = off;
446
447 /* if it fails, continue on old connection */
448 if (http_open_cnx(h) < 0) {
08c8e66a
BC
449 memcpy(s->buffer, old_buf, old_buf_size);
450 s->buf_ptr = s->buffer;
451 s->buf_end = s->buffer + old_buf_size;
558b86a5
LB
452 s->hd = old_hd;
453 s->off = old_off;
454 return -1;
455 }
456 url_close(old_hd);
457 return off;
458}
459
f0a80394
RB
460static int
461http_get_file_handle(URLContext *h)
462{
463 HTTPContext *s = h->priv_data;
464 return url_get_file_handle(s->hd);
465}
466
558b86a5
LB
467URLProtocol http_protocol = {
468 "http",
469 http_open,
470 http_read,
471 http_write,
472 http_seek,
473 http_close,
f0a80394 474 .url_get_file_handle = http_get_file_handle,
558b86a5 475};