/* ********************************************************************************************************* * uC/HTTP * Hypertext Transfer Protocol * * Copyright 2004-2020 Silicon Laboratories Inc. www.silabs.com * * SPDX-License-Identifier: APACHE-2.0 * * This software is subject to an open source license and is distributed by * Silicon Laboratories Inc. pursuant to the terms of the Apache License, * Version 2.0 available at www.apache.org/licenses/LICENSE-2.0. * ********************************************************************************************************* */ /* ********************************************************************************************************* * * HTTP CLIENT REQUEST MODULE * * Filename : http-c_req.c * Version : V3.01.00 ********************************************************************************************************* */ /* ********************************************************************************************************* ********************************************************************************************************* * INCLUDE FILES ********************************************************************************************************* ********************************************************************************************************* */ #define MICRIUM_SOURCE #define HTTPc_REQ_MODULE #include #include #include "http-c_req.h" #include "http-c_conn.h" #include "http-c_sock.h" #ifdef HTTPc_WEBSOCK_MODULE_EN #include "http-c_websock.h" #endif /* ********************************************************************************************************* ********************************************************************************************************* * LOCAL FUNCTION PROTOTYPES ********************************************************************************************************* ********************************************************************************************************* */ #if (HTTPc_CFG_FORM_EN == DEF_ENABLED) static CPU_SIZE_T HTTPcReq_PrepareFormAppLen(HTTPc_CONN *p_conn, HTTPc_ERR *p_err); static CPU_SIZE_T HTTPcReq_PrepareFormMultipartLen(HTTPc_CONN *p_conn, HTTPc_ERR *p_err); #endif static void HTTPcReq_Line(HTTPc_CONN *p_conn, HTTPc_ERR *p_err); #if (HTTPc_CFG_QUERY_STR_EN == DEF_ENABLED) static CPU_CHAR *HTTPcReq_QueryStrTbl(HTTPc_CONN *p_conn, CPU_CHAR *p_buf, CPU_INT16U buf_len_rem, HTTPc_ERR *p_err); static CPU_CHAR *HTTPcReq_QueryStrHook(HTTPc_CONN *p_conn, CPU_CHAR *p_buf, CPU_INT16U buf_len_rem, HTTPc_ERR *p_err); #endif static void HTTPcReq_Hdr(HTTPc_CONN *p_conn, HTTPc_ERR *p_err); #if (HTTPc_CFG_HDR_TX_EN == DEF_ENABLED) static CPU_CHAR *HTTPcReq_HdrExtTbl(HTTPc_CONN *p_conn, CPU_CHAR *p_buf, CPU_INT16U buf_len_rem, HTTPc_ERR *p_err); static CPU_CHAR *HTTPcReq_HdrExtHook(HTTPc_CONN *p_conn, CPU_CHAR *p_buf, CPU_INT16U buf_len_rem, HTTPc_ERR *p_err); static HTTPc_HDR *HTTPcReq_HdrSearch(HTTPc_CONN *p_conn, HTTP_HDR_FIELD hdr_field); #endif static void HTTPcReq_Body(HTTPc_CONN *p_conn, HTTPc_ERR *p_err); #if (HTTPc_CFG_FORM_EN == DEF_ENABLED) static void HTTPcReq_FormApp(HTTPc_CONN *p_conn, HTTPc_ERR *p_err); static void HTTPcReq_FormMultipart(HTTPc_CONN *p_conn, HTTPc_ERR *p_err); #endif static void HTTPcReq_BodyData(HTTPc_CONN *p_conn, HTTPc_ERR *p_err); /* ********************************************************************************************************* * HTTPcReq_Prepare() * * Description : (1) Additional preparation before request processing. Sets which HTTP mandatory headers to * include: * * (a) Host * (b) Connection * (c) Content-Type * (d) Content-Length * (e) Transfer-Encoding * * (2) In case form table is present, calculate form size for Content-Length header. * * * Argument(s) : p_conn Pointer to current HTTPc Connection. * * p_err Pointer to variable that will receive the return error code from this function : * * HTTPc_ERR_NONE Request preparation successful * HTTPc_ERR_REQ_HDR_UNAUTHORIZED Hdr in ext tbl must only be set by core. * HTTPc_ERR_REQ_BODY_INVALID Invalid body/form ptr set by app. * HTTPc_ERR_REQ_TRANSFER_ENCODING_INVALID Invalid transfer-encoding set by app. * HTTPc_ERR_REQ_CONTENT_TYPE_INVALID Invalid content-type set by app. * * Return(s) : None. * * Caller(s) : HTTPcTask_Handler(). * * Note(s) : (1) The following HTTP headers must not be passed inside the extra header table. They * will be set by the HTTP client core according to the arguments passed by the API * functions : * (a) Host * (b) Connection * (c) Content-Type * (d) Content-Length * (e) Transfer-Encoding * * (2) The Host Header is mandatory for HTTP/1.1 protocol version communication. * * (3) If a body is present in the request, one of the bellow header MUST be present : * (a) Transfer-Encoding * (b) Content-Length * * (4) Chunk Transfer-Encoding is only available in HTTP/1.1. ********************************************************************************************************* */ void HTTPcReq_Prepare(HTTPc_CONN *p_conn, HTTPc_ERR *p_err) { #if (HTTPc_CFG_HDR_TX_EN == DEF_ENABLED) HTTPc_HDR *p_hdr; #endif HTTPc_REQ *p_req; HTTP_DICT *p_entry; CPU_BOOLEAN persistent; CPU_BOOLEAN chunk_en; CPU_BOOLEAN body_present; CPU_BOOLEAN form_present; #ifdef HTTPc_TASK_MODULE_EN CPU_BOOLEAN no_block; #endif #ifdef HTTPc_WEBSOCK_MODULE_EN CPU_BOOLEAN websocket; #endif p_req = p_conn->ReqListHeadPtr; persistent = DEF_BIT_IS_SET(p_conn->Flags, HTTPc_FLAG_CONN_PERSISTENT); chunk_en = DEF_BIT_IS_SET(p_req->Flags, HTTPc_FLAG_REQ_BODY_CHUNK_TRANSFER); body_present = DEF_BIT_IS_SET(p_req->Flags, HTTPc_FLAG_REQ_BODY_PRESENT); form_present = DEF_BIT_IS_SET(p_req->Flags, HTTPc_FLAG_REQ_FORM_PRESENT); #ifdef HTTPc_WEBSOCK_MODULE_EN websocket = DEF_BIT_IS_SET(p_req->Flags, HTTPc_FLAG_REQ_UPGRADE_WEBSOCKET); #endif /* -------------- VALIDATE QUERY STRING --------------- */ #if (HTTPc_CFG_QUERY_STR_EN == DEF_ENABLED) if ((p_req->QueryStrNbr > 0) && (p_req->QueryStrTbl == DEF_NULL)) { *p_err = HTTPc_ERR_REQ_PARAM_QUERY_STR_TBL_INVALID; goto exit; } #endif /* ----------- VALIDATE ADDITIONAL HEADERS ------------ */ #if (HTTPc_CFG_HDR_TX_EN == DEF_ENABLED) if ((p_req->HdrNbr > 0) && (p_req->HdrTbl == DEF_NULL)) { *p_err = HTTPc_ERR_REQ_PARAM_HDR_INVALID_TBL_PTR; goto exit; } #endif #if (HTTPc_CFG_FORM_EN == DEF_ENABLED) if ((p_req->DataPtr != DEF_NULL) && /* Cannot have a body pointer and a form pointer at ... */ (p_req->FormFieldTbl != DEF_NULL)) { /* ... same time. */ *p_err = HTTPc_ERR_REQ_PARAM_BODY_INVALID; goto exit; } #endif /* -------------- VALIDATE REQUEST BODY --------------- */ if (body_present == DEF_YES) { p_entry = HTTP_Dict_EntryGet(HTTP_Dict_ContentType, HTTP_Dict_ContentTypeSize, p_req->ContentType); if (p_entry == DEF_NULL) { *p_err = HTTPc_ERR_REQ_PARAM_CONTENT_TYPE_INVALID; goto exit; } if (form_present == DEF_NO) { if ((chunk_en == DEF_NO) && (p_req->ContentLen == 0u)) { *p_err = HTTPc_ERR_REQ_PARAM_CONTENT_LEN_INVALID; goto exit; } } } /* -------------- VALIDATE REQUEST FORM --------------- */ if (form_present == DEF_YES) { #if (HTTPc_CFG_FORM_EN == DEF_ENABLED) switch (p_req->ContentType) { case HTTP_CONTENT_TYPE_APP_FORM: case HTTP_CONTENT_TYPE_MULTIPART_FORM: break; default: *p_err = HTTPc_ERR_REQ_PARAM_CONTENT_TYPE_INVALID; goto exit; } if ((p_req->FormFieldNbr > 0) && (p_req->FormFieldTbl == DEF_NULL)) { *p_err = HTTPc_ERR_REQ_PARAM_FORM_FIELD_TBL_INVALID; goto exit; } #else *p_err = HTTPc_ERR_FEATURE_DIS; goto exit; #endif } /* ---------------- VALIDATE CALLBACKS ---------------- */ #ifdef HTTPc_TASK_MODULE_EN no_block = DEF_BIT_IS_SET(p_req->Flags, HTTPc_FLAG_REQ_NO_BLOCK); if (no_block == DEF_YES) { if (p_req->OnTransComplete == DEF_NULL) { *p_err = HTTPc_ERR_REQ_PARAM_TRANS_COMPLETE_CALLBACK_INVALID; goto exit; } if (p_req->OnErr == DEF_NULL) { *p_err = HTTPc_ERR_REQ_PARAM_ERR_CALLBACK_INVALID; goto exit; } } #endif /* ------ VALIDATE HTTP HEADERS RELATED TO CONN ------- */ /* HOST HEADER CASE. */ /* See Note #2. */ #if (HTTPc_CFG_HDR_TX_EN == DEF_ENABLED) p_hdr = HTTPcReq_HdrSearch(p_conn, /* Search Host header in header list. */ HTTP_HDR_FIELD_HOST); if (p_hdr != DEF_NULL) { *p_err = HTTPc_ERR_REQ_PARAM_HDR_UNAUTHORIZED; /* See Note #1. */ goto exit; } #endif DEF_BIT_SET(p_req->HdrFlags, HTTPc_FLAG_REQ_HDR_HOST_ADD); /* WEBSOCKET CASE. */ #ifdef HTTPc_WEBSOCK_MODULE_EN if (websocket == DEF_YES) { p_hdr = HTTPcReq_HdrSearch(p_conn, /* Search Connection header in header list. */ HTTP_HDR_FIELD_CONN); if (p_hdr != DEF_NULL) { *p_err = HTTPc_ERR_REQ_PARAM_HDR_UNAUTHORIZED; /* See Note #1. */ goto exit; } DEF_BIT_SET(p_req->HdrFlags, HTTPc_FLAG_REQ_HDR_CONNECTION_ADD); DEF_BIT_SET(p_req->HdrFlags, HTTPc_FLAG_REQ_HDR_UPGRADE_ADD); DEF_BIT_SET(p_req->HdrFlags, HTTPc_FLAG_REQ_HDR_WEBSOCKET_ADD); } #endif /* CONNECTION HEADER CASE. */ #ifdef HTTPc_WEBSOCK_MODULE_EN if ((persistent == DEF_NO) && (websocket == DEF_NO)) { #else if (persistent == DEF_NO) { #endif #if (HTTPc_CFG_HDR_TX_EN == DEF_ENABLED) p_hdr = HTTPcReq_HdrSearch(p_conn, /* Search Connection header in header list. */ HTTP_HDR_FIELD_CONN); if (p_hdr != DEF_NULL) { *p_err = HTTPc_ERR_REQ_PARAM_HDR_UNAUTHORIZED; /* See Note #1. */ goto exit; } #endif DEF_BIT_SET(p_req->HdrFlags, HTTPc_FLAG_REQ_HDR_CONNECTION_ADD); DEF_BIT_SET(p_conn->Flags, HTTPc_FLAG_CONN_TO_CLOSE); p_conn->CloseStatus = HTTPc_CONN_CLOSE_STATUS_NO_PERSISTENT; } /* ------ VALIDATE HTTP HEADERS RELATED TO BODY ------- */ if (body_present == DEF_YES) { /* CONTENT-TYPE HEADER CASE. */ #if (HTTPc_CFG_HDR_TX_EN == DEF_ENABLED) p_hdr = HTTPcReq_HdrSearch(p_conn, /* Search Content-Type header in header list. */ HTTP_HDR_FIELD_CONTENT_TYPE); if (p_hdr != DEF_NULL) { *p_err = HTTPc_ERR_REQ_PARAM_HDR_UNAUTHORIZED; /* See Note #1. */ goto exit; } #endif DEF_BIT_SET(p_req->HdrFlags, HTTPc_FLAG_REQ_HDR_CONTENT_TYPE_ADD); /* TRANSFER-ENCODING HEADER CASE. */ #if (HTTPc_CFG_HDR_TX_EN == DEF_ENABLED) p_hdr = HTTPcReq_HdrSearch(p_conn, /* Search Transfer-Encoding header in header list. */ HTTP_HDR_FIELD_TRANSFER_ENCODING); if (p_hdr != DEF_NULL) { *p_err = HTTPc_ERR_REQ_PARAM_HDR_UNAUTHORIZED; /* See Note #1. */ goto exit; } #endif if (chunk_en == DEF_YES) { DEF_BIT_SET(p_req->HdrFlags, HTTPc_FLAG_REQ_HDR_TRANSFER_ENCODE_ADD); } /* CONTENT-LENGTH HEADER CASE. */ #if (HTTPc_CFG_HDR_TX_EN == DEF_ENABLED) p_hdr = HTTPcReq_HdrSearch(p_conn, /* Search Content-Length header in header list. */ HTTP_HDR_FIELD_CONTENT_LEN); if (p_hdr != DEF_NULL) { *p_err = HTTPc_ERR_REQ_PARAM_HDR_UNAUTHORIZED; /* See Note #1. */ goto exit; } #endif if (p_req->ContentLen > 0u) { DEF_BIT_SET(p_req->HdrFlags, HTTPc_FLAG_REQ_HDR_CONTENT_LENGTH_ADD); } #if (HTTPc_CFG_FORM_EN == DEF_ENABLED) /* - CALCULATE FORM LENGTH FOR CONTENT-LENGTH HEADER -- */ if (form_present == DEF_YES) { DEF_BIT_SET(p_req->HdrFlags, HTTPc_FLAG_REQ_HDR_CONTENT_LENGTH_ADD); switch (p_req->ContentType) { case HTTP_CONTENT_TYPE_APP_FORM: p_req->ContentLen = HTTPcReq_PrepareFormAppLen(p_conn, p_err); break; case HTTP_CONTENT_TYPE_MULTIPART_FORM: p_req->ContentLen = HTTPcReq_PrepareFormMultipartLen(p_conn, p_err); break; default: *p_err = HTTPc_ERR_REQ_PARAM_CONTENT_TYPE_INVALID; goto exit; } DEF_BIT_CLR(p_req->HdrFlags, HTTPc_FLAG_REQ_HDR_TRANSFER_ENCODE_ADD); } #endif } *p_err = HTTPc_ERR_NONE; exit: return; } /* ********************************************************************************************************* * HTTPcReq() * * Description : (1) Send data to TCP-IP stack if data is ready to be sent. * (2) State Machine for the Request processing: * (a) Prepare Request Line. * (b) Prepare Headers. * (c) Prepare Body. * * * Argument(s) : p_conn Pointer to current HTTPc Connection. * * p_err Pointer to variable that will receive the return error code from this function : * * ------------ RETURNED BY HTTPcSock_ConnDataTx() ------------ * See HTTPcSock_ConnDataTx() for additional return error codes. * * ------------ RETURNED BY HTTPcReq_Line() ------------ * See HTTPcReq_Line() for additional return error codes. * * ------------ RETURNED BY HTTPcReq_Hdr() ------------ * See HTTPcReq_Hdr() for additional return error codes. * * ------------ RETURNED BY HTTPcReq_Body() ------------ * See HTTPcReq_Body() for additional return error codes. * * Return(s) : None. * * Caller(s) : HTTPcTask_Handler(). * * Note(s) : None. ********************************************************************************************************* */ /*修改,发送处理函数 */ CPU_BOOLEAN HTTPcReq(HTTPc_CONN *p_conn, HTTPc_ERR *p_err) { CPU_BOOLEAN process; CPU_BOOLEAN done; CPU_BOOLEAN sock_rdy_wr; CPU_BOOLEAN sock_rdy_err; done = DEF_NO; process = DEF_YES; /* -------- PASS DATA TO SEND TO TCP-IP STACK --------- */ if (p_conn->TxDataLen > 0) { /* sock_rdy_wr = DEF_BIT_IS_SET(p_conn->SockFlags, HTTPc_FLAG_SOCK_RDY_WR); */ /* sock_rdy_err = DEF_BIT_IS_SET(p_conn->SockFlags, HTTPc_FLAG_SOCK_RDY_ERR); */ /* if((sock_rdy_wr == DEF_YES) || */ /* (sock_rdy_err == DEF_YES)) { */ process = HTTPcSock_ConnDataTx(p_conn, p_err); switch (*p_err) { case HTTPc_ERR_NONE: case HTTPc_ERR_CONN_SOCK_TX: break; default: goto exit; } /* } */ } /* -------- PROCESS INCOMING REQUEST FROM APP --------- */ if (process == DEF_YES) { switch (p_conn->State) { case HTTPc_CONN_STATE_REQ_LINE_METHOD: case HTTPc_CONN_STATE_REQ_LINE_URI: case HTTPc_CONN_STATE_REQ_LINE_QUERY_STR: case HTTPc_CONN_STATE_REQ_LINE_PROTO_VER: HTTPcReq_Line(p_conn, p_err); /* Prepare Request Line. */ break; case HTTPc_CONN_STATE_REQ_HDR_HOST: case HTTPc_CONN_STATE_REQ_HDR_CONTENT_TYPE: case HTTPc_CONN_STATE_REQ_HDR_CONTENT_LEN: case HTTPc_CONN_STATE_REQ_HDR_TRANSFER_ENCODE: case HTTPc_CONN_STATE_REQ_HDR_CONN: case HTTPc_CONN_STATE_REQ_HDR_EXT: case HTTPc_CONN_STATE_REQ_HDR_LAST: HTTPcReq_Hdr(p_conn, p_err); /* Prepare Request Headers. */ break; case HTTPc_CONN_STATE_REQ_BODY: case HTTPc_CONN_STATE_REQ_BODY_DATA: case HTTPc_CONN_STATE_REQ_BODY_DATA_CHUNK_SIZE: case HTTPc_CONN_STATE_REQ_BODY_DATA_CHUNK_DATA: case HTTPc_CONN_STATE_REQ_BODY_DATA_CHUNK_END: case HTTPc_CONN_STATE_REQ_BODY_FORM_APP: case HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_BOUNDARY: case HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_HDR_CONTENT_DISPO: case HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_HDR_CONTENT_TYPE: case HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_DATA: case HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_DATA_END: case HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_BOUNDARY_END: HTTPcReq_Body(p_conn, p_err); /* Prepare Request Body. */ break; case HTTPc_CONN_STATE_REQ_END: *p_err = HTTPc_ERR_NONE; if (p_conn->TxDataLen == 0u) { /* Validate Tx is completed before switch to Resp. */ done = DEF_YES; p_conn->State = HTTPc_CONN_STATE_RESP_INIT; } break; default: *p_err = HTTPc_ERR_CONN_INVALID_STATE; break; } } exit: return (done); } /* ********************************************************************************************************* * HTTPcReq_HdrCopyToBuf() * * Description : Construct and Copy HTTP header to HTTP buffer. * * Argument(s) : p_buf Pointer to HTTP buffer. * * buf_len Length remaining in HTTP buffer. * * hdr_type HTTP header type. * * p_val Pointer to HTTP header value. * * val_len HTTP header value length. * * p_err Pointer to variable that will receive the return error code from this function : * * HTTPc_ERR_NONE Header copy to buffer successfully. * HTTPc_ERR_REQ_HDR_INVALID Header field type not found in dictionary. * HTTPc_ERR_REQ_HDR_CREATE Fatal error while creating header. * HTTPc_ERR_TRANS_BUF_LEN_INVALID HTTP buffer length is not enough big. * HTTPc_ERR_TRANS_TX_BUF_FULL HTTP buffer is full, data must be Tx. * * Return(s) : Pointer to location in HTTP buffer after data written. * * Caller(s) : HTTPcReq_Hdr(). * * Note(s) : None. ********************************************************************************************************* */ CPU_CHAR *HTTPcReq_HdrCopyToBuf(CPU_CHAR *p_buf, CPU_INT16U buf_len, CPU_SIZE_T buf_len_rem, HTTP_HDR_FIELD hdr_type, const CPU_CHAR *p_val, CPU_SIZE_T val_len, CPU_BOOLEAN add_CRLF, HTTPc_ERR *p_err) { HTTP_DICT *p_entry; CPU_CHAR *p_str; CPU_SIZE_T len_need; p_str = p_buf; /* Find Header type in dictionary. */ p_entry = HTTP_Dict_EntryGet(HTTP_Dict_HdrField, HTTP_Dict_HdrFieldSize, hdr_type); if (p_entry == DEF_NULL) { p_str = DEF_NULL; *p_err = HTTPc_ERR_REQ_HDR_INVALID; goto exit; } len_need = p_entry->StrLen + val_len + 2; /* Calculate length needed for header. */ if (add_CRLF == DEF_YES) { len_need += STR_CR_LF_LEN; } if (len_need > buf_len) { /* Total len of hdr must be smaller than HTTP buf size. */ p_str = DEF_NULL; *p_err = HTTPc_ERR_TRANS_BUF_LEN_INVALID; goto exit; } if (len_need > buf_len_rem) { /* Missing space in HTTP buf. Data must be Tx. */ p_str = DEF_NULL; *p_err = HTTPc_ERR_TRANS_TX_BUF_FULL; goto exit; } /* Copy Header Type to buffer. */ p_str = Str_Copy_N(p_str, p_entry->StrPtr, p_entry->StrLen); if (p_str == DEF_NULL) { *p_err = HTTPc_ERR_REQ_HDR_CREATE; goto exit; } p_str += p_entry->StrLen; *p_str = ASCII_CHAR_COLON; /* Copy ":" character to buffer. */ p_str++; *p_str = ASCII_CHAR_SPACE; /* Copy space character to buffer. */ p_str++; p_str = Str_Copy_N(p_str, p_val, val_len); /* Copy header value to buffer. */ if (p_str == DEF_NULL) { *p_err = HTTPc_ERR_REQ_HDR_CREATE; goto exit; } p_str += val_len; if (add_CRLF == DEF_YES) { /* Copy end of line CRLN characters to buffer. */ p_str = Str_Copy_N(p_str, STR_CR_LF, STR_CR_LF_LEN); if (p_str == DEF_NULL) { *p_err = HTTPc_ERR_REQ_HDR_CREATE; goto exit; } p_str += STR_CR_LF_LEN; } *p_err = HTTPc_ERR_NONE; exit: return (p_str); } /* ********************************************************************************************************* ********************************************************************************************************* * LOCAL FUNCTIONS ********************************************************************************************************* ********************************************************************************************************* */ /* ********************************************************************************************************* * HTTPcReq_PrepareFormAppLen() * * Description : Calculate length of Application form by parsing form table passed by application. * * Argument(s) : p_conn Pointer to current HTTPc Connection. * * p_err Pointer to variable that will receive the return error code from this function : * * HTTPc_ERR_NONE Form size successfully calculate. * * Return(s) : Length of the application form. * Length 0 in case of error. * * Caller(s) : HTTPcReq_Prepare(). * * Note(s) : (1) In case form table is passed by application and Chunked Transfer is disabled, length * of form must be calculate to put inside Content-Length header. * * (2) Application type Form must be URL-Encode. ********************************************************************************************************* */ #if (HTTPc_CFG_FORM_EN == DEF_ENABLED) static CPU_SIZE_T HTTPcReq_PrepareFormAppLen(HTTPc_CONN *p_conn, HTTPc_ERR *p_err) { HTTPc_REQ *p_req; HTTPc_FORM_TBL_FIELD *p_form_tbl; HTTPc_FORM_TBL_FIELD *p_tbl_entry; HTTPc_KEY_VAL *p_kvp; CPU_SIZE_T total_len; CPU_INT16U char_encode_nbr; CPU_INT16U i; p_req = p_conn->ReqListHeadPtr; p_form_tbl = (HTTPc_FORM_TBL_FIELD *)p_req->FormFieldTbl; total_len = 0; /* ----------- PARSE APPLICATION FORM TABLE ----------- */ for (i = 0; i < p_req->FormFieldNbr; i++) { p_tbl_entry = &p_form_tbl[i]; switch (p_tbl_entry->Type) { case HTTPc_FORM_FIELD_TYPE_KEY_VAL: break; case HTTPc_FORM_FIELD_TYPE_KEY_VAL_EXT: case HTTPc_FORM_FIELD_TYPE_FILE: default: total_len = 0; *p_err = HTTPc_ERR_FORM_TYPE_INVALID; goto exit; } p_kvp = (HTTPc_KEY_VAL *)p_tbl_entry->FieldObjPtr; char_encode_nbr = HTTP_URL_CharEncodeNbr(p_kvp->KeyPtr, p_kvp->KeyLen); total_len += (p_kvp->KeyLen + char_encode_nbr * HTTP_URL_ENCODING_JUMP); char_encode_nbr = HTTP_URL_CharEncodeNbr(p_kvp->ValPtr, p_kvp->ValLen); total_len += (p_kvp->ValLen + char_encode_nbr * HTTP_URL_ENCODING_JUMP); } total_len += 2 * p_req->FormFieldNbr - 1; /* Add length of all "=" and "&" char to include ... */ /* ... in the form. */ *p_err = HTTPc_ERR_NONE; exit: return (total_len); } #endif /* ********************************************************************************************************* * HTTPcReq_PrepareFormMultipartLen() * * Description : Calculate length of Multipart form by parsing form table passed by application. * * Argument(s) : p_conn Pointer to current HTTPc Connection. * * p_err Pointer to variable that will receive the return error code from this function : * * HTTPc_ERR_NONE Form size successfully calculate. * * Return(s) : Length of the application form. * Length 0 in case of error. * * Caller(s) : HTTPcReq_Prepare(). * * Note(s) : (1) In case form table is passed by application and Chunked Transfer is disabled, length * of form must be calculate to put inside Content-Length header. ********************************************************************************************************* */ #if (HTTPc_CFG_FORM_EN == DEF_ENABLED) static CPU_SIZE_T HTTPcReq_PrepareFormMultipartLen(HTTPc_CONN *p_conn, HTTPc_ERR *p_err) { HTTPc_REQ *p_req; HTTPc_FORM_TBL_FIELD *p_form_tbl; HTTPc_FORM_TBL_FIELD *p_tbl_entry; HTTPc_KEY_VAL *p_kvp; HTTPc_KEY_VAL_EXT *p_kvp_big; HTTPc_MULTIPART_FILE *p_multipart_file; HTTP_DICT *p_entry; CPU_SIZE_T total_len; CPU_INT16U char_encode_nbr; CPU_INT16U i; p_req = p_conn->ReqListHeadPtr; p_form_tbl = p_req->FormFieldTbl; total_len = 0; for (i = 0; i < p_req->FormFieldNbr; i++) { p_tbl_entry = &p_form_tbl[i]; /* ----------- ADD LENGTH OF START BOUNDARY ----------- */ total_len += HTTPc_STR_BOUNDARY_START_LEN + STR_CR_LF_LEN; /* ------ ADD LENGTH OF CONTENT-DISPOSITION HDR ------- */ total_len += HTTP_STR_HDR_FIELD_CONTENT_DISPOSITION_LEN + 2 + HTTP_STR_CONTENT_DISPOSITION_FORM_DATA_LEN + 2 + HTTP_STR_MULTIPART_FIELD_NAME_LEN + 3 + STR_CR_LF_LEN; switch (p_tbl_entry->Type) { case HTTPc_FORM_FIELD_TYPE_KEY_VAL: p_kvp = (HTTPc_KEY_VAL *)p_tbl_entry->FieldObjPtr; /* Add length of Name field. */ char_encode_nbr = HTTP_URL_CharEncodeNbr(p_kvp->KeyPtr, p_kvp->KeyLen); total_len += (p_kvp->KeyLen + char_encode_nbr * HTTP_URL_ENCODING_JUMP); /* Add length of data. */ total_len += p_kvp->ValLen + 2 * STR_CR_LF_LEN; break; case HTTPc_FORM_FIELD_TYPE_KEY_VAL_EXT: p_kvp_big = (HTTPc_KEY_VAL_EXT *)p_tbl_entry->FieldObjPtr; /* Add length of Name field. */ char_encode_nbr = HTTP_URL_CharEncodeNbr(p_kvp_big->KeyPtr, p_kvp_big->KeyLen); total_len += (p_kvp_big->KeyLen + char_encode_nbr * HTTP_URL_ENCODING_JUMP); /* Add length of data. */ total_len += p_kvp_big->ValLen + 2 * STR_CR_LF_LEN; break; case HTTPc_FORM_FIELD_TYPE_FILE: p_multipart_file = (HTTPc_MULTIPART_FILE *)p_tbl_entry->FieldObjPtr; /* Add length of Name field. */ char_encode_nbr = HTTP_URL_CharEncodeNbr(p_multipart_file->NamePtr, p_multipart_file->NameLen); total_len += (p_multipart_file->NameLen + char_encode_nbr * HTTP_URL_ENCODING_JUMP); /* Add length of File Name field. */ char_encode_nbr = HTTP_URL_CharEncodeNbr(p_multipart_file->FileNamePtr, p_multipart_file->FileNameLen); total_len += (p_multipart_file->FileNameLen + char_encode_nbr * HTTP_URL_ENCODING_JUMP); total_len += 2 + HTTP_STR_MULTIPART_FIELD_FILE_NAME_LEN + 3; /* Add length of Content-Type Header. */ p_entry = HTTP_Dict_EntryGet(HTTP_Dict_ContentType, HTTP_Dict_ContentTypeSize, p_multipart_file->ContentType); if (p_entry != DEF_NULL) { total_len += HTTP_STR_HDR_FIELD_CONTENT_TYPE_LEN + 2 + p_entry->StrLen + STR_CR_LF_LEN; } /* Add length of data. */ total_len += p_multipart_file->FileLen + 2 * STR_CR_LF_LEN; break; default: total_len = 0; *p_err = HTTPc_ERR_FORM_TYPE_INVALID; goto exit; } } /* ------------ ADD LENGTH OF END BOUNDARY ------------ */ total_len += HTTPc_STR_BOUNDARY_END_LEN + STR_CR_LF_LEN; *p_err = HTTPc_ERR_NONE; exit: return (total_len); } #endif /* ********************************************************************************************************* * HTTPcReq_Line() * * Description : (1) Prepare and Copy HTTP Request line to HTTP Tx buffer. * (2) An HTTP line consist of : * (a) Method * (b) URI * (c) Query String * (d) Protocol Version * * * Argument(s) : p_conn Pointer to current HTTPc Connection. * * p_err Pointer to variable that will receive the return error code from this function : * * HTTPc_ERR_NONE Req State successfully processed. * HTTPc_ERR_REQ_LINE_CREATE Fatal error in the Req Line process. * HTTPc_ERR_TRANS_BUF_LEN_INVALID HTTP buffer length is not enough big. * HTTPc_ERR_TRANS_TX_BUF_FULL HTTP buffer is full, data must be Tx. * * ------------ RETURNED BY HTTPcReq_QueryStrTbl() ------------ * See HTTPcReq_QueryStrTbl() for additional return error codes. * * Return(s) : None. * * Caller(s) : HTTPcReq(). * * Note(s) : (3) HTTP Line parameters previously validate in API function HTTPc_SetReqLineParam(). * * (4) The Request line processing is design as a state machine with 4 different states: * HTTPc_CONN_STATE_REQ_LINE_METHOD * HTTPc_CONN_STATE_REQ_LINE_URI * HTTPc_CONN_STATE_REQ_LINE_QUERY_STR * HTTPc_CONN_STATE_REQ_LINE_PROTO_VER * * (5) The data length of each state MUST be smaller than the configured HTTPc buffer length. * Else the process is aborted and the err code HTTPc_ERR_TRANS_BUF_LEN_INVALID is returned. * The buffer length MUST be reconfigured and increased. * Exception for the URI, see Note #7. * Exception for the Query String, see Note #8. * * (6) The function will not return until all the request line has been copied to the HTTP * buffer or if the buffer is full before the end of the line processing. In that case, * the state is saved and the function will exit to allow the data to be transmit. * Next time around the process will resume at the saved state. * * (7) The URI can be bigger than the HTTP buffer length. In that case, the URI is not copied * into the HTTP buffer, the pointer to the URI will be passed directly to the TCPI-IP * stack. * * (8) See Note #1 of function HTTPcReq_QueryStrTbl() and HTTPcReq_QueryStrHook(). ********************************************************************************************************* */ static void HTTPcReq_Line(HTTPc_CONN *p_conn, HTTPc_ERR *p_err) { HTTPc_REQ *p_req; HTTP_DICT *p_entry; CPU_CHAR *p_buf; CPU_CHAR *p_buf_wr_start; CPU_CHAR *p_buf_wr_end; CPU_SIZE_T buf_len; CPU_SIZE_T str_len; p_req = p_conn->ReqListHeadPtr; p_buf = p_conn->TxBufPtr + p_conn->TxDataLen; buf_len = p_conn->BufLen - p_conn->TxDataLen; p_buf_wr_start = p_buf; p_buf_wr_end = p_buf; switch (p_conn->State) { case HTTPc_CONN_STATE_REQ_LINE_METHOD: /* -------------- PREPARE REQ LINE METHOD-------------- */ /* Find Method in HTTP dictionary. */ p_entry = HTTP_Dict_EntryGet(HTTP_Dict_ReqMethod, HTTP_Dict_ReqMethodSize, p_req->Method); if (p_entry == DEF_NULL) { *p_err = HTTPc_ERR_REQ_LINE_CREATE; /* Should not occur. See note #1. */ goto exit; } str_len = p_entry->StrLen + 1; /* Length of method + length of space char. */ if (str_len > p_conn->BufLen) { *p_err = HTTPc_ERR_TRANS_BUF_LEN_INVALID; /* Should not occur. */ goto exit; } if (str_len > buf_len) { /* HTTP buf space not enough, return to Tx data. */ p_conn->TxDataLen += (p_buf_wr_start - p_buf); p_conn->TxDataPtr = p_conn->TxBufPtr; p_conn->State = HTTPc_CONN_STATE_REQ_LINE_METHOD; *p_err = HTTPc_ERR_TRANS_TX_BUF_FULL; goto exit; } /* Write Request method to buffer. */ p_buf_wr_end = Str_Copy_N(p_buf_wr_start, p_entry->StrPtr, p_entry->StrLen); if (p_buf_wr_end == DEF_NULL) { *p_err = HTTPc_ERR_REQ_LINE_CREATE; goto exit; } p_buf_wr_end += p_entry->StrLen; *p_buf_wr_end = ASCII_CHAR_SPACE; /* Write space character to buffer. */ p_buf_wr_end++; buf_len -= (p_buf_wr_end - p_buf_wr_start); /* Update buffer length remaining. */ p_buf_wr_start = p_buf_wr_end; /* 'break' intentionally omitted. */ case HTTPc_CONN_STATE_REQ_LINE_URI: /* --------------- PREPARE REQ LINE URI---------------- */ str_len = Str_Len_N(p_req->ResourcePathPtr, p_req->ResourcePathLen); if (str_len <= buf_len) { /* IF HTTP buf space is enough, copy Resource path. */ p_buf_wr_end = Str_Copy_N(p_buf_wr_start, p_req->ResourcePathPtr, str_len); if (p_buf_wr_end == DEF_NULL) { *p_err = HTTPc_ERR_REQ_LINE_CREATE; goto exit; } p_buf_wr_end += str_len; buf_len -= (p_buf_wr_end - p_buf_wr_start); /* Update buffer length remaining. */ p_buf_wr_start = p_buf_wr_end; } else { /* ELSE (buffer space is not enough) ... */ switch (p_conn->State) { case HTTPc_CONN_STATE_REQ_LINE_METHOD: /* Start by exit function to allow remaining data in ...*/ /* ... buffer to be transmit. */ p_conn->TxDataLen += (p_buf_wr_end - p_buf); p_conn->TxDataPtr = p_conn->TxBufPtr; p_conn->State = HTTPc_CONN_STATE_REQ_LINE_URI; break; case HTTPc_CONN_STATE_REQ_LINE_URI: /* Second time around, set TxDataPtr to point to ... */ /* ... Resource Path ptr. */ p_conn->TxDataLen = str_len; p_conn->TxDataPtr = p_req->ResourcePathPtr; p_conn->State = HTTPc_CONN_STATE_REQ_LINE_QUERY_STR; break; default: *p_err = HTTPc_ERR_CONN_INVALID_STATE; goto exit; } break; /* break from upper HTTPc_CONN_STATE_REQ_LINE_URI case. */ } /* 'break' intentionally omitted. */ case HTTPc_CONN_STATE_REQ_LINE_QUERY_STR: *p_err = HTTPc_ERR_NONE; /* ---------- PREPARE REQ LINE QUERY STRINGS ---------- */ #if (HTTPc_CFG_QUERY_STR_EN == DEF_ENABLED) if (p_req->QueryStrTbl != DEF_NULL) { /* Write String Query to buffer. */ p_buf_wr_end = HTTPcReq_QueryStrTbl(p_conn, p_buf_wr_start, buf_len, p_err); } if (p_req->OnQueryStrTx != DEF_NULL) { /* Write String Query to buffer. */ p_buf_wr_end = HTTPcReq_QueryStrHook(p_conn, p_buf_wr_start, buf_len, p_err); } switch (*p_err) { case HTTPc_ERR_NONE: /* All the Query Strings were copied to the HTTP buf. */ buf_len -= (p_buf_wr_end - p_buf_wr_start); p_buf_wr_start = p_buf_wr_end; break; case HTTPc_ERR_TRANS_TX_BUF_FULL: /* HTTP buf full before all Queries were copied. */ p_conn->TxDataLen += (p_buf_wr_end - p_buf); p_conn->TxDataPtr = p_conn->TxBufPtr; p_conn->State = HTTPc_CONN_STATE_REQ_LINE_QUERY_STR; goto exit; default: p_conn->TxDataLen = 0; goto exit; } #endif p_conn->TxDataPtr = p_conn->TxBufPtr; /* Reset TxDataPtr to point to the HTTP buffer. */ /* 'break' intentionally omitted. */ case HTTPc_CONN_STATE_REQ_LINE_PROTO_VER: /* -------- PREPARE REQ LINE PROTOCOL VERSION --------- */ /* Find HTTP version in dictionary. */ p_entry = HTTP_Dict_EntryGet(HTTP_Dict_ProtocolVer, HTTP_Dict_ProtocolVerSize, HTTP_PROTOCOL_VER_1_1); if (p_entry == DEF_NULL) { *p_err = HTTPc_ERR_REQ_LINE_CREATE; /* Should not occur. See note #1. */ goto exit; } str_len = p_entry->StrLen + 1 + STR_CR_LF_LEN; /* total len = len of version + len of space + */ /* len of end of line. */ if (str_len > p_conn->BufLen) { *p_err = HTTPc_ERR_TRANS_BUF_LEN_INVALID; /* See Note #5. */ goto exit; } if (str_len > buf_len) { /* HTTP Buf space remaining not enough. */ p_conn->TxDataLen += (p_buf_wr_start - p_buf); p_conn->TxDataPtr = p_conn->TxBufPtr; p_conn->State = HTTPc_CONN_STATE_REQ_LINE_PROTO_VER; *p_err = HTTPc_ERR_TRANS_TX_BUF_FULL; goto exit; } *p_buf_wr_end = ASCII_CHAR_SPACE; /* Copy Space character. */ p_buf_wr_end++; /* Copy HTTP Protocol version. */ p_buf_wr_end = Str_Copy_N(p_buf_wr_end, p_entry->StrPtr, p_entry->StrLen); if (p_buf_wr_end == DEF_NULL) { *p_err = HTTPc_ERR_REQ_LINE_CREATE; goto exit; } p_buf_wr_end += p_entry->StrLen; /* Copy end of line CRLF. */ p_buf_wr_end = Str_Copy_N(p_buf_wr_end, STR_CR_LF, buf_len); if (p_buf_wr_end == DEF_NULL) { *p_err = HTTPc_ERR_REQ_LINE_CREATE; goto exit; } p_buf_wr_end += STR_CR_LF_LEN; p_conn->TxDataLen = (p_buf_wr_end - p_buf); /* Update data length to transmit. */ p_conn->TxDataPtr = p_conn->TxBufPtr; p_conn->State = HTTPc_CONN_STATE_REQ_HDR_HOST; /* Set next state to prepare request headers. */ break; default: *p_err = HTTPc_ERR_CONN_INVALID_STATE; goto exit; } *p_err = HTTPc_ERR_NONE; exit: return; } /* ********************************************************************************************************* * HTTPcReq_QueryStrTbl() * * Description : Parse Query String table and prepare the query string to be transmitted. * * Argument(s) : p_conn Pointer to current HTTPc connection. * * p_buf Pointer to where the data must be copied in the HTTP buffer. * * buf_len_rem Buffer length remaining. * * p_err Pointer to variable that will receive the return error code from this function : * * HTTPc_ERR_NONE All query strings prepared and copied to buf. * HTTPc_ERR_TRANS_BUF_LEN_INVALID HTTP buffer length is not enough big. * HTTPc_ERR_TRANS_TX_BUF_FULL HTTP buffer is full, data must be Tx. * HTTPc_ERR_REQ_QUERY_STR_CREATE Fatal error occurred in query preparation. * * Return(s) : Pointer to location after the end of the data written. * * Caller(s) : HTTPcReq_Line(). * * Note(s) : (1) Since the Query Strings are passed by the App in the form of a table, the process will * copy as much as queries possible to the HTTP buffer before returning. If not all the * queries fit inside the buffer, the index of the table will be saved and the function * will exit to allow transmission of data. Next time around the process will resume * at the saved index. * * (2) Note that each Query (Key-Value Pair) MUST individually fit inside the HTTP buffer * since a query is not divisible. ********************************************************************************************************* */ #if HTTPc_CFG_QUERY_STR_EN == DEF_ENABLED static CPU_CHAR *HTTPcReq_QueryStrTbl(HTTPc_CONN *p_conn, CPU_CHAR *p_buf, CPU_INT16U buf_len_rem, HTTPc_ERR *p_err) { HTTPc_REQ *p_req; HTTPc_KEY_VAL *p_kvp; CPU_CHAR *p_buf_wr; CPU_SIZE_T str_len_key; CPU_SIZE_T str_len_val; CPU_SIZE_T space_req; CPU_BOOLEAN first; CPU_BOOLEAN url_encode; CPU_INT16U encode_nbr; CPU_INT08U i; p_req = p_conn->ReqListHeadPtr; /* ------------- PARSE QUERY STRING TABLE ------------- */ p_buf_wr = p_buf; p_kvp = &p_req->QueryStrTbl[0]; first = DEF_BIT_IS_CLR(p_conn->ReqFlags, HTTPc_FLAG_REQ_LINE_QUERY_STR_BEGIN); for (i = p_conn->ReqQueryStrTxIx; i < p_req->QueryStrNbr; i++) { if (first == DEF_YES) { /* Query String start with a ? after the URI. */ *p_buf_wr = ASCII_CHAR_QUESTION_MARK; p_buf_wr++; buf_len_rem--; DEF_BIT_SET(p_conn->ReqFlags, HTTPc_FLAG_REQ_LINE_QUERY_STR_BEGIN); first = DEF_NO; } /* Calculate total length required by one key-val pair. */ space_req = 0; str_len_key = Str_Len_N(p_kvp->KeyPtr, p_kvp->KeyLen); encode_nbr = HTTP_URL_CharEncodeNbr(p_kvp->KeyPtr, str_len_key); space_req += (str_len_key + encode_nbr * HTTP_URL_ENCODING_JUMP); if (p_kvp->ValPtr != DEF_NULL) { str_len_val = Str_Len_N(p_kvp->ValPtr, p_kvp->ValLen); encode_nbr = HTTP_URL_CharEncodeNbr(p_kvp->ValPtr, str_len_val); space_req += (str_len_val + encode_nbr * HTTP_URL_ENCODING_JUMP); space_req += 1; /* Add length of "=" character. */ } space_req += 1; /* Add length of "&" character. */ if (space_req > p_conn->BufLen) { /* Return error if HTTP buf is to small for 1 pair. */ *p_err = HTTPc_ERR_TRANS_BUF_LEN_INVALID; goto exit; } if (space_req > buf_len_rem) { /* HTTP buf remaining space not enough. */ p_conn->ReqQueryStrTxIx = i; /* Save table index to resume at right place. */ *p_err = HTTPc_ERR_TRANS_TX_BUF_FULL; goto exit; /* Exit to allow data to be transmitted. */ } url_encode = HTTP_URL_EncodeStr(p_kvp->KeyPtr, /* URL-encode and copy the query key. */ p_buf_wr, &str_len_key, buf_len_rem); if (url_encode == DEF_FAIL) { p_buf_wr = p_buf; *p_err = HTTPc_ERR_REQ_QUERY_STR_CREATE; goto exit; } p_buf_wr += str_len_key; buf_len_rem -= str_len_key; if (p_kvp->ValPtr != DEF_NULL) { *p_buf_wr = ASCII_CHAR_EQUALS_SIGN; /* Copy "=" character between key and value. */ p_buf_wr++; buf_len_rem--; url_encode = HTTP_URL_EncodeStr(p_kvp->ValPtr, /* URL-encode and copy the query value. */ p_buf_wr, &str_len_val, buf_len_rem); if (url_encode == DEF_FAIL) { p_buf_wr = p_buf; *p_err = HTTPc_ERR_REQ_QUERY_STR_CREATE; goto exit; } p_buf_wr += str_len_val; buf_len_rem -= str_len_val; } if (i < (p_req->QueryStrNbr - 1)) { *p_buf_wr = ASCII_CHAR_AMPERSAND; /* Copy "&" character if query pair is not the last. */ p_buf_wr++; buf_len_rem--; } else { p_conn->ReqQueryStrTxIx = 0u; } p_buf = p_buf_wr; p_kvp++; } *p_err = HTTPc_ERR_NONE; exit: return (p_buf_wr); } #endif /* ********************************************************************************************************* * HTTPcReq_QueryStrHook() * * Description : (1) Call hook function to recover each part of the query string. * (2) Prepare the query string to be transmitted. * * * Argument(s) : p_conn Pointer to current HTTPc Connection object. * * p_buf Pointer to where the data must be copied in the HTTP buffer. * * buf_len_rem Buffer length remaining. * * p_err Pointer to variable that will receive the return error code from this function : * * HTTPc_ERR_NONE All query strings prepared and copied to buf. * HTTPc_ERR_TRANS_BUF_LEN_INVALID HTTP buffer length is not enough big. * HTTPc_ERR_TRANS_TX_BUF_FULL HTTP buffer is full, data must be Tx. * HTTPc_ERR_REQ_QUERY_STR_CREATE Fatal error occurred in query preparation. * * Return(s) : Pointer to location after the end of the data written. * * Caller(s) : HTTPcReq_Line(). * * Note(s) : (1) Since the Query Strings are passed by the App hook function, the process will * copy as much as queries possible to the HTTP buffer before returning. If not all the * queries fit inside the buffer, a pointer to the last key-value pair will be saved and * the function will exit to allow transmission of data. Next time around the process will resume * at the saved pointer before calling the hook. * * (2) Note that each Query (Key-Value Pair) MUST individually fit inside the HTTP buffer * since a query is not divisible. ********************************************************************************************************* */ #if HTTPc_CFG_QUERY_STR_EN == DEF_ENABLED static CPU_CHAR *HTTPcReq_QueryStrHook(HTTPc_CONN *p_conn, CPU_CHAR *p_buf, CPU_INT16U buf_len_rem, HTTPc_ERR *p_err) { HTTPc_CONN_OBJ *p_conn_const; HTTPc_REQ *p_req; HTTPc_REQ_OBJ *p_req_const; HTTPc_KEY_VAL *p_kvp; CPU_CHAR *p_buf_wr; CPU_SIZE_T str_len_key; CPU_SIZE_T str_len_val; CPU_SIZE_T space_req; CPU_BOOLEAN first; CPU_BOOLEAN done; CPU_BOOLEAN url_encode; CPU_INT16U encode_nbr; p_conn_const = (HTTPc_CONN_OBJ *)p_conn; p_req = p_conn->ReqListHeadPtr; p_req_const = (HTTPc_REQ_OBJ *)p_req; p_buf_wr = p_buf; done = DEF_NO; first = DEF_BIT_IS_CLR(p_conn->ReqFlags, HTTPc_FLAG_REQ_LINE_QUERY_STR_BEGIN); while (done == DEF_NO) { if (p_conn->ReqQueryStrTempPtr != DEF_NULL) { p_kvp = p_conn->ReqQueryStrTempPtr; done = DEF_BIT_IS_SET(p_conn->ReqFlags, HTTPc_FLAG_REQ_LINE_QUERY_STR_DONE); p_conn->ReqQueryStrTempPtr = DEF_NULL; } else { done = p_req->OnQueryStrTx(p_conn_const, p_req_const, &p_kvp); if (p_kvp == DEF_NULL) { *p_err = HTTPc_ERR_REQ_QUERY_STR_INVALID; goto exit; } } if (first == DEF_YES) { /* Query String start with a ? after the URI. */ *p_buf_wr = ASCII_CHAR_QUESTION_MARK; p_buf_wr++; buf_len_rem--; DEF_BIT_SET(p_conn->ReqFlags, HTTPc_FLAG_REQ_LINE_QUERY_STR_BEGIN); first = DEF_NO; } /* Calculate total length required by one key-val pair. */ space_req = 0; str_len_key = Str_Len_N(p_kvp->KeyPtr, p_kvp->KeyLen); encode_nbr = HTTP_URL_CharEncodeNbr(p_kvp->KeyPtr, str_len_key); space_req += (str_len_key + encode_nbr * HTTP_URL_ENCODING_JUMP); if (p_kvp->ValPtr != DEF_NULL) { str_len_val = Str_Len_N(p_kvp->ValPtr, p_kvp->ValLen); encode_nbr = HTTP_URL_CharEncodeNbr(p_kvp->ValPtr, str_len_val); space_req += (str_len_val + encode_nbr * HTTP_URL_ENCODING_JUMP); space_req += 1; /* Add length of "=" character. */ } space_req += 1; /* Add length of "&" character. */ if (space_req > p_conn->BufLen) { /* Return error if HTTP buf is to small for 1 pair. */ *p_err = HTTPc_ERR_TRANS_BUF_LEN_INVALID; goto exit; } if (space_req > buf_len_rem) { /* HTTP buf remaining space not enough. */ p_conn->ReqQueryStrTempPtr = p_kvp; if (done == DEF_YES) { DEF_BIT_SET(p_conn->ReqFlags, HTTPc_FLAG_REQ_LINE_QUERY_STR_DONE); } *p_err = HTTPc_ERR_TRANS_TX_BUF_FULL; goto exit; /* Exit to allow data to be transmitted. */ } url_encode = HTTP_URL_EncodeStr(p_kvp->KeyPtr, /* URL-encode and copy the query key. */ p_buf_wr, &str_len_key, buf_len_rem); if (url_encode == DEF_FAIL) { p_buf_wr = p_buf; *p_err = HTTPc_ERR_REQ_QUERY_STR_CREATE; goto exit; } p_buf_wr += str_len_key; buf_len_rem -= str_len_key; if (p_kvp->ValPtr != DEF_NULL) { *p_buf_wr = ASCII_CHAR_EQUALS_SIGN; /* Copy "=" character between key and value. */ p_buf_wr++; buf_len_rem--; url_encode = HTTP_URL_EncodeStr(p_kvp->ValPtr, /* URL-encode and copy the query value. */ p_buf_wr, &str_len_val, buf_len_rem); if (url_encode == DEF_FAIL) { p_buf_wr = p_buf; *p_err = HTTPc_ERR_REQ_QUERY_STR_CREATE; goto exit; } p_buf_wr += str_len_val; buf_len_rem -= str_len_val; } if (done == DEF_NO) { *p_buf_wr = ASCII_CHAR_AMPERSAND; /* Copy "&" character if query pair is not the last. */ p_buf_wr++; buf_len_rem--; } p_buf = p_buf_wr; } *p_err = HTTPc_ERR_NONE; exit: return (p_buf_wr); } #endif /* ********************************************************************************************************* * HTTPcReq_Hdr() * * Description : (1) Prepare and Copy HTTP Request Headers to HTTP Tx buffer. * * (a) Host, Connection, Content-Type, Content-Length and Transfer-Encoding headers * are processed by the HTTPc core. * * (b) Extra Headers table given by application is parse to add headers to request. * * * Argument(s) : p_conn Pointer to current HTTPc Connection. * * p_err Pointer to variable that will receive the return error code from this function : * * HTTPc_ERR_NONE All headers copy to buffer successfully. * HTTPc_ERR_REQ_HDR_CREATE Fatal error while creating header. * HTTPc_ERR_TRANS_BUF_LEN_INVALID HTTP buffer length is not enough big. * HTTPc_ERR_TRANS_TX_BUF_FULL HTTP buffer is full, data must be Tx. * HTTPc_ERR_CONN_INVALID_STATE Invalid connection state. * * ------------ RETURNED BY HTTPcReq_HdrCopyToBuf() ------------ * See HTTPcReq_HdrCopyToBuf() for additional return error codes. * * Return(s) : None. * * Caller(s) : HTTPcReq(). * * Note(s) : (2) HTTP Header parameters previously validate in API function HTTPc_SetReqExtHdr() * and in HTTPcReq_Prepare(). * * (3) The Request headers processing is design as a state machine with 4 different states: * HTTPc_CONN_STATE_REQ_HDR_HOST * HTTPc_CONN_STATE_REQ_HDR_CONN * HTTPc_CONN_STATE_REQ_HDR_CONTENT_TYPE * HTTPc_CONN_STATE_REQ_HDR_TRANSFER_ENCODE * HTTPc_CONN_STATE_REQ_HDR_CONTENT_LEN * HTTPc_CONN_STATE_REQ_HDR_EXT * * (4) The data length of each state MUST be smaller than the configured HTTPc buffer length. * Else the process is aborted and the err code HTTPc_ERR_TRANS_BUF_LEN_INVALID is returned. * The buffer length MUST be reconfigured and increased. * * (5) The function will not return until all the request headers has been copied to the HTTP * buffer or if the buffer is full before the end of the header processing. In that case, * the state is saved and the function will exit to allow the data to be transmit. * Next time around the process will resume at the saved state. ********************************************************************************************************* */ static void HTTPcReq_Hdr(HTTPc_CONN *p_conn, HTTPc_ERR *p_err) { HTTPc_REQ *p_req; HTTP_DICT *p_entry; CPU_CHAR *p_str; CPU_CHAR *p_buf; CPU_CHAR *p_buf_wr; CPU_CHAR content_len_str[DEF_INT_32U_NBR_DIG_MAX + 1]; CPU_SIZE_T buf_len; CPU_SIZE_T len; CPU_BOOLEAN flag_en; CPU_INT32U key; #ifdef HTTPc_WEBSOCK_MODULE_EN HTTPc_WEBSOCK_REQ *p_ws_req; #endif p_req = p_conn->ReqListHeadPtr; p_buf = p_conn->TxBufPtr + p_conn->TxDataLen; buf_len = p_conn->BufLen - p_conn->TxDataLen; p_buf_wr = p_buf; /* --------------- ADD REQUIRED HEADERS --------------- */ switch (p_conn->State) { case HTTPc_CONN_STATE_REQ_HDR_HOST: /* --------------- COPY HOST HDR FIELD ---------------- */ flag_en = DEF_BIT_IS_SET(p_req->HdrFlags, HTTPc_FLAG_REQ_HDR_HOST_ADD); if (flag_en == DEF_YES) { if ((p_conn->ServerPort == HTTP_DFLT_PORT_NBR) || (p_conn->ServerPort == HTTP_DFLT_PORT_NBR_SECURE)) { p_str = HTTPcReq_HdrCopyToBuf(p_buf_wr, p_conn->BufLen, buf_len, HTTP_HDR_FIELD_HOST, p_conn->HostNamePtr, p_conn->HostNameLen, DEF_YES, p_err); if (p_str == DEF_NULL) { goto exit; } buf_len -= (p_str - p_buf_wr); p_buf_wr = p_str; } else { /* If not http dflt port, add the port nbr to host hdr. */ p_str = HTTPcReq_HdrCopyToBuf(p_buf_wr, p_conn->BufLen, buf_len, HTTP_HDR_FIELD_HOST, p_conn->HostNamePtr, p_conn->HostNameLen, DEF_NO, p_err); if (p_str == DEF_NULL) { goto exit; } buf_len -= (p_str - p_buf_wr); p_buf_wr = p_str; if (buf_len >= 1) { *p_str = ASCII_CHAR_COLON; p_str++; } else { *p_err = HTTPc_ERR_REQ_HDR_CREATE; goto exit; } buf_len -= (p_str - p_buf_wr); p_buf_wr = p_str; p_str = Str_FmtNbr_Int32U(p_conn->ServerPort, 5u, DEF_NBR_BASE_DEC, DEF_NULL, DEF_NO, DEF_YES, p_buf_wr); if (p_str == DEF_NULL) { *p_err = HTTPc_ERR_REQ_HDR_CREATE; goto exit; } p_str += Str_Len(p_str); buf_len -= (p_str - p_buf_wr); p_buf_wr = p_str; p_str = Str_Copy_N(p_str, STR_CR_LF, buf_len); if (p_str == DEF_NULL) { *p_err = HTTPc_ERR_REQ_HDR_CREATE; goto exit; } p_str += STR_CR_LF_LEN; buf_len -= p_str - p_buf_wr; p_buf_wr = p_str; } } p_conn->State = HTTPc_CONN_STATE_REQ_HDR_CONN; /* 'break' intentionally omitted. */ case HTTPc_CONN_STATE_REQ_HDR_CONN: /* ------------- COPY CONNECTION HDR FIELD ------------- */ key = HTTP_DICT_KEY_INVALID; flag_en = DEF_BIT_IS_SET(p_req->HdrFlags, HTTPc_FLAG_REQ_HDR_CONNECTION_ADD); if (flag_en == DEF_YES) { /* Check the value for the connection hdr field. */ flag_en = DEF_BIT_IS_SET(p_conn->Flags, HTTPc_FLAG_CONN_TO_CLOSE); if (flag_en == DEF_YES) { key = HTTP_HDR_FIELD_CONN_CLOSE; } else { #ifdef HTTPc_WEBSOCK_MODULE_EN flag_en = DEF_BIT_IS_SET(p_req->Flags, HTTPc_FLAG_REQ_UPGRADE_WEBSOCKET); if (flag_en == DEF_YES) { key = HTTP_HDR_FIELD_CONN_UPGRADE; } #endif } p_entry = HTTP_Dict_EntryGet(HTTP_Dict_HdrFieldConnVal, HTTP_Dict_HdrFieldConnValSize, key); if (p_entry == DEF_NULL) { *p_err = HTTPc_ERR_REQ_HDR_CREATE; goto exit; } p_str = HTTPcReq_HdrCopyToBuf(p_buf_wr, p_conn->BufLen, buf_len, HTTP_HDR_FIELD_CONN, p_entry->StrPtr, p_entry->StrLen, DEF_YES, p_err); if (p_str == DEF_NULL) { goto exit; } buf_len -= (p_str - p_buf_wr); p_buf_wr = p_str; } p_conn->State = HTTPc_CONN_STATE_REQ_HDR_CONTENT_TYPE; /* 'break' intentionally omitted. */ case HTTPc_CONN_STATE_REQ_HDR_CONTENT_TYPE: /* ----------- COPY CONTENT-TYPE HDR FIELD ------------ */ flag_en = DEF_BIT_IS_SET(p_req->HdrFlags, HTTPc_FLAG_REQ_HDR_CONTENT_TYPE_ADD); if (flag_en == DEF_YES) { p_entry = HTTP_Dict_EntryGet(HTTP_Dict_ContentType, HTTP_Dict_ContentTypeSize, p_req->ContentType); if (p_entry == DEF_NULL) { *p_err = HTTPc_ERR_REQ_HDR_CREATE; goto exit; } if (p_req->ContentType != HTTP_CONTENT_TYPE_MULTIPART_FORM) { p_str = HTTPcReq_HdrCopyToBuf(p_buf_wr, p_conn->BufLen, buf_len, HTTP_HDR_FIELD_CONTENT_TYPE, p_entry->StrPtr, p_entry->StrLen, DEF_YES, p_err); if (p_str == DEF_NULL) { goto exit; } } else { len = HTTP_STR_HDR_FIELD_CONTENT_TYPE_LEN + 2 + p_entry->StrLen + 2 + HTTP_STR_MULTIPART_BOUNDARY_LEN + 1 + HTTPc_STR_BOUNDARY_LEN + STR_CR_LF_LEN; if (len > p_conn->BufLen) { *p_err = HTTPc_ERR_TRANS_BUF_LEN_INVALID; goto exit; } if (len > buf_len) { *p_err = HTTPc_ERR_TRANS_TX_BUF_FULL; goto exit; } p_str = HTTPcReq_HdrCopyToBuf(p_buf_wr, p_conn->BufLen, buf_len, HTTP_HDR_FIELD_CONTENT_TYPE, p_entry->StrPtr, p_entry->StrLen, DEF_NO, p_err); if (p_str == DEF_NULL) { goto exit; } buf_len -= (p_str - p_buf_wr); p_buf_wr = p_str; *p_str = ASCII_CHAR_SEMICOLON; p_str++; *p_str = ASCII_CHAR_SPACE; p_str++; p_str = Str_Copy_N(p_str, HTTP_STR_MULTIPART_BOUNDARY, HTTP_STR_MULTIPART_BOUNDARY_LEN); if (p_str == DEF_NULL) { *p_err = HTTPc_ERR_REQ_HDR_CREATE; goto exit; } p_str += HTTP_STR_MULTIPART_BOUNDARY_LEN; *p_str = ASCII_CHAR_EQUALS_SIGN; p_str++; p_str = Str_Copy_N(p_str, HTTPc_STR_BOUNDARY, HTTPc_STR_BOUNDARY_LEN); if (p_str == DEF_NULL) { *p_err = HTTPc_ERR_REQ_HDR_CREATE; goto exit; } p_str += HTTPc_STR_BOUNDARY_LEN; p_str = Str_Copy_N(p_str, STR_CR_LF, STR_CR_LF_LEN); if (p_str == DEF_NULL) { *p_err = HTTPc_ERR_REQ_HDR_CREATE; goto exit; } p_str += STR_CR_LF_LEN; } buf_len -= (p_str - p_buf_wr); p_buf_wr = p_str; } p_conn->State = HTTPc_CONN_STATE_REQ_HDR_TRANSFER_ENCODE; /* 'break' intentionally omitted. */ case HTTPc_CONN_STATE_REQ_HDR_TRANSFER_ENCODE: /* --------- COPY TRANSFER-ENCODING HDR FIELD --------- */ flag_en = DEF_BIT_IS_SET(p_req->HdrFlags, HTTPc_FLAG_REQ_HDR_TRANSFER_ENCODE_ADD); if (flag_en == DEF_YES) { p_entry = HTTP_Dict_EntryGet(HTTP_Dict_HdrFieldTransferTypeVal, HTTP_Dict_HdrFieldTransferTypeValSize, HTTP_HDR_FIELD_TRANSFER_TYPE_CHUNKED); if (p_entry == DEF_NULL) { *p_err = HTTPc_ERR_REQ_HDR_CREATE; goto exit; } p_str = HTTPcReq_HdrCopyToBuf(p_buf_wr, p_conn->BufLen, buf_len, HTTP_HDR_FIELD_TRANSFER_ENCODING, p_entry->StrPtr, p_entry->StrLen, DEF_YES, p_err); if (p_str == DEF_NULL) { goto exit; } buf_len -= (p_str - p_buf_wr); p_buf_wr = p_str; } p_conn->State = HTTPc_CONN_STATE_REQ_HDR_CONTENT_LEN; /* 'break' intentionally omitted. */ case HTTPc_CONN_STATE_REQ_HDR_CONTENT_LEN: /* ---------- COPY CONTENT-LENGTH HDR FIELD ----------- */ flag_en = DEF_BIT_IS_SET(p_req->HdrFlags, HTTPc_FLAG_REQ_HDR_CONTENT_LENGTH_ADD); if (flag_en == DEF_YES) { p_str = Str_FmtNbr_Int32U(p_req->ContentLen, DEF_INT_32U_NBR_DIG_MAX, DEF_NBR_BASE_DEC, '\0', DEF_NO, DEF_YES, content_len_str); if (p_str == DEF_NULL) { *p_err = HTTPc_ERR_REQ_HDR_CREATE; goto exit; } len = Str_Len_N(p_str, (DEF_INT_32U_NBR_DIG_MAX + 1)); p_str = HTTPcReq_HdrCopyToBuf(p_buf_wr, p_conn->BufLen, buf_len, HTTP_HDR_FIELD_CONTENT_LEN, p_str, len, DEF_YES, p_err); if (p_str == DEF_NULL) { goto exit; } buf_len -= (p_str - p_buf_wr); p_buf_wr = p_str; } p_conn->State = HTTPc_CONN_STATE_REQ_HDR_UPGRADE; /* 'break' intentionally omitted. */ case HTTPc_CONN_STATE_REQ_HDR_UPGRADE: #ifdef HTTPc_WEBSOCK_MODULE_EN flag_en = DEF_BIT_IS_SET(p_req->HdrFlags, HTTPc_FLAG_REQ_HDR_UPGRADE_ADD); if (flag_en == DEF_YES) { p_entry = HTTP_Dict_EntryGet(HTTP_Dict_HdrFieldUpgradeVal, HTTP_Dict_HdrFieldUpgradeValSize, HTTP_HDR_FIELD_UPGRADE_WEBSOCKET); if (p_entry == DEF_NULL) { *p_err = HTTPc_ERR_REQ_HDR_CREATE; goto exit; } p_str = HTTPcReq_HdrCopyToBuf(p_buf_wr, p_conn->BufLen, buf_len, HTTP_HDR_FIELD_UPGRADE, p_entry->StrPtr, p_entry->StrLen, DEF_YES, p_err); if (p_str == DEF_NULL) { goto exit; } buf_len -= (p_str - p_buf_wr); p_buf_wr = p_str; } #endif p_conn->State = HTTPc_CONN_STATE_REQ_HDR_WEBSOCKET; /* 'break' intentionally omitted. */ case HTTPc_CONN_STATE_REQ_HDR_WEBSOCKET: #ifdef HTTPc_WEBSOCK_MODULE_EN flag_en = DEF_BIT_IS_SET(p_req->HdrFlags, HTTPc_FLAG_REQ_HDR_UPGRADE_ADD); if (flag_en == DEF_YES) { p_ws_req = p_req->WebSockPtr; p_str = HTTPcReq_HdrCopyToBuf(p_buf_wr, p_conn->BufLen, buf_len, HTTP_HDR_FIELD_WEBSOCKET_KEY, p_ws_req->Key, HTTPc_WEBSOCK_KEY_ENCODED_LEN - 1, DEF_YES, p_err); if (p_str == DEF_NULL) { goto exit; } buf_len -= (p_str - p_buf_wr); p_buf_wr = p_str; p_str = HTTPcReq_HdrCopyToBuf(p_buf_wr, p_conn->BufLen, buf_len, HTTP_HDR_FIELD_WEBSOCKET_VERSION, HTTPc_WEBSOCK_PROTOCOL_VERSION_13_STR, HTTPc_WEBSOCK_PROTOCOL_VERSION_13_STR_LEN, DEF_YES, p_err); if (p_str == DEF_NULL) { goto exit; } buf_len -= (p_str - p_buf_wr); p_buf_wr = p_str; } #endif p_conn->State = HTTPc_CONN_STATE_REQ_HDR_EXT; /* 'break' intentionally omitted. */ case HTTPc_CONN_STATE_REQ_HDR_EXT: /* -------------- COPY EXTRA HDR FIELDS --------------- */ #if (HTTPc_CFG_HDR_TX_EN == DEF_ENABLED) if (p_req->HdrTbl != DEF_NULL) { p_buf_wr = HTTPcReq_HdrExtTbl(p_conn, p_buf_wr, buf_len, p_err); if (*p_err != HTTPc_ERR_NONE) { goto exit; } } if (p_req->OnHdrTx != DEF_NULL) { p_buf_wr = HTTPcReq_HdrExtHook(p_conn, p_buf_wr, buf_len, p_err); if (*p_err != HTTPc_ERR_NONE) { goto exit; } } #endif p_conn->State = HTTPc_CONN_STATE_REQ_HDR_LAST; /* 'break' intentionally omitted. */ case HTTPc_CONN_STATE_REQ_HDR_LAST: /* ------------- WRITE CRLF AFTER HEADERS ------------- */ if (buf_len < STR_CR_LF_LEN) { *p_err = HTTPc_ERR_TRANS_TX_BUF_FULL; goto exit; } p_str = Str_Copy_N(p_buf_wr, STR_CR_LF, buf_len); if (p_str == DEF_NULL) { *p_err = HTTPc_ERR_REQ_HDR_CREATE; goto exit; } p_buf_wr += STR_CR_LF_LEN; p_conn->State = HTTPc_CONN_STATE_REQ_BODY; break; default: *p_err = HTTPc_ERR_CONN_INVALID_STATE; break; } *p_err = HTTPc_ERR_NONE; exit: p_conn->TxDataLen += (p_buf_wr - p_buf); /* Update Data Length to transmit. */ return; } /* ********************************************************************************************************* * HTTPcReq_HdrExtTbl() * * Description : Add Additional headers include in the table to the HTTP Request. * * Argument(s) : p_conn Pointer to the current HTTPc Connection. * * p_buf Pointer to where the data must be copied in the HTTP buffer. * * buf_len_rem Buffer length remaining. * * p_err Pointer to variable that will receive the return error code from this function : * * HTTPc_ERR_NONE Headers successfully added to request. * HTTPc_ERR_TRANS_TX_BUF_FULL Buffer is full. Data must be Tx. * * ------------ RETURNED BY HTTPcReq_HdrCopyToBuf() ------------ * See HTTPcReq_HdrCopyToBuf() for additional return error codes. * * * Return(s) : Pointer to location in buffer after the end of the data written. * * Caller(s) : HTTPcReq_Hdr(). * * Note(s) : (1) Since the additional headers are passed by the App in the form of a table, the process will * copy as much as headers possible to the HTTP buffer before returning. If not all the * headers fit inside the buffer, the index of the table will be saved and the function * will exit to allow transmission of data. Next time around the process will resume * at the saved index. * * (2) Note that each header MUST individually fit inside the HTTP buffer * since a header field is not divisible. ********************************************************************************************************* */ #if (HTTPc_CFG_HDR_TX_EN == DEF_ENABLED) static CPU_CHAR *HTTPcReq_HdrExtTbl(HTTPc_CONN *p_conn, CPU_CHAR *p_buf, CPU_INT16U buf_len_rem, HTTPc_ERR *p_err) { HTTPc_REQ *p_req; HTTPc_HDR *p_hdr; CPU_CHAR *p_str; CPU_INT08U ix; CPU_INT08U i; p_req = p_conn->ReqListHeadPtr; ix = p_conn->ReqHdrTxIx; p_hdr = &p_req->HdrTbl[ix]; for (i = ix; i < p_req->HdrNbr; i++) { p_str = HTTPcReq_HdrCopyToBuf(p_buf, p_conn->BufLen, buf_len_rem, p_hdr->HdrField, p_hdr->ValPtr, p_hdr->ValLen, DEF_YES, p_err); switch (*p_err) { case HTTPc_ERR_NONE: break; case HTTPc_ERR_TRANS_TX_BUF_FULL: p_conn->ReqHdrTxIx = i; goto exit; case HTTPc_ERR_TRANS_BUF_LEN_INVALID: case HTTPc_ERR_REQ_HDR_CREATE: default: goto exit; } buf_len_rem -= (p_str - p_buf); p_buf = p_str; if (i == (p_req->HdrNbr - 1)) { p_conn->ReqHdrTxIx = 0u; } p_hdr++; } *p_err = HTTPc_ERR_NONE; exit: return (p_buf); } #endif /* ********************************************************************************************************* * HTTPcReq_HdrExtHook() * * Description : Call request Header hook function to add additional headers to the request. * * Argument(s) : p_conn Pointer to current HTTPc Connection. * * p_buf Pointer to where the data must be copied in the HTTP buffer. * * buf_len_rem Buffer length remaining. * * p_err Pointer to variable that will receive the return error code from this function : * * HTTPc_ERR_NONE Headers successfully added to request. * HTTPc_ERR_TRANS_TX_BUF_FULL Buffer is full. Data must be Tx. * * ------------ RETURNED BY HTTPcReq_HdrCopyToBuf() ------------ * See HTTPcReq_HdrCopyToBuf() for additional return error codes. * * Return(s) : Pointer to location in buffer after the end of the data written. * * Caller(s) : HTTPcReq_Hdr(). * * Note(s) : (1) Since the Additional Headers are passed by the App hook function, the process will * copy as much as headers possible to the HTTP buffer before returning. If not all the * headers fit inside the buffer, a pointer to the header field will be saved and * the function will exit to allow transmission of data. Next time around the process will * resume at the saved pointer before calling the hook. * * (2) Note that each header MUST individually fit inside the HTTP buffer * since a header field is not divisible. ********************************************************************************************************* */ #if (HTTPc_CFG_HDR_TX_EN == DEF_ENABLED) static CPU_CHAR *HTTPcReq_HdrExtHook(HTTPc_CONN *p_conn, CPU_CHAR *p_buf, CPU_INT16U buf_len_rem, HTTPc_ERR *p_err) { HTTPc_CONN_OBJ *p_conn_const; HTTPc_REQ *p_req; HTTPc_REQ_OBJ *p_req_const; HTTPc_HDR *p_hdr; CPU_CHAR *p_str; CPU_BOOLEAN done; p_conn_const = (HTTPc_CONN_OBJ *)p_conn; p_req = p_conn->ReqListHeadPtr; p_req_const = (HTTPc_REQ_OBJ *)p_req; done = DEF_NO; while (done != DEF_YES) { if (p_conn->ReqHdrTempPtr != DEF_NULL) { p_hdr = p_conn->ReqHdrTempPtr; done = DEF_BIT_IS_SET(p_conn->ReqFlags, HTTPc_FLAG_REQ_HDR_DONE); p_conn->ReqHdrTempPtr = DEF_NULL; } else { done = p_req->OnHdrTx(p_conn_const, p_req_const, &p_hdr); if (p_hdr == DEF_NULL) { *p_err = HTTPc_ERR_REQ_HDR_INVALID; goto exit; } } p_str = HTTPcReq_HdrCopyToBuf(p_buf, p_conn->BufLen, buf_len_rem, p_hdr->HdrField, p_hdr->ValPtr, p_hdr->ValLen, DEF_YES, p_err); switch (*p_err) { case HTTPc_ERR_NONE: break; case HTTPc_ERR_TRANS_TX_BUF_FULL: p_conn->ReqHdrTempPtr = p_hdr; if (done == DEF_YES) { DEF_BIT_SET(p_conn->ReqFlags, HTTPc_FLAG_REQ_HDR_DONE); } goto exit; case HTTPc_ERR_TRANS_BUF_LEN_INVALID: case HTTPc_ERR_REQ_HDR_CREATE: default: goto exit; } buf_len_rem -= (p_str - p_buf); p_buf = p_str; } *p_err = HTTPc_ERR_NONE; exit: return (p_buf); } #endif /* ********************************************************************************************************* * HTTPcReq_HdrSearch() * * Description : Search for a HTTP header type inside the extra header table passed by the upper application. * * Argument(s) : p_conn Pointer to current HTTPc Connection. * * hdr_field HTTP header type to search for in table. * * Return(s) : Pointer to HTTP header object found. * DEF_NULL if no HTTP header found. * * Caller(s) : HTTPc_ReqPrepare(). * * Note(s) : None. ********************************************************************************************************* */ #if (HTTPc_CFG_HDR_TX_EN == DEF_ENABLED) static HTTPc_HDR *HTTPcReq_HdrSearch(HTTPc_CONN *p_conn, HTTP_HDR_FIELD hdr_field) { HTTPc_REQ *p_req; HTTPc_HDR *p_hdr; CPU_BOOLEAN found; CPU_INT08U i; p_req = p_conn->ReqListHeadPtr; p_hdr = p_req->HdrTbl; if (p_hdr == DEF_NULL) { return (DEF_NULL); } found = DEF_NO; for (i = 0u; i < p_req->HdrNbr; i++) { if (p_hdr->HdrField == hdr_field) { found = DEF_YES; break; } p_hdr++; } if (found == DEF_YES) { return (p_hdr); } else { return (DEF_NULL); } } #endif /* ********************************************************************************************************* * HTTPcReq_Body() * * Description : Prepare and Copy/Transmit HTTP Request Body. * * Argument(s) : p_conn Pointer to current HTTPc Connection. * * p_err Pointer to variable that will receive the return error code from this function : * * HTTPc_ERR_NONE Body processing successful. * HTTPc_ERR_REQ_BODY_NOT_PRESENT No body present for method that requires one. * HTTPc_ERR_REQ_CONTENT_TYPE_INVALID Invalid Content-Type with POST method. * HTTPc_ERR_REQ_METHOD_INVALID Invalid HTTP method. * HTTPc_ERR_CONN_INVALID_STATE Invalid connection state. * * ------------ RETURNED BY HTTPcReq_BodyData() ------------- * See HTTPcReq_BodyData() for additional return error codes. * * ------------ RETURNED BY HTTPcReq_FormApp() ------------- * See HTTPcReq_FormApp() for additional return error codes. * * ------------ RETURNED BY HTTPcReq_FormMultipart() ------------- * See HTTPcReq_FormMultipart() for additional return error codes. * * Return(s) : None. * * Caller(s) : HTTPcReq(). * * Note(s) : None. ********************************************************************************************************* */ static void HTTPcReq_Body(HTTPc_CONN *p_conn, HTTPc_ERR *p_err) { HTTPc_REQ *p_req; HTTP_METHOD method; HTTP_CONTENT_TYPE content_type; CPU_BOOLEAN body_present; p_req = p_conn->ReqListHeadPtr; method = p_req->Method; content_type = p_req->ContentType; body_present = DEF_BIT_IS_SET(p_req->Flags, HTTPc_FLAG_REQ_BODY_PRESENT); switch (p_conn->State) { case HTTPc_CONN_STATE_REQ_BODY: switch (method) { case HTTP_METHOD_GET: case HTTP_METHOD_HEAD: case HTTP_METHOD_DELETE: case HTTP_METHOD_OPTIONS: if (body_present == DEF_NO) { p_conn->State = HTTPc_CONN_STATE_REQ_END; } else { p_conn->State = HTTPc_CONN_STATE_REQ_BODY_DATA; } break; case HTTP_METHOD_TRACE: case HTTP_METHOD_CONNECT: p_conn->State = HTTPc_CONN_STATE_REQ_END; break; case HTTP_METHOD_POST: if (body_present == DEF_NO) { p_conn->State = HTTPc_CONN_STATE_REQ_END; break; } #if (HTTPc_CFG_FORM_EN == DEF_ENABLED) if (p_req->FormFieldTbl != DEF_NULL) { switch (content_type) { case HTTP_CONTENT_TYPE_APP_FORM: p_conn->State = HTTPc_CONN_STATE_REQ_BODY_FORM_APP; break; case HTTP_CONTENT_TYPE_MULTIPART_FORM: p_conn->State = HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_BOUNDARY; break; default: *p_err = HTTPc_ERR_REQ_PARAM_CONTENT_TYPE_INVALID; goto exit; } } else { p_conn->State = HTTPc_CONN_STATE_REQ_BODY_DATA; } #else (void)content_type; p_conn->State = HTTPc_CONN_STATE_REQ_BODY_DATA; #endif break; case HTTP_METHOD_PUT: if (body_present == DEF_NO) { p_conn->State = HTTPc_CONN_STATE_REQ_END; break; } p_conn->State = HTTPc_CONN_STATE_REQ_BODY_DATA; break; case HTTP_METHOD_UNKNOWN: default: *p_err = HTTPc_ERR_REQ_PARAM_METHOD_INVALID; goto exit; } break; case HTTPc_CONN_STATE_REQ_BODY_DATA: case HTTPc_CONN_STATE_REQ_BODY_DATA_CHUNK_SIZE: case HTTPc_CONN_STATE_REQ_BODY_DATA_CHUNK_DATA: case HTTPc_CONN_STATE_REQ_BODY_DATA_CHUNK_END: HTTPcReq_BodyData(p_conn, p_err); if (*p_err != HTTPc_ERR_NONE) { goto exit; } break; case HTTPc_CONN_STATE_REQ_BODY_FORM_APP: #if (HTTPc_CFG_FORM_EN == DEF_ENABLED) HTTPcReq_FormApp(p_conn, p_err); if (*p_err != HTTPc_ERR_NONE) { goto exit; } p_conn->State = HTTPc_CONN_STATE_REQ_END; break; #else *p_err = HTTPc_ERR_FEATURE_DIS; goto exit; #endif case HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_BOUNDARY: case HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_HDR_CONTENT_DISPO: case HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_HDR_CONTENT_TYPE: case HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_DATA: case HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_DATA_END: case HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_BOUNDARY_END: #if (HTTPc_CFG_FORM_EN == DEF_ENABLED) HTTPcReq_FormMultipart(p_conn, p_err); if (*p_err != HTTPc_ERR_NONE) { goto exit; } break; #else *p_err = HTTPc_ERR_FEATURE_DIS; goto exit; #endif default: *p_err = HTTPc_ERR_CONN_INVALID_STATE; goto exit; } *p_err = HTTPc_ERR_NONE; exit: return; } /* ********************************************************************************************************* * HTTPcReq_FormApp() * * Description : Prepare the application type form and copy it to HTTP buffer. * * Argument(s) : p_conn Pointer to current HTTPc Connection. * * p_err Pointer to variable that will receive the return error code from this function : * * HTTPc_ERR_NONE Form created and copied to buf successfully. * HTTPc_ERR_TRANS_BUF_LEN_INVALID HTTP buffer length is not enough big. * HTTPc_ERR_TRANS_TX_BUF_FULL HTTP buffer is full, data must be Tx. * HTTPc_ERR_REQ_FORM_CREATE Fatal error occurred in form creation. * * Return(s) : None. * * Caller(s) : HTTPcReq_Body(). * * Note(s) : (1) The application-form is created with the table form passed by the upper application. ********************************************************************************************************* */ #if (HTTPc_CFG_FORM_EN == DEF_ENABLED) static void HTTPcReq_FormApp(HTTPc_CONN *p_conn, HTTPc_ERR *p_err) { HTTPc_REQ *p_req; CPU_CHAR *p_buf; CPU_CHAR *p_buf_wr; HTTPc_FORM_TBL_FIELD *p_form_tbl; HTTPc_FORM_TBL_FIELD *p_tbl_entry; HTTPc_KEY_VAL *p_form_field; CPU_INT16U key_char_encode_nbr; CPU_INT16U val_char_encode_nbr; CPU_SIZE_T buf_len; CPU_SIZE_T str_len_key; CPU_SIZE_T str_len_val; CPU_SIZE_T data_size = 0; CPU_BOOLEAN url_encode; CPU_INT16U i; p_req = p_conn->ReqListHeadPtr; p_buf = p_conn->TxBufPtr + p_conn->TxDataLen; /* Point to beginning of free space in Tx buffer. */ buf_len = p_conn->BufLen - p_conn->TxDataLen; p_buf_wr = p_buf; p_form_tbl = (HTTPc_FORM_TBL_FIELD *)p_req->FormFieldTbl; for (i = p_conn->ReqFormDataTxIx; i < p_req->FormFieldNbr; i++) { p_tbl_entry = &p_form_tbl[i]; p_form_field = (HTTPc_KEY_VAL *)p_tbl_entry->FieldObjPtr; /* Calculate length of key and value without encoding. */ str_len_key = p_form_field->KeyLen; str_len_val = p_form_field->ValLen; /* Found number of character needing URL encoding. */ key_char_encode_nbr = HTTP_URL_CharEncodeNbr(p_form_field->KeyPtr, str_len_key); val_char_encode_nbr = HTTP_URL_CharEncodeNbr(p_form_field->ValPtr, str_len_val); /* Calculate total size needed for key & value encoded. */ data_size = str_len_key + str_len_val + HTTP_URL_ENCODING_JUMP * key_char_encode_nbr + HTTP_URL_ENCODING_JUMP * val_char_encode_nbr + 1; /* + 1 for the "=" between key & value. */ if (i < (p_req->FormFieldNbr - 1)) { data_size++; /* + 1 for the "&" between each key&value pair. */ } if (data_size > p_conn->BufLen) { /* Return if error if conn buf is to small for 1 pair. */ *p_err = HTTPc_ERR_TRANS_BUF_LEN_INVALID; goto exit; } if (data_size > buf_len) { /* Return if no more space in buf. Need to Tx. */ p_conn->ReqFormDataTxIx = i; /* Save the tbl index to start from there next time. */ *p_err = HTTPc_ERR_TRANS_TX_BUF_FULL; goto exit; } url_encode = HTTP_URL_EncodeStr(p_form_field->KeyPtr, /* Encode and Write to buffer the Key. */ p_buf_wr, &str_len_key, buf_len); if (url_encode == DEF_FAIL) { *p_err = HTTPc_ERR_REQ_FORM_CREATE; goto exit; } p_buf_wr += str_len_key; buf_len -= str_len_key; *p_buf_wr = ASCII_CHAR_EQUALS_SIGN; /* Write the "=" sign. */ p_buf_wr++; buf_len--; url_encode = HTTP_URL_EncodeStr(p_form_field->ValPtr, /* Encode and Write to buffer the Value. */ p_buf_wr, &str_len_val, buf_len); if (url_encode == DEF_FAIL) { *p_err = HTTPc_ERR_REQ_FORM_CREATE; goto exit; } p_buf_wr += str_len_val; buf_len -= str_len_val; if (i < (p_req->FormFieldNbr - 1)) { *p_buf_wr = ASCII_CHAR_AMPERSAND; /* Write the "&" sign between pairs. */ p_buf_wr++; buf_len--; } else { p_conn->ReqFormDataTxIx = 0; } p_form_field++; } *p_err = HTTPc_ERR_NONE; exit: p_conn->TxDataLen += (p_buf_wr - p_buf); /* Update Data Length to transmit. */ return; } #endif /* ********************************************************************************************************* * HTTPcReq_FormMultipart() * * Description : Prepare the multipart type form and copy it to HTTP buffer. * * Argument(s) : p_conn Pointer to current HTTPc Connection. * * p_err Pointer to variable that will receive the return error code from this function : * * HTTPc_ERR_NONE Form created and copied to buf successfully. * HTTPc_ERR_REQ_FORM_FIELD_NULL_PTR Null pointer in form table. * HTTPc_ERR_REQ_FORM_FIELD_INVALID_LEN Invalid value length pass in form table. * HTTPc_ERR_REQ_FORM_CREATE Fatal error occurred in form creation. * HTTPc_ERR_TRANS_BUF_LEN_INVALID HTTP buffer length is not enough big. * HTTPc_ERR_TRANS_TX_BUF_FULL HTTP buffer is full, data must be Tx. * * Return(s) : None. * * Caller(s) : HTTPcReq_Body(). * * Note(s) : (1) Do not support Compression, Encryption and Encoding inside multipart. * Only header Content-Disposition will be added with parameter "name". For file * upload, parameter "filename" will be added also. * Header Content-Type will be added if content-type passed is recognized by client. * * (2) Only support 1 level of multipart. ********************************************************************************************************* */ #if (HTTPc_CFG_FORM_EN == DEF_ENABLED) static void HTTPcReq_FormMultipart(HTTPc_CONN *p_conn, HTTPc_ERR *p_err) { HTTPc_CONN_OBJ *p_conn_const; HTTPc_REQ *p_req; HTTPc_REQ_OBJ *p_req_const; CPU_CHAR *p_buf; CPU_CHAR *p_buf_wr; CPU_CHAR *p_str; CPU_CHAR *p_name; CPU_CHAR *p_filename; HTTPc_FORM_TBL_FIELD *p_form_tbl; HTTPc_FORM_TBL_FIELD *p_tbl_entry; HTTPc_KEY_VAL *p_kvp; HTTPc_KEY_VAL_EXT *p_kvp_big; HTTPc_MULTIPART_FILE *p_multipart_file; HTTP_DICT *p_entry; CPU_SIZE_T name_field_len; CPU_SIZE_T filename_field_len; CPU_SIZE_T buf_len; CPU_SIZE_T data_size; CPU_INT16U data_len_wr; CPU_INT16U name_char_encode_nbr; CPU_INT16U filename_char_encode_nbr; CPU_INT16U i; CPU_BOOLEAN url_encode; CPU_BOOLEAN done; p_conn_const = (HTTPc_CONN_OBJ *)p_conn; p_req = p_conn->ReqListHeadPtr; p_req_const = (HTTPc_REQ_OBJ *)p_req; p_buf = p_conn->TxBufPtr + p_conn->TxDataLen; /* Point to beginning of free space in Tx buffer. */ buf_len = p_conn->BufLen - p_conn->TxDataLen; p_buf_wr = p_buf; data_size = 0; p_form_tbl = p_req->FormFieldTbl; for (i = p_conn->ReqFormDataTxIx; i < p_req->FormFieldNbr; i++) { p_tbl_entry = &p_form_tbl[i]; switch (p_conn->State) { case HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_BOUNDARY: /* Calculate size of needed for data to write. */ data_size = HTTPc_STR_BOUNDARY_START_LEN + STR_CR_LF_LEN; if (data_size > p_conn->BufLen) { /* Validate that buffer size is enough big. */ *p_err = HTTPc_ERR_TRANS_BUF_LEN_INVALID; goto exit; } if (data_size > buf_len) { /* Validate that remaining space in buffer is enough.*/ p_conn->TxDataLen += (p_buf_wr - p_buf);/* Update Data Length to transmit. */ p_conn->ReqFormDataTxIx = i; /* Save the tbl index to start from there next time. */ p_conn->State = HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_BOUNDARY; *p_err = HTTPc_ERR_TRANS_TX_BUF_FULL; goto exit; } /* Write start of boundary. */ p_str = Str_Copy_N(p_buf_wr, HTTPc_STR_BOUNDARY_START, data_size); if (p_str == DEF_NULL) { *p_err = HTTPc_ERR_REQ_FORM_CREATE; goto exit; } p_str += HTTPc_STR_BOUNDARY_START_LEN; /* Write CRLF after boundary. */ p_str = Str_Copy_N(p_str, STR_CR_LF, STR_CR_LF_LEN); if (p_str == DEF_NULL) { *p_err = HTTPc_ERR_REQ_FORM_CREATE; goto exit; } p_str += STR_CR_LF_LEN; buf_len -= (p_str - p_buf_wr); p_buf_wr = p_str; /* 'break' intentionally omitted. */ case HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_HDR_CONTENT_DISPO: switch (p_tbl_entry->Type) { case HTTPc_FORM_FIELD_TYPE_KEY_VAL: p_kvp = (HTTPc_KEY_VAL *)p_tbl_entry->FieldObjPtr; /* Found nbr of chars to encode in name. */ name_field_len = p_kvp->KeyLen; name_char_encode_nbr = HTTP_URL_CharEncodeNbr(p_kvp->KeyPtr, name_field_len); p_name = p_kvp->KeyPtr; p_filename = DEF_NULL; break; case HTTPc_FORM_FIELD_TYPE_KEY_VAL_EXT: p_kvp_big = (HTTPc_KEY_VAL_EXT *)p_tbl_entry->FieldObjPtr; /* Found nbr of chars to encode in name. */ name_field_len = p_kvp_big->KeyLen; name_char_encode_nbr = HTTP_URL_CharEncodeNbr(p_kvp_big->KeyPtr, name_field_len); p_name = p_kvp_big->KeyPtr; p_filename = DEF_NULL; break; case HTTPc_FORM_FIELD_TYPE_FILE: p_multipart_file = (HTTPc_MULTIPART_FILE *)p_tbl_entry->FieldObjPtr; /* Found nbr of chars to encode in name. */ name_field_len = p_multipart_file->NameLen; name_char_encode_nbr = HTTP_URL_CharEncodeNbr(p_multipart_file->NamePtr, name_field_len); p_name = p_multipart_file->NamePtr; /* Found nbr of chars to encode in filename. */ filename_field_len = p_multipart_file->FileNameLen; filename_char_encode_nbr = HTTP_URL_CharEncodeNbr(p_multipart_file->FileNamePtr, filename_field_len); p_filename = p_multipart_file->FileNamePtr; data_size += (2 + /* "; " */ HTTP_STR_MULTIPART_FIELD_FILE_NAME_LEN + /* "filename" */ 3 + /* "=\"\"" */ filename_field_len + /* "filename_rx_in_form" */ HTTP_URL_ENCODING_JUMP * filename_char_encode_nbr);/* nbr of char to encode. */ break; default: *p_err = HTTPc_ERR_FORM_TYPE_INVALID; goto exit; } /* Calculate size needed for data to write. */ data_size += HTTP_STR_HDR_FIELD_CONTENT_DISPOSITION_LEN + /* "Content-Disposition" */ 2 + /* ": " */ HTTP_STR_CONTENT_DISPOSITION_FORM_DATA_LEN + /* "form-data" */ 2 + /* "; " */ HTTP_STR_MULTIPART_FIELD_NAME_LEN + /* "name" */ 3 + /* "=\"\"" */ name_field_len + /* "name_rx_in_form" */ HTTP_URL_ENCODING_JUMP * name_char_encode_nbr + /* nbr of char to encode. */ STR_CR_LF_LEN; /* "\r\n" */ if (data_size > p_conn->BufLen) { /* Validate that buffer size is enough big. */ *p_err = HTTPc_ERR_TRANS_BUF_LEN_INVALID; goto exit; } if (data_size > buf_len) { /* Validate that remaining space in buffer is enough. */ p_conn->TxDataLen += (p_buf_wr - p_buf); /* Update Data Length to transmit. */ p_conn->ReqFormDataTxIx = i; /* Save the tbl index to start from there next time. */ p_conn->State = HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_HDR_CONTENT_DISPO; *p_err = HTTPc_ERR_TRANS_TX_BUF_FULL; goto exit; } /* Write Content-Disposition with name (and filename). */ p_str = HTTPcReq_HdrCopyToBuf(p_buf_wr, p_conn->BufLen, buf_len, HTTP_HDR_FIELD_CONTENT_DISPOSITION, HTTP_STR_CONTENT_DISPOSITION_FORM_DATA, HTTP_STR_CONTENT_DISPOSITION_FORM_DATA_LEN, DEF_NO, p_err); if (p_str == DEF_NULL) { *p_err = HTTPc_ERR_REQ_FORM_CREATE; goto exit; } *p_str = ASCII_CHAR_SEMICOLON; p_str++; *p_str = ASCII_CHAR_SPACE; p_str++; p_str = Str_Copy_N(p_str, HTTP_STR_MULTIPART_FIELD_NAME, HTTP_STR_MULTIPART_FIELD_NAME_LEN); if (p_str == DEF_NULL) { *p_err = HTTPc_ERR_REQ_FORM_CREATE; goto exit; } p_str += HTTP_STR_MULTIPART_FIELD_NAME_LEN; *p_str = ASCII_CHAR_EQUALS_SIGN; p_str++; *p_str = ASCII_CHAR_QUOTATION_MARK; p_str++; url_encode = HTTP_URL_EncodeStr(p_name, p_str, &name_field_len, buf_len); if (url_encode == DEF_FAIL) { *p_err = HTTPc_ERR_REQ_FORM_CREATE; goto exit; } p_str += name_field_len; *p_str = ASCII_CHAR_QUOTATION_MARK; p_str++; if (p_filename != DEF_NULL) { *p_str = ASCII_CHAR_SEMICOLON; p_str++; *p_str = ASCII_CHAR_SPACE; p_str++; p_str = Str_Copy_N(p_str, HTTP_STR_MULTIPART_FIELD_FILE_NAME, HTTP_STR_MULTIPART_FIELD_FILE_NAME_LEN); if (p_str == DEF_NULL) { *p_err = HTTPc_ERR_REQ_FORM_CREATE; goto exit; } p_str += HTTP_STR_MULTIPART_FIELD_FILE_NAME_LEN; *p_str = ASCII_CHAR_EQUALS_SIGN; p_str++; *p_str = ASCII_CHAR_QUOTATION_MARK; p_str++; url_encode = HTTP_URL_EncodeStr(p_filename, p_str, &filename_field_len, buf_len); if (url_encode == DEF_FAIL) { *p_err = HTTPc_ERR_REQ_FORM_CREATE; goto exit; } p_str += filename_field_len; *p_str = ASCII_CHAR_QUOTATION_MARK; p_str++; } p_str = Str_Copy_N(p_str, STR_CR_LF, STR_CR_LF_LEN); if (p_str == DEF_NULL) { *p_err = HTTPc_ERR_REQ_FORM_CREATE; goto exit; } p_str += STR_CR_LF_LEN; buf_len -= (p_str - p_buf_wr); p_buf_wr = p_str; /* 'break' intentionally omitted. */ case HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_HDR_CONTENT_TYPE: switch (p_tbl_entry->Type) { case HTTPc_FORM_FIELD_TYPE_KEY_VAL: case HTTPc_FORM_FIELD_TYPE_KEY_VAL_EXT: p_multipart_file = DEF_NULL; break; case HTTPc_FORM_FIELD_TYPE_FILE: p_multipart_file = (HTTPc_MULTIPART_FILE *)p_tbl_entry->FieldObjPtr; break; default: *p_err = HTTPc_ERR_FORM_TYPE_INVALID; goto exit; } if (p_multipart_file != DEF_NULL) { p_entry = HTTP_Dict_EntryGet(HTTP_Dict_ContentType, HTTP_Dict_ContentTypeSize, p_multipart_file->ContentType); if (p_entry != DEF_NULL) { p_str = HTTPcReq_HdrCopyToBuf(p_buf_wr, p_conn->BufLen, buf_len, HTTP_HDR_FIELD_CONTENT_TYPE, p_entry->StrPtr, p_entry->StrLen, DEF_YES, p_err); switch (*p_err) { case HTTPc_ERR_NONE: break; case HTTPc_ERR_TRANS_TX_BUF_FULL: /* Update Data Length to transmit. */ p_conn->TxDataLen += (p_buf_wr - p_buf); p_conn->ReqFormDataTxIx = i; /* Save the tbl index to start from there next time. */ p_conn->State = HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_HDR_CONTENT_TYPE; goto exit; case HTTPc_ERR_TRANS_BUF_LEN_INVALID: case HTTPc_ERR_REQ_HDR_CREATE: default: goto exit; } } } buf_len -= (p_str - p_buf_wr); p_buf_wr = p_str; if (STR_CR_LF_LEN > p_conn->BufLen) { /* Validate that buffer size is enough big. */ *p_err = HTTPc_ERR_TRANS_BUF_LEN_INVALID; goto exit; } if (STR_CR_LF_LEN > buf_len) { /* Validate that remaining space in buf is enough. */ p_conn->TxDataLen += (p_buf_wr - p_buf); /* Update Data Length to transmit. */ p_conn->ReqFormDataTxIx = i; /* Save the tbl index to start from there next time. */ p_conn->State = HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_HDR_CONTENT_TYPE; *p_err = HTTPc_ERR_TRANS_TX_BUF_FULL; goto exit; } p_str = Str_Copy_N(p_str, STR_CR_LF, STR_CR_LF_LEN); if (p_str == DEF_NULL) { *p_err = HTTPc_ERR_REQ_FORM_CREATE; goto exit; } p_str += STR_CR_LF_LEN; buf_len -= (p_str - p_buf_wr); p_buf_wr = p_str; /* 'break' intentionally omitted. */ case HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_DATA: switch (p_tbl_entry->Type) { case HTTPc_FORM_FIELD_TYPE_KEY_VAL: p_kvp = (HTTPc_KEY_VAL *)p_tbl_entry->FieldObjPtr; if (p_kvp->ValLen > p_conn->BufLen) { /* Validate that buffer size is enough big. */ *p_err = HTTPc_ERR_TRANS_BUF_LEN_INVALID; goto exit; } if (p_kvp->ValLen > buf_len) { /* Validate that remaining space in buf is enough. */ p_conn->TxDataLen += (p_buf_wr - p_buf);/* Update Data Length to transmit. */ p_conn->ReqFormDataTxIx = i; /* Save the tbl index to start from there next time.*/ p_conn->State = HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_DATA; *p_err = HTTPc_ERR_TRANS_TX_BUF_FULL; goto exit; } Mem_Copy(p_buf_wr, p_kvp->ValPtr, p_kvp->ValLen); buf_len -= p_kvp->ValLen;; p_buf_wr += p_kvp->ValLen; break; case HTTPc_FORM_FIELD_TYPE_KEY_VAL_EXT: p_kvp_big = (HTTPc_KEY_VAL_EXT *)p_tbl_entry->FieldObjPtr; done = p_kvp_big->OnValTx(p_conn_const, p_req_const, p_kvp_big, p_buf_wr, buf_len, &data_len_wr); p_conn->ReqDataOffset += data_len_wr; if (data_len_wr > buf_len) { *p_err = HTTPc_ERR_REQ_FORM_CREATE; goto exit; } if (p_conn->ReqDataOffset > p_kvp_big->ValLen) { *p_err = HTTPc_ERR_REQ_FORM_CREATE; goto exit; } if ((done == DEF_YES) && (p_conn->ReqDataOffset < p_kvp_big->ValLen)) { *p_err = HTTPc_ERR_REQ_FORM_CREATE; goto exit; } buf_len -= data_len_wr; p_buf_wr += data_len_wr; if (done == DEF_NO) { p_conn->TxDataLen += (p_buf_wr - p_buf); p_conn->ReqFormDataTxIx = i; /* Save the tbl index to start from there next time.*/ p_conn->State = HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_DATA; *p_err = HTTPc_ERR_TRANS_TX_BUF_FULL; goto exit; } else { p_conn->ReqDataOffset = 0; } break; case HTTPc_FORM_FIELD_TYPE_FILE: p_multipart_file = (HTTPc_MULTIPART_FILE *)p_tbl_entry->FieldObjPtr; done = p_multipart_file->OnFileTx(p_conn_const, p_req_const, p_multipart_file, p_buf_wr, buf_len, &data_len_wr); p_conn->ReqDataOffset += data_len_wr; if (data_len_wr > buf_len) { *p_err = HTTPc_ERR_REQ_FORM_CREATE; goto exit; } if (p_conn->ReqDataOffset > p_multipart_file->FileLen) { *p_err = HTTPc_ERR_REQ_FORM_CREATE; goto exit; } if ((done == DEF_YES) && (p_conn->ReqDataOffset < p_multipart_file->FileLen)) { *p_err = HTTPc_ERR_REQ_FORM_CREATE; goto exit; } buf_len -= data_len_wr; p_buf_wr += data_len_wr; if (done == DEF_NO) { p_conn->TxDataLen += (p_buf_wr - p_buf); p_conn->ReqFormDataTxIx = i; /* Save the tbl index to start from there next time.*/ p_conn->State = HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_DATA; *p_err = HTTPc_ERR_TRANS_TX_BUF_FULL; goto exit; } else { p_conn->ReqDataOffset = 0; } break; default: *p_err = HTTPc_ERR_FORM_TYPE_INVALID; goto exit; } /* 'break' intentionally omitted. */ case HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_DATA_END: p_conn->TxDataPtr = p_conn->TxBufPtr; /* Reset TxDataPtr to the HTTP buffer pointer. */ if (STR_CR_LF_LEN > p_conn->BufLen) { /* Validate that buffer size is enough big. */ *p_err = HTTPc_ERR_TRANS_BUF_LEN_INVALID; goto exit; } if (STR_CR_LF_LEN > buf_len) { /* Validate that remaining space in buffer is enough. */ p_conn->TxDataLen += (p_buf_wr - p_buf); /* Update Data Length to transmit. */ p_conn->ReqFormDataTxIx = i; /* Save the tbl index to start from there next time. */ p_conn->State = HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_DATA_END; *p_err = HTTPc_ERR_TRANS_TX_BUF_FULL; goto exit; } p_str = Str_Copy_N(p_buf_wr, STR_CR_LF, STR_CR_LF_LEN); if (p_str == DEF_NULL) { *p_err = HTTPc_ERR_REQ_FORM_CREATE; goto exit; } p_str += STR_CR_LF_LEN; buf_len -= (p_str - p_buf_wr); p_buf_wr = p_str; /* 'break' intentionally omitted. */ case HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_BOUNDARY_END: if (i == (p_req->FormFieldNbr - 1)) { /* ----------- WRITE END OF BOUNDARY + CRLF ----------- */ /* Calculate size of needed for data to write. */ data_size = Str_Len(HTTPc_STR_BOUNDARY_END) + STR_CR_LF_LEN; if (data_size > p_conn->BufLen) { /* Validate that buffer size is enough big. */ *p_err = HTTPc_ERR_TRANS_BUF_LEN_INVALID; goto exit; } if (data_size > buf_len) { p_conn->TxDataLen += (p_buf_wr - p_buf); p_conn->ReqFormDataTxIx = i; p_conn->State = HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_BOUNDARY_END; *p_err = HTTPc_ERR_TRANS_TX_BUF_FULL; goto exit; } /* Write end of boundary. */ p_str = Str_Copy_N(p_buf_wr, HTTPc_STR_BOUNDARY_END, HTTPc_STR_BOUNDARY_END_LEN); if (p_str == DEF_NULL) { *p_err = HTTPc_ERR_REQ_FORM_CREATE; goto exit; } p_str += HTTPc_STR_BOUNDARY_END_LEN; /* Write CRLF after last boundary. */ p_str = Str_Copy_N(p_str, STR_CR_LF, STR_CR_LF_LEN); if (p_str == DEF_NULL) { *p_err = HTTPc_ERR_REQ_FORM_CREATE; goto exit; } p_str += STR_CR_LF_LEN; buf_len -= (p_str - p_buf_wr); p_buf_wr = p_str; } p_conn->State = HTTPc_CONN_STATE_REQ_BODY_FORM_MULTIPART_BOUNDARY; break; default: *p_err = HTTPc_ERR_CONN_INVALID_STATE; goto exit; } } p_conn->TxDataLen += (p_buf_wr - p_buf); p_conn->ReqFormDataTxIx = 0; p_conn->State = HTTPc_CONN_STATE_REQ_END; *p_err = HTTPc_ERR_NONE; exit: return; } #endif /* ********************************************************************************************************* * HTTPcReq_BodyData() * * Description : Prepare and transmit Data passed by the application. * * Argument(s) : p_conn Pointer to current HTTPc Connection. * * p_err Pointer to variable that will receive the return error code from this function : * * HTTPc_ERR_NONE body transfer is taken place successfully. * HTTPc_ERR_REQ_BODY_CHUNK_PREPARE Fatal error occurred in chunk preparation. * HTTPc_ERR_CONN_INVALID_STATE HTTPc Connection invalid state. * * Return(s) : None. * * Caller(s) : HTTPcReq_Body(). * * Note(s) : (1) Data can be send with the chunked transfer-encoding or with header Content-Length * when data length is known. ********************************************************************************************************* */ static void HTTPcReq_BodyData(HTTPc_CONN *p_conn, HTTPc_ERR *p_err) { HTTPc_REQ *p_req; CPU_SIZE_T buf_len; CPU_BOOLEAN chunk_en; HTTPc_CONN_OBJ *p_conn_const; HTTPc_REQ_OBJ *p_req_const; CPU_CHAR *p_buf; CPU_CHAR *p_buf_wr; #if (HTTPc_CFG_CHUNK_TX_EN == DEF_ENABLED) CPU_SIZE_T nbr_dig_max; CPU_SIZE_T data_size; CPU_CHAR *p_str; CPU_BOOLEAN last_chunk; #endif p_req = p_conn->ReqListHeadPtr; p_conn_const = (HTTPc_CONN_OBJ *)p_conn; p_req_const = (HTTPc_REQ_OBJ *)p_req; p_buf = p_conn->TxBufPtr + p_conn->TxDataLen; /* Point to beginning of free space in Tx buffer. */ buf_len = p_conn->BufLen - p_conn->TxDataLen; p_buf_wr = p_buf; chunk_en = DEF_BIT_IS_SET(p_req->Flags, HTTPc_FLAG_REQ_BODY_CHUNK_TRANSFER); if ((p_conn->TxDataLen != 0u) && (p_req->DataPtr != DEF_NULL)) { *p_err = HTTPc_ERR_NONE; goto exit; } /* -------------- STANDARD TRANSFER MODE -------------- */ if (chunk_en == DEF_NO) { /* Call hook fnct to setup data pointer. */ (void)p_req->OnBodyTx(p_conn_const, p_req_const, &p_req->DataPtr, p_buf_wr, buf_len, &p_conn->ReqDataOffset); if (p_conn->ReqDataOffset == 0) { *p_err = HTTPc_ERR_NONE; goto exit; } if (p_req->DataPtr == DEF_NULL) { /* Case 1: Data was copied in Tx buffer. */ if (p_conn->ReqDataOffset > buf_len) { *p_err = HTTPc_ERR_REQ_BODY_CHUNK_INVALID; goto exit; } p_conn->TxDataPtr = p_conn->TxBufPtr; /* Set TxDataPtr to HTTP buffer ptr. */ } else { /* Case 2: Application Data Pointer was passed. */ p_conn->TxDataPtr = p_req->DataPtr; /* Set pointer to data to Tx. */ } p_conn->TxDataLen += p_conn->ReqDataOffset; /* Update Data Length to transmit. */ if (p_conn->ReqDataOffset > p_req->ContentLen) { *p_err = HTTPc_ERR_REQ_BODY_CHUNK_INVALID; goto exit; } p_req->ContentLen -= p_conn->ReqDataOffset; /* Calculate size of data remaining to Tx. */ if (p_req->ContentLen <= 0) { /* Return if all data sent. */ p_conn->State = HTTPc_CONN_STATE_REQ_END; *p_err = HTTPc_ERR_NONE; goto exit; } /* --------------- CHUNK TRANSFER MODE ---------------- */ } else { #if (HTTPc_CFG_CHUNK_TX_EN == DEF_ENABLED) switch (p_conn->State) { case HTTPc_CONN_STATE_REQ_BODY_DATA: nbr_dig_max = HTTP_StrSizeHexDigReq(p_conn->BufLen); data_size = nbr_dig_max + STR_CR_LF_LEN; if (data_size >= p_conn->BufLen) { *p_err = HTTPc_ERR_TRANS_BUF_LEN_INVALID; goto exit; } if (data_size >= buf_len) { *p_err = HTTPc_ERR_TRANS_TX_BUF_FULL; goto exit; } p_buf_wr += data_size; buf_len -= data_size; /* Call hook fnct to setup data pointer. */ last_chunk = p_req->OnBodyTx(p_conn_const, p_req_const, &p_req->DataPtr, p_buf_wr, buf_len, (CPU_INT16U *)&p_req->ContentLen); /* Use ContentLen to store length of chunk. */ if ((p_req->ContentLen == 0) && (last_chunk == DEF_NO)) { *p_err = HTTPc_ERR_REQ_BODY_CHUNK_INVALID; goto exit; } if (p_req->DataPtr == DEF_NULL) { /* Case 1: Data was copied in Tx buffer. */ if (p_req->ContentLen > buf_len) { *p_err = HTTPc_ERR_REQ_BODY_CHUNK_INVALID; goto exit; } p_conn->TxDataPtr = p_conn->TxBufPtr; /* Set TxDataPtr to HTTP buffer ptr. */ } else { /* Case 2: Application data pointer was passed. */ p_conn->TxDataPtr = p_req->DataPtr; } if (last_chunk == DEF_YES) { DEF_BIT_SET(p_conn->ReqFlags, HTTPc_FLAG_REQ_BODY_CHUNK_LAST); } else { DEF_BIT_CLR(p_conn->ReqFlags, HTTPc_FLAG_REQ_BODY_CHUNK_LAST); } p_conn->State = HTTPc_CONN_STATE_REQ_BODY_DATA_CHUNK_SIZE; /* 'break' intentionally omitted. */ case HTTPc_CONN_STATE_REQ_BODY_DATA_CHUNK_SIZE: nbr_dig_max = HTTP_StrSizeHexDigReq(p_conn->BufLen); data_size = nbr_dig_max + STR_CR_LF_LEN; p_buf_wr -= data_size; buf_len = data_size; p_str = HTTP_ChunkTransferWrSize(p_buf_wr, buf_len, nbr_dig_max, p_req->ContentLen); if (p_str == DEF_NULL) { *p_err = HTTPc_ERR_REQ_BODY_CHUNK_PREPARE; goto exit; } /* Update Data Length to transmit. */ if (p_conn->TxDataPtr == p_conn->TxBufPtr) { p_conn->TxDataLen += (p_str - p_buf_wr) + p_req->ContentLen; p_conn->State = HTTPc_CONN_STATE_REQ_BODY_DATA_CHUNK_END; } else { p_conn->TxDataLen += (p_str - p_buf_wr); p_conn->State = HTTPc_CONN_STATE_REQ_BODY_DATA_CHUNK_DATA; } p_conn->TxDataPtr = p_conn->TxBufPtr; /* Set TxDataPtr to HTTP buffer ptr. */ break; case HTTPc_CONN_STATE_REQ_BODY_DATA_CHUNK_DATA: p_conn->TxDataLen += p_req->ContentLen; /* Update Data Length to transmit. */ p_conn->TxDataPtr = p_req->DataPtr; /* Set TxDataPtr to App data ptr. */ p_conn->State = HTTPc_CONN_STATE_REQ_BODY_DATA_CHUNK_END; break; case HTTPc_CONN_STATE_REQ_BODY_DATA_CHUNK_END: nbr_dig_max = HTTP_StrSizeHexDigReq(p_conn->BufLen); data_size = nbr_dig_max + 3 * STR_CR_LF_LEN; if (data_size > p_conn->BufLen) { *p_err = HTTPc_ERR_TRANS_BUF_LEN_INVALID; goto exit; } if (data_size > buf_len) { *p_err = HTTPc_ERR_TRANS_TX_BUF_FULL; goto exit; } p_str = Str_Copy_N(p_buf_wr, STR_CR_LF, STR_CR_LF_LEN); if (p_str == DEF_NULL) { *p_err = HTTPc_ERR_REQ_BODY_CHUNK_PREPARE; goto exit; } p_str += STR_CR_LF_LEN; buf_len -= STR_CR_LF_LEN; p_buf_wr = p_str; p_conn->TxDataPtr = p_conn->TxBufPtr; /* Set TxDataPtr to internal buffer. */ p_conn->TxDataLen += STR_CR_LF_LEN; /* Update Data Length to transmit. */ last_chunk = DEF_BIT_IS_SET(p_conn->ReqFlags, HTTPc_FLAG_REQ_BODY_CHUNK_LAST); if (last_chunk == DEF_YES) { p_str = HTTP_ChunkTransferWrSize(p_buf_wr, buf_len, nbr_dig_max, 0); if (p_str == DEF_NULL) { *p_err = HTTPc_ERR_REQ_BODY_CHUNK_PREPARE; goto exit; } p_conn->TxDataLen += (p_str - p_buf_wr); /* Update Data Length to transmit. */ p_buf_wr = p_str; p_str = Str_Copy_N(p_buf_wr, STR_CR_LF, STR_CR_LF_LEN); if (p_str == DEF_NULL) { *p_err = HTTPc_ERR_REQ_BODY_CHUNK_PREPARE; goto exit; } p_str += STR_CR_LF_LEN; p_conn->TxDataLen += (p_str - p_buf_wr); /* Update Data Length to transmit. */ p_conn->State = HTTPc_CONN_STATE_REQ_END; } else { p_conn->State = HTTPc_CONN_STATE_REQ_BODY_DATA; } break; default: *p_err = HTTPc_ERR_CONN_INVALID_STATE; goto exit; } #else *p_err = HTTPc_ERR_FEATURE_DIS; goto exit; #endif } *p_err = HTTPc_ERR_NONE; exit: return; }