00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "avformat.h"
00022 #include <unistd.h>
00023 #include "network.h"
00024
00025 #include "base64.h"
00026 #include "avstring.h"
00027
00028
00029
00030
00031
00032
00033
00034 #define BUFFER_SIZE 1024
00035 #define URL_SIZE 4096
00036 #define MAX_REDIRECTS 8
00037
00038 typedef struct {
00039 URLContext *hd;
00040 unsigned char buffer[BUFFER_SIZE], *buf_ptr, *buf_end;
00041 int line_count;
00042 int http_code;
00043 offset_t off, filesize;
00044 char location[URL_SIZE];
00045 } HTTPContext;
00046
00047 static int http_connect(URLContext *h, const char *path, const char *hoststr,
00048 const char *auth, int *new_location);
00049 static int http_write(URLContext *h, uint8_t *buf, int size);
00050
00051
00052
00053 static int http_open_cnx(URLContext *h)
00054 {
00055 const char *path, *proxy_path;
00056 char hostname[1024], hoststr[1024];
00057 char auth[1024];
00058 char path1[1024];
00059 char buf[1024];
00060 int port, use_proxy, err, location_changed = 0, redirects = 0;
00061 HTTPContext *s = h->priv_data;
00062 URLContext *hd = NULL;
00063
00064 proxy_path = getenv("http_proxy");
00065 use_proxy = (proxy_path != NULL) && !getenv("no_proxy") &&
00066 av_strstart(proxy_path, "http://", NULL);
00067
00068
00069 redo:
00070
00071 url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port,
00072 path1, sizeof(path1), s->location);
00073 if (port > 0) {
00074 snprintf(hoststr, sizeof(hoststr), "%s:%d", hostname, port);
00075 } else {
00076 av_strlcpy(hoststr, hostname, sizeof(hoststr));
00077 }
00078
00079 if (use_proxy) {
00080 url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port,
00081 NULL, 0, proxy_path);
00082 path = s->location;
00083 } else {
00084 if (path1[0] == '\0')
00085 path = "/";
00086 else
00087 path = path1;
00088 }
00089 if (port < 0)
00090 port = 80;
00091
00092 snprintf(buf, sizeof(buf), "tcp://%s:%d", hostname, port);
00093 err = url_open(&hd, buf, URL_RDWR);
00094 if (err < 0)
00095 goto fail;
00096
00097 s->hd = hd;
00098 if (http_connect(h, path, hoststr, auth, &location_changed) < 0)
00099 goto fail;
00100 if ((s->http_code == 302 || s->http_code == 303) && location_changed == 1) {
00101
00102 url_close(hd);
00103 if (redirects++ >= MAX_REDIRECTS)
00104 return AVERROR(EIO);
00105 location_changed = 0;
00106 goto redo;
00107 }
00108 return 0;
00109 fail:
00110 if (hd)
00111 url_close(hd);
00112 return AVERROR(EIO);
00113 }
00114
00115 static int http_open(URLContext *h, const char *uri, int flags)
00116 {
00117 HTTPContext *s;
00118 int ret;
00119
00120 h->is_streamed = 1;
00121
00122 s = av_malloc(sizeof(HTTPContext));
00123 if (!s) {
00124 return AVERROR(ENOMEM);
00125 }
00126 h->priv_data = s;
00127 s->filesize = -1;
00128 s->off = 0;
00129 av_strlcpy(s->location, uri, URL_SIZE);
00130
00131 ret = http_open_cnx(h);
00132 if (ret != 0)
00133 av_free (s);
00134 return ret;
00135 }
00136 static int http_getc(HTTPContext *s)
00137 {
00138 int len;
00139 if (s->buf_ptr >= s->buf_end) {
00140 len = url_read(s->hd, s->buffer, BUFFER_SIZE);
00141 if (len < 0) {
00142 return AVERROR(EIO);
00143 } else if (len == 0) {
00144 return -1;
00145 } else {
00146 s->buf_ptr = s->buffer;
00147 s->buf_end = s->buffer + len;
00148 }
00149 }
00150 return *s->buf_ptr++;
00151 }
00152
00153 static int process_line(URLContext *h, char *line, int line_count,
00154 int *new_location)
00155 {
00156 HTTPContext *s = h->priv_data;
00157 char *tag, *p;
00158
00159
00160 if (line[0] == '\0')
00161 return 0;
00162
00163 p = line;
00164 if (line_count == 0) {
00165 while (!isspace(*p) && *p != '\0')
00166 p++;
00167 while (isspace(*p))
00168 p++;
00169 s->http_code = strtol(p, NULL, 10);
00170 #ifdef DEBUG
00171 printf("http_code=%d\n", s->http_code);
00172 #endif
00173
00174 if (s->http_code >= 400 && s->http_code < 600)
00175 return -1;
00176 } else {
00177 while (*p != '\0' && *p != ':')
00178 p++;
00179 if (*p != ':')
00180 return 1;
00181
00182 *p = '\0';
00183 tag = line;
00184 p++;
00185 while (isspace(*p))
00186 p++;
00187 if (!strcmp(tag, "Location")) {
00188 strcpy(s->location, p);
00189 *new_location = 1;
00190 } else if (!strcmp (tag, "Content-Length") && s->filesize == -1) {
00191 s->filesize = atoll(p);
00192 } else if (!strcmp (tag, "Content-Range")) {
00193
00194 const char *slash;
00195 if (!strncmp (p, "bytes ", 6)) {
00196 p += 6;
00197 s->off = atoll(p);
00198 if ((slash = strchr(p, '/')) && strlen(slash) > 0)
00199 s->filesize = atoll(slash+1);
00200 }
00201 h->is_streamed = 0;
00202 }
00203 }
00204 return 1;
00205 }
00206
00207 static int http_connect(URLContext *h, const char *path, const char *hoststr,
00208 const char *auth, int *new_location)
00209 {
00210 HTTPContext *s = h->priv_data;
00211 int post, err, ch;
00212 char line[1024], *q;
00213 char *auth_b64;
00214 int auth_b64_len = strlen(auth)* 4 / 3 + 12;
00215 offset_t off = s->off;
00216
00217
00218
00219 post = h->flags & URL_WRONLY;
00220 auth_b64 = av_malloc(auth_b64_len);
00221 av_base64_encode(auth_b64, auth_b64_len, (uint8_t *)auth, strlen(auth));
00222 snprintf(s->buffer, sizeof(s->buffer),
00223 "%s %s HTTP/1.1\r\n"
00224 "User-Agent: %s\r\n"
00225 "Accept: */*\r\n"
00226 "Range: bytes=%"PRId64"-\r\n"
00227 "Host: %s\r\n"
00228 "Authorization: Basic %s\r\n"
00229 "Connection: close\r\n"
00230 "\r\n",
00231 post ? "POST" : "GET",
00232 path,
00233 LIBAVFORMAT_IDENT,
00234 s->off,
00235 hoststr,
00236 auth_b64);
00237
00238 av_freep(&auth_b64);
00239 if (http_write(h, s->buffer, strlen(s->buffer)) < 0)
00240 return AVERROR(EIO);
00241
00242
00243 s->buf_ptr = s->buffer;
00244 s->buf_end = s->buffer;
00245 s->line_count = 0;
00246 s->off = 0;
00247 s->filesize = -1;
00248 if (post) {
00249 usleep(1000*1000);
00250 return 0;
00251 }
00252
00253
00254 q = line;
00255 for(;;) {
00256 ch = http_getc(s);
00257 if (ch < 0)
00258 return AVERROR(EIO);
00259 if (ch == '\n') {
00260
00261 if (q > line && q[-1] == '\r')
00262 q--;
00263 *q = '\0';
00264 #ifdef DEBUG
00265 printf("header='%s'\n", line);
00266 #endif
00267 err = process_line(h, line, s->line_count, new_location);
00268 if (err < 0)
00269 return err;
00270 if (err == 0)
00271 break;
00272 s->line_count++;
00273 q = line;
00274 } else {
00275 if ((q - line) < sizeof(line) - 1)
00276 *q++ = ch;
00277 }
00278 }
00279
00280 return (off == s->off) ? 0 : -1;
00281 }
00282
00283
00284 static int http_read(URLContext *h, uint8_t *buf, int size)
00285 {
00286 HTTPContext *s = h->priv_data;
00287 int len;
00288
00289
00290 len = s->buf_end - s->buf_ptr;
00291 if (len > 0) {
00292 if (len > size)
00293 len = size;
00294 memcpy(buf, s->buf_ptr, len);
00295 s->buf_ptr += len;
00296 } else {
00297 len = url_read(s->hd, buf, size);
00298 }
00299 if (len > 0)
00300 s->off += len;
00301 return len;
00302 }
00303
00304
00305 static int http_write(URLContext *h, uint8_t *buf, int size)
00306 {
00307 HTTPContext *s = h->priv_data;
00308 return url_write(s->hd, buf, size);
00309 }
00310
00311 static int http_close(URLContext *h)
00312 {
00313 HTTPContext *s = h->priv_data;
00314 url_close(s->hd);
00315 av_free(s);
00316 return 0;
00317 }
00318
00319 static offset_t http_seek(URLContext *h, offset_t off, int whence)
00320 {
00321 HTTPContext *s = h->priv_data;
00322 URLContext *old_hd = s->hd;
00323 offset_t old_off = s->off;
00324
00325 if (whence == AVSEEK_SIZE)
00326 return s->filesize;
00327 else if ((s->filesize == -1 && whence == SEEK_END) || h->is_streamed)
00328 return -1;
00329
00330
00331 s->hd = NULL;
00332 if (whence == SEEK_CUR)
00333 off += s->off;
00334 else if (whence == SEEK_END)
00335 off += s->filesize;
00336 s->off = off;
00337
00338
00339 if (http_open_cnx(h) < 0) {
00340 s->hd = old_hd;
00341 s->off = old_off;
00342 return -1;
00343 }
00344 url_close(old_hd);
00345 return off;
00346 }
00347
00348 URLProtocol http_protocol = {
00349 "http",
00350 http_open,
00351 http_read,
00352 http_write,
00353 http_seek,
00354 http_close,
00355 };