#include #include #include "wm_socket_fwup.h" #include "wm_fwup.h" #include "wm_http_fwup.h" #include "wm_debug.h" #include "wm_mem.h" #if TLS_CONFIG_HTTP_CLIENT #define HTTP_CLIENT_BUFFER_SIZE 2048 #define USE_BREAK_POINT_RESUME 1 #define RECV_TIMEOUT 5 enum ota_state {PREPARE_PACKET, SETUP_LINK_AND_REQ, RECV_RSP, HANDLE_HEADER, HANDLE_BODY, SHUTDOWN_LINK, QUIT_OTA}; /* ninlight: SHUTDOWN_LINK resumes an unfinished transfer via PREPARE_PACKET forever - a server * that closes the connection early (or never sends the full body) used to hang this task, and * with it the caller of t_http_fwup(), permanently (see sdk/PATCHES.md point 1). Cap the number * of resume rounds generously so a slow-but-healthy download still completes. */ #define NL_OTA_MAX_RETRY_ROUNDS 200 int http_fwup(HTTPParameters ClientParams) { char* Buffer = NULL; char token[32]; char headRange[32] = {0}; int nRetCode = 0; u32 content_length=0, size=32; u32 partLen; u32 totalLen = 0; u32 recvLen = 0; u32 retryRounds = 0; T_BOOTER *booter; #if USE_BREAK_POINT_RESUME == 0 u32 breakFlag = 0; u32 breakLen = 0; #endif struct pbuf *p; HTTP_SESSION_HANDLE pHTTP; enum ota_state now_state = PREPARE_PACKET; Buffer = (char*)tls_mem_alloc(HTTP_CLIENT_BUFFER_SIZE); if (!Buffer) return HTTP_CLIENT_ERROR_NO_MEMORY; memset(Buffer, 0, HTTP_CLIENT_BUFFER_SIZE); while(1) { switch(now_state) { case PREPARE_PACKET: { memset(token, 0, 32); size = 32; pHTTP = HTTPClientOpenRequest(0); #if USE_BREAK_POINT_RESUME HTTPClientSetVerb(pHTTP,VerbGet); sprintf(headRange, "bytes=%d-", recvLen); if((nRetCode = HTTPClientAddRequestHeaders(pHTTP,"Range", headRange, 1))!= HTTP_CLIENT_SUCCESS){ now_state = QUIT_OTA; } #else if( recvLen != 0 ){ breakFlag = 1; breakLen = 0; } #endif now_state = SETUP_LINK_AND_REQ; } break; case SETUP_LINK_AND_REQ: { if( (nRetCode = HTTPClientSendRequest(pHTTP,ClientParams.Uri,NULL,0,FALSE,0,0)) != HTTP_CLIENT_SUCCESS ){ tls_os_time_delay(HZ*2); now_state = SHUTDOWN_LINK; } else now_state = RECV_RSP; } break; case RECV_RSP: { if((nRetCode = HTTPClientRecvResponse(pHTTP, RECV_TIMEOUT)) != HTTP_CLIENT_SUCCESS) now_state = SHUTDOWN_LINK; else now_state = HANDLE_HEADER; } break; case HANDLE_HEADER: { if((nRetCode = HTTPClientFindFirstHeader(pHTTP, "content-length", (CHAR *)token, &size)) != HTTP_CLIENT_SUCCESS){ HTTPClientFindCloseHeader(pHTTP); now_state = SHUTDOWN_LINK; } else { HTTPClientFindCloseHeader(pHTTP); content_length = atol(strstr(token,":")+1); printf("content_length: %d\n", content_length); now_state = HANDLE_BODY; if(recvLen == 0){ nRetCode = socket_fwup_accept(0, ERR_OK); if(nRetCode != ERR_OK) now_state = QUIT_OTA; } } } break; case HANDLE_BODY: { partLen = 0; while(nRetCode != HTTP_CLIENT_EOS ) { u32 nSize = HTTP_CLIENT_BUFFER_SIZE; nRetCode = HTTPClientReadData(pHTTP,Buffer+3,nSize,RECV_TIMEOUT,&nSize); if( recvLen == 0 ){ //fileSize = headerSize(fixed: 56) + appCodeSize booter =(T_BOOTER *) (Buffer+3); if (TRUE == tls_fwup_img_header_check(booter)) { totalLen = booter->upd_img_len + sizeof(T_BOOTER); //OTA_ResetProgress(); //OTA_SetTotalBytes(totalLen); } else { /* ninlight: nRetCode is still HTTP_CLIENT_SUCCESS here (the read itself * worked, only the image header is bad), so the "if(nRetCode)" cleanup * at the end of this function used to be skipped entirely - leaving * socket_fwup_err()/tls_fwup_exit() uncalled and the fwup session stuck * busy for every later attempt. Force the cleanup path (see * sdk/PATCHES.md point 1). */ nRetCode = HTTP_CLIENT_ERROR_BAD_HEADER; now_state = QUIT_OTA; break; } } if(nRetCode != HTTP_CLIENT_SUCCESS && nRetCode != HTTP_CLIENT_EOS){ now_state = SHUTDOWN_LINK; break; } if (nSize == 0) { /* ninlight: a zero-length read is not a fwup rejection - it's what * HTTPClient.c's HTTPIntrnRecv()/nSocketEvents<0 paths report for a server * that closed mid-body or a transient socket hiccup (WLAN dropout), while * still returning HTTP_CLIENT_SUCCESS/HTTP_CLIENT_EOS. Passing an empty * packet into socket_fwup_recv() would turn it into the exact same ERR_ABRT * as a genuine checksum/trailer rejection (wm_socket_fwup.c: datalen==0 goes * to the same "goto err" as a failed tls_fwup_request_sync()) - the QUIT_OTA- * on-ERR_ABRT fix below must not see this case, or a routine WLAN dropout * would abort the update instead of resuming via SHUTDOWN_LINK/PREPARE_PACKET * like before. See sdk/PATCHES.md point 1 (Fix-Runde 3). */ now_state = SHUTDOWN_LINK; break; } #if USE_BREAK_POINT_RESUME == 0 if(breakFlag == 1){ breakLen += nSize; if(breakLen <= recvLen){ continue; } else{ Buffer = Buffer+3+nSize-(breakLen-recvLen)-3; nSize = (breakLen-recvLen); breakFlag = 0; } } #endif p = pbuf_alloc(PBUF_TRANSPORT, nSize + 3, PBUF_REF); while( !p){ tls_os_time_delay(1); p = pbuf_alloc(PBUF_TRANSPORT, nSize + 3, PBUF_REF); } if(recvLen == 0) *(Buffer+0) = SOCKET_FWUP_START; else if(nRetCode == HTTP_CLIENT_EOS && recvLen == (totalLen-nSize)) *(Buffer+0) = SOCKET_FWUP_END; else *(Buffer+0) = SOCKET_FWUP_DATA; *(Buffer+1) = (nSize>>8) & 0xFF; *(Buffer+2) = nSize & 0xFF; p->payload = Buffer; p->len = p->tot_len = nSize + 3; // Send received data to fwup thread and deal with socket issues. s8 ret = socket_fwup_recv(0, p, ERR_OK); if(ret != ERR_OK){ /* ninlight: nSize > 0 is guaranteed here (see the nSize==0 check above), so * this ERR_ABRT is NOT the "empty packet" case - it means * wm_socket_fwup.c's socket_fwup_recv() actually rejected non-empty data: * either tls_fwup_enter() refused the session (e.g. still busy from a * previous attempt) on the first chunk, or tls_fwup_request_sync() reported a * checksum/trailer/hash mismatch on a later one (see wm_fwup.c). Either way * recvLen never advances past this point, so going through SHUTDOWN_LINK used * to start a full Range-resume retry (up to NL_OTA_MAX_RETRY_ROUNDS rounds) * whose data the fwup task would just keep discarding. Bail out to QUIT_OTA * directly so the caller gets the real error immediately instead of after * minutes. Force nRetCode nonzero so the socket_fwup_err() cleanup below * still runs. See sdk/PATCHES.md point 1. */ nRetCode = HTTP_CLIENT_ERROR_BAD_STATE; now_state = QUIT_OTA; break; } else{ //OTA_IncrementProgress(nSize); recvLen += nSize; partLen += nSize; //printf("download %d / %d\n", recvLen, totalLen); //Status is being printed in our source if(partLen == content_length){ now_state = SHUTDOWN_LINK; break; } } } if (now_state == QUIT_OTA); else now_state = SHUTDOWN_LINK; } break; case SHUTDOWN_LINK: { if(pHTTP){ HTTPClientCloseRequest(&pHTTP); if (recvLen == totalLen) { now_state = QUIT_OTA; } else if (++retryRounds > NL_OTA_MAX_RETRY_ROUNDS) { /* ninlight: give up instead of resuming forever, see the comment above * NL_OTA_MAX_RETRY_ROUNDS. */ nRetCode = HTTP_CLIENT_ERROR_SOCKET_TIME_OUT; now_state = QUIT_OTA; } else { now_state = PREPARE_PACKET; } } } break; default: break; } //printf("now_state %d\n", now_state); if(now_state == QUIT_OTA) break; } tls_mem_free(Buffer); if(pHTTP) HTTPClientCloseRequest(&pHTTP); if(ClientParams.Verbose == TRUE) { printf("\n\nHTTP Client terminated %d (got %d kb)\n\n",nRetCode,(recvLen/ 1024)); } if(nRetCode) socket_fwup_err(0, nRetCode); return nRetCode; } int t_http_fwup(char *url) { HTTPParameters httpParams; memset(&httpParams, 0, sizeof(HTTPParameters)); if (url == NULL) { httpParams.Uri = "http://192.168.1.100:8080/WM_W600_SEC.img"; } else { httpParams.Uri = url; } printf("Location: %s\n",httpParams.Uri); httpParams.Verbose = TRUE; return http_fwup(httpParams); } #endif //TLS_CONFIG_HTTP_CLIENT && TLS_CONFIG_SOCKET_RAW