Add:map_binfile:Redirects for map download
authormartin-s <martin-s@ffa7fe5e-494d-0410-b361-a75ebd5db220>
Thu, 6 Jan 2011 10:57:30 +0000 (10:57 +0000)
committermartin-s <martin-s@ffa7fe5e-494d-0410-b361-a75ebd5db220>
Thu, 6 Jan 2011 10:57:30 +0000 (10:57 +0000)
git-svn-id: https://navit.svn.sourceforge.net/svnroot/navit/trunk@3870 ffa7fe5e-494d-0410-b361-a75ebd5db220

navit/navit/attr_def.h
navit/navit/file.c
navit/navit/file.h
navit/navit/map/binfile/binfile.c

index 3d45016..e8f74ba 100644 (file)
@@ -227,6 +227,7 @@ ATTR(autostart)
 ATTR(readwrite)
 ATTR(cache)
 ATTR(create)
+ATTR(persistent)
 ATTR2(0x0002ffff,type_int_end)
 ATTR2(0x00030000,type_string_begin)
 ATTR(type)
index ea3726f..36c9a18 100644 (file)
@@ -37,6 +37,7 @@
 #include "atom.h"
 #include "config.h"
 #include "item.h"
+#include "util.h"
 #ifdef HAVE_SOCKET
 #include <sys/socket.h>
 #include <netdb.h>
@@ -107,12 +108,11 @@ file_socket_connect(char *host, char *service)
        freeaddrinfo(result);
        return fd;
 }
-#endif
 
 static void
-file_http_request(struct file *file, char *method, char *host, char *path, char *header)
+file_http_request(struct file *file, char *method, char *host, char *path, char *header, int persistent)
 {
-       char *request=g_strdup_printf("%s %s HTTP/1.0\r\nUser-Agent: navit %s\r\nHost: %s%s%s%s\r\n\r\n",method,path,version,host,header?"\r\n":"",header?header:"",header?"\r\n":"");
+       char *request=g_strdup_printf("%s %s HTTP/1.0\r\nUser-Agent: navit %s\r\nHost: %s\r\n%s%s%s\r\n",method,path,version,host,persistent?"Connection: Keep-Alive\r\n":"",header?header:"",header?"\r\n":"");
        write(file->fd, request, strlen(request));
        dbg(1,"%s\n",request);
        file->requests++;
@@ -138,6 +138,68 @@ file_http_header_end(char *str, int len)
        return NULL;
 }
 
+static int
+file_request_do(struct file *file, struct attr **options, int connect)
+{
+       struct attr *attr;
+       char *name;
+
+       if (!options)
+               return 0;
+       attr=attr_search(options, NULL, attr_url);
+       if (!attr)
+               return 0;
+       name=attr->u.str;
+       if (!name)
+               return 0;
+       g_free(file->name);
+       file->name = g_strdup(name);
+       if (!strncmp(name,"http://",7)) {
+               char *host=g_strdup(name+7);
+               char *port=strchr(host,':');
+               char *path=strchr(name+7,'/');
+               char *method="GET";
+               char *header=NULL;
+               int persistent=0;
+               if ((attr=attr_search(options, NULL, attr_http_method)) && attr->u.str)
+                       method=attr->u.str;
+               if ((attr=attr_search(options, NULL, attr_http_header)) && attr->u.str)
+                       header=attr->u.str;
+               if ((attr=attr_search(options, NULL, attr_persistent)))
+                       persistent=attr->u.num;
+               if (path) 
+                       host[path-name-7]='\0';
+               if (port)
+                       *port++='\0';
+               dbg(1,"host=%s path=%s\n",host,path);
+               if (connect) 
+                       file->fd=file_socket_connect(host,port?port:"80");
+               file_http_request(file,method,host,path,header,persistent);
+               file->special=1;
+               g_free(host);
+       }
+       return 1;
+}
+#endif
+
+int
+file_request(struct file *f, struct attr **options)
+{
+#ifdef HAVE_SOCKET
+       return file_request_do(f, options, 0);
+#else
+       return 0;
+#endif
+}
+
+char *
+file_http_header(struct file *f, char *header)
+{
+       if (!f->headers)
+               return NULL;
+       return g_hash_table_lookup(f->headers, header);
+}
+
 struct file *
 file_create(char *name, struct attr **options)
 {
@@ -146,30 +208,9 @@ file_create(char *name, struct attr **options)
        struct attr *attr;
        int open_flags=O_LARGEFILE|O_BINARY;
 
-       if (options && (attr=attr_search(options, NULL, attr_url)) && attr->u.str) {
+       if (options && (attr=attr_search(options, NULL, attr_url))) {
 #ifdef HAVE_SOCKET
-               name=attr->u.str;
-               file->name = g_strdup(name);
-               if (!strncmp(name,"http://",7)) {
-                       char *host=g_strdup(name+7);
-                       char *port=strchr(host,':');
-                       char *path=strchr(name+7,'/');
-                       char *method="GET";
-                       char *header=NULL;
-                       if ((attr=attr_search(options, NULL, attr_http_method)) && attr->u.str)
-                               method=attr->u.str;
-                       if ((attr=attr_search(options, NULL, attr_http_header)) && attr->u.str)
-                               header=attr->u.str;
-                       if (path) 
-                               host[path-name-7]='\0';
-                       if (port)
-                               *port++='\0';
-                       dbg(1,"host=%s path=%s\n",host,path);
-                       file->fd=file_socket_connect(host,port?port:"80");
-                       file_http_request(file,method,host,path,header);
-                       file->special=1;
-                       g_free(host);
-               }
+               file_request_do(file, options, 1);
 #endif
        } else {
                if (options && (attr=attr_search(options, NULL, attr_readwrite)) && attr->u.num) {
@@ -306,13 +347,31 @@ static void
 file_process_headers(struct file *file, char *headers)
 {
        char *tok;
-       char *cl="Content-Length: ";
+       char *cl;
+       if (file->headers)
+               g_hash_table_destroy(file->headers);
+       file->headers=g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
        while ((tok=strtok(headers, "\r\n"))) {
-               if (!strncasecmp(tok,cl,strlen(cl))) {
-                       file->size=atoll(tok+strlen(cl));
+               char *sep;
+               tok=g_strdup(tok);
+               sep=strchr(tok,':');
+               if (!sep)
+                       sep=strchr(tok,'/');
+               if (!sep) {
+                       g_free(tok);
+                       continue;
                }
+               *sep++='\0';
+               if (*sep == ' ')
+                       sep++;
+               strtolower(tok, tok);
+               dbg(1,"header '%s'='%s'\n",tok,sep);
+               g_hash_table_insert(file->headers, tok, sep);
                headers=NULL;
        }
+       cl=g_hash_table_lookup(file->headers, "content-length");
+       if (cl) 
+               file->size=atoll(cl);
 }
 
 static void
@@ -334,7 +393,7 @@ file_data_read_special(struct file *file, int size, int *size_ret)
        if (!file->buffer)
                file->buffer=g_malloc(buffer_size);
        ret=g_malloc(size);
-       while (size > 0 && (!eof || file->buffer_len)) {
+       while ((size > 0 || file->requests) && (!eof || file->buffer_len)) {
                int toread=buffer_size-file->buffer_len;
                if (toread >= 4096 && !eof) {
                        rd=read(file->fd, file->buffer+file->buffer_len, toread);
@@ -351,8 +410,11 @@ file_data_read_special(struct file *file, int size, int *size_ret)
                                file_process_headers(file, file->buffer);
                                file_shift_buffer(file, hdr-file->buffer);
                                file->requests--;
+                               if (file_http_header(file, "location"))
+                                       break;
                        }
-               } else {
+               }
+               if (!file->requests) {
                        rd=file->buffer_len;
                        if (rd > size)
                                rd=size;
@@ -649,6 +711,8 @@ file_create_caseinsensitive(char *name, struct attr **options)
 void
 file_destroy(struct file *f)
 {
+       if (f->headers)
+               g_hash_table_destroy(f->headers);
        switch (f->special) {
        case 0:
        case 1:
index be5278a..6a037fa 100644 (file)
@@ -56,9 +56,12 @@ struct file {
        int requests;
        unsigned char *buffer;
        int buffer_len;
+       GHashTable *headers;
 };
 
 /* prototypes */
+int file_request(struct file *f, struct attr **options);
+char *file_http_header(struct file *f, char *header);
 struct file *file_create(char *name, struct attr **options);
 int file_is_dir(char *name);
 long long file_size(struct file *file);
index e255eb7..c5c44ac 100644 (file)
@@ -59,8 +59,8 @@ struct map_download {
        struct map_priv *m;
        struct map_rect_priv *mr;
        struct file *http,*file;
-       int zipfile,toffset,tlength,progress,read;
-       long long offset,cd1offset,size;
+       int zipfile,toffset,tlength,progress,read,dl_size;
+       long long offset,start_offset,cd1offset,size;
        struct zip64_eoc *zip64_eoc;
        struct zip64_eocl *zip64_eocl;
        struct zip_eoc *zip_eoc;
@@ -71,7 +71,7 @@ struct map_priv {
        int id;
        char *filename;
        char *cachedir;
-       struct file *fi;
+       struct file *fi,*http;
        struct file **fis;
        struct zip_cd *index_cd;
        int index_offset;
@@ -93,6 +93,7 @@ struct map_priv {
        char *progress;
        struct callback_list *cbl;
        struct map_download *download;
+       int redirect;
 };
 
 struct map_rect_priv {
@@ -869,53 +870,101 @@ zipfile_to_tile(struct map_priv *m, struct zip_cd *cd, struct tile *t)
        return t->start != NULL;
 }
 
+
+static int
+map_binfile_handle_redirect(struct map_priv *m)
+{
+       char *location=file_http_header(m->http, "location");
+       if (!location) {
+               m->redirect=0;
+               return 0;
+       }
+       if (m->redirect) 
+               return 0;
+       m->redirect=1;
+       dbg(0,"redirected from %s to %s\n",m->url,location);
+       g_free(m->url);
+       file_destroy(m->http);
+       m->http=NULL;
+       m->url=g_strdup(location);
+
+       return 1;
+}
+
+static int
+map_binfile_http_request(struct map_priv *m, struct attr **attrs)
+{
+       if (!m->http) {
+               m->http=file_create(NULL, attrs);
+       } else {
+               file_request(m->http, attrs);
+       }
+       return 1;
+}
+
+
 static long long
 map_binfile_download_size(struct map_priv *m)
 {
        struct attr url={attr_url};
        struct attr http_method={attr_http_method};
-       struct attr *attrs[3];
+       struct attr persistent={attr_persistent};
+       struct attr *attrs[4];
        int size_ret;
        long long ret;
-       struct file *http;
        void *data;
 
-       attrs[0]=&url;
-       url.u.str=m->url;
-       attrs[1]=&http_method;
-       http_method.u.str="HEAD";
-       attrs[2]=NULL;
-
-       http=file_create(NULL, attrs);
-       if (!http)
-               return 0;
-       data=file_data_read_special(http, 4096, &size_ret);
-       g_free(data);
-       if (size_ret < 0) 
-               return 0;
-       ret=file_size(http);
-       file_destroy(http);
+       do {
+               attrs[0]=&url;
+               url.u.str=m->url;
+               attrs[1]=&http_method;
+               http_method.u.str="HEAD";
+               persistent.u.num=1;
+               attrs[2]=&persistent;
+               attrs[3]=NULL;
+
+               map_binfile_http_request(m, attrs);
+               data=file_data_read_special(m->http, 0, &size_ret);
+               g_free(data);
+               if (size_ret < 0) 
+                       return 0;
+       } while (map_binfile_handle_redirect(m));
+       
+       ret=file_size(m->http);
        return ret;
 }
 
+
+static int
+map_binfile_http_close(struct map_priv *m)
+{
+       if (m->http) {
+               file_destroy(m->http);
+               m->http=NULL;
+       }
+       return 1;
+}
+
+
 static struct file *
 map_binfile_http_range(struct map_priv *m, long long offset, int size)
 {
-       struct file *http;
-       struct attr *attrs[3];
+       struct attr *attrs[4];
        struct attr url={attr_url};
        struct attr http_header={attr_http_header};
-       
+       struct attr persistent={attr_persistent};
+
+       persistent.u.num=1;     
        attrs[0]=&url;
        attrs[1]=&http_header;
-       attrs[2]=NULL;
+       attrs[2]=&persistent;
+       attrs[3]=NULL;
 
        url.u.str=m->url;
        http_header.u.str=g_strdup_printf("Range: bytes=%Ld-%Ld",offset, offset+size-1);
-
-       http=file_create(NULL, attrs);
+       map_binfile_http_request(m, attrs);
        g_free(http_header.u.str);
-       return http;
+       return m->http;
 }
 
 static unsigned char *
@@ -930,7 +979,6 @@ map_binfile_download_range(struct map_priv *m, long long offset, int size)
                g_free(ret);
                return NULL;
        }
-       file_destroy(http);
        return ret;
 }
 
@@ -945,64 +993,78 @@ download_cd(struct map_download *download)
        return cd;
 }
 
+static int
+download_request(struct map_download *download)
+{
+       struct attr url={attr_url};
+       struct attr http_header={attr_http_header};
+       struct attr persistent={attr_persistent};
+       struct attr *attrs[4];
+
+       attrs[0]=&url;
+       persistent.u.num=1;
+       attrs[1]=&persistent;
+       attrs[2]=NULL;
+       if (strchr(download->m->url,'?')) {
+               url.u.str=g_strdup_printf("%smemberid=%d",download->m->url,download->zipfile);
+               download->dl_size=-1;
+       } else {
+               long long offset=binfile_cd_offset(download->cd_copy);
+               int size=download->cd_copy->zipcsiz+sizeof(struct zip_lfh)+download->cd_copy->zipcfnl;
+               url.u.str=g_strdup(download->m->url);
+               http_header.u.str=g_strdup_printf("Range: bytes=%Ld-%Ld",offset,offset+size-1);
+               attrs[2]=&http_header;
+               attrs[3]=NULL;
+               download->dl_size=size;
+       }
+       dbg(0,"encountered missing tile %d %s(%s), Downloading %d bytes at %Ld\n",download->zipfile, url.u.str,(char *)(download->cd_copy+1), download->dl_size, download->offset);
+       map_binfile_http_request(download->m, attrs);
+       g_free(url.u.str);
+       download->http=download->m->http;
+       return 1;
+}
+
 
 static int
 download_start(struct map_download *download)
 {
-       char tilename[download->cd->zipcfnl+1];
        long long offset;
        struct zip_eoc *eoc;
-       struct zip_cd_ext *ext;
-       struct attr url={attr_url};
-       struct attr http_header={attr_http_header};
-       struct attr *attrs[3];
 
        if (!download->cd->zipcensig) {
                download->cd_copy=download_cd(download);
        } else {
                download->cd_copy=g_malloc(download->m->cde_size);
                memcpy(download->cd_copy, download->cd, download->m->cde_size);
-               file_data_free(download->file, (unsigned char *)download->cd);
        }
+       file_data_free(download->file, (unsigned char *)download->cd);
        download->cd=NULL;
-       strncpy(tilename,(char *)(download->cd_copy+1),download->cd_copy->zipcfnl);
-       tilename[download->cd_copy->zipcfnl]='\0';
-       attrs[0]=&url;
-       attrs[1]=NULL;
-       if (strchr(download->m->url,'?')) 
-               url.u.str=g_strdup_printf("%smemberid=%d",download->m->url,download->zipfile);
-       else {
-               long long offset=binfile_cd_offset(download->cd_copy);
-               int size=download->cd_copy->zipcsiz+sizeof(struct zip_lfh)+download->cd_copy->zipcfnl;
-               url.u.str=g_strdup(download->m->url);
-               http_header.u.str=g_strdup_printf("Range: bytes=%Ld-%Ld",offset,offset+size-1);
-               attrs[1]=&http_header;
-               attrs[2]=NULL;
-       }
        offset=file_size(download->file);
        offset-=sizeof(struct zip_eoc);
        eoc=(struct zip_eoc *)file_data_read(download->file, offset, sizeof(struct zip_eoc));
        download->zip_eoc=g_malloc(sizeof(struct zip_eoc));
        memcpy(download->zip_eoc, eoc, sizeof(struct zip_eoc));
        file_data_free(download->file, (unsigned char *)eoc);
-       dbg(0,"encountered missing tile %d %s(%s), downloading at %Ld\n",download->zipfile, url.u.str,tilename,offset);
-       ext=binfile_cd_ext(download->cd_copy);
-       if (ext) 
-               ext->zipofst=offset;
-       else
-               download->cd_copy->zipofst=offset;
-       download->http=file_create(NULL, attrs);
-       download->offset=offset;
-       g_free(url.u.str);
-       return 1;
+       download->start_offset=download->offset=offset;
+       return download_request(download);
 }
 
 static int
 download_download(struct map_download *download)
 {
-       int size=64*1024,size_ret,tile_size;
+       int size=64*1024,size_ret;
        unsigned char *data;
+       if (download->dl_size != -1 && size > download->dl_size)
+               size=download->dl_size;
+       if (!size)
+               return 1;
        data=file_data_read_special(download->http, size, &size_ret);
+       if (!download->read && map_binfile_handle_redirect(download->m)) {
+               g_free(data);
+               download_request(download);
+               return 0;
+       }
+
        dbg(1,"got %d bytes writing at offset %Ld\n",size_ret,download->offset);
        if (size_ret <= 0) {
                g_free(data);
@@ -1011,9 +1073,9 @@ download_download(struct map_download *download)
        file_data_write(download->file, download->offset, size_ret, data);
        download->offset+=size_ret;
        download->read+=size_ret;
-       tile_size=file_size(download->http);
-       if (tile_size)
-               download->progress=download->read*100/tile_size;
+       download->dl_size-=size_ret;
+       if (download->dl_size != -1)
+               download->progress=download->read*100/(download->read+download->dl_size);
        return 0;
 }
 
@@ -1022,9 +1084,15 @@ download_finish(struct map_download *download)
 {
        struct zip_lfh *lfh;
        char *lfh_filename;
-       
+       struct zip_cd_ext *ext;
+
        file_data_write(download->file, download->offset, sizeof(struct zip_eoc), (void *)download->zip_eoc);
-       lfh=(struct zip_lfh *)(file_data_read(download->file,binfile_cd_offset(download->cd_copy), sizeof(struct zip_lfh)));
+       lfh=(struct zip_lfh *)(file_data_read(download->file,download->start_offset, sizeof(struct zip_lfh)));
+       ext=binfile_cd_ext(download->cd_copy);
+       if (ext) 
+               ext->zipofst=download->start_offset;
+       else
+               download->cd_copy->zipofst=download->start_offset;
        download->cd_copy->zipcsiz=lfh->zipsize;
        download->cd_copy->zipcunc=lfh->zipuncmp;
        download->cd_copy->zipccrc=lfh->zipcrc;
@@ -1033,6 +1101,7 @@ download_finish(struct map_download *download)
        file_data_free(download->file,(void *)lfh_filename);
        file_data_free(download->file,(void *)lfh);
        file_data_write(download->file, download->m->eoc->zipeofst + download->zipfile*download->m->cde_size, binfile_cd_extra(download->cd_copy)+sizeof(struct zip_cd), (void *)download->cd_copy);
+
        g_free(download->cd_copy);
        download->cd=(struct zip_cd *)(file_data_read(download->file, download->m->eoc->zipeofst + download->zipfile*download->m->cde_size, download->m->cde_size));
        cd_to_cpu(download->cd);
@@ -1111,7 +1180,7 @@ download_directory_do(struct map_download *download)
 static int
 download_directory_finish(struct map_download *download)
 {
-       file_destroy(download->http);
+       download->http=NULL;
        return 1;
 }
 
@@ -1320,6 +1389,7 @@ map_rect_new_binfile_int(struct map_priv *map, struct map_selection *sel)
        dbg(1,"map_rect_new_binfile\n");
        if (!map->fi && !map->url)
                return NULL;
+       map_binfile_http_close(map);
        mr=g_new0(struct map_rect_priv, 1);
        mr->m=map;
        mr->sel=sel;
@@ -1422,6 +1492,7 @@ map_rect_destroy_binfile(struct map_rect_priv *mr)
        if (mr->tiles[0].fi && mr->tiles[0].start)
                file_data_free(mr->tiles[0].fi, (unsigned char *)(mr->tiles[0].start));
        g_free(mr->url);
+       map_binfile_http_close(mr->m);
         g_free(mr);
 }