http protocol now uses tcp: protocol (simpler)
[libav.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