refactor: network - harden TCP/IP and unify HTTP clients
This commit is contained in:
+29
-179
@@ -8,7 +8,7 @@
|
||||
|
||||
#include <montauk/syscall.h>
|
||||
#include <montauk/string.h>
|
||||
#include <tls/tls.hpp>
|
||||
#include <http/http.hpp>
|
||||
|
||||
extern "C" {
|
||||
#include <string.h>
|
||||
@@ -56,11 +56,6 @@ static bool parse_uint16(const char* s, uint16_t* out) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static void format_ip(char* buf, uint32_t ip) {
|
||||
snprintf(buf, 32, "%u.%u.%u.%u",
|
||||
ip & 0xFF, (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, (ip >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
// ---- URL parser ----
|
||||
|
||||
struct ParsedUrl {
|
||||
@@ -124,38 +119,6 @@ static ParsedUrl parse_url(const char* url) {
|
||||
return u;
|
||||
}
|
||||
|
||||
// ---- HTTP response parser ----
|
||||
|
||||
static int find_header_end(const char* buf, int len) {
|
||||
for (int i = 0; i + 3 < len; i++) {
|
||||
if (buf[i] == '\r' && buf[i+1] == '\n' && buf[i+2] == '\r' && buf[i+3] == '\n')
|
||||
return i + 4;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int parse_status_code(const char* buf, int len) {
|
||||
int i = 0;
|
||||
while (i < len && buf[i] != ' ') i++;
|
||||
if (i >= len) return -1;
|
||||
i++;
|
||||
if (i + 2 >= len) return -1;
|
||||
if (buf[i] < '0' || buf[i] > '9') return -1;
|
||||
return (buf[i] - '0') * 100 + (buf[i+1] - '0') * 10 + (buf[i+2] - '0');
|
||||
}
|
||||
|
||||
static void parse_status_text(const char* buf, int len, char* out, int outMax) {
|
||||
int i = 0;
|
||||
while (i < len && buf[i] != ' ') i++;
|
||||
i++;
|
||||
while (i < len && buf[i] != ' ') i++;
|
||||
i++;
|
||||
int j = 0;
|
||||
while (i < len && buf[i] != '\r' && buf[i] != '\n' && j < outMax - 1)
|
||||
out[j++] = buf[i++];
|
||||
out[j] = '\0';
|
||||
}
|
||||
|
||||
// ---- Keyboard abort check for TLS ----
|
||||
|
||||
static bool check_keyboard_abort() {
|
||||
@@ -167,95 +130,23 @@ static bool check_keyboard_abort() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---- Plain HTTP exchange (no TLS) ----
|
||||
|
||||
static int plain_http_exchange(int fd, const char* request, int reqLen,
|
||||
char* respBuf, int respMax) {
|
||||
// Send request
|
||||
int sent = 0;
|
||||
uint64_t deadline = montauk::get_milliseconds() + 15000;
|
||||
while (sent < reqLen) {
|
||||
int r = montauk::send(fd, request + sent, reqLen - sent);
|
||||
if (r > 0) { sent += r; deadline = montauk::get_milliseconds() + 15000; }
|
||||
else if (r < 0) return -1;
|
||||
else {
|
||||
if (montauk::get_milliseconds() >= deadline) return -1;
|
||||
montauk::sleep_ms(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Receive response
|
||||
int respLen = 0;
|
||||
deadline = montauk::get_milliseconds() + 15000;
|
||||
while (respLen < respMax - 1) {
|
||||
if (montauk::is_key_available()) {
|
||||
montauk::abi::KeyEvent ev;
|
||||
montauk::getkey(&ev);
|
||||
if (ev.pressed && ev.ctrl && ev.ascii == 'q') return -2; // aborted
|
||||
}
|
||||
|
||||
int r = montauk::recv(fd, respBuf + respLen, respMax - 1 - respLen);
|
||||
if (r > 0) { respLen += r; deadline = montauk::get_milliseconds() + 15000; }
|
||||
else if (r < 0) break;
|
||||
else {
|
||||
uint64_t now = montauk::get_milliseconds();
|
||||
if (now >= deadline) break;
|
||||
uint32_t signals = montauk::wait_handle(
|
||||
fd,
|
||||
montauk::abi::IPC_SIGNAL_READABLE | montauk::abi::IPC_SIGNAL_PEER_CLOSED,
|
||||
deadline - now
|
||||
);
|
||||
if (signals == 0 || signals == (uint32_t)-1) break;
|
||||
}
|
||||
}
|
||||
return respLen;
|
||||
}
|
||||
|
||||
// ---- Print response body ----
|
||||
|
||||
static void print_response(const char* respBuf, int respLen, bool verbose) {
|
||||
if (respLen <= 0) {
|
||||
montauk::print("Error: empty response\n");
|
||||
return;
|
||||
}
|
||||
|
||||
int headerEnd = find_header_end(respBuf, respLen);
|
||||
if (headerEnd < 0) {
|
||||
montauk::print("Warning: malformed response (no header boundary)\n\n");
|
||||
// Print raw
|
||||
char chunk[512];
|
||||
int printed = 0;
|
||||
while (printed < respLen) {
|
||||
int n = respLen - printed;
|
||||
if (n > 511) n = 511;
|
||||
memcpy(chunk, respBuf + printed, n);
|
||||
chunk[n] = '\0';
|
||||
montauk::print(chunk);
|
||||
printed += n;
|
||||
}
|
||||
montauk::putchar('\n');
|
||||
return;
|
||||
}
|
||||
|
||||
int statusCode = parse_status_code(respBuf, headerEnd);
|
||||
char statusText[64];
|
||||
parse_status_text(respBuf, headerEnd, statusText, sizeof(statusText));
|
||||
int bodyLen = respLen - headerEnd;
|
||||
|
||||
static void print_response(const http::Response& response, bool verbose) {
|
||||
if (verbose) {
|
||||
char msg[256];
|
||||
snprintf(msg, sizeof(msg), "HTTP %d %s (%d bytes)\n\n", statusCode, statusText, bodyLen);
|
||||
char msg[128];
|
||||
snprintf(msg, sizeof(msg), "HTTP %d (%d bytes)\n\n",
|
||||
response.status, response.body_len);
|
||||
montauk::print(msg);
|
||||
}
|
||||
|
||||
if (bodyLen > 0) {
|
||||
const char* body = respBuf + headerEnd;
|
||||
if (response.body_len > 0) {
|
||||
char chunk[512];
|
||||
int printed = 0;
|
||||
while (printed < bodyLen) {
|
||||
int n = bodyLen - printed;
|
||||
while (printed < response.body_len) {
|
||||
int n = response.body_len - printed;
|
||||
if (n > 511) n = 511;
|
||||
memcpy(chunk, body + printed, n);
|
||||
memcpy(chunk, response.body + printed, n);
|
||||
chunk[n] = '\0';
|
||||
montauk::print(chunk);
|
||||
printed += n;
|
||||
@@ -339,20 +230,8 @@ extern "C" void _start() {
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve host to IP
|
||||
uint32_t serverIp;
|
||||
if (!parse_ip(hostStr, &serverIp)) {
|
||||
serverIp = montauk::resolve(hostStr);
|
||||
if (serverIp == 0) {
|
||||
montauk::print("Error: could not resolve ");
|
||||
montauk::print(hostStr);
|
||||
montauk::putchar('\n');
|
||||
montauk::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
char ipStr[32];
|
||||
format_ip(ipStr, serverIp);
|
||||
uint32_t numericIp = 0;
|
||||
parse_ip(hostStr, &numericIp);
|
||||
|
||||
if (verbose) {
|
||||
char msg[256];
|
||||
@@ -361,16 +240,6 @@ extern "C" void _start() {
|
||||
montauk::print(msg);
|
||||
}
|
||||
|
||||
// Build HTTP request
|
||||
char request[1024];
|
||||
int reqLen = snprintf(request, sizeof(request),
|
||||
"GET %s HTTP/1.0\r\n"
|
||||
"Host: %s\r\n"
|
||||
"User-Agent: MontaukOS/1.0\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n",
|
||||
path, hostStr);
|
||||
|
||||
if (verbose) {
|
||||
char msg[128];
|
||||
snprintf(msg, sizeof(msg), "GET %s\n", path);
|
||||
@@ -385,11 +254,9 @@ extern "C" void _start() {
|
||||
montauk::exit(1);
|
||||
}
|
||||
|
||||
int respLen;
|
||||
|
||||
tls::TrustAnchors tas = {};
|
||||
if (useHttps) {
|
||||
// ---- TLS handshake and exchange ----
|
||||
tls::TrustAnchors tas = tls::load_trust_anchors();
|
||||
tas = tls::load_trust_anchors();
|
||||
if (verbose) {
|
||||
char msg[64];
|
||||
snprintf(msg, sizeof(msg), "Loaded %u trust anchors\n", (unsigned)tas.count);
|
||||
@@ -415,43 +282,26 @@ extern "C" void _start() {
|
||||
montauk::print("TLS handshake...\n");
|
||||
}
|
||||
|
||||
respLen = tls::https_fetch(hostStr, serverIp, port,
|
||||
request, reqLen, tas,
|
||||
respBuf, RESP_MAX, check_keyboard_abort);
|
||||
|
||||
if (verbose && respLen > 0) {
|
||||
montauk::print("TLS connection established\n");
|
||||
}
|
||||
} else {
|
||||
// ---- Plain HTTP ----
|
||||
int fd = montauk::socket(montauk::abi::SOCK_TCP);
|
||||
if (fd < 0) {
|
||||
montauk::print("Error: failed to create socket\n");
|
||||
montauk::exit(1);
|
||||
}
|
||||
|
||||
if (montauk::connect(fd, serverIp, port) < 0) {
|
||||
montauk::print("Error: connection failed\n");
|
||||
montauk::closesocket(fd);
|
||||
montauk::exit(1);
|
||||
}
|
||||
|
||||
respLen = plain_http_exchange(fd, request, reqLen, respBuf, RESP_MAX);
|
||||
montauk::closesocket(fd);
|
||||
|
||||
if (respLen == -2) {
|
||||
montauk::print("\nAborted.\n");
|
||||
montauk::exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (respLen <= 0) {
|
||||
montauk::print("Error: no response received\n");
|
||||
http::RequestOptions options;
|
||||
options.secure = useHttps;
|
||||
options.port = port;
|
||||
options.resolved_ip = numericIp;
|
||||
options.extra_headers = "User-Agent: MontaukOS/1.0\r\n";
|
||||
options.abort_check = check_keyboard_abort;
|
||||
options.timeout_ms = 15000;
|
||||
http::Response response = http::request_into(
|
||||
"GET", hostStr, path, nullptr, nullptr, 0, useHttps ? &tas : nullptr,
|
||||
respBuf, RESP_MAX, options);
|
||||
if (response.error != http::Error::NONE) {
|
||||
montauk::print("Error: ");
|
||||
montauk::print(http::error_string(response.error));
|
||||
montauk::putchar('\n');
|
||||
montauk::exit(1);
|
||||
}
|
||||
|
||||
respBuf[respLen] = '\0';
|
||||
print_response(respBuf, respLen, verbose);
|
||||
if (verbose && useHttps) montauk::print("TLS connection established\n");
|
||||
print_response(response, verbose);
|
||||
|
||||
montauk::exit(0);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include <gui/svg.hpp>
|
||||
#include <gui/truetype.hpp>
|
||||
#include <gui/mtk.hpp>
|
||||
#include <tls/tls.hpp>
|
||||
#include <http/http.hpp>
|
||||
|
||||
extern "C" {
|
||||
#include <string.h>
|
||||
@@ -77,26 +77,6 @@ static bool g_tls_ready = false;
|
||||
static uint32_t g_server_ip = 0;
|
||||
static tls::TrustAnchors g_tas = {nullptr, 0, 0};
|
||||
|
||||
// ============================================================================
|
||||
// HTTP parsing
|
||||
// ============================================================================
|
||||
|
||||
static int find_header_end(const char* buf, int len) {
|
||||
for (int i = 0; i + 3 < len; i++)
|
||||
if (buf[i]=='\r' && buf[i+1]=='\n' && buf[i+2]=='\r' && buf[i+3]=='\n')
|
||||
return i + 4;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int parse_status_code(const char* buf, int len) {
|
||||
int i = 0;
|
||||
while (i < len && buf[i] != ' ') i++;
|
||||
if (i >= len || i + 3 >= len) return -1;
|
||||
i++;
|
||||
if (buf[i] < '0' || buf[i] > '9') return -1;
|
||||
return (buf[i]-'0')*100 + (buf[i+1]-'0')*10 + (buf[i+2]-'0');
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// JSON parsing
|
||||
// ============================================================================
|
||||
@@ -237,39 +217,27 @@ static void do_fetch() {
|
||||
g_tls_ready = true;
|
||||
}
|
||||
|
||||
static char request[512];
|
||||
int reqLen = snprintf(request, sizeof(request),
|
||||
"GET /?format=j1 HTTP/1.0\r\n"
|
||||
"Host: %s\r\n"
|
||||
http::RequestOptions options;
|
||||
options.resolved_ip = g_server_ip;
|
||||
options.extra_headers =
|
||||
"User-Agent: MontaukOS/1.0 weather\r\n"
|
||||
"Accept: application/json\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n",
|
||||
WTTR_HOST);
|
||||
|
||||
int respLen = tls::https_fetch(WTTR_HOST, g_server_ip, 443,
|
||||
request, reqLen, g_tas,
|
||||
g_resp_buf, RESP_MAX);
|
||||
if (respLen <= 0) {
|
||||
snprintf(g_status, sizeof(g_status), "Error: no response from server");
|
||||
"Accept: application/json\r\n";
|
||||
http::Response response = http::request_into(
|
||||
"GET", WTTR_HOST, "/?format=j1", nullptr, nullptr, 0, &g_tas,
|
||||
g_resp_buf, RESP_MAX, options);
|
||||
if (response.error != http::Error::NONE) {
|
||||
snprintf(g_status, sizeof(g_status), "Error: %s",
|
||||
http::error_string(response.error));
|
||||
g_phase = AppPhase::ERR; return;
|
||||
}
|
||||
g_resp_buf[respLen] = '\0';
|
||||
|
||||
int headerEnd = find_header_end(g_resp_buf, respLen);
|
||||
if (headerEnd < 0) {
|
||||
snprintf(g_status, sizeof(g_status), "Error: malformed HTTP response");
|
||||
if (response.status != 200) {
|
||||
snprintf(g_status, sizeof(g_status), "Error: HTTP %d from server",
|
||||
response.status);
|
||||
g_phase = AppPhase::ERR; return;
|
||||
}
|
||||
|
||||
int status = parse_status_code(g_resp_buf, headerEnd);
|
||||
if (status != 200) {
|
||||
snprintf(g_status, sizeof(g_status), "Error: HTTP %d from server", status);
|
||||
g_phase = AppPhase::ERR; return;
|
||||
}
|
||||
|
||||
const char* body = g_resp_buf + headerEnd;
|
||||
int bodyLen = respLen - headerEnd;
|
||||
const char* body = response.body;
|
||||
int bodyLen = response.body_len;
|
||||
|
||||
// Extract core weather fields
|
||||
static char temp_raw[16], feels_raw[16], code_raw[8];
|
||||
|
||||
+28
-85
@@ -10,7 +10,7 @@
|
||||
|
||||
#include <montauk/syscall.h>
|
||||
#include <montauk/string.h>
|
||||
#include <tls/tls.hpp>
|
||||
#include <http/http.hpp>
|
||||
|
||||
extern "C" {
|
||||
#include <string.h>
|
||||
@@ -82,39 +82,15 @@ static bool check_keyboard_abort() {
|
||||
|
||||
// ---- HTTPS fetch wrapper ----
|
||||
|
||||
static int wiki_fetch(const char* path, char* respBuf, int respMax) {
|
||||
static char request[2560]; // keep off stack
|
||||
int reqLen = snprintf(request, sizeof(request),
|
||||
"GET %s HTTP/1.0\r\n"
|
||||
"Host: %s\r\n"
|
||||
static http::Response wiki_fetch(const char* path, char* respBuf, int respMax) {
|
||||
http::RequestOptions options;
|
||||
options.resolved_ip = g_serverIp;
|
||||
options.extra_headers =
|
||||
"User-Agent: MontaukOS/1.0 wiki\r\n"
|
||||
"Accept: application/json\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n",
|
||||
path, WIKI_HOST);
|
||||
return tls::https_fetch(WIKI_HOST, g_serverIp, 443,
|
||||
request, reqLen, g_tas,
|
||||
respBuf, respMax, check_keyboard_abort);
|
||||
}
|
||||
|
||||
// ---- HTTP response parsing ----
|
||||
|
||||
static int find_header_end(const char* buf, int len) {
|
||||
for (int i = 0; i + 3 < len; i++) {
|
||||
if (buf[i] == '\r' && buf[i+1] == '\n' && buf[i+2] == '\r' && buf[i+3] == '\n')
|
||||
return i + 4;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int parse_status_code(const char* buf, int len) {
|
||||
int i = 0;
|
||||
while (i < len && buf[i] != ' ') i++;
|
||||
if (i >= len) return -1;
|
||||
i++;
|
||||
if (i + 2 >= len) return -1;
|
||||
if (buf[i] < '0' || buf[i] > '9') return -1;
|
||||
return (buf[i] - '0') * 100 + (buf[i+1] - '0') * 10 + (buf[i+2] - '0');
|
||||
"Accept: application/json\r\n";
|
||||
options.abort_check = check_keyboard_abort;
|
||||
return http::request_into("GET", WIKI_HOST, path, nullptr, nullptr, 0,
|
||||
&g_tas, respBuf, respMax, options);
|
||||
}
|
||||
|
||||
// ---- URL encoding ----
|
||||
@@ -725,23 +701,13 @@ extern "C" void _start() {
|
||||
"/w/api.php?action=query&format=json&formatversion=2"
|
||||
"&prop=extracts&explaintext=1&titles=%s", encoded);
|
||||
|
||||
int respLen = wiki_fetch(path, respBuf, RESP_MAX);
|
||||
if (respLen <= 0) {
|
||||
http::Response response = wiki_fetch(path, respBuf, RESP_MAX);
|
||||
if (response.error != http::Error::NONE) {
|
||||
montauk::print("\x01"); // error sentinel
|
||||
montauk::sleep_ms(100);
|
||||
montauk::exit(1);
|
||||
}
|
||||
respBuf[respLen] = '\0';
|
||||
|
||||
int headerEnd = find_header_end(respBuf, respLen);
|
||||
if (headerEnd < 0) {
|
||||
montauk::print("\x01");
|
||||
montauk::sleep_ms(100);
|
||||
montauk::exit(1);
|
||||
}
|
||||
|
||||
int statusCode = parse_status_code(respBuf, headerEnd);
|
||||
if (statusCode == 404) {
|
||||
if (response.status == 404) {
|
||||
montauk::print("\x01");
|
||||
montauk::sleep_ms(100);
|
||||
montauk::exit(1);
|
||||
@@ -749,8 +715,8 @@ extern "C" void _start() {
|
||||
|
||||
// Output raw JSON body in chunks to avoid overflowing
|
||||
// the 4KB kernel ring buffer (parent polls at ~60fps)
|
||||
const char* body = respBuf + headerEnd;
|
||||
int bodyLen = respLen - headerEnd;
|
||||
const char* body = response.body;
|
||||
int bodyLen = response.body_len;
|
||||
static char chunk[2049];
|
||||
int sent = 0;
|
||||
while (sent < bodyLen) {
|
||||
@@ -774,21 +740,13 @@ extern "C" void _start() {
|
||||
"/w/api.php?action=opensearch&search=%s&limit=10&format=json",
|
||||
encoded);
|
||||
|
||||
int respLen = wiki_fetch(path, respBuf, RESP_MAX);
|
||||
if (respLen <= 0) {
|
||||
http::Response response = wiki_fetch(path, respBuf, RESP_MAX);
|
||||
if (response.error != http::Error::NONE) {
|
||||
montauk::print("\033[1;31mError:\033[0m no response from Wikipedia\n");
|
||||
montauk::exit(1);
|
||||
}
|
||||
respBuf[respLen] = '\0';
|
||||
|
||||
int headerEnd = find_header_end(respBuf, respLen);
|
||||
if (headerEnd < 0) {
|
||||
montauk::print("\033[1;31mError:\033[0m malformed response\n");
|
||||
montauk::exit(1);
|
||||
}
|
||||
|
||||
const char* body = respBuf + headerEnd;
|
||||
int bodyLen = respLen - headerEnd;
|
||||
const char* body = response.body;
|
||||
int bodyLen = response.body_len;
|
||||
|
||||
static char titles[MAX_SEARCH_RESULTS][256];
|
||||
int titleCount = parse_search_titles(body, bodyLen, titles, MAX_SEARCH_RESULTS);
|
||||
@@ -831,8 +789,8 @@ extern "C" void _start() {
|
||||
snprintf(articlePath, sizeof(articlePath),
|
||||
"/api/rest_v1/page/summary/%s", articleEncoded);
|
||||
|
||||
respLen = wiki_fetch(articlePath, respBuf, RESP_MAX);
|
||||
if (respLen <= 0) {
|
||||
response = wiki_fetch(articlePath, respBuf, RESP_MAX);
|
||||
if (response.error != http::Error::NONE) {
|
||||
sb_reset();
|
||||
sb_cursor_to(infoRow, 3);
|
||||
sb_puts("\033[2K\033[1;31mFetch failed. Press any key.\033[0m");
|
||||
@@ -841,16 +799,10 @@ extern "C" void _start() {
|
||||
montauk::abi::KeyEvent ev; montauk::getkey(&ev);
|
||||
continue;
|
||||
}
|
||||
respBuf[respLen] = '\0';
|
||||
body = response.body;
|
||||
bodyLen = response.body_len;
|
||||
|
||||
headerEnd = find_header_end(respBuf, respLen);
|
||||
if (headerEnd < 0) continue;
|
||||
|
||||
int statusCode = parse_status_code(respBuf, headerEnd);
|
||||
body = respBuf + headerEnd;
|
||||
bodyLen = respLen - headerEnd;
|
||||
|
||||
if (statusCode == 404) {
|
||||
if (response.status == 404) {
|
||||
sb_reset();
|
||||
sb_cursor_to(infoRow, 3);
|
||||
sb_puts("\033[2K\033[1;31mArticle not found. Press any key.\033[0m");
|
||||
@@ -893,24 +845,15 @@ extern "C" void _start() {
|
||||
"&prop=extracts&explaintext=1&titles=%s", encoded);
|
||||
}
|
||||
|
||||
int respLen = wiki_fetch(path, respBuf, RESP_MAX);
|
||||
if (respLen <= 0) {
|
||||
http::Response response = wiki_fetch(path, respBuf, RESP_MAX);
|
||||
if (response.error != http::Error::NONE) {
|
||||
montauk::print("\033[1;31mError:\033[0m no response from Wikipedia\n");
|
||||
montauk::exit(1);
|
||||
}
|
||||
respBuf[respLen] = '\0';
|
||||
const char* body = response.body;
|
||||
int bodyLen = response.body_len;
|
||||
|
||||
int headerEnd = find_header_end(respBuf, respLen);
|
||||
if (headerEnd < 0) {
|
||||
montauk::print("\033[1;31mError:\033[0m malformed response\n");
|
||||
montauk::exit(1);
|
||||
}
|
||||
|
||||
int statusCode = parse_status_code(respBuf, headerEnd);
|
||||
const char* body = respBuf + headerEnd;
|
||||
int bodyLen = respLen - headerEnd;
|
||||
|
||||
if (statusCode == 404) {
|
||||
if (response.status == 404) {
|
||||
montauk::print("\033[1;31mArticle not found:\033[0m ");
|
||||
montauk::print(query);
|
||||
montauk::putchar('\n');
|
||||
|
||||
@@ -6,19 +6,14 @@
|
||||
|
||||
#include "wikipedia.h"
|
||||
|
||||
int wiki_fetch(const char* path, char* respBuf, int respMax) {
|
||||
static char request[2560];
|
||||
int reqLen = snprintf(request, sizeof(request),
|
||||
"GET %s HTTP/1.0\r\n"
|
||||
"Host: %s\r\n"
|
||||
http::Response wiki_fetch(const char* path, char* respBuf, int respMax) {
|
||||
http::RequestOptions options;
|
||||
options.resolved_ip = g_server_ip;
|
||||
options.extra_headers =
|
||||
"User-Agent: MontaukOS/1.0 wikipedia\r\n"
|
||||
"Accept: application/json\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n",
|
||||
path, WIKI_HOST);
|
||||
return tls::https_fetch(WIKI_HOST, g_server_ip, 443,
|
||||
request, reqLen, g_tas,
|
||||
respBuf, respMax);
|
||||
"Accept: application/json\r\n";
|
||||
return http::request_into("GET", WIKI_HOST, path, nullptr, nullptr, 0,
|
||||
&g_tas, respBuf, respMax, options);
|
||||
}
|
||||
|
||||
bool ensure_wiki_tls_ready(char* err, int err_len) {
|
||||
@@ -74,27 +69,16 @@ void do_welcome_fetch() {
|
||||
return;
|
||||
}
|
||||
|
||||
int respLen = wiki_fetch(path, g_resp_buf, RESP_MAX);
|
||||
if (respLen <= 0) {
|
||||
http::Response response = wiki_fetch(path, g_resp_buf, RESP_MAX);
|
||||
if (response.error != http::Error::NONE) {
|
||||
snprintf(g_welcome_status, sizeof(g_welcome_status),
|
||||
"Daily featured article unavailable; search is ready.");
|
||||
build_welcome_lines(true);
|
||||
return;
|
||||
}
|
||||
g_resp_buf[respLen] = '\0';
|
||||
|
||||
int headerEnd = find_header_end(g_resp_buf, respLen);
|
||||
if (headerEnd < 0) {
|
||||
snprintf(g_welcome_status, sizeof(g_welcome_status),
|
||||
"Daily featured article unavailable; search is ready.");
|
||||
build_welcome_lines(true);
|
||||
return;
|
||||
}
|
||||
|
||||
int status = parse_status_code(g_resp_buf, headerEnd);
|
||||
const char* body = g_resp_buf + headerEnd;
|
||||
int bodyLen = respLen - headerEnd;
|
||||
if (status < 200 || status >= 300) {
|
||||
const char* body = response.body;
|
||||
int bodyLen = response.body_len;
|
||||
if (response.status < 200 || response.status >= 300) {
|
||||
snprintf(g_welcome_status, sizeof(g_welcome_status),
|
||||
"Daily featured article unavailable; search is ready.");
|
||||
build_welcome_lines(true);
|
||||
@@ -148,24 +132,16 @@ void do_search(const char* query) {
|
||||
"/w/api.php?action=query&format=json&formatversion=2"
|
||||
"&prop=extracts&explaintext=1&titles=%s", encoded);
|
||||
|
||||
int respLen = wiki_fetch(path, g_resp_buf, RESP_MAX);
|
||||
if (respLen <= 0) {
|
||||
snprintf(g_status, sizeof(g_status), "Error: no response from Wikipedia");
|
||||
http::Response response = wiki_fetch(path, g_resp_buf, RESP_MAX);
|
||||
if (response.error != http::Error::NONE) {
|
||||
snprintf(g_status, sizeof(g_status), "Error: %s",
|
||||
http::error_string(response.error));
|
||||
g_phase = AppPhase::ERR; return;
|
||||
}
|
||||
g_resp_buf[respLen] = '\0';
|
||||
const char* body = response.body;
|
||||
int bodyLen = response.body_len;
|
||||
|
||||
int headerEnd = find_header_end(g_resp_buf, respLen);
|
||||
if (headerEnd < 0) {
|
||||
snprintf(g_status, sizeof(g_status), "Error: malformed HTTP response");
|
||||
g_phase = AppPhase::ERR; return;
|
||||
}
|
||||
|
||||
int status = parse_status_code(g_resp_buf, headerEnd);
|
||||
const char* body = g_resp_buf + headerEnd;
|
||||
int bodyLen = respLen - headerEnd;
|
||||
|
||||
if (status == 404) {
|
||||
if (response.status == 404) {
|
||||
snprintf(g_status, sizeof(g_status), "Article not found: %s", query);
|
||||
g_phase = AppPhase::ERR; return;
|
||||
}
|
||||
|
||||
@@ -140,26 +140,6 @@ unsigned decode_utf8_codepoint(const char* buf, int len, int* consumed) {
|
||||
return '?';
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// HTTP parsing
|
||||
// ============================================================================
|
||||
|
||||
int find_header_end(const char* buf, int len) {
|
||||
for (int i = 0; i + 3 < len; i++)
|
||||
if (buf[i]=='\r' && buf[i+1]=='\n' && buf[i+2]=='\r' && buf[i+3]=='\n')
|
||||
return i + 4;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int parse_status_code(const char* buf, int len) {
|
||||
int i = 0;
|
||||
while (i < len && buf[i] != ' ') i++;
|
||||
if (i >= len || i + 3 >= len) return -1;
|
||||
i++;
|
||||
if (buf[i] < '0' || buf[i] > '9') return -1;
|
||||
return (buf[i]-'0')*100 + (buf[i+1]-'0')*10 + (buf[i+2]-'0');
|
||||
}
|
||||
|
||||
int find_substr(const char* buf, int len, const char* needle) {
|
||||
int nlen = (int)strlen(needle);
|
||||
if (!buf || !needle || nlen <= 0 || nlen > len) return -1;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include <gui/standalone.hpp>
|
||||
#include <gui/svg.hpp>
|
||||
#include <gui/truetype.hpp>
|
||||
#include <tls/tls.hpp>
|
||||
#include <http/http.hpp>
|
||||
|
||||
extern "C" {
|
||||
#include <string.h>
|
||||
@@ -190,8 +190,6 @@ void underscores_to_spaces(char* text);
|
||||
bool is_main_page_query(const char* query);
|
||||
unsigned decode_utf8_codepoint(const char* buf, int len, int* consumed);
|
||||
|
||||
int find_header_end(const char* buf, int len);
|
||||
int parse_status_code(const char* buf, int len);
|
||||
int find_substr(const char* buf, int len, const char* needle);
|
||||
|
||||
int url_encode_title(const char* in, char* out, int maxLen);
|
||||
@@ -219,7 +217,7 @@ bool handle_reader_option_click(int mx, int my);
|
||||
// network.cpp -- TLS fetch, welcome fetch, search
|
||||
// ============================================================================
|
||||
|
||||
int wiki_fetch(const char* path, char* respBuf, int respMax);
|
||||
http::Response wiki_fetch(const char* path, char* respBuf, int respMax);
|
||||
bool ensure_wiki_tls_ready(char* err, int err_len);
|
||||
bool welcome_feed_path(char* path, int path_len);
|
||||
void do_welcome_fetch();
|
||||
|
||||
Reference in New Issue
Block a user