00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "libavutil/base64.h"
00023 #include "libavutil/avstring.h"
00024 #include "avformat.h"
00025 #include <unistd.h>
00026 #include "network.h"
00027 #include "os_support.h"
00028
00029
00030
00031
00032
00033
00034
00035 #define BUFFER_SIZE 1024
00036 #define URL_SIZE 4096
00037 #define MAX_REDIRECTS 8
00038
00039 typedef struct {
00040 URLContext *hd;
00041 unsigned char buffer[BUFFER_SIZE], *buf_ptr, *buf_end;
00042 int line_count;
00043 int http_code;
00044 int64_t off, filesize;
00045 char location[URL_SIZE];
00046 } HTTPContext;
00047
00048 static int http_connect(URLContext *h, const char *path, const char *hoststr,
00049 const char *auth, int *new_location);
00050 static int http_write(URLContext *h, uint8_t *buf, int size);
00051
00052
00053
00054 static int http_open_cnx(URLContext *h)
00055 {
00056 const char *path, *proxy_path;
00057 char hostname[1024], hoststr[1024];
00058 char auth[1024];
00059 char path1[1024];
00060 char buf[1024];
00061 int port, use_proxy, err, location_changed = 0, redirects = 0;
00062 HTTPContext *s = h->priv_data;
00063 URLContext *hd = NULL;
00064
00065 proxy_path = getenv("http_proxy");
00066 use_proxy = (proxy_path != NULL) && !getenv("no_proxy") &&
00067 av_strstart(proxy_path, "http://", NULL);
00068
00069
00070 redo:
00071
00072 url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port,
00073 path1, sizeof(path1), s->location);
00074 if (port > 0) {
00075 snprintf(hoststr, sizeof(hoststr), "%s:%d", hostname, port);
00076 } else {
00077 av_strlcpy(hoststr, hostname, sizeof(hoststr));
00078 }
00079
00080 if (use_proxy) {
00081 url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port,
00082 NULL, 0, proxy_path);
00083 path = s->location;
00084 } else {
00085 if (path1[0] == '\0')
00086 path = "/";
00087 else
00088 path = path1;
00089 }
00090 if (port < 0)
00091 port = 80;
00092
00093 snprintf(buf, sizeof(buf), "tcp://%s:%d", hostname, port);
00094 err = url_open(&hd, buf, URL_RDWR);
00095 if (err < 0)
00096 goto fail;
00097
00098 s->hd = hd;
00099 if (http_connect(h, path, hoststr, auth, &location_changed) < 0)
00100 goto fail;
00101 if ((s->http_code == 302 || s->http_code == 303) && location_changed == 1) {
00102
00103 url_close(hd);
00104 if (redirects++ >= MAX_REDIRECTS)
00105 return AVERROR(EIO);
00106 location_changed = 0;
00107 goto redo;
00108 }
00109 return 0;
00110 fail:
00111 if (hd)
00112 url_close(hd);
00113 return AVERROR(EIO);
00114 }
00115
00116 static int http_open(URLContext *h, const char *uri, int flags)
00117 {
00118 HTTPContext *s;
00119 int ret;
00120
00121 h->is_streamed = 1;
00122
00123 s = av_malloc(sizeof(HTTPContext));
00124 if (!s) {
00125 return AVERROR(ENOMEM);
00126 }
00127 h->priv_data = s;
00128 s->filesize = -1;
00129 s->off = 0;
00130 av_strlcpy(s->location, uri, URL_SIZE);
00131
00132 ret = http_open_cnx(h);
00133 if (ret != 0)
00134 av_free (s);
00135 return ret;
00136 }
00137 static int http_getc(HTTPContext *s)
00138 {
00139 int len;
00140 if (s->buf_ptr >= s->buf_end) {
00141 len = url_read(s->hd, s->buffer, BUFFER_SIZE);
00142 if (len < 0) {
00143 return AVERROR(EIO);
00144 } else if (len == 0) {
00145 return -1;
00146 } else {
00147 s->buf_ptr = s->buffer;
00148 s->buf_end = s->buffer + len;
00149 }
00150 }
00151 return *s->buf_ptr++;
00152 }
00153
00154 static int process_line(URLContext *h, char *line, int line_count,
00155 int *new_location)
00156 {
00157 HTTPContext *s = h->priv_data;
00158 char *tag, *p;
00159
00160
00161 if (line[0] == '\0')
00162 return 0;
00163
00164 p = line;
00165 if (line_count == 0) {
00166 while (!isspace(*p) && *p != '\0')
00167 p++;
00168 while (isspace(*p))
00169 p++;
00170 s->http_code = strtol(p, NULL, 10);
00171 #ifdef DEBUG
00172 printf("http_code=%d\n", s->http_code);
00173 #endif
00174
00175 if (s->http_code >= 400 && s->http_code < 600)
00176 return -1;
00177 } else {
00178 while (*p != '\0' && *p != ':')
00179 p++;
00180 if (*p != ':')
00181 return 1;
00182
00183 *p = '\0';
00184 tag = line;
00185 p++;
00186 while (isspace(*p))
00187 p++;
00188 if (!strcmp(tag, "Location")) {
00189 strcpy(s->location, p);
00190 *new_location = 1;
00191 } else if (!strcmp (tag, "Content-Length") && s->filesize == -1) {
00192 s->filesize = atoll(p);
00193 } else if (!strcmp (tag, "Content-Range")) {
00194
00195 const char *slash;
00196 if (!strncmp (p, "bytes ", 6)) {
00197 p += 6;
00198 s->off = atoll(p);
00199 if ((slash = strchr(p, '/')) && strlen(slash) > 0)
00200 s->filesize = atoll(slash+1);
00201 }
00202 h->is_streamed = 0;
00203 }
00204 }
00205 return 1;
00206 }
00207
00208 static int http_connect(URLContext *h, const char *path, const char *hoststr,
00209 const char *auth, int *new_location)
00210 {
00211 HTTPContext *s = h->priv_data;
00212 int post, err, ch;
00213 char line[1024], *q;
00214 char *auth_b64;
00215 int auth_b64_len = strlen(auth)* 4 / 3 + 12;
00216 int64_t off = s->off;
00217
00218
00219
00220 post = h->flags & URL_WRONLY;
00221 auth_b64 = av_malloc(auth_b64_len);
00222 av_base64_encode(auth_b64, auth_b64_len, auth, strlen(auth));
00223 snprintf(s->buffer, sizeof(s->buffer),
00224 "%s %s HTTP/1.1\r\n"
00225 "User-Agent: %s\r\n"
00226 "Accept: */*\r\n"
00227 "Range: bytes=%"PRId64"-\r\n"
00228 "Host: %s\r\n"
00229 "Authorization: Basic %s\r\n"
00230 "Connection: close\r\n"
00231 "\r\n",
00232 post ? "POST" : "GET",
00233 path,
00234 LIBAVFORMAT_IDENT,
00235 s->off,
00236 hoststr,
00237 auth_b64);
00238
00239 av_freep(&auth_b64);
00240 if (http_write(h, s->buffer, strlen(s->buffer)) < 0)
00241 return AVERROR(EIO);
00242
00243
00244 s->buf_ptr = s->buffer;
00245 s->buf_end = s->buffer;
00246 s->line_count = 0;
00247 s->off = 0;
00248 s->filesize = -1;
00249 if (post) {
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 int64_t http_seek(URLContext *h, int64_t off, int whence)
00320 {
00321 HTTPContext *s = h->priv_data;
00322 URLContext *old_hd = s->hd;
00323 int64_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 };