#ifndef HTTP_H #define HTTP_H #include "../Platforms.h" #include "TCP.h" #include "DNS.h" #include "../Debug.h" class HTTPClient; typedef void (*HTTPCBodyDataCallback)(HTTPClient* httpc, const uint8_t* data, uint16_t len); typedef void (*HTTPCConnectCallback)(HTTPClient* httpc); typedef void (*HTTPCDisconnectCallback)(HTTPClient* httpc); class HTTPClient { static constexpr const char* NAME = "HTTPC"; TCP tcp; DNS dns; ip_addr_t ip; char host[64]; char query[128]; bool gotHeader = false; int numBreaks = 0; HTTPCConnectCallback onConnect = nullptr; HTTPCDisconnectCallback onDisconnect = nullptr; HTTPCBodyDataCallback onBodyData = nullptr; public: /** ctor */ HTTPClient() { debugMod(NAME, "init()"); tcp.setUserData(this); tcp.setDataCallback(_onData); tcp.setConnectCallback(_onConnect); tcp.setDisconnectCallback(_onDisconnect); } void hold() { tcp.hold(); } void unhold() { tcp.unhold(); } void setBodyDataCallback(HTTPCBodyDataCallback c) { this->onBodyData = c; } void setConnectCallback(HTTPCConnectCallback c) { this->onConnect = c; } void setDisconnectCallback(HTTPCDisconnectCallback c) { this->onDisconnect = c; } /** connect to the given IP/Port */ void connect(const char* url) { //strcpy(this->url, url); const char* hostStart = &url[7]; // skip the "http://" const char* hostEnd = strchr(hostStart, '/'); // find the terminating / strncpy(this->host, hostStart, hostEnd-hostStart); // hostname only this->host[hostEnd-hostStart] = 0; // 0 terminator!! strcpy(this->query, hostEnd); // query only debugMod3(NAME, "connect(%s) -> host: '%s' query: '%s'", url, this->host, this->query); dns.resolveHost(this->host, _onHostResolved, (void*)this); } void disconnect() { tcp.disconnect(); gotHeader = false; numBreaks = 0; } void consumed(uint16_t len) { tcp.consumed(len); } private: /** from TCP: connected */ static void _onConnect(TCP* tcp) { debugMod(NAME, "connected"); HTTPClient* httpc = (HTTPClient*) tcp->getUserData(); if (httpc->onConnect) {httpc->onConnect(httpc);} httpc->sendRequest(); } /** from TCP: disconnected */ static void _onDisconnect(TCP* tcp) { debugMod(NAME, "disconnected"); HTTPClient* httpc = (HTTPClient*) tcp->getUserData(); if (httpc->onDisconnect) {httpc->onDisconnect(httpc);} } /** from TCP: got data */ static void _onData(TCP* tcp, const uint8_t* data, const uint16_t dataLen) { HTTPClient* httpc = (HTTPClient*) tcp->getUserData(); httpc->onData(data, dataLen); } /** analyze incoming data */ void onData(const uint8_t* data, uint16_t dataLen) { // header not yet received. scan incoming data. count number of consecutive linebreaks if (!gotHeader) { while(dataLen && !gotHeader) { const char c = *data; if (c == '\r' || c == '\n') {++numBreaks;} else {numBreaks = 0;} if (numBreaks == 4) { debugMod(NAME, "got header"); gotHeader = true; } ++data; --dataLen; } } // maybe we got the header now if (gotHeader && dataLen) { if (onBodyData) { onBodyData(this, data, dataLen); } } } private: /** send the request header */ void sendRequest() { char buf[128]; const int len = sprintf(buf, "GET %s HTTP/1.1\r\n\r\n", this->query); //const int len = sprintf(buf, "GET %s HTTP/1.1\r\nicy-metadata:1\r\n\r\n", this->query); printf(buf); //debugMod(NAME, (const char*) buf); tcp.send((const uint8_t*)buf, len); } static void _onHostResolved(const char* name, const ip_addr_t* ipaddr, void* arg ) { HTTPClient* http = (HTTPClient*) arg; if (ipaddr) { //debugMod1(NAME, "dns lookup returned: %s", tmp.toString()); http->tcp.connect(IP4(*ipaddr), 80); } else { debugMod1(NAME, "dns lookup failed: %s", name); } } }; #endif // HTTP_H