123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 |
- /*
- * Copyright (C) 2019-2020 Alibaba Group Holding Limited
- */
- #include <yoc/fota.h>
- #if CONFIG_FOTA_USE_HTTPC == 1
- #include <yoc/netio.h>
- #include <ulog/ulog.h>
- #include <aos/kernel.h>
- #include <errno.h>
- #include <http_client.h>
- #include "util/network.h"
- #define TAG "fota-httpc"
- typedef struct {
- http_client_handle_t http_client;
- const char *cert;
- const char *path;
- } httpc_priv_t;
- static int _http_event_handler(http_client_event_t *evt)
- {
- switch(evt->event_id) {
- case HTTP_EVENT_ERROR:
- LOGD(TAG, "HTTP_EVENT_ERROR");
- break;
- case HTTP_EVENT_ON_CONNECTED:
- LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
- break;
- case HTTP_EVENT_HEADER_SENT:
- LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
- break;
- case HTTP_EVENT_ON_HEADER:
- // LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
- break;
- case HTTP_EVENT_ON_DATA:
- // LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
- break;
- case HTTP_EVENT_ON_FINISH:
- LOGD(TAG, "HTTP_EVENT_ON_FINISH");
- break;
- case HTTP_EVENT_DISCONNECTED:
- LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
- break;
- }
- return 0;
- }
- static bool process_again(int status_code)
- {
- switch (status_code) {
- case HttpStatus_MovedPermanently:
- case HttpStatus_Found:
- case HttpStatus_TemporaryRedirect:
- case HttpStatus_Unauthorized:
- return true;
- default:
- return false;
- }
- return false;
- }
- static http_errors_t _http_handle_response_code(http_client_handle_t http_client, int status_code, char *buffer, int buf_size, int data_size)
- {
- http_errors_t err;
- if (status_code == HttpStatus_MovedPermanently || status_code == HttpStatus_Found || status_code == HttpStatus_TemporaryRedirect) {
- err = http_client_set_redirection(http_client);
- if (err != HTTP_CLI_OK) {
- LOGE(TAG, "URL redirection Failed");
- return err;
- }
- } else if (status_code == HttpStatus_Unauthorized) {
- return HTTP_CLI_FAIL;
- } else if(status_code == HttpStatus_NotFound || status_code == HttpStatus_Forbidden) {
- LOGE(TAG, "File not found(%d)", status_code);
- return HTTP_CLI_FAIL;
- } else if (status_code == HttpStatus_InternalError) {
- LOGE(TAG, "Server error occurred(%d)", status_code);
- return HTTP_CLI_FAIL;
- }
- // process_again() returns true only in case of redirection.
- if (data_size > 0 && process_again(status_code)) {
- /*
- * In case of redirection, http_client_read() is called
- * to clear the response buffer of http_client.
- */
- int data_read;
- while (data_size > buf_size) {
- data_read = http_client_read(http_client, buffer, buf_size);
- if (data_read <= 0) {
- return HTTP_CLI_OK;
- }
- data_size -= buf_size;
- }
- data_read = http_client_read(http_client, buffer, data_size);
- if (data_read <= 0) {
- return HTTP_CLI_OK;
- }
- }
- return HTTP_CLI_OK;
- }
- static http_errors_t _http_connect(http_client_handle_t http_client, char *buffer, int buf_size)
- {
- #define MAX_REDIRECTION_COUNT 10
- http_errors_t err = HTTP_CLI_FAIL;
- int status_code = 0, header_ret;
- int redirect_counter = 0;
- do {
- if (redirect_counter++ > MAX_REDIRECTION_COUNT) {
- LOGE(TAG, "redirect_counter is max");
- return HTTP_CLI_FAIL;
- }
- if (process_again(status_code)) {
- LOGD(TAG, "process again,status code:%d", status_code);
- }
- err = http_client_open(http_client, 0);
- if (err != HTTP_CLI_OK) {
- LOGE(TAG, "Failed to open HTTP connection");
- return err;
- }
- header_ret = http_client_fetch_headers(http_client);
- if (header_ret < 0) {
- LOGE(TAG, "header_ret:%d", header_ret);
- return header_ret;
- }
- LOGD(TAG, "header_ret:%d", header_ret);
- status_code = http_client_get_status_code(http_client);
- LOGD(TAG, "status code:%d", status_code);
- err = _http_handle_response_code(http_client, status_code, buffer, buf_size, header_ret);
- if (err != HTTP_CLI_OK) {
- LOGE(TAG, "e handle resp code:%d", err);
- return err;
- }
- } while (process_again(status_code));
- return err;
- }
- static void _http_cleanup(http_client_handle_t client)
- {
- LOGD(TAG, "httpc cleanup...");
- if (client) {
- http_client_cleanup(client);
- }
- }
- static int http_read(netio_t *io, uint8_t *buffer, int length, int timeoutms)
- {
- #define RANGE_BUF_SIZE 56
- int read_len;
- long long time1ms;
- httpc_priv_t *priv = (httpc_priv_t *)io->private;
- http_client_handle_t client = priv->http_client;
- if (client == NULL) {
- int ret = 0;
- int statuscode;
- char *range = NULL;
- char *buffer = NULL;
- http_errors_t err;
- http_client_config_t config = {0};
- config.method = HTTP_METHOD_GET;
- config.url = priv->path;
- config.timeout_ms = timeoutms;
- config.buffer_size = BUFFER_SIZE;
- config.cert_pem = priv->cert;
- config.event_handler = _http_event_handler;
- client = http_client_init(&config);
- if (!client) {
- LOGE(TAG, "Client init e");
- ret = -1;
- goto exit;
- }
- LOGD(TAG, "http client init ok.[%s]", config.url);
- LOGD(TAG, "http read connecting........");
- buffer = aos_zalloc(BUFFER_SIZE + 1);
- if (!buffer) {
- LOGE(TAG, "http open nomem.");
- ret = -ENOMEM;
- goto exit;
- }
- range = (void *)aos_zalloc(RANGE_BUF_SIZE);
- if (!range) {
- LOGE(TAG, "range malloc e");
- ret = -ENOMEM;
- goto exit;
- }
- snprintf(range, RANGE_BUF_SIZE, "bytes=%d-", io->offset);
- LOGD(TAG, "range:%s", range);
- err = http_client_set_header(client, "Range", range);
- if (err != HTTP_CLI_OK) {
- LOGE(TAG, "set header Range e");
- ret = -1;
- goto exit;
- }
- err = http_client_set_header(client, "Connection", "keep-alive");
- if (err != HTTP_CLI_OK) {
- LOGE(TAG, "set header Connection e");
- ret = -1;
- goto exit;
- }
- err = http_client_set_header(client, "Cache-Control", "no-cache");
- if (err != HTTP_CLI_OK) {
- LOGE(TAG, "set header Cache-Control e");
- ret = -1;
- goto exit;
- }
- err = _http_connect(client, buffer, BUFFER_SIZE);
- if (err != HTTP_CLI_OK) {
- LOGE(TAG, "Client connect e");
- ret = -1;
- goto exit;
- }
- statuscode = http_client_get_status_code(client);
- if (statuscode != 206) {
- LOGE(TAG, "not 206 Partial Content");
- ret = -1;
- goto exit;
- }
- io->size = http_client_get_content_length(client);
- io->size += io->offset;
- LOGD(TAG, "range_len: %d", io->size);
- priv->http_client = client;
- exit:
- if (buffer) aos_free(buffer);
- if (range) aos_free(range);
- if (ret != 0) {
- _http_cleanup(client);
- return ret;
- }
- }
- if (io->offset >= io->size) {
- LOGW(TAG, "http_read done: offset:%d tsize:%d", io->offset, io->size);
- return 0;
- }
- time1ms = aos_now_ms();
- read_len = 0;
- while (read_len < length) {
- if (aos_now_ms() - time1ms > timeoutms) {
- break;
- }
- int data_read = http_client_read(client, (char *)buffer + read_len, length - read_len);
- if (data_read < 0) {
- LOGW(TAG, "http client read error:%d, errno:%d", data_read, errno);
- return data_read;
- } else if (data_read == 0) {
- LOGD(TAG, "http client read 0 size");
- break;
- }
- read_len += data_read;
- }
- io->offset += read_len;
- return read_len;
- }
- static int http_open(netio_t *io, const char *path)
- {
- const char *cert;
- LOGD(TAG, "http open:%s", path);
- cert = (const char *)io->cls->private;
- io->size = 0;
- io->offset = 0;
- io->block_size = CONFIG_FOTA_BUFFER_SIZE;
- httpc_priv_t *priv = aos_zalloc_check(sizeof(httpc_priv_t));
- if (!priv) {
- LOGE(TAG, "http open nomem e");
- return -1;
- }
- priv->http_client = NULL;
- priv->path = path;
- priv->cert = cert;
- io->private = priv;
- #if 0 // just for test, mem leak...
- char *tempbuffer = (char *)aos_malloc(400);
- if (tempbuffer == NULL) {
- LOGE(TAG, "malloc tempbuffer failed");
- return -1;
- }
- memset(tempbuffer, 0, 400);
- if (strstr(priv->path, "https://")) {
- snprintf(tempbuffer, 400, "http://%s", priv->path + strlen("https://"));
- }
- priv->path = tempbuffer;
- #endif
- #if 0 //just for test redirection
- priv->path = "http://192.168.1.102:8889";
- #endif
- return 0;
- }
- static int http_seek(netio_t *io, size_t offset, int whence)
- {
- io->offset = offset;
- return 0;
- }
- static int http_close(netio_t *io)
- {
- httpc_priv_t *priv = (httpc_priv_t *)io->private;
- http_client_handle_t client = priv->http_client;
- _http_cleanup(client);
- aos_free(priv);
- return 0;
- }
- netio_cls_t httpc_cls = {
- .name = "http",
- .read = http_read,
- .seek = http_seek,
- .open = http_open,
- .close = http_close,
- };
- int netio_register_httpc(const char *cert)
- {
- httpc_cls.private = (void *)cert;
- return netio_register(&httpc_cls);
- }
- #endif
|