disable encoders where appropriate (patch courtesy of BERO
[ffmpeg.git] / libavformat / 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 <sys/types.h>
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #ifndef __BEOS__
25 # include <arpa/inet.h>
26 #else
27 # include "barpainet.h"
28 #endif
29 #include <netdb.h>
30
31
32 /* XXX: POST protocol is not completly implemented because ffmpeg use
33    only a subset of it */
34
35 //#define DEBUG
36
37 /* used for protocol handling */
38 #define BUFFER_SIZE 1024
39 #define URL_SIZE    4096
40
41 typedef struct {
42     URLContext *hd;
43     unsigned char buffer[BUFFER_SIZE], *buf_ptr, *buf_end;
44     int line_count;
45     int http_code;
46     char location[URL_SIZE];
47 } HTTPContext;
48
49 static int http_connect(URLContext *h, const char *path, const char *hoststr);
50 static int http_write(URLContext *h, uint8_t *buf, int size);
51
52
53 /* return non zero if error */
54 static int http_open(URLContext *h, const char *uri, int flags)
55 {
56     const char *path, *proxy_path;
57     char hostname[1024], hoststr[1024];
58     char path1[1024];
59     char buf[1024];
60     int port, use_proxy, err;
61     HTTPContext *s;
62     URLContext *hd = NULL;
63
64     h->is_streamed = 1;
65
66     s = av_malloc(sizeof(HTTPContext));
67     if (!s) {
68         return -ENOMEM;
69     }
70     h->priv_data = s;
71
72     proxy_path = getenv("http_proxy");
73     use_proxy = (proxy_path != NULL) && !getenv("no_proxy") && 
74         strstart(proxy_path, "http://", NULL);
75
76     /* fill the dest addr */
77  redo:
78     /* needed in any case to build the host string */
79     url_split(NULL, 0, hostname, sizeof(hostname), &port, 
80               path1, sizeof(path1), uri);
81     if (port > 0) {
82         snprintf(hoststr, sizeof(hoststr), "%s:%d", hostname, port);
83     } else {
84         pstrcpy(hoststr, sizeof(hoststr), hostname);
85     }
86
87     if (use_proxy) {
88         url_split(NULL, 0, hostname, sizeof(hostname), &port, 
89                   NULL, 0, proxy_path);
90         path = uri;
91     } else {
92         if (path1[0] == '\0')
93             path = "/";
94         else
95             path = path1;
96     }
97     if (port < 0)
98         port = 80;
99
100     snprintf(buf, sizeof(buf), "tcp://%s:%d", hostname, port);
101     err = url_open(&hd, buf, URL_RDWR);
102     if (err < 0)
103         goto fail;
104
105     s->hd = hd;
106     if (http_connect(h, path, hoststr) < 0)
107         goto fail;
108     if (s->http_code == 303 && s->location[0] != '\0') {
109         /* url moved, get next */
110         uri = s->location;
111         url_close(hd);
112         goto redo;
113     }
114     return 0;
115  fail:
116     if (hd)
117         url_close(hd);
118     av_free(s);
119     return -EIO;
120 }
121
122 static int http_getc(HTTPContext *s)
123 {
124     int len;
125     if (s->buf_ptr >= s->buf_end) {
126         len = url_read(s->hd, s->buffer, BUFFER_SIZE);
127         if (len < 0) {
128             return -EIO;
129         } else if (len == 0) {
130             return -1;
131         } else {
132             s->buf_ptr = s->buffer;
133             s->buf_end = s->buffer + len;
134         }
135     }
136     return *s->buf_ptr++;
137 }
138
139 static int process_line(HTTPContext *s, char *line, int line_count)
140 {
141     char *tag, *p;
142     
143     /* end of header */
144     if (line[0] == '\0')
145         return 0;
146
147     p = line;
148     if (line_count == 0) {
149         while (!isspace(*p) && *p != '\0')
150             p++;
151         while (isspace(*p))
152             p++;
153         s->http_code = strtol(p, NULL, 10);
154 #ifdef DEBUG
155         printf("http_code=%d\n", s->http_code);
156 #endif
157     } else {
158         while (*p != '\0' && *p != ':')
159             p++;
160         if (*p != ':') 
161             return 1;
162         
163         *p = '\0';
164         tag = line;
165         p++;
166         while (isspace(*p))
167             p++;
168         if (!strcmp(tag, "Location")) {
169             strcpy(s->location, p);
170         }
171     }
172     return 1;
173 }
174
175 static int http_connect(URLContext *h, const char *path, const char *hoststr)
176 {
177     HTTPContext *s = h->priv_data;
178     int post, err, ch;
179     char line[1024], *q;
180
181
182     /* send http header */
183     post = h->flags & URL_WRONLY;
184
185     snprintf(s->buffer, sizeof(s->buffer),
186              "%s %s HTTP/1.0\n"
187              "User-Agent: FFmpeg %s\n"
188              "Accept: */*\n"
189              "Host: %s\n"
190              "\n",
191              post ? "POST" : "GET",
192              path,
193              LIBAVFORMAT_VERSION,
194              hoststr);
195     
196     if (http_write(h, s->buffer, strlen(s->buffer)) < 0)
197         return -EIO;
198         
199     /* init input buffer */
200     s->buf_ptr = s->buffer;
201     s->buf_end = s->buffer;
202     s->line_count = 0;
203     s->location[0] = '\0';
204     if (post) {
205         sleep(1);
206         return 0;
207     }
208     
209     /* wait for header */
210     q = line;
211     for(;;) {
212         ch = http_getc(s);
213         if (ch < 0)
214             return -EIO;
215         if (ch == '\n') {
216             /* process line */
217             if (q > line && q[-1] == '\r')
218                 q--;
219             *q = '\0';
220 #ifdef DEBUG
221             printf("header='%s'\n", line);
222 #endif
223             err = process_line(s, line, s->line_count);
224             if (err < 0)
225                 return err;
226             if (err == 0)
227                 return 0;
228             s->line_count++;
229             q = line;
230         } else {
231             if ((q - line) < sizeof(line) - 1)
232                 *q++ = ch;
233         }
234     }
235 }
236
237
238 static int http_read(URLContext *h, uint8_t *buf, int size)
239 {
240     HTTPContext *s = h->priv_data;
241     int size1, len;
242
243     size1 = size;
244     while (size > 0) {
245         /* read bytes from input buffer first */
246         len = s->buf_end - s->buf_ptr;
247         if (len > 0) {
248             if (len > size)
249                 len = size;
250             memcpy(buf, s->buf_ptr, len);
251             s->buf_ptr += len;
252         } else {
253             len = url_read (s->hd, buf, size);
254             if (len < 0) {
255                 return len;
256             } else if (len == 0) {
257                 break;
258             }
259         }
260         size -= len;
261         buf += len;
262     }
263     return size1 - size;
264 }
265
266 /* used only when posting data */
267 static int http_write(URLContext *h, uint8_t *buf, int size)
268 {
269     HTTPContext *s = h->priv_data;
270     return url_write(s->hd, buf, size);
271 }
272
273 static int http_close(URLContext *h)
274 {
275     HTTPContext *s = h->priv_data;
276     url_close(s->hd);
277     av_free(s);
278     return 0;
279 }
280
281 URLProtocol http_protocol = {
282     "http",
283     http_open,
284     http_read,
285     http_write,
286     NULL, /* seek */
287     http_close,
288 };
289