From fd2109adc52ed0b0e91e2653e0211f08c7b98c5b Mon Sep 17 00:00:00 2001 From: ac-stef Date: Sun, 6 Sep 2015 19:23:09 +0200 Subject: [PATCH] add support for port and path settings for autodownload servers actually, addpckserver now supports a full url as "host" field - but only domain, port and path are used --- source/src/autodownload.cpp | 36 +++++++++++--------- source/src/http.cpp | 67 +++++++++++++++++++++++++++++++++++++ source/src/protos.h | 14 +++++++- 3 files changed, 101 insertions(+), 16 deletions(-) diff --git a/source/src/autodownload.cpp b/source/src/autodownload.cpp index d2edf99e..7a857c5e 100644 --- a/source/src/autodownload.cpp +++ b/source/src/autodownload.cpp @@ -67,38 +67,41 @@ const char *updatestxt = "/updates.txt"; int pingpckserver(void *data) // fetch updates.txt from a media server, measure required time { httpget h; + urlparse u; pckserver *s = (pckserver *)data; - - s->resolved = h.set_host(s->host) ? 1 : 0; + u.set(s->host); + s->resolved = h.set_host(u.domain) ? 1 : 0; SDL_mutexP(pckpinglog_lock); - cvecprintf(pckping_log, "lresolving hostname %s %s\n", s->host, s->resolved ? "succeeded" : "failed"); + cvecprintf(pckping_log, "lresolving hostname %s %s (%s)\n", u.domain, s->resolved ? "succeeded" : "failed", iptoa(ntohl(h.ip.host))); SDL_mutexV(pckpinglog_lock); if(s->resolved) { - int res = h.get(updatestxt, 5000, 10000, 0, true); // http HEAD request only - to get a nice ping value + if(*u.port) h.set_port(atoi(u.port)); + defformatstring(url)("%s%s", u.path, updatestxt); + int res = h.get(url, 5000, 10000, 0, true); // http HEAD request only - to get a nice ping value if(h.response > 0) { s->ping = h.elapsedtime; SDL_mutexP(pckpinglog_lock); - cvecprintf(pckping_log, "lhttp head %s%s response time: %d, status: %d, contentlength: %d\n", s->host, updatestxt, s->ping, h.response, h.contentlength); + cvecprintf(pckping_log, "lhttp head %s:%d%s response time: %d, status: %d, contentlength: %d\n", u.domain, h.ip.port, url, s->ping, h.response, h.contentlength); SDL_mutexV(pckpinglog_lock); if(h.response == 200) { h.outvec = new vector; - res = h.get(updatestxt, 5000, 20000); // http GET + res = h.get(url, 5000, 20000); // http GET if(res >= 0 && h.response == 200) { // parse updates.txt h.outvec->add('\0'); s->updates.clear(); parseupdatelist(s->updates, (char *)h.outvec->getbuf()); // don't filter entries SDL_mutexP(pckpinglog_lock); - cvecprintf(pckping_log, "lgot %s%s: %d bytes, %d valid update lines\n", s->host, updatestxt, res, s->updates.numelems); + cvecprintf(pckping_log, "lgot %s:%d%s: %d bytes, %d valid update lines\n", u.domain, h.ip.port, url, res, s->updates.numelems); SDL_mutexV(pckpinglog_lock); } else { SDL_mutexP(pckpinglog_lock); - cvecprintf(pckping_log, "lretrieving %s%s failed: response %d%s%s\n", s->host, updatestxt, h.response, h.err ? ", err: " : "", h.err ? h.err : ""); + cvecprintf(pckping_log, "lretrieving %s:%d%s failed: response %d%s%s\n", u.domain, h.ip.port, url, h.response, h.err ? ", err: " : "", h.err ? h.err : ""); SDL_mutexV(pckpinglog_lock); } DELETEP(h.outvec); @@ -355,37 +358,40 @@ int progress_callback_dlpackage(void *data, float progress) bool dlpackage(httpget &h, package *pck, pckserver *s) // download one package from one server { - const char *host = s ? s->host : pck->host; + urlparse u; + u.set(s ? s->host : pck->host); h.callbackfunc = progress_callback_dlpackage; h.callbackdata = pck->name; progress_n = progress_of - pendingpackages.length(); - if(*host && h.set_host(host)) + if(*u.domain && h.set_host(u.domain)) { + if(*u.port) h.set_port(atoi(u.port)); h.outstream = openvecfile(NULL); - int got = h.get(pck->requestname, 6000, 90000); + defformatstring(url)("%s%s", u.path, pck->requestname); + int got = h.get(url, 6000, 90000); if(!h.response) { if(!canceldownloads && s) s->ping = 0; // received a hard error from the server connection, better not try this one again - clientlogf("download %s%s failed%s%s", host, pck->requestname, h.err ? ", err: " : "", h.err ? h.err : ""); + clientlogf("download %s:%d%s failed%s%s", u.domain, h.ip.port, url, h.err ? ", err: " : "", h.err ? h.err : ""); h.disconnect(); } else { if(h.response == 200) { - clientlogf("downloaded %s%s, %d bytes (%d raw), %d msec", host, pck->requestname, got, h.contentlength, h.elapsedtime); + clientlogf("downloaded %s:%d%s, %d bytes (%d raw), %d msec", u.domain, h.ip.port, url, got, h.contentlength, h.elapsedtime); processdownload(pck, h.outstream); h.outstream = NULL; // deleted by processdownload() return true; } - else clientlogf("download %s%s failed, server response %d%s%s", host, pck->requestname, h.response, h.err ? ", err: " : "", h.err ? h.err : ""); + else clientlogf("download %s:%d%s failed, server response %d%s%s", u.domain, h.ip.port, url, h.response, h.err ? ", err: " : "", h.err ? h.err : ""); } DELETEP(h.outstream); } else { if(s) s->resolved = 0; - clientlogf("resolving host \"%s\" failed", host); + clientlogf("resolving host \"%s\" failed", u.domain); h.disconnect(); } return false; diff --git a/source/src/http.cpp b/source/src/http.cpp index b8b623c9..4f7a09e2 100644 --- a/source/src/http.cpp +++ b/source/src/http.cpp @@ -49,6 +49,14 @@ bool httpget::set_host(const char *newhostname) // set and resolve host return ip.host != ENET_HOST_ANY; } +void httpget::set_port(int port) // set port (disconnect, if different to previous) +{ + port &= 65535; + if(port < 80) port = 80; + if(port != ip.port) disconnect(); + ip.port = port; +} + bool httpget::execcallback(float progress) { if(callbackfunc && (*callbackfunc)(callbackdata, progress)) @@ -334,3 +342,62 @@ char *urlencode(const char *s, bool strict) return newstring(d.getbuf()); } +// parse URLs: scheme:[//[user:password@]domain[:port]][/]path[?query][#fragment] + +void urlparse::set(const char *newurl) +{ + DELSTRING(buf); + char *p, *s = buf = newstring(newurl, strlen(newurl) + 3); + scheme = userpassword = port = path = query = fragment = ""; + + filtertext(s, s, FTXT_NOWHITE); + if((p = strstr(s, "://"))) // domain part is mandatory, which makes the double slash a given - if a scheme is specified + { // scheme found + scheme = s; + *p = '\0'; + s = p + 3; + } + if((p = strchr(s, '@'))) + { // user:password found + userpassword = s; + *p = '\0'; + s = p + 1; + } + domain = s; + if(*s == '[' && (p = strchr(s, ']'))) s = p + 1; // [....] + else while(isalnum(*s) || *s == '.' || *s == '-') s++; // skip domain + if(*s == ':') + { // port found + *s = '\0'; + port = ++s; + while(isdigit(*s)) s++; + } + if(*s) + { + memmove(s + 2, s, strlen(s) + 1); + *s++ = '\0'; // end domain or port string + *s++ = '/'; // add mandatory slash for path + if(*s != '/') s--; + path = s; + if((p = strchr(s, '?'))) + { // query found + *p++ = '\0'; + query = s = p; + } + if((p = strchr(s, '#'))) + { // fragment found + *p++ = '\0'; + fragment = s = p; + } + // fix path, if necessary + size_t pl = strlen(path); + if(pl < 2) path = ""; // ignore single slash + else + { + p = (char *)path; + unixpath(p); + p += pl - 1; + if(*p == '/') *p = '\0'; // remove trailing slash + } + } +} diff --git a/source/src/protos.h b/source/src/protos.h index d3dfa1a0..2b24d423 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -245,7 +245,7 @@ struct httpget const char *hostname, *url; // set by set_host() and get() const char *useragent, *referrer; // can be set manually with "newstrings", deleted automatically const char *err; // read-only - ENetAddress ip; // set by set_host(), port may be changed manually + ENetAddress ip; // set by set_host() int maxredirects, maxtransfer, maxsize; // set manually int (__cdecl *callbackfunc)(void *data, float progress); void *callbackdata; @@ -266,12 +266,24 @@ struct httpget ~httpget() { reset(); DELSTRING(useragent); DELSTRING(referrer); DELETEP(outvec); DELETEP(outstream); } void reset(int keep = 0); // cleanup bool set_host(const char *newhostname); // set and resolve host + void set_port(int port); // set port (disconnect, if different to previous) bool execcallback(float progress); void disconnect(); bool connect(bool force = false); int get(const char *url1, uint timeout, uint totaltimeout, int range = 0, bool head = false); // get web ressource }; +struct urlparse +{ + char *buf; + // result (scheme:[//[user:password@]domain[:port]][/]path[?query][#fragment]) + const char *scheme, *userpassword, *domain, *port, *path, *query, *fragment; + + urlparse() { memset(&buf, 0, sizeof(struct urlparse)); } + ~urlparse() { DELSTRING(buf); } + void set(const char *newurl); // set and parse url +}; + extern char *urlencode(const char *s, bool strict = false); struct packetqueue