diff --git a/Pkg/Pkg.HC b/Pkg/Pkg.HC index ecd3fd69..28f58f13 100644 --- a/Pkg/Pkg.HC +++ b/Pkg/Pkg.HC @@ -8,7 +8,6 @@ #define PKG_EVERSION (-20004) #define PKG_EOSVERSION (-20005) #define PKG_EUNSUITABLE (-20006) -#define PKG_EEOF (-20007) #define PKG_VERSION 10 @@ -38,72 +37,6 @@ static U8* StripDir(U8* file_path) { return file_path; } -// If this turns out to be useful, it might be moved into ShrineNet/Url -static I64 UrlGetWithProgress(U8* url, U8** data_out, I64* size_out) { - CUrl curl; - UrlInit(&curl); - - I64 error = 0; - I64 size = 0; - - if (UrlParse(url, &curl)) { - if (!StrCmp(curl.protocol, "http")) { - I64 sock = HttpOpenGet(curl.host, curl.port, curl.path, &size); - - if (sock > 0) { - U8* data = MAlloc(1 + size); - I64 total = 0; - I64 progress = 0; - "[$FG,3$"; - - while (total < size) { - I64 step = size - total; - - if (step > 1024) - step = 1024; - - I64 got = recv(sock, data + total, step, 0); - - if (got <= 0) { - error = PKG_EEOF; - break; - } - - total += got; - - I64 new_progress = (20 * total + size - 1) / size; - while (progress < new_progress) { - '' 0xfe; - progress++; - } - } - - close(sock); - - if (error) { - "$FG,4$x\n$FG$"; - Free(data); - return error; - } - - "$FG$]\n"; - data[total] = 0; - *data_out = data; - *size_out = total; - } - else - error = sock; - } - else - error = URL_EPROTOCOL; - } - else - error = URL_EPARSE; - - UrlFree(&curl); - return error; -} - U0 PkgInfoInit(CPkgInfo* pinf) { pinf->package_name = 0; diff --git a/Shrine/mkdist.script b/Shrine/mkdist.script index b66d0b32..52e3979f 100644 --- a/Shrine/mkdist.script +++ b/Shrine/mkdist.script @@ -13,6 +13,7 @@ command DirMk("/Adam/Net"); put /Adam/Net/Http.HC.Z SnailNet/Http.HC put /Adam/Net/Socket.HC.Z SnailNet/SocketDummy.HC put /Adam/Net/Url.HC.Z SnailNet/Url.HC +put /Adam/Net/UrlParse.HC.Z SnailNet/UrlParse.HC # Inject Pkg put /Apps/Pkg.HC.Z Pkg/Pkg.HC @@ -85,6 +86,7 @@ put /Tmp/SnailNet/Socket.HC.Z SnailNet/Socket.HC put /Tmp/SnailNet/Tcp.HC.Z SnailNet/Tcp.HC put /Tmp/SnailNet/Udp.HC.Z SnailNet/Udp.HC put /Tmp/SnailNet/Url.HC.Z SnailNet/Url.HC +put /Tmp/SnailNet/UrlParse.HC.Z SnailNet/UrlParse.HC put /Tmp/SnailNet.MF SnailNet/manifest command PkgMakeFromDir("/Tmp/SnailNet.MF", "/Tmp/SnailNet"); list /Tmp/SnailNet.ISO.C PkgBin/SnailNet.ISO.C diff --git a/SnailNet/Http.HC b/SnailNet/Http.HC index 9e6bb7e3..9b93f6c5 100644 --- a/SnailNet/Http.HC +++ b/SnailNet/Http.HC @@ -1,14 +1,20 @@ // vim: set ft=cpp: #include "::/Adam/Net/Socket" +#include "::/Adam/Net/UrlParse" #define HTTP_ECONNECT (-101) #define HTTP_EPROTOCOL (-102) #define HTTP_EREQUEST (-103) +#define HTTP_EREDIRECT (-104) +#define HTTP_EEOF (-105) +#define HTTP_MAX_REDIRECTS 5 #define HTTP_USER_AGENT "SnailNet ($TX+CX,"TempleOS",D="DD_OS_NAME_VERSION"$)" -I64 HttpOpenGet(U8* host, U16 port = 0, U8* path, I64* len_out) { +// Returns socket (>= 0) on success, error code on failure +I64 HttpOpenGet(U8* host, U16 port = 0, U8* path, I64* len_out, + I64 allowed_redirects = HTTP_MAX_REDIRECTS) { U8 line[256]; I64 error = 0; @@ -32,6 +38,9 @@ I64 HttpOpenGet(U8* host, U16 port = 0, U8* path, I64* len_out) { Bool haveHTTP = FALSE; Bool haveContentLength = FALSE; + U8* location = NULL; + + I64 code = -1; while (1) { error = recvLine(sock, line, sizeof(line), 0); @@ -45,11 +54,21 @@ I64 HttpOpenGet(U8* host, U16 port = 0, U8* path, I64* len_out) { break; } + U8* delim; + //"%s\n", line; if (!haveHTTP) { - U8* code = StrFirstOcc(line, " "); - if (code && StrNCmp(line, "HTTP/", 5) == 0 && StrNCmp(code + 1, "200", 3) == 0) { - haveHTTP = TRUE; + delim = StrFirstOcc(line, " "); + if (delim && StrNCmp(line, "HTTP/", 5) == 0) { + code = Str2I64(delim + 1); + + if (code >= 200 && code <= 399) { + haveHTTP = TRUE; + } + else { + error = HTTP_EREQUEST; + break; + } } else { error = HTTP_EREQUEST; @@ -57,7 +76,7 @@ I64 HttpOpenGet(U8* host, U16 port = 0, U8* path, I64* len_out) { } } else { - U8* delim = StrFirstOcc(line, ":"); + delim = StrFirstOcc(line, ":"); if (!delim) { error = HTTP_EPROTOCOL; @@ -74,8 +93,40 @@ I64 HttpOpenGet(U8* host, U16 port = 0, U8* path, I64* len_out) { StrScan(delim, "%d", len_out); haveContentLength = TRUE; } + else if (!StrCmp(line, "Location")) { + location = StrNew(delim); + } + } + } + + // HTTP Code 3xx -- Redirection + if (!error && code >= 300 && code <= 399) { + if (allowed_redirects > 0) { + CUrl curl; + UrlInit(&curl); + + if (UrlParse(location, &curl)) { + if (!StrCmp(curl.protocol, "http")) { + close(sock); + sock = HttpOpenGet(curl.host, curl.port, curl.path, len_out, allowed_redirects - 1); + + if (sock < 0) + error = sock; + } + else + error = HTTP_EPROTOCOL; + } + else + error = HTTP_EREDIRECT; + + UrlFree(&curl); + } + else { + error = HTTP_EREDIRECT; } } + + Free(location); } else error = HTTP_ECONNECT; @@ -84,8 +135,9 @@ I64 HttpOpenGet(U8* host, U16 port = 0, U8* path, I64* len_out) { close(sock); return error; } - else + else { return sock; + } } I64 HttpGet(U8* host, U16 port = 0, U8* path, U8** data_out = NULL, I64* len_out = NULL) { diff --git a/SnailNet/SnailLib.HC b/SnailNet/SnailLib.HC index d56313dc..b2c72746 100644 --- a/SnailNet/SnailLib.HC +++ b/SnailNet/SnailLib.HC @@ -122,8 +122,6 @@ I64 create_connection(U8* addr, U16 port) { } I64 recv(I64 sockfd, U8* buf, I64 len, I64 flags) { - I64 total = 0; - // This will be problematic for UDP if (len > SNAIL_FRAME_SIZE) len = SNAIL_FRAME_SIZE; diff --git a/SnailNet/Url.HC b/SnailNet/Url.HC index ab844a56..b52c2184 100644 --- a/SnailNet/Url.HC +++ b/SnailNet/Url.HC @@ -2,87 +2,80 @@ #include "::/Adam/Net/Http" -#define URL_EPARSE (-201) -#define URL_EPROTOCOL (-202) - -class CUrl { - U8* protocol; - U8* host; - U16 port; - U8* path; -}; - -U0 UrlInit(CUrl* url) { - url->protocol = 0; - url->host = 0; - url->port = 0; - url->path = 0; -} +I64 UrlGet(U8* url, U8** data_out = NULL, I64* size_out = NULL) { + CUrl curl; + UrlInit(&curl); + + I64 error = 0; -U0 UrlFree(CUrl* url) { - Free(url->protocol); - Free(url->host); - Free(url->path); - UrlInit(url); + if (UrlParse(url, &curl)) { + if (!StrCmp(curl.protocol, "http")) + error = HttpGet(curl.host, curl.port, curl.path, data_out, size_out); + else + error = URL_EPROTOCOL; + } + else + error = URL_EPARSE; + + UrlFree(&curl); + return error; } -Bool UrlParse(U8* url, CUrl* url_out) { - U8* colon = StrFirstOcc(url, ":"); - U8* protosep = StrFind("//", url); +I64 UrlGetWithProgress(U8* url, U8** data_out, I64* size_out) { + CUrl curl; + UrlInit(&curl); - if (colon && colon < protosep) { - I64 len = colon - url; - url_out->protocol = MAlloc(len + 1); - MemCpy(url_out->protocol, url, len); - url_out->protocol[len] = 0; + I64 error = 0; + I64 size = 0; - url = colon + 1; - while (*url == '/') - url++; - } - else { - url_out->protocol = StrNew("http"); - } + if (UrlParse(url, &curl)) { + if (!StrCmp(curl.protocol, "http")) { + I64 sock = HttpOpenGet(curl.host, curl.port, curl.path, &size); - I64 pos = 0; + if (sock > 0) { + U8* data = MAlloc(1 + size); + I64 total = 0; + I64 progress = 0; + "[$FG,3$"; - while (url[pos]) { - if (url[pos] == ':' || url[pos] == '/') { - url_out->host = MAlloc(pos + 1); - MemCpy(url_out->host, url, pos); - url_out->host[pos] = 0; + while (total < size) { + I64 step = size - total; - if (url[pos] == ':') { - I64 port = 0; - U8* end = 0; - port = Str2I64(url + pos + 1, 10, &end); + if (step > 1024) + step = 1024; - url_out->port = port; - url_out->path = StrNew(end); - } - else { - url_out->path = StrNew(url + pos); - } + I64 got = recv(sock, data + total, step, 0); - return TRUE; - } + if (got <= 0) { + error = HTTP_EEOF; + break; + } - pos++; - } + total += got; - url_out->host = StrNew(url); - return TRUE; -} + I64 new_progress = (20 * total + size - 1) / size; + while (progress < new_progress) { + '' 0xfe; + progress++; + } + } -I64 UrlGet(U8* url, U8** data_out = NULL, I64* size_out = NULL) { - CUrl curl; - UrlInit(&curl); + close(sock); - I64 error = 0; + if (error) { + "$FG,4$x\n$FG$"; + Free(data); + return error; + } - if (UrlParse(url, &curl)) { - if (!StrCmp(curl.protocol, "http")) - error = HttpGet(curl.host, curl.port, curl.path, data_out, size_out); + "$FG$]\n"; + data[total] = 0; + *data_out = data; + *size_out = total; + } + else + error = sock; + } else error = URL_EPROTOCOL; } diff --git a/SnailNet/UrlParse.HC b/SnailNet/UrlParse.HC new file mode 100644 index 00000000..f47be843 --- /dev/null +++ b/SnailNet/UrlParse.HC @@ -0,0 +1,73 @@ +// vim: set ft=cpp: + +#define URL_EPARSE (-201) +#define URL_EPROTOCOL (-202) + +class CUrl { + U8* protocol; + U8* host; + U16 port; + U8* path; +}; + +U0 UrlInit(CUrl* url) { + url->protocol = 0; + url->host = 0; + url->port = 0; + url->path = 0; +} + +U0 UrlFree(CUrl* url) { + Free(url->protocol); + Free(url->host); + Free(url->path); + UrlInit(url); +} + +Bool UrlParse(U8* url, CUrl* url_out) { + U8* colon = StrFirstOcc(url, ":"); + U8* protosep = StrFind("//", url); + + if (colon && colon < protosep) { + I64 len = colon - url; + url_out->protocol = MAlloc(len + 1); + MemCpy(url_out->protocol, url, len); + url_out->protocol[len] = 0; + + url = colon + 1; + while (*url == '/') + url++; + } + else { + url_out->protocol = StrNew("http"); + } + + I64 pos = 0; + + while (url[pos]) { + if (url[pos] == ':' || url[pos] == '/') { + url_out->host = MAlloc(pos + 1); + MemCpy(url_out->host, url, pos); + url_out->host[pos] = 0; + + if (url[pos] == ':') { + I64 port = 0; + U8* end = 0; + port = Str2I64(url + pos + 1, 10, &end); + + url_out->port = port; + url_out->path = StrNew(end); + } + else { + url_out->path = StrNew(url + pos); + } + + return TRUE; + } + + pos++; + } + + url_out->host = StrNew(url); + return TRUE; +}