void HTTPClient::ReadyFile() { std::stringstream _header_path_stream; _header_path_stream << HTTPManager::Share()->GetTemporaryHeaderPath() + "/"; _header_path_stream << __http_client_identity; header_file_path = _header_path_stream.str(); header.open(header_file_path.c_str()); OPEN_FD(); HTTP_DEBUG(!header.is_open(), std::string("응답헤더를 저장할 파일이 열리지 않습니다.HTTPConfig.h의 HTTP_MANAGER_DIRECTORY, HTTP_RESPONSE_HEADER_FILE_PREFIX의 경로를 확인해주세요.권한문제 혹은 잘못된 경로일 수 있습니다.\n경로: ") + header_file_path + "\n"); std::stringstream _body_path_stream; _body_path_stream << HTTPManager::Share()->GetTemporaryBodyPath() + "/"; _body_path_stream << __http_client_identity; body_file_path = _body_path_stream.str(); body.open(body_file_path.c_str()); OPEN_FD(); HTTP_DEBUG(!body.is_open(), std::string("응답바디를 저장할 파일이 열리지 않습니다.HTTPConfig.h의 HTTP_MANAGER_DIRECTORY, HTTP_RESPONSE_BODY_FILE_PREFIX의 경로를 확인해주세요.권한문제 혹은 잘못된 경로일 수 있습니다.\n경로: ") + body_file_path + "\n"); __http_client_identity++; #ifdef ANDROID_NDK if (is_https) { requestHttps(tag, request.url, this); } #endif }
void ICACHE_FLASH_ATTR http_raw_request(const char * hostname, int port, const char * path, const char * post_data, http_callback user_callback) { HTTP_DEBUG("DNS request\n"); request_args *req = (request_args *)os_malloc(sizeof(request_args)); req->hostname = esp_strdup(hostname); req->path = esp_strdup(path); req->port = port; req->post_data = esp_strdup(post_data); req->buffer_size = 1; req->buffer = (char *)os_malloc(1); req->buffer[0] = '\0'; // Empty string. req->user_callback = user_callback; ip_addr_t addr; err_t error = espconn_gethostbyname((struct espconn *)req, // It seems we don't need a real espconn pointer here. hostname, &addr, dns_callback); if (error == ESPCONN_INPROGRESS) { HTTP_DEBUG("DNS pending\n"); } else if (error == ESPCONN_OK) { // Already in the local names table (or hostname was an IP address), execute the callback ourselves. dns_callback(hostname, &addr, req); } else if (error == ESPCONN_ARG) { HTTP_DEBUG("DNS error %s\n", hostname); } else { HTTP_DEBUG("DNS error code %d\n", error); } }
/* * Parse an URL of the form http://host:port/path * <host> can be a hostname or an IP address * <port> is optional */ void ICACHE_FLASH_ATTR http_post(const char * url, const char * post_data, const char * headers, http_callback user_callback) { // FIXME: handle HTTP auth with http://user:pass@host/ // FIXME: get rid of the #anchor part if present. char hostname[128] = ""; int port = 80; bool secure = false; bool is_http = os_strncmp(url, "http://", strlen("http://")) == 0; bool is_https = os_strncmp(url, "https://", strlen("https://")) == 0; if (is_http) url += strlen("http://"); // Get rid of the protocol. else if (is_https) { port = 443; secure = true; url += strlen("https://"); // Get rid of the protocol. } else { os_printf("URL is not HTTP or HTTPS %s\n", url); return; } char * path = os_strchr(url, '/'); if (path == NULL) { path = os_strchr(url, '\0'); // Pointer to end of string. } char * colon = os_strchr(url, ':'); if (colon > path) { colon = NULL; // Limit the search to characters before the path. } if (colon == NULL) { // The port is not present. os_memcpy(hostname, url, path - url); hostname[path - url] = '\0'; } else { port = atoi(colon + 1); if (port == 0) { os_printf("Port error %s\n", url); return; } os_memcpy(hostname, url, colon - url); hostname[colon - url] = '\0'; } if (path[0] == '\0') { // Empty path is not allowed. path = "/"; } HTTP_DEBUG("hostname=%s\n", hostname); HTTP_DEBUG("port=%d\n", port); HTTP_DEBUG("path=%s\n", path); http_raw_request(hostname, port, path, post_data, headers, user_callback); }
static void ICACHE_FLASH_ATTR reconnect_callback(void * arg, sint8 errType) { struct espconn *conn = (struct espconn *)arg; HTTP_DEBUG("RECONNECT\n"); if (errType != ESPCONN_OK) HTTP_DEBUG("Connection error: %d - %s\r\n", errType, ((errType>-16)&&(errType<1))? sEspconnErr[-errType] : "?"); if(conn->proto.tcp != NULL) { os_free(conn->proto.tcp); HTTP_DEBUG("os_free: conn->proto.tcp\r\n"); } os_free(conn); HTTP_DEBUG("os_free: conn\r\n"); }
static void ICACHE_FLASH_ATTR receive_callback(void * arg, char * buf, unsigned short len) { struct espconn * conn = (struct espconn *)arg; request_args * req = (request_args *)conn->reverse; if (req->buffer == NULL) { return; } // Let's do the equivalent of a realloc(). const int new_size = req->buffer_size + len; char * new_buffer; if (new_size > BUFFER_SIZE_MAX || NULL == (new_buffer = (char *)os_malloc(new_size))) { HTTP_DEBUG("Response too long %d\n", new_size); os_free(req->buffer); req->buffer = NULL; // TODO: espconn_disconnect(conn) without crashing. return; } os_memcpy(new_buffer, req->buffer, req->buffer_size); os_memcpy(new_buffer + req->buffer_size - 1 /*overwrite the null character*/, buf, len); // Append new data. new_buffer[new_size - 1] = '\0'; // Make sure there is an end of string. os_free(req->buffer); req->buffer = new_buffer; req->buffer_size = new_size; }
static void ICACHE_FLASH_ATTR dns_callback(const char * hostname, ip_addr_t * addr, void * arg) { request_args * req = (request_args *)arg; if (addr == NULL) { os_printf("DNS failed for %s\n", hostname); if (req->user_callback != NULL) { req->user_callback("", -1, ""); } os_free(req); } else { HTTP_DEBUG("DNS found %s " IPSTR "\n", hostname, IP2STR(addr)); struct espconn * conn = (struct espconn *)os_malloc(sizeof(struct espconn)); conn->type = ESPCONN_TCP; conn->state = ESPCONN_NONE; conn->proto.tcp = (esp_tcp *)os_malloc(sizeof(esp_tcp)); conn->proto.tcp->local_port = espconn_port(); conn->proto.tcp->remote_port = req->port; conn->reverse = req; os_memcpy(conn->proto.tcp->remote_ip, addr, 4); espconn_regist_connectcb(conn, connect_callback); espconn_regist_disconcb(conn, disconnect_callback); espconn_regist_reconcb(conn, error_callback); espconn_connect(conn); } }
static void ICACHE_FLASH_ATTR sent_callback(void * arg) { struct espconn * conn = (struct espconn *)arg; request_args * req = (request_args *)conn->reverse; if (req->post_data == NULL) { HTTP_DEBUG("All sent\n"); } else { // The headers were sent, now send the contents. HTTP_DEBUG("Sending request body\n"); espconn_sent(conn, (uint8_t *)req->post_data, strlen(req->post_data)); os_free(req->post_data); req->post_data = NULL; } }
static void ICACHE_FLASH_ATTR sent_callback(void * arg) { struct espconn * conn = (struct espconn *)arg; request_args * req = (request_args *)conn->reverse; if (req->post_data == NULL) { HTTP_DEBUG("All sent\n"); } else { // The headers were sent, now send the contents. HTTP_DEBUG("Sending request body\n"); sint8 espsent_status = espconn_sent(conn, (uint8_t *)req->post_data, strlen(req->post_data)); if(espsent_status == ESPCONN_OK) { HTTP_DEBUG("Request body sent, req->post_data = %s\n", req->post_data); } else { HTTP_DEBUG("Error while sending request body.\n"); } os_free(req->post_data); req->post_data = NULL; } }
static void ICACHE_FLASH_ATTR connect_callback(void * arg) { HTTP_DEBUG("Connected\n"); struct espconn * conn = (struct espconn *)arg; request_args * req = (request_args *)conn->reverse; espconn_regist_recvcb(conn, receive_callback); espconn_regist_sentcb(conn, sent_callback); const char * method = "GET"; char post_headers[32] = ""; if (req->post_data != NULL) { // If there is data this is a POST request. method = "POST"; os_sprintf(post_headers, "Content-Length: %d\r\n", strlen(req->post_data)); } char buf[69 + strlen(method) + strlen(req->path) + strlen(req->hostname) + strlen(req->headers) + strlen(post_headers)]; char hostname[strlen(req->hostname)+7]; if (req->port != 80) { os_sprintf(hostname, "%s:%d", req->hostname, req->port); } else { os_strcpy(hostname, req->hostname); } int len = os_sprintf(buf, "%s %s HTTP/1.1\r\n" "Host: %s\r\n" "Connection: close\r\n" "User-Agent: ESP8266\r\n" "%s" "%s" "\r\n", method, req->path, hostname, req->headers, post_headers); espconn_sent(conn, (uint8_t *)buf, len); os_free(req->headers); req->headers = NULL; HTTP_DEBUG("Sending request header\n"); }
static void ICACHE_FLASH_ATTR disconnect_callback(void * arg) { HTTP_DEBUG("Disconnected\n"); struct espconn *conn = (struct espconn *)arg; if(conn == NULL) { return; } if(conn->proto.tcp != NULL) { os_free(conn->proto.tcp); } if(conn->reverse != NULL) { request_args * req = (request_args *)conn->reverse; if (req->buffer != NULL) { // FIXME: make sure this is not a partial response, using the Content-Length header. const char * version = "HTTP/1.1 "; if (os_strncmp(req->buffer, version, strlen(version)) != 0) { HTTP_DEBUG("Invalid version in %s\n", req->buffer); return; } int http_status = atoi(req->buffer + strlen(version)); //char * body = (char *)os_strstr(req->buffer, "\r\n\r\n") + 4; char * body = (char *)os_strstr(req->buffer, "\r\n\r\n"); if (body) body+=4; if (req->user_callback != NULL) { // Callback is optional. req->user_callback(body, http_status, req->buffer); } os_free(req->buffer); } os_free(req->hostname); os_free(req->path); os_free(req); } os_free(conn); }
static char * esp_strdup(const char * str) { if (str == NULL) { return NULL; } char * new_str = (char *)os_malloc(os_strlen(str) + 1); // 1 for null character if (new_str == NULL) { HTTP_DEBUG("esp_strdup: malloc error"); return NULL; } os_strcpy(new_str, str); return new_str; }
static void ICACHE_FLASH_ATTR connect_callback(void * arg) { HTTP_DEBUG("Connected\n"); struct espconn * conn = (struct espconn *)arg; request_args * req = (request_args *)conn->reverse; espconn_regist_recvcb(conn, receive_callback); espconn_regist_sentcb(conn, sent_callback); const char * method = "GET"; char post_headers[128] = ""; if (req->post_data != NULL) { // If there is data this is a POST request. method = "POST"; os_sprintf(post_headers, "Content-Type: application/x-www-form-urlencoded\r\n" "Content-Length: %d\r\n", strlen(req->post_data)); } char buf[2048]; int len = os_sprintf(buf, "%s %s HTTP/1.1\r\n" "Host: %s:%d\r\n" "Connection: close\r\n" "User-Agent: ESP8266\r\n" "%s" "\r\n", method, req->path, req->hostname, req->port, post_headers); sint8 espsent_status = espconn_sent(conn, (uint8_t *)buf, len); if(espsent_status == ESPCONN_OK) { HTTP_DEBUG("Data sent, buf = %s\n", buf); } else { HTTP_DEBUG("Error while sending data.\n"); } HTTP_DEBUG("Sending request header\n"); }
HTTPRequest::HTTPRequest() : m_method((http_method) -1), m_path(0), m_query(0), m_currentHeader(-1), m_headerLength(0), m_headerValue(true), m_age(millis()), m_id(__id++) { HTTP_DEBUG(" <%lu> HTTPRequest::HTTPRequest\n", m_id) http_parser_url_init(&m_urlP); // we store some headers by defult storeHeader("Host"); storeHeader("Content-Type"); storeHeader("Content-Length"); // CORS support storeHeader("Access-Control-Request-Method"); storeHeader("Access-Control-Request-Headers"); }
static void ICACHE_FLASH_ATTR dns_callback(const char * hostname, ip_addr_t * addr, void * arg) { request_args * req = (request_args *)arg; if (addr == NULL) { HTTP_DEBUG("DNS failed, host: %s\n", hostname); } else { HTTP_DEBUG("DNS found, host: %s, address: " IPSTR "\n", hostname, IP2STR(addr)); struct espconn * conn = (struct espconn *)os_malloc(sizeof(struct espconn)); conn->type = ESPCONN_TCP; conn->state = ESPCONN_NONE; conn->proto.tcp = (esp_tcp *)os_malloc(sizeof(esp_tcp)); conn->proto.tcp->local_port = espconn_port(); conn->proto.tcp->remote_port = req->port; conn->reverse = req; os_memcpy(conn->proto.tcp->remote_ip, addr, 4); espconn_regist_connectcb(conn, connect_callback); espconn_regist_disconcb(conn, disconnect_callback); espconn_regist_reconcb(conn, reconnect_callback); // TODO: consider using espconn_regist_reconcb (for timeouts?) // cf esp8266_sdk_v0.9.1/examples/at/user/at_ipCmd.c (TCP ARQ retransmission?) sint8 espcon_status = espconn_connect(conn); switch(espcon_status) { case ESPCONN_OK: HTTP_DEBUG("TCP created.\r\n"); break; case ESPCONN_RTE: HTTP_DEBUG("Error connection, routing problem.\r\n"); break; case ESPCONN_TIMEOUT: HTTP_DEBUG("Error connection, timeout.\r\n"); break; default: HTTP_DEBUG("Connection error: %d - %s\r\n", espcon_status, ((espcon_status>-16)&&(espcon_status<1))? sEspconnErr[-espcon_status] : "?"); } } }
http_parser_url_init(&m_urlP); // we store some headers by defult storeHeader("Host"); storeHeader("Content-Type"); storeHeader("Content-Length"); // CORS support storeHeader("Access-Control-Request-Method"); storeHeader("Access-Control-Request-Headers"); } HTTPRequest::~HTTPRequest() { HTTP_DEBUG(" <%lu> HTTPRequest::~HTTPRequest \n", m_id) } void HTTPRequest::storeHeader(const char * name, bool store) { int i = 0; while (i < HTTPSERVER_MAX_HEADER_LINES && m_header[i].field.buf()) i++; if (i < HTTPSERVER_MAX_HEADER_LINES) { m_header[i].field.set(name); m_header[i].store = true; } } int HTTPRequest::findHeader(const char * startsWith, size_t length) { int i = 0; while (i < HTTPSERVER_MAX_HEADER_LINES && m_header[i].field) {
bool HTTPClient::Init(void* _ptr) { if(_ptr) { is_raise_error_multi_handle = true; code_multi = *(CURLMcode*)_ptr; return true; } if(is_raise_error_multi_handle) return true; //디비에서 URL이랑 매치되는 필드가져오기 sqlite3* _db = NULL; char* _db_message = NULL; HTTP_DEBUG((sqlite3_open(HTTPManager::Share()->GetDBPath().c_str(), &_db)), "sqlite 디비 열기에 실패하였습니다." << opened_file << "\nError Message:" << sqlite3_errmsg(_db) << "\n"); std::string _query = "SELECT * FROM request WHERE url='" + request.url + "' LIMIT 1"; HTTP_DEBUG((sqlite3_exec(_db, _query.c_str(), SQLMatchURLCallback, this, &_db_message)), "sqllite 디비 검색에 실패하였습니다." << "\nError Message:" << _db_message); HTTP_DEBUG((sqlite3_close(_db)), "sqlite 디비 닫기에 실패하였습니다." << "\nError Message:" << sqlite3_errmsg(_db) << "\n"); if(db_id.length()) { if(db_expires > HTTP::CurrentTime()) { //저장된것 사용 cache_type = HTTPResponse::CacheType_Expires; } else if(db_max_age > 0) { if(last_updated - db_last_updated < db_max_age) { //저장된것 사용 cache_type = HTTPResponse::CacheType_Expires; } else { //연결(이후 Last-Modified로 캐쉬 체크) cache_type = HTTPResponse::CacheType_Last_Modified; } } else { //연결(이후 Last-Modified로 캐쉬 체크) cache_type = HTTPResponse::CacheType_Last_Modified; } } else { //처음 다운 cache_type = HTTPResponse::CacheType_None; } if(!request.use_cache) cache_type = HTTPResponse::CacheType_None; //파일 준비 ReadyFile(); #ifdef ANDROID_NDK if (is_https) return true; #endif //연결이 필요한경우는 curl생성 if(cache_type != HTTPResponse::CacheType_Expires) curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, request.GetURL().c_str()); ReadyHeader(); ReadyBody(); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30); curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HTTPClient::ReadBody); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, ReadHeader); curl_easy_setopt(curl, CURLOPT_WRITEHEADER, this); curl_easy_setopt(curl, CURLOPT_WRITEDATA, this); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, HTTPClient::Progress); curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, this); curl_multi_add_handle(HTTPManager::Share()->GetCURLMulti(), curl); } return true; }
bool HTTPClient::UpdateDB() { if(!is_recieve) return false; if(code != CURLE_OK) return false; if(header.is_open()) { header.close(); CLOSE_FD(); } if(body.is_open()) { body.close(); CLOSE_FD(); } if(request.GetUseCache()) { if(request.force_max_age > 0) { max_age = request.force_max_age; if(expires == 0 && max_age != 0) { expires = HTTP::CurrentTime() + max_age; } } } std::stringstream _query; bool _need_update = false; bool _is_insert = false; if(cache_type == HTTPResponse::CacheType_None) { if(!db_id.length()) { _query << "INSERT OR REPLACE INTO request (url, Expires, Last_Modified, Last_Updated, Max_Age) VALUES ('" << request.url << "', '" << expires << "', '" << last_modified << "', '" << last_updated << "', '" << max_age << "')"; _is_insert = true; } else { _query << "UPDATE request SET Expires='" << expires << "', Last_Modified='" << last_modified << "', Last_Updated='" << last_updated << "', Max_Age='" << max_age << "' WHERE id='" << db_id << "'"; } _need_update = true; } else if(cache_type == HTTPResponse::CacheType_Last_Modified) { if(last_modified && db_last_modified) { if(last_modified != db_last_modified) { _query << "UPDATE request SET Last_Modified='" << last_modified << "', Last_Updated='" << last_updated << "' WHERE id='" << db_id << "'"; _need_update = true; } } } if(!_need_update) { return false; } sqlite3* _db = NULL; char* _db_message = NULL; HTTP_DEBUG((sqlite3_open(HTTPManager::Share()->GetDBPath().c_str(), &_db)), "열기실패" << "\nError Message:" << sqlite3_errmsg(_db) << "\n"); HTTP_DEBUG((sqlite3_exec(_db, _query.str().c_str(), NULL, NULL, &_db_message)), "error:" << _db_message); if(_is_insert) { _query.str(""); _query << "SELECT * FROM request WHERE url='" << request.url << "' LIMIT 1"; HTTP_DEBUG((sqlite3_exec(_db, _query.str().c_str(), SQLMatchURLCallback, this, &_db_message)), "sqllite 디비 검색에 실패하였습니다." << "\nError Message:" << _db_message); } HTTP_DEBUG((sqlite3_close(_db)), "닫기실패" << "\nError Message:" << sqlite3_errmsg(_db) << "\n"); rename(header_file_path.c_str(), cache_header_file_path.c_str()); rename(body_file_path.c_str(), cache_body_file_path.c_str()); response = HTTPResponse(cache_header_file_path, cache_body_file_path, cache_type); return true; }
static void ICACHE_FLASH_ATTR error_callback(void *arg, sint8 errType) { HTTP_DEBUG("Disconnected with error\n"); disconnect_callback(arg); }