mirror of https://github.com/minexew/Shrine.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
197 lines
4.1 KiB
197 lines
4.1 KiB
// 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_ECONTENTLENGTH (-106) |
|
|
|
#define HTTP_MAX_REDIRECTS 5 |
|
#define HTTP_USER_AGENT "SnailNet ($TX+CX,"TempleOS",D="DD_OS_NAME_VERSION"$)" |
|
|
|
/** |
|
* @param len_out (required) requires the content length, or -1 if unspecified |
|
* @return 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; |
|
|
|
if (!port) |
|
port = 80; |
|
|
|
// Should this be done here though? |
|
if (*path == 0) |
|
path = "/"; |
|
|
|
//"Connect(%s:%d)\n", host, port; |
|
I64 sock = create_connection(host, port); |
|
//"create_connection: %d\n", sock; |
|
if (sock >= 0) { |
|
StrPrint(line, "GET %s HTTP/1.0\r\n", path); |
|
sendString(sock, line, 0); |
|
StrPrint(line, "Host: %s\r\n", host); |
|
sendString(sock, line, 0); |
|
sendString(sock, "User-Agent: " HTTP_USER_AGENT "\r\n", 0); |
|
sendString(sock, "\r\n", 0); |
|
|
|
Bool haveHTTP = FALSE; |
|
U8* location = NULL; |
|
|
|
I64 code = -1; |
|
|
|
*len_out = -1; |
|
|
|
while (1) { |
|
error = recvLine(sock, line, sizeof(line), 0); |
|
|
|
if (error < 0) { |
|
break; |
|
} |
|
else if (error == 0) { |
|
if (!haveHTTP) |
|
error = HTTP_EPROTOCOL; |
|
break; |
|
} |
|
|
|
U8* delim; |
|
|
|
//"%s\n", line; |
|
if (!haveHTTP) { |
|
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; |
|
break; |
|
} |
|
} |
|
else { |
|
delim = StrFirstOcc(line, ":"); |
|
|
|
if (!delim) { |
|
error = HTTP_EPROTOCOL; |
|
break; |
|
} |
|
|
|
*delim = 0; |
|
|
|
do { delim++; } |
|
while (*delim == ' '); |
|
|
|
//"%s=%s\n", line, delim; |
|
if (!StrCmp(line, "Content-Length")) { |
|
StrScan(delim, "%d", len_out); |
|
} |
|
else if (!StrCmp(line, "Location")) { |
|
// This will leak on malformed response |
|
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; |
|
|
|
if (error) { |
|
close(sock); |
|
return error; |
|
} |
|
else { |
|
return sock; |
|
} |
|
} |
|
|
|
I64 HttpGet(U8* host, U16 port = 0, U8* path, U8** data_out = NULL, I64* len_out = NULL) { |
|
I64 error = 0; |
|
I64 len = 0; |
|
I64 sock = HttpOpenGet(host, port, path, &len); |
|
|
|
if (sock > 0) { |
|
if (len >= 0) { |
|
U8* data = MAlloc(1 + len); |
|
I64 total = 0; |
|
|
|
while (total < len) { |
|
I64 step = len - total; |
|
|
|
if (step > 1024) |
|
step = 1024; |
|
|
|
I64 got = recv(sock, data + total, step, 0); |
|
|
|
if (got <= 0) { |
|
error = HTTP_EEOF; |
|
break; |
|
} |
|
|
|
total += got; |
|
} |
|
|
|
if (error) { |
|
Free(data); |
|
} |
|
else { |
|
if (data_out) { |
|
data[total] = 0; |
|
*data_out = data; |
|
} |
|
|
|
if (len_out) |
|
*len_out = len; |
|
} |
|
} |
|
else |
|
// We currently don't handle dynamic-length HTTP responses |
|
error = HTTP_ECONTENTLENGTH; |
|
|
|
close(sock); |
|
} |
|
else |
|
error = sock; |
|
|
|
return error; |
|
}
|
|
|