/* * Copyright (C) 2015-2017 Alibaba Group Holding Limited */ #include #include #include #include "utils_timer.h" #include "lite-log.h" #include "utils_httpc.h" #include "bk_uart.h" #include #include #include #include "security_ota.h" #if HTTP_WR_TO_FLASH #if CONFIG_FLASH_ORIGIN_API #include "bk_flash.h" #else #include "driver/flash.h" #endif #endif #ifdef CONFIG_HTTP_AB_PARTITION #include "modules/ota.h" #include "vendor_flash_partition.h" extern part_flag update_part_flag; #endif #if CONFIG_HTTP #define HTTPCLIENT_MIN(x,y) (((x)<(y))?(x):(y)) #define HTTPCLIENT_MAX(x,y) (((x)>(y))?(x):(y)) #define HTTPCLIENT_AUTHB_SIZE 128 #define HTTPCLIENT_CHUNK_SIZE 256 #define HTTPCLIENT_SEND_BUF_SIZE 2048 #define HTTPCLIENT_MAX_HOST_LEN 64 #define HTTPCLIENT_MAX_URL_LEN 256 #define HTTPCLIENT_MAX_PORT_LEN 6 #define HTTP_RETRIEVE_MORE_DATA (1) /**< More data needs to be retrieved. */ #if CONFIG_FLASH_ORIGIN_API extern void flash_protection_op(UINT8 mode, PROTECT_TYPE type); #endif #if CONFIG_OTA_TFTP #define HTTP_FLASH_WR_BUF_MAX WR_BUF_MAX #else #define HTTP_FLASH_WR_BUF_MAX 1024 #endif HTTP_DATA_ST bk_http = { .http_total = 0, .do_data = 0, #if HTTP_WR_TO_FLASH || CONFIG_DIRECT_XIP || CONFIG_OTA_OVERWRITE .wr_buf = NULL, .wr_last_len = 0, .wr_tmp_buf = NULL, #endif }; HTTP_DATA_ST *bk_http_ptr = &bk_http; #if HTTP_WR_TO_FLASH static UINT32 ota_wr_block = 0; #endif #if CONFIG_OTA_POSITION_INDEPENDENT_AB static uint32 ota_partition_length = 0; #endif // static int httpclient_parse_host(const char *url, char *host, uint32_t maxhost_len); static int httpclient_parse_url(const char *url, char *scheme, uint32_t max_scheme_len, char *host, uint32_t maxhost_len, int *port, char *path, uint32_t max_path_len); static int httpclient_conn(httpclient_t *client); static int httpclient_recv(httpclient_t *client, char *buf, int min_len, int max_len, int *p_read_len, uint32_t timeout); static int httpclient_retrieve_content(httpclient_t *client, char *data, int len, uint32_t timeout, httpclient_data_t *client_data); static int httpclient_response_parse(httpclient_t *client, char *data, int len, uint32_t timeout, httpclient_data_t *client_data); static void httpclient_base64enc(char *out, const char *in) { const char code[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; int i = 0, x = 0, l = 0; for (; *in; in++) { x = x << 8 | *in; for (l += 8; l >= 6; l -= 6) out[i++] = code[(x >> (l - 6)) & 0x3f]; } if (l > 0) { x <<= 6 - l; out[i++] = code[x & 0x3f]; } for (; i % 4;) out[i++] = '='; out[i] = '\0'; } static uint32 http_get_sapp_partition_length(bk_partition_t partition) { bk_logic_partition_t *bk_ptr = NULL; uint32 ret_length; bk_ptr = bk_flash_partition_get_info(partition); if(NULL == bk_ptr) { os_printf("get s_app partition fail! \r\n"); bk_reboot(); } ret_length = bk_ptr->partition_length; return ret_length; } uint32_t get_http_flash_wr_buf_max(void) { return HTTP_FLASH_WR_BUF_MAX; } int httpclient_conn(httpclient_t *client) { if (0 != client->net.connect(&client->net)) { log_err("establish connection failed"); return ERROR_HTTP_CONN; } return SUCCESS_RETURN; } int httpclient_parse_url(const char *url, char *scheme, uint32_t max_scheme_len, char *host, uint32_t maxhost_len, int *port, char *path, uint32_t max_path_len) { char *scheme_ptr = (char *) url; char *host_ptr = (char *) os_strstr(url, "://"); char port_array[HTTPCLIENT_MAX_PORT_LEN] = {0}; uint32_t host_len = 0; uint32_t port_len = 0; uint32_t path_len; char *port_ptr; char *path_ptr; char *fragment_ptr; if (host_ptr == NULL) { log_err("Could not find host"); return ERROR_HTTP_PARSE; /* URL is invalid */ } if (max_scheme_len < host_ptr - scheme_ptr + 1) { /* including NULL-terminating char */ log_err("Scheme str is too small (%u >= %u)", max_scheme_len, (uint32_t)(host_ptr - scheme_ptr + 1)); return ERROR_HTTP_PARSE; } os_memcpy(scheme, scheme_ptr, host_ptr - scheme_ptr); scheme[host_ptr - scheme_ptr] = '\0'; host_ptr += 3; path_ptr = os_strchr(host_ptr, '/'); if (NULL == path_ptr) { log_err("invalid path"); return -1; } port_ptr = os_strchr(host_ptr, ':'); if (NULL == port_ptr) { *port = 0; port_len = 0; } else { port_len = path_ptr - port_ptr; if(port_len < HTTPCLIENT_MAX_PORT_LEN) { //port_ptr -> :8192 os_memcpy(port_array, port_ptr + 1, port_len - 1); port_array[port_len - 1] = '\0'; *port= os_strtoul(port_array, NULL, 10); } else { log_err("parse port error, port_len(%d).", port_len); *port = 0; port_len = 0; } } log_info("port (%d) port_len(%d).", *port, port_len); host_len = path_ptr - host_ptr - port_len; if (host_len == 0) { log_err("Parse Host error."); return ERROR_HTTP_PARSE; } if (maxhost_len < host_len + 1) { /* including NULL-terminating char */ log_err("Host str is too long (host_len(%d) >= max_len(%d))", host_len + 1, maxhost_len); return ERROR_HTTP_PARSE; } os_memcpy(host, host_ptr, host_len); host[host_len] = '\0'; fragment_ptr = os_strchr(host_ptr, '#'); if (fragment_ptr != NULL) path_len = fragment_ptr - path_ptr; else path_len = os_strlen(path_ptr); if (max_path_len < path_len + 1) { /* including NULL-terminating char */ log_err("Path str is too small (%d >= %d)", max_path_len, path_len + 1); return ERROR_HTTP_PARSE; } os_memcpy(path, path_ptr, path_len); path[path_len] = '\0'; return SUCCESS_RETURN; } // int httpclient_parse_host(const char *url, char *host, uint32_t maxhost_len) // { // const char *host_ptr = (const char *) os_strstr(url, "://"); // uint32_t host_len = 0; // char *path_ptr; // if (host_ptr == NULL) { // log_err("Could not find host"); // return ERROR_HTTP_PARSE; /* URL is invalid */ // } // host_ptr += 3; // path_ptr = os_strchr(host_ptr, '/'); // if (host_len == 0) // host_len = path_ptr - host_ptr; // if (maxhost_len < host_len + 1) { // /* including NULL-terminating char */ // log_err("Host str is too small (%d >= %d)", maxhost_len, host_len + 1); // return ERROR_HTTP_PARSE; // } // os_memcpy(host, host_ptr, host_len); // host[host_len] = '\0'; // return SUCCESS_RETURN; // } int httpclient_get_info(httpclient_t *client, char *send_buf, int *send_idx, char *buf, uint32_t len) /* 0 on success, err code on failure */ { int ret; int cp_len; int idx = *send_idx; if (len == 0) len = os_strlen(buf); do { if ((HTTPCLIENT_SEND_BUF_SIZE - idx) >= len) cp_len = len; else cp_len = HTTPCLIENT_SEND_BUF_SIZE - idx; os_memcpy(send_buf + idx, buf, cp_len); idx += cp_len; len -= cp_len; if (idx == HTTPCLIENT_SEND_BUF_SIZE) { // if (client->remote_port == HTTPS_PORT) // { // WRITE_IOT_ERROR_LOG("send buffer overflow"); // return ERROR_HTTP; // } //ret = httpclient_tcp_send_all(client->handle, send_buf, HTTPCLIENT_SEND_BUF_SIZE); ret = client->net.write(&client->net, send_buf, HTTPCLIENT_SEND_BUF_SIZE, 5000); if (ret) return (ret); } } while (len); *send_idx = idx; return SUCCESS_RETURN; } void httpclient_set_custom_header(httpclient_t *client, char *header) { client->header = header; } int httpclient_basic_auth(httpclient_t *client, char *user, char *password) { if ((os_strlen(user) + os_strlen(password)) >= HTTPCLIENT_AUTHB_SIZE) return ERROR_HTTP; client->auth_user = user; client->auth_password = password; return SUCCESS_RETURN; } int httpclient_send_auth(httpclient_t *client, char *send_buf, int *send_idx) { char b_auth[(int)((HTTPCLIENT_AUTHB_SIZE + 3) * 4 / 3 + 1)]; char base64buff[HTTPCLIENT_AUTHB_SIZE + 3]; httpclient_get_info(client, send_buf, send_idx, "Authorization: Basic ", 0); sprintf(base64buff, "%s:%s", client->auth_user, client->auth_password); log_debug("bAuth: %s", base64buff) ; httpclient_base64enc(b_auth, base64buff); b_auth[os_strlen(b_auth) + 1] = '\0'; b_auth[os_strlen(b_auth)] = '\n'; log_debug("b_auth:%s", b_auth) ; httpclient_get_info(client, send_buf, send_idx, b_auth, 0); return SUCCESS_RETURN; } int httpclient_send_header(httpclient_t *client, const char *url, int method, httpclient_data_t *client_data) { char scheme[8] = { 0 }; char *host; char *path; int len; char *send_buf; char *buf; char *meth = (method == HTTPCLIENT_GET) ? "GET" : (method == HTTPCLIENT_POST) ? "POST" : (method == HTTPCLIENT_PUT) ? "PUT" : (method == HTTPCLIENT_DELETE) ? "DELETE" : (method == HTTPCLIENT_HEAD) ? "HEAD" : ""; int ret; int port; int rc = SUCCESS_RETURN; if (NULL == (host = (char *)os_malloc(HTTPCLIENT_MAX_HOST_LEN))) { log_err("not enough memory"); return FAIL_RETURN; } if (NULL == (path = (char *)os_malloc(HTTPCLIENT_MAX_URL_LEN))) { log_err("not enough memory"); rc = FAIL_RETURN; goto GO_ERR_3; } if (NULL == (send_buf = (char *)os_malloc(HTTPCLIENT_SEND_BUF_SIZE))) { log_err("not enough memory"); rc = FAIL_RETURN; goto GO_ERR_2; } if (NULL == (buf = (char *)os_malloc(HTTPCLIENT_SEND_BUF_SIZE))) { log_err("not enough memory"); rc = FAIL_RETURN; goto GO_ERR_1; } /* First we need to parse the url (http[s]://host[:port][/[path]]) */ int res = httpclient_parse_url(url, scheme, sizeof(scheme), host, HTTPCLIENT_MAX_HOST_LEN, &port, path, HTTPCLIENT_MAX_URL_LEN); if (res != SUCCESS_RETURN) { log_err("httpclient_parse_url returned %d", res); //return res; rc = res; goto GO_ERR; } //if (client->remote_port == 0) //{ if (os_strcmp(scheme, "http") == 0) { //client->remote_port = HTTP_PORT; } else if (os_strcmp(scheme, "https") == 0) { //client->remote_port = HTTPS_PORT; } //} /* Send request */ os_memset(send_buf, 0, HTTPCLIENT_SEND_BUF_SIZE); os_memset(buf, 0, HTTPCLIENT_SEND_BUF_SIZE); len = 0; /* Reset send buffer */ snprintf(buf, HTTPCLIENT_SEND_BUF_SIZE, "%s %s HTTP/1.1\r\nHost: %s\r\n", meth, path, host); /* Write request */ ret = httpclient_get_info(client, send_buf, &len, buf, os_strlen(buf)); if (ret) { log_err("Could not write request"); //return ERROR_HTTP_CONN; rc = ERROR_HTTP_CONN; goto GO_ERR; } /* Send all headers */ if (client->auth_user) { httpclient_send_auth(client, send_buf, &len); /* send out Basic Auth header */ } /* Add user header information */ if (client->header) httpclient_get_info(client, send_buf, &len, (char *) client->header, os_strlen(client->header)); if (client_data->post_buf != NULL) { snprintf(buf, HTTPCLIENT_SEND_BUF_SIZE, "Content-Length: %d\r\n", client_data->post_buf_len); httpclient_get_info(client, send_buf, &len, buf, os_strlen(buf)); if (client_data->post_content_type != NULL) { snprintf(buf, HTTPCLIENT_SEND_BUF_SIZE, "Content-Type: %s\r\n", client_data->post_content_type); httpclient_get_info(client, send_buf, &len, buf, os_strlen(buf)); } } /* Close headers */ httpclient_get_info(client, send_buf, &len, "\r\n", 0); log_multi_line(LOG_DEBUG_LEVEL, "REQUEST", "%s", send_buf, ">"); //ret = httpclient_tcp_send_all(client->net.handle, send_buf, len); ret = client->net.write(&client->net, send_buf, len, 5000); if (ret > 0) log_debug("Written %d bytes", ret); else if (ret == 0) { log_err("ret == 0,Connection was closed by server"); //return ERROR_HTTP_CLOSED; /* Connection was closed by server */ rc = ERROR_HTTP_CLOSED; } else { log_err("Connection error (send returned %d)", ret); //return ERROR_HTTP_CONN; rc = ERROR_HTTP_CONN; } GO_ERR: os_free(buf); GO_ERR_1: os_free(send_buf); GO_ERR_2: os_free(path); GO_ERR_3: os_free(host); return rc;//SUCCESS_RETURN; } int httpclient_send_userdata(httpclient_t *client, httpclient_data_t *client_data) { int ret = 0; if (client_data->post_buf && client_data->post_buf_len) { log_debug("client_data->post_buf: %s", client_data->post_buf); { //ret = httpclient_tcp_send_all(client->handle, (char *)client_data->post_buf, client_data->post_buf_len); ret = client->net.write(&client->net, (char *)client_data->post_buf, client_data->post_buf_len, 5000); if (ret > 0) log_debug("Written %d bytes", ret); else if (ret == 0) { log_err("ret == 0,Connection was closed by server"); return ERROR_HTTP_CLOSED; /* Connection was closed by server */ } else { log_err("Connection error (send returned %d)", ret); return ERROR_HTTP_CONN; } } } return SUCCESS_RETURN; } /* 0 on success, err code on failure */ int httpclient_recv(httpclient_t *client, char *buf, int min_len, int max_len, int *p_read_len, uint32_t timeout_ms) { int ret = 0; iotx_time_t timer; iotx_time_init(&timer); utils_time_countdown_ms(&timer, timeout_ms); *p_read_len = 0; ret = client->net.read(&client->net, buf, max_len, iotx_time_left(&timer)); //log_debug("Recv: | %s", buf); if (ret > 0) *p_read_len = ret; else if (ret == 0) { //timeout return ERROR_HTTP_CONN; } else if (-1 == ret) { log_info("Connection closed."); return ERROR_HTTP_CONN; } else if (-3 == ret) { log_info("checksum fail."); return FAIL_RETURN; } else { log_err("Connection error (recv returned %d)", ret); return ERROR_HTTP_CONN; } log_info("%u bytes has been read", *p_read_len); return 0; } /* * when bt INT effect the OTA update */ #define S_WAKE_UP (0) #define S_SLEEP (1) #define S_POWER_OFF (2) #define S_NO_BT (3) #define ERASE_TOUCH_TIMEOUT (3000)//ms #define ERASE_FLASH_TIMEOUT (56)//ms #define WRITE_FLASH_TIMEOUT (4)//ms static u32 bt_sleepend_time = -1; //when cb return,bt is sleep ,recoeding the bt will sleep how long static u32 bt_cb_anchor_time = 0; //when cb return ,record current time ; static u8 bt_sleep_state = S_NO_BT; //record bt state;the default is 3(S_NO_BT); void ble_sleep_cb(uint8_t is_sleeping, uint32_t slp_period) { GLOBAL_INT_DECLARATION(); GLOBAL_INT_DISABLE(); bt_sleep_state = is_sleeping ; bt_cb_anchor_time = rtos_get_time(); if (is_sleeping == S_SLEEP) { bt_sleepend_time = bt_cb_anchor_time + slp_period/32; } GLOBAL_INT_RESTORE(); } static int ble_callback_deal_handler(uint32_t deal_flash_time) { uint32_t cur_time =0; uint32_t temp_time = 0; int ret_val = 0; cur_time = rtos_get_time(); GLOBAL_INT_DECLARATION(); GLOBAL_INT_DISABLE(); do { if(bt_sleep_state == S_POWER_OFF) //poweroff { ret_val = 1; break; } else if(bt_sleep_state == S_WAKE_UP) //wakeup { if(cur_time >= bt_cb_anchor_time) { temp_time = (cur_time - bt_cb_anchor_time); } else { temp_time = 0xFFFFFFFF - bt_cb_anchor_time + cur_time; } if(temp_time >= ERASE_TOUCH_TIMEOUT) { bt_sleep_state = S_NO_BT; ret_val = 1; break; } ret_val = 0; break; } else if(bt_sleep_state == S_SLEEP) //sleep { if(bt_sleepend_time > bt_cb_anchor_time) { if(bt_sleepend_time < cur_time) { ret_val = 1; break; } else if(cur_time < bt_cb_anchor_time) { ret_val = 1; break; } else if((bt_sleepend_time - cur_time) >= deal_flash_time) { ret_val = 1; break; } else { ret_val = 0; break; } } else { temp_time = 0; if((cur_time > bt_sleepend_time)&&(bt_cb_anchor_time > cur_time)) { ret_val = 1; break; } else if(bt_cb_anchor_time <= cur_time) { temp_time = 0xFFFFFFFF - cur_time + bt_sleepend_time; } else { temp_time = bt_sleepend_time - cur_time; } if(temp_time >= deal_flash_time ) { ret_val = 1; break; } else { ret_val = 0; break; } } } else { ret_val = 1; break; } }while(0); GLOBAL_INT_RESTORE(); return ret_val; } #if HTTP_WR_TO_FLASH void http_flash_wr(UINT8 *src, unsigned len) { UINT32 param; UINT32 anchor_time = 0; UINT32 temp_time = 0; UINT8 flash_erase_ready = 0; //GLOBAL_INT_DECLARATION(); if (bk_http_ptr->flash_address % 0x1000 == 0) { anchor_time = rtos_get_time(); while(1) { flash_erase_ready = ble_callback_deal_handler(ERASE_FLASH_TIMEOUT); temp_time = rtos_get_time(); if(temp_time >= anchor_time) { temp_time -= anchor_time; } else { temp_time += (0xFFFFFFFF - anchor_time); } if(temp_time >= ERASE_TOUCH_TIMEOUT) flash_erase_ready = 1; //os_printf("flash_erase_ready~111 :%d\n",flash_erase_ready); if(flash_erase_ready == 1) { param = bk_http_ptr->flash_address; //GLOBAL_INT_DISABLE(); #if CONFIG_FLASH_ORIGIN_API ddev_control(bk_http_ptr->flash_hdl, CMD_FLASH_ERASE_SECTOR, (void *)¶m); #else #if CONFIG_OTA_POSITION_INDEPENDENT_AB if((len != 0) && (((u32)bk_http_ptr->flash_address + len) <= (bk_http_ptr->pt->partition_start_addr + ota_partition_length))) #else if((len != 0) && (((u32)bk_http_ptr->flash_address + len) <= (bk_http_ptr->pt->partition_start_addr + bk_http_ptr->pt->partition_length))) #endif { bk_flash_erase_sector(param); } #endif //GLOBAL_INT_RESTORE(); flash_erase_ready = 0; break; } else { rtos_delay_milliseconds(2); } } } #if CONFIG_OTA_POSITION_INDEPENDENT_AB if (((u32)bk_http_ptr->flash_address >= bk_http_ptr->pt->partition_start_addr) && (((u32)bk_http_ptr->flash_address + len) <= (bk_http_ptr->pt->partition_start_addr + ota_partition_length))) #else if (((u32)bk_http_ptr->flash_address >= bk_http_ptr->pt->partition_start_addr) && (((u32)bk_http_ptr->flash_address + len) <= (bk_http_ptr->pt->partition_start_addr + bk_http_ptr->pt->partition_length))) #endif { while(1) { flash_erase_ready = ble_callback_deal_handler(WRITE_FLASH_TIMEOUT); temp_time = rtos_get_time(); if(temp_time >= anchor_time) { temp_time -= anchor_time; } else { temp_time += (0xFFFFFFFF - anchor_time); } if(temp_time >= ERASE_TOUCH_TIMEOUT) flash_erase_ready = 1; //os_printf("flash_erase_ready~222 :%d\n",flash_erase_ready); if(flash_erase_ready == 1) { //GLOBAL_INT_DISABLE(); #if CONFIG_FLASH_ORIGIN_API ddev_write(bk_http_ptr->flash_hdl, (char *)src, len, (u32)bk_http_ptr->flash_address); #else bk_flash_write_bytes(bk_http_ptr->flash_address, (uint8_t *)src, len); #endif //GLOBAL_INT_RESTORE(); if (bk_http_ptr->wr_tmp_buf) { //GLOBAL_INT_DISABLE(); #if CONFIG_FLASH_ORIGIN_API ddev_read(bk_http_ptr->flash_hdl, (char *)bk_http_ptr->wr_tmp_buf, len, (u32)bk_http_ptr->flash_address); #else bk_flash_read_bytes(bk_http_ptr->flash_address, (uint8_t *)bk_http_ptr->wr_tmp_buf, len); #endif //GLOBAL_INT_RESTORE(); if (!os_memcmp(src, bk_http_ptr->wr_tmp_buf, len)) { } else os_printf("wr flash write err\n"); } bk_http_ptr->flash_address += len; //os_printf("ad %x.\r\n",bk_http_ptr->flash_address); flash_erase_ready = 0; break; } else { rtos_delay_milliseconds(2); } } } } void http_flash_init(void) { #if CONFIG_FLASH_ORIGIN_API UINT32 status; #endif bk_http_ptr->wr_buf = NULL; bk_http_ptr->wr_tmp_buf = NULL; if (!bk_http_ptr->wr_buf) { bk_http_ptr->wr_buf = os_malloc(HTTP_FLASH_WR_BUF_MAX * sizeof(char)); if (! bk_http_ptr->wr_buf) os_printf("wr_buf malloc err\r\n"); } if (!bk_http_ptr->wr_tmp_buf) { bk_http_ptr->wr_tmp_buf = os_malloc(HTTP_FLASH_WR_BUF_MAX * sizeof(char)); if (! bk_http_ptr->wr_tmp_buf) os_printf("wr_tmp_buf malloc err\r\n"); } #if CONFIG_FLASH_ORIGIN_API bk_http_ptr->pt = bk_flash_get_info(BK_PARTITION_OTA); bk_http_ptr->flash_hdl = ddev_open(DD_DEV_TYPE_FLASH, &status, 0); BK_ASSERT(DD_HANDLE_UNVALID != bk_http_ptr->flash_hdl); #else #ifndef CONFIG_HTTP_AB_PARTITION bk_http_ptr->pt = bk_flash_partition_get_info(BK_PARTITION_OTA); #else #if CONFIG_OTA_POSITION_INDEPENDENT_AB ota_partition_length = http_get_sapp_partition_length(BK_PARTITION_S_APP_USER); if(update_part_flag == UPDATE_B_PART) { os_printf("UPDATE_B_PART\r\n"); bk_http_ptr->pt = bk_flash_partition_get_info(BK_PARTITION_S_APP_USER); //update B_parition } else { os_printf("UPDATE_A_PART\r\n"); bk_http_ptr->pt = bk_flash_partition_get_info(BK_PARTITION_APPLICATION);//update A_parition. } #else bk_http_ptr->pt = bk_flash_partition_get_info(BK_PARTITION_S_APP_USER); #endif #if CONFIG_OTA_EVADE_METHOD uint8_t download_status_flag = DOWNLOAD_START_FLAG; ota_write_flash(BK_PARTITION_OTA_FINA_EXECUTIVE, download_status_flag, DOWNLOAD_STATUS_POS); #endif #endif #endif bk_http_ptr->wr_last_len = 0; ota_wr_block = 0; bk_http_ptr->flash_address = bk_http_ptr->pt->partition_start_addr; #if CONFIG_FLASH_ORIGIN_API bk_flash_enable_security(FLASH_PROTECT_NONE); #else bk_flash_set_protect_type(FLASH_PROTECT_NONE); #endif os_printf("ota write to 0x%x\r\n", bk_http_ptr->flash_address); } void http_flash_deinit(void) { os_free(bk_http_ptr->wr_buf); os_free(bk_http_ptr->wr_tmp_buf); os_memset(bk_http_ptr, 0, sizeof(HTTP_DATA_ST)); ota_wr_block = 0; #if CONFIG_FLASH_ORIGIN_API ddev_close(bk_http_ptr->flash_hdl); bk_flash_enable_security(FLASH_UNPROTECT_LAST_BLOCK); #else bk_flash_set_protect_type(FLASH_UNPROTECT_LAST_BLOCK); #endif os_printf("write over\r\n"); } void http_wr_to_flash(char *page, UINT32 len) { UINT8 *tmp; UINT32 w_l = 0, i = 0; i = 0; tmp = (UINT8 *)page; while (i < len) { w_l = min(len - i, HTTP_FLASH_WR_BUF_MAX - bk_http_ptr->wr_last_len); os_memcpy(bk_http_ptr->wr_buf + bk_http_ptr->wr_last_len, tmp + i, w_l); i += w_l; bk_http_ptr->wr_last_len += w_l; if (bk_http_ptr->wr_last_len >= HTTP_FLASH_WR_BUF_MAX) { //os_printf("."); #if CONFIG_OTA_TFTP//support bk ota format store_block(ota_wr_block, bk_http_ptr->wr_buf, HTTP_FLASH_WR_BUF_MAX); ota_wr_block++; #else //direct wrtie to flash http_flash_wr(bk_http_ptr->wr_buf, HTTP_FLASH_WR_BUF_MAX); #endif bk_http_ptr->wr_last_len = 0; } } } #endif #if CONFIG_UVC_OTA_DEMO static http_data_process_callback_func http_data_process_cb = NULL; void http_data_process_register_callback(http_data_process_callback_func cb) { http_data_process_cb = cb; } #endif int http_data_process(char *buf, UINT32 len, UINT32 recived, UINT32 total) { #if CONFIG_UVC_OTA_DEMO if(http_data_process_cb) http_data_process_cb(buf, len, recived, total); else #endif #if HTTP_WR_TO_FLASH http_wr_to_flash(buf, len); os_printf("cyg_recvlen_per:(%.2f)%%\r\n",(((float)(recived))/(total))*100); #else #if (CONFIG_SECURITY_OTA) if (security_ota_parse_data(buf, len) !=0){ return BK_FAIL; } #else os_printf("d"); #endif #endif return BK_OK; } int httpclient_retrieve_content(httpclient_t *client, char *data, int len, uint32_t timeout_ms, httpclient_data_t *client_data) { int count = 0; int templen = 0; int crlf_pos; iotx_time_t timer; char *b_data = NULL; int ret = 0; iotx_time_init(&timer); utils_time_countdown_ms(&timer, timeout_ms); /* Receive data */ log_debug("Current data: %s", data); client_data->is_more = true; if (client_data->response_content_len == -1 && client_data->is_chunked == false) { while (true) { int max_len; //if (count + len < client_data->response_buf_len - 1) { /* modify by acl, only used for ota */ // os_memcpy(client_data->response_buf + count, data, len); count += len; // client_data->response_buf[count] = '\0'; //} else { // os_memcpy(client_data->response_buf + count, data, client_data->response_buf_len - 1 - count); // client_data->response_buf[client_data->response_buf_len - 1] = '\0'; // return HTTP_RETRIEVE_MORE_DATA; //} max_len = HTTPCLIENT_MIN(HTTPCLIENT_CHUNK_SIZE - 1, client_data->response_buf_len - 1 - count); ret = httpclient_recv(client, data, 1, max_len, &len, iotx_time_left(&timer)); /* Receive data */ log_debug("data len: %d %d", len, count); if (ret == ERROR_HTTP_CONN) { log_debug("ret == ERROR_HTTP_CONN"); return ret; } if (len == 0) { /* read no more data */ log_debug("no more len == 0"); client_data->is_more = false; return SUCCESS_RETURN; } } } while (true) { uint32_t readLen = 0; if (client_data->is_chunked && client_data->retrieve_len <= 0) { /* Read chunk header */ bool foundCrlf; int n; do { foundCrlf = false; crlf_pos = 0; data[len] = 0; if (len >= 2) { for (; crlf_pos < len - 2; crlf_pos++) { if (data[crlf_pos] == '\r' && data[crlf_pos + 1] == '\n') { foundCrlf = true; break; } } } if (!foundCrlf) { /* Try to read more */ if (len < HTTPCLIENT_CHUNK_SIZE) { int new_trf_len; ret = httpclient_recv(client, data + len, 0, HTTPCLIENT_CHUNK_SIZE - len - 1, &new_trf_len, iotx_time_left(&timer)); len += new_trf_len; if (ret == ERROR_HTTP_CONN) return ret; else continue; } else return ERROR_HTTP; } } while (!foundCrlf); data[crlf_pos] = '\0'; n = sscanf(data, "%x", &readLen);/* chunk length */ client_data->retrieve_len = readLen; client_data->response_content_len += client_data->retrieve_len; if (n != 1) { log_err("Could not read chunk length"); return ERROR_HTTP_UNRESOLVED_DNS; } os_memmove(data, &data[crlf_pos + 2], len - (crlf_pos + 2)); /* Not need to move NULL-terminating char any more */ len -= (crlf_pos + 2); if (readLen == 0) { /* Last chunk */ client_data->is_more = false; log_debug("no more (last chunk)"); break; } } else readLen = client_data->retrieve_len; log_debug("Total-Payload: %d Bytes; Read: %d Bytes", readLen, len); #if HTTP_WR_TO_FLASH http_flash_init(); http_wr_to_flash(data, len); #endif #if (CONFIG_SECURITY_OTA) security_ota_init(); if(security_ota_parse_data(data, len) != 0){ return FAIL_RETURN; } #endif b_data = os_malloc((TCP_LEN_MAX + 1) * sizeof(char)); bk_http_ptr->do_data = 1; bk_http_ptr->http_total = readLen - len; do { templen = HTTPCLIENT_MIN(len, readLen); //if (count + templen < client_data->response_buf_len - 1) { /* modify by acl, only used for ota */ count += templen; // client_data->response_buf[count] = '\0'; client_data->retrieve_len -= templen; //} else { // client_data->response_buf[client_data->response_buf_len - 1] = '\0'; // client_data->retrieve_len -= (client_data->response_buf_len - 1 - count); // ret = HTTP_RETRIEVE_MORE_DATA; // break; //} if (len > readLen) { log_debug("memmove %d %d %d\n", readLen, len, client_data->retrieve_len); os_memmove(b_data, &b_data[readLen], len - readLen); /* chunk case, read between two chunks */ len -= readLen; readLen = 0; client_data->retrieve_len = 0; } else readLen -= len; if (readLen) { int max_len = HTTPCLIENT_MIN(TCP_LEN_MAX, client_data->response_buf_len - 1 - count); max_len = HTTPCLIENT_MIN(max_len, readLen); ret = httpclient_recv(client, b_data, 1, max_len, &len, iotx_time_left(&timer)); if (ret == ERROR_HTTP_CONN) { os_printf("%s (line:%d) ERROR_HTTP_CONN len:%d readLen:%d\r\n", __func__, __LINE__, len, readLen); break; } } if (readLen == len) { readLen = 0; } } while (readLen); bk_http_ptr->do_data = 0; os_free(b_data); b_data = NULL; if(ret != 0) { return ret; } if (client_data->is_chunked) { if (len < 2) { int new_trf_len; /* Read missing chars to find end of chunk */ ret = httpclient_recv(client, data + len, 2 - len, HTTPCLIENT_CHUNK_SIZE - len - 1, &new_trf_len, iotx_time_left(&timer)); if (ret == ERROR_HTTP_CONN) return ret; len += new_trf_len; } if ((data[0] != '\r') || (data[1] != '\n')) { log_err("Format error, %s", data); /* after memmove, the beginning of next chunk */ return ERROR_HTTP_UNRESOLVED_DNS; } os_memmove(data, &data[2], len - 2); /* remove the \r\n */ len -= 2; } else { log_debug("no more (content-length)"); #if HTTP_WR_TO_FLASH #if CONFIG_OTA_TFTP//support bk ota format store_block(ota_wr_block, bk_http_ptr->wr_buf, bk_http_ptr->wr_last_len); #else //direct wrtie to flash http_flash_wr(bk_http_ptr->wr_buf, bk_http_ptr->wr_last_len); #endif http_flash_deinit(); #endif #if (CONFIG_SECURITY_OTA) if (security_ota_deinit() != 0) { return FAIL_RETURN; } #endif client_data->is_more = false; break; } } return SUCCESS_RETURN; } int httpclient_response_parse(httpclient_t *client, char *data, int len, uint32_t timeout_ms, httpclient_data_t *client_data) { int crlf_pos; iotx_time_t timer; iotx_time_init(&timer); utils_time_countdown_ms(&timer, timeout_ms); client_data->response_content_len = -1; char *crlf_ptr = os_strstr(data, "\r\n"); if (crlf_ptr == NULL) { log_err("\r\n not found"); return ERROR_HTTP_UNRESOLVED_DNS; } crlf_pos = crlf_ptr - data; data[crlf_pos] = '\0'; /* Parse HTTP response */ if (sscanf(data, "HTTP/%*d.%*d %d %*[^\r\n]", &(client->response_code)) != 1) { /* Cannot match string, error */ log_err("Not a correct HTTP answer : %s\n", data); return ERROR_HTTP_UNRESOLVED_DNS; } if ((client->response_code < 200) || (client->response_code >= 400)) { /* Did not return a 2xx code; TODO fetch headers/(&data?) anyway and implement a mean of writing/reading headers */ log_warning("Response code %d", client->response_code); } log_debug("Reading headers%s", data); os_memmove(data, &data[crlf_pos + 2], len - (crlf_pos + 2) + 1); /* Be sure to move NULL-terminating char as well */ len -= (crlf_pos + 2); client_data->is_chunked = false; /* Now get headers */ while (true) { char key[32]; char value[32]; int n; key[31] = '\0'; value[31] = '\0'; crlf_ptr = os_strstr(data, "\r\n"); if (crlf_ptr == NULL) { if (len < HTTPCLIENT_CHUNK_SIZE - 1) { int new_trf_len, ret; ret = httpclient_recv(client, data + len, 1, HTTPCLIENT_CHUNK_SIZE - len - 1, &new_trf_len, iotx_time_left(&timer)); len += new_trf_len; data[len] = '\0'; log_debug("Read %d chars; In buf: [%s]", new_trf_len, data); if (ret == ERROR_HTTP_CONN) return ret; else continue; } else { log_debug("header len > chunksize"); return ERROR_HTTP; } } crlf_pos = crlf_ptr - data; if (crlf_pos == 0) { /* End of headers */ os_memmove(data, &data[2], len - 2 + 1); /* Be sure to move NULL-terminating char as well */ len -= 2; break; } data[crlf_pos] = '\0'; n = sscanf(data, "%31[^:]: %31[^\r\n]", key, value); if (n == 2) { log_debug("Read header : %s: %s", key, value); if (!os_strcmp(key, "Content-Length")) { sscanf(value, "%d", &(client_data->response_content_len)); client_data->retrieve_len = client_data->response_content_len; } else if (!os_strcmp(key, "Transfer-Encoding")) { if (!os_strcmp(value, "Chunked") || !os_strcmp(value, "chunked")) { client_data->is_chunked = true; client_data->response_content_len = 0; client_data->retrieve_len = 0; } } os_memmove(data, &data[crlf_pos + 2], len - (crlf_pos + 2) + 1); /* Be sure to move NULL-terminating char as well */ len -= (crlf_pos + 2); } else { log_err("Could not parse header"); return ERROR_HTTP; } } if (client->response_code != 200) { os_printf("Could not found\r\n"); return MQTT_SUB_INFO_NOT_FOUND_ERROR; } return httpclient_retrieve_content(client, data, len, iotx_time_left(&timer), client_data); } iotx_err_t httpclient_connect(httpclient_t *client) { client->net.handle = 0; return httpclient_conn(client); } iotx_err_t httpclient_send_request(httpclient_t *client, const char *url, int method, httpclient_data_t *client_data) { int ret = ERROR_HTTP_CONN; if (0 == client->net.handle) { log_debug("not connection have been established"); return ret; } ret = httpclient_send_header(client, url, method, client_data); if (ret != 0) { log_err("httpclient_send_header is error,ret = %d", ret); return ret; } if (method == HTTPCLIENT_POST || method == HTTPCLIENT_PUT) ret = httpclient_send_userdata(client, client_data); return ret; } iotx_err_t httpclient_recv_response(httpclient_t *client, uint32_t timeout_ms, httpclient_data_t *client_data) { int reclen = 0, ret = ERROR_HTTP_CONN; char buf[HTTPCLIENT_CHUNK_SIZE] = { 0 }; iotx_time_t timer; iotx_time_init(&timer); utils_time_countdown_ms(&timer, timeout_ms); if (0 == client->net.handle) { log_debug("not connection have been established"); return ret; } if (client_data->is_more) { client_data->response_buf[0] = '\0'; ret = httpclient_retrieve_content(client, buf, reclen, iotx_time_left(&timer), client_data); } else { client_data->is_more = 1; ret = httpclient_recv(client, buf, 1, HTTPCLIENT_CHUNK_SIZE - 1, &reclen, iotx_time_left(&timer)); if (ret != 0) return ret; buf[reclen] = '\0'; if (reclen) { log_multi_line(LOG_DEBUG_LEVEL, "RESPONSE", "%s", buf, "<"); ret = httpclient_response_parse(client, buf, reclen, iotx_time_left(&timer), client_data); } } return ret; } void httpclient_close(httpclient_t *client) { if (client->net.handle > 0) client->net.disconnect(&client->net); client->net.handle = 0; } int httpclient_common(httpclient_t *client, const char *url, int port, const char *ca_crt, int method, uint32_t timeout_ms, httpclient_data_t *client_data) { iotx_time_t timer; int ret = 0; int url_port = 0; char *host = NULL; char *path = NULL; char scheme[8] = { 0 }; do { if (NULL == (host = (char *)os_zalloc(HTTPCLIENT_MAX_HOST_LEN))) { log_err("not enough memory"); ret = FAIL_RETURN; break; } if (NULL == (path = (char *)os_zalloc(HTTPCLIENT_MAX_URL_LEN))) { log_err("not enough memory"); ret = FAIL_RETURN; break; } if (0 == client->net.handle) { //Establish connection if no. // httpclient_parse_host(url, host, sizeof(host)); /* First we need to parse the url (http[s]://host[:port][/[path]]) */ ret = httpclient_parse_url(url, scheme, sizeof(scheme), host, HTTPCLIENT_MAX_HOST_LEN, &url_port, path, HTTPCLIENT_MAX_URL_LEN); if (ret != SUCCESS_RETURN) { log_err("httpclient_parse_url returned %d", ret); break; } log_info("host: '%s', port: %d, url_port: %d.", host, port, url_port); if(url_port == 0) { url_port = port; } iotx_net_init(&client->net, host, url_port, ca_crt); ret = httpclient_connect(client); if (0 != ret) { log_err("httpclient_connect is error,ret = %d", ret); httpclient_close(client); break; } ret = httpclient_send_request(client, url, method, client_data); if (0 != ret) { log_err("httpclient_send_request is error,ret = %d", ret); httpclient_close(client); break; } } iotx_time_init(&timer); utils_time_countdown_ms(&timer, timeout_ms); #ifdef CONFIG_HTTP_OTA_WITH_BLE #if CONFIG_BLUETOOTH bk_ble_register_sleep_state_callback(ble_sleep_cb); #endif #endif if ((NULL != client_data->response_buf) || (0 != client_data->response_buf_len)) { ret = httpclient_recv_response(client, iotx_time_left(&timer), client_data); if (ret < 0) { log_err("httpclient_recv_response is error,ret = %d", ret); #if HTTP_WR_TO_FLASH http_flash_deinit(); httpclient_close(client); #endif break; } } if (! client_data->is_more) { //Close the HTTP if no more data. log_info("close http channel"); httpclient_close(client); } ret = (ret >= 0)? 0 : -1; } while(0); if (NULL != host) { os_free(host); host = NULL; } if (NULL != path) { os_free(path); path = NULL; } return ret; } int utils_get_response_code(httpclient_t *client) { return client->response_code; } iotx_err_t iotx_post( httpclient_t *client, const char *url, int port, const char *ca_crt, uint32_t timeout_ms, httpclient_data_t *client_data) { return httpclient_common(client, url, port, ca_crt, HTTPCLIENT_POST, timeout_ms, client_data); } #endif