* Add code to pace sending a stream when it is being sent from a file.
[ffmpeg.git] / libav / http.c
1 /*
2  * HTTP protocol for ffmpeg client
3  * Copyright (c) 2000, 2001 Fabrice Bellard.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19 #include "avformat.h"
20 #include <unistd.h>
21 #include <ctype.h>
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
26 #include <netdb.h>
27
28
29 /* XXX: POST protocol is not completly implemented because ffmpeg use
30    only a subset of it */
31
32 //#define DEBUG
33
34 /* used for protocol handling */
35 #define BUFFER_SIZE 1024
36 #define URL_SIZE    4096
37
38 typedef struct {
39     URLContext *hd;
40     unsigned char buffer[BUFFER_SIZE], *buf_ptr, *buf_end;
41     int line_count;
42     int http_code;
43     char location[URL_SIZE];
44 } HTTPContext;
45
46 static int http_connect(URLContext *h, const char *path, const char *hoststr);
47 static int http_write(URLContext *h, UINT8 *buf, int size);
48
49
50 /* return non zero if error */
51 static int http_open(URLContext *h, const char *uri, int flags)
52 {
53     const char *path, *proxy_path;
54     char hostname[1024], hoststr[1024];
55     char path1[1024];
56     char buf[1024];
57     int port, use_proxy, err;
58     HTTPContext *s;
59     URLContext *hd = NULL;
60
61     h->is_streamed = 1;
62
63     s = av_malloc(sizeof(HTTPContext));
64     if (!s) {
65         return -ENOMEM;
66     }
67     h->priv_data = s;
68
69     proxy_path = getenv("http_proxy");
70     use_proxy = (proxy_path != NULL) && !getenv("no_proxy") && 
71         strstart(proxy_path, "http://", NULL);
72
73     /* fill the dest addr */
74  redo:
75     /* needed in any case to build the host string */
76     url_split(NULL, 0, hostname, sizeof(hostname), &port, 
77               path1, sizeof(path1), uri);
78     if (port > 0) {
79         snprintf(hoststr, sizeof(hoststr), "%s:%d", hostname, port);
80     } else {
81         pstrcpy(hoststr, sizeof(hoststr), hostname);
82     }
83
84     if (use_proxy) {
85         url_split(NULL, 0, hostname, sizeof(hostname), &port, 
86                   NULL, 0, proxy_path);
87         path = uri;
88     } else {
89         if (path1[0] == '\0')
90             path = "/";
91         else
92             path = path1;
93     }
94     if (port < 0)
95         port = 80;
96
97     snprintf(buf, sizeof(buf), "tcp://%s:%d", hostname, port);
98     err = url_open(&hd, buf, URL_RDWR);
99     if (err < 0)
100         goto fail;
101
102     s->hd = hd;
103     if (http_connect(h, path, hoststr) < 0)
104         goto fail;
105     if (s->http_code == 303 && s->location[0] != '\0') {
106         /* url moved, get next */
107         uri = s->location;
108         url_close(hd);
109         goto redo;
110     }
111     return 0;
112  fail:
113     if (hd)
114         url_close(hd);
115     av_free(s);
116     return -EIO;
117 }
118
119 static int http_getc(HTTPContext *s)
120 {
121     int len;
122     if (s->buf_ptr >= s->buf_end) {
123         len = url_read(s->hd, s->buffer, BUFFER_SIZE);
124         if (len < 0) {
125             return -EIO;
126         } else if (len == 0) {
127             return -1;
128         } else {
129             s->buf_ptr = s->buffer;
130             s->buf_end = s->buffer + len;
131         }
132     }
133     return *s->buf_ptr++;
134 }
135
136 static int process_line(HTTPContext *s, char *line, int line_count)
137 {
138     char *tag, *p;
139     
140     /* end of header */
141     if (line[0] == '\0')
142         return 0;
143
144     p = line;
145     if (line_count == 0) {
146         while (!isspace(*p) && *p != '\0')
147             p++;
148         while (isspace(*p))
149             p++;
150         s->http_code = strtol(p, NULL, 10);
151 #ifdef DEBUG
152         printf("http_code=%d\n", s->http_code);
153 #endif
154     } else {
155         while (*p != '\0' && *p != ':')
156             p++;
157         if (*p != ':') 
158             return 1;
159         
160         *p = '\0';
161         tag = line;
162         p++;
163         while (isspace(*p))
164             p++;
165         if (!strcmp(tag, "Location")) {
166             strcpy(s->location, p);
167         }
168     }
169     return 1;
170 }
171
172 static int http_connect(URLContext *h, const char *path, const char *hoststr)
173 {
174     HTTPContext *s = h->priv_data;
175     int post, err, ch;
176     char line[1024], *q;
177
178
179     /* send http header */
180     post = h->flags & URL_WRONLY;
181
182     snprintf(s->buffer, sizeof(s->buffer),
183              "%s %s HTTP/1.0\n"
184              "User-Agent: FFmpeg %s\n"
185              "Accept: */*\n"
186              "Host: %s\n"
187              "\n",
188              post ? "POST" : "GET",
189              path,
190              FFMPEG_VERSION,
191              hoststr);
192     
193     if (http_write(h, s->buffer, strlen(s->buffer)) < 0)
194         return -EIO;
195         
196     /* init input buffer */
197     s->buf_ptr = s->buffer;
198     s->buf_end = s->buffer;
199     s->line_count = 0;
200     s->location[0] = '\0';
201     if (post) {
202         sleep(1);
203         return 0;
204     }
205     
206     /* wait for header */
207     q = line;
208     for(;;) {
209         ch = http_getc(s);
210         if (ch < 0)
211             return -EIO;
212         if (ch == '\n') {
213             /* process line */
214             if (q > line && q[-1] == '\r')
215                 q--;
216             *q = '\0';
217 #ifdef DEBUG
218             printf("header='%s'\n", line);
219 #endif
220             err = process_line(s, line, s->line_count);
221             if (err < 0)
222                 return err;
223             if (err == 0)
224                 return 0;
225             s->line_count++;
226             q = line;
227         } else {
228             if ((q - line) < sizeof(line) - 1)
229                 *q++ = ch;
230         }
231     }
232 }
233
234
235 static int http_read(URLContext *h, UINT8 *buf, int size)
236 {
237     HTTPContext *s = h->priv_data;
238     int size1, len;
239
240     size1 = size;
241     while (size > 0) {
242         /* read bytes from input buffer first */
243         len = s->buf_end - s->buf_ptr;
244         if (len > 0) {
245             if (len > size)
246                 len = size;
247             memcpy(buf, s->buf_ptr, len);
248             s->buf_ptr += len;
249         } else {
250             len = url_read (s->hd, buf, size);
251             if (len < 0) {
252                 return len;
253             } else if (len == 0) {
254                 break;
255             }
256         }
257         size -= len;
258         buf += len;
259     }
260     return size1 - size;
261 }
262
263 /* used only when posting data */
264 static int http_write(URLContext *h, UINT8 *buf, int size)
265 {
266     HTTPContext *s = h->priv_data;
267     return url_write(s->hd, buf, size);
268 }
269
270 static int http_close(URLContext *h)
271 {
272     HTTPContext *s = h->priv_data;
273     url_close(s->hd);
274     av_free(s);
275     return 0;
276 }
277
278 URLProtocol http_protocol = {
279     "http",
280     http_open,
281     http_read,
282     http_write,
283     NULL, /* seek */
284     http_close,
285 };
286