@@ -104,6 +104,12 @@ static void http_finish_request(struct http_request *req);
104104static int http_start_request (struct http_request * req );
105105static bool http_headers_complete (struct http_request * req , char * header_end ,
106106 struct sm_socket * sock , bool hup );
107+ static void http_warn_incomplete_transfer (const struct http_request * req );
108+ static int http_recv_read (struct http_request * req , struct sm_socket * sock );
109+ static void http_process_recv_headers (struct http_request * req , struct sm_socket * sock ,
110+ uint8_t events );
111+ static void http_process_recv_body (struct http_request * req , struct sm_socket * sock ,
112+ uint8_t events );
107113static int parse_http_status_code (const char * buf , int * status_code );
108114static int parse_content_length (const char * buf , const char * header_end , int * length );
109115static bool parse_connection_close (const char * buf , const char * header_end );
@@ -635,8 +641,9 @@ static bool http_headers_complete(struct http_request *req, char *header_end,
635641 LOG_DBG ("HTTP %d: Headers complete, status %d" , req -> fd , req -> status_code );
636642
637643 /*
638- * If POLLHUP co-fired the connection is already closing; finish now
639- * rather than re-arming POLLIN on a socket that will never fire again.
644+ * If POLLHUP arrived together with POLLIN, the connection is already
645+ * closing; finish now rather than re-arming POLLIN on a socket that
646+ * will never fire again.
640647 */
641648 if (hup ) {
642649 if (req -> content_length > 0 && req -> bytes_sent < req -> content_length )
@@ -662,6 +669,112 @@ static bool http_headers_complete(struct http_request *req, char *header_end,
662669 return false;
663670}
664671
672+ static void http_warn_incomplete_transfer (const struct http_request * req )
673+ {
674+ if (req -> content_length > 0 &&
675+ req -> total_received < req -> content_length &&
676+ !req -> connection_close ) {
677+ LOG_WRN ("HTTP %d: Incomplete transfer - received %d/%d bytes" ,
678+ req -> fd , req -> total_received , req -> content_length );
679+ }
680+ }
681+
682+ /*
683+ * Read from the socket into recv_buf. On success, updates recv_buf_len,
684+ * total_received, and the idle timeout.
685+ *
686+ * Returns: >0 bytes read
687+ * 0 EOF (connection closed)
688+ * -1 EAGAIN (POLLIN re-armed)
689+ * -errno other recv error (caller should fail the request)
690+ */
691+ static int http_recv_read (struct http_request * req , struct sm_socket * sock )
692+ {
693+ int ret ;
694+
695+ ret = nrf_recv (req -> fd , req -> recv_buf + req -> recv_buf_len ,
696+ HTTP_RECV_BUF_SIZE - req -> recv_buf_len - 1 , NRF_MSG_DONTWAIT );
697+
698+ if (ret < 0 ) {
699+ if (errno == EAGAIN || errno == EWOULDBLOCK ) {
700+ set_xapoll_events (sock , NRF_POLLIN );
701+ return -1 ;
702+ }
703+ if (errno == ETIMEDOUT ) {
704+ LOG_ERR ("Recv timed out" );
705+ return - ETIMEDOUT ;
706+ }
707+ LOG_ERR ("Recv failed: %d" , errno );
708+ return - errno ;
709+ }
710+
711+ if (ret == 0 ) {
712+ return 0 ;
713+ }
714+
715+ req -> recv_buf_len += ret ;
716+ req -> recv_buf [req -> recv_buf_len ] = '\0' ;
717+ req -> total_received += ret ;
718+ req -> timeout_timestamp = k_uptime_get () + HTTP_RESPONSE_TIMEOUT_MS ;
719+
720+ return ret ;
721+ }
722+
723+ static void http_process_recv_headers (struct http_request * req , struct sm_socket * sock ,
724+ uint8_t events )
725+ {
726+ char * header_end = strstr ((char * )req -> recv_buf , "\r\n\r\n" );
727+
728+ if (header_end ) {
729+ if (http_headers_complete (req , header_end , sock , events & NRF_POLLHUP )) {
730+ return ;
731+ }
732+ req -> need_rearm_pollin = true;
733+ return ;
734+ }
735+
736+ if (req -> recv_buf_len >= HTTP_RECV_BUF_SIZE - 1 ) {
737+ LOG_ERR ("HTTP headers too large" );
738+ http_fail_request (req );
739+ return ;
740+ }
741+
742+ req -> need_rearm_pollin = true;
743+ }
744+
745+ static void http_process_recv_body (struct http_request * req , struct sm_socket * sock ,
746+ uint8_t events )
747+ {
748+ bool body_done ;
749+
750+ if (req -> manual_mode ) {
751+ /*
752+ * POLLIN fired in body state after xapoll_stop (race).
753+ * nrf_recv already consumed bytes from the socket buffer
754+ * into recv_buf and incremented total_received. Keep
755+ * recv_buf intact so the host can pull it; do NOT reset
756+ * recv_buf_len or the data is silently lost.
757+ */
758+ xapoll_stop (sock );
759+ return ;
760+ }
761+
762+ body_done = chunked_eof (req -> recv_buf , req -> recv_buf_len );
763+
764+ http_send_data (req , req -> recv_buf , req -> recv_buf_len );
765+ req -> recv_buf_len = 0 ;
766+
767+ /* Finish if content-length satisfied, chunked EOF, or connection closing. */
768+ if (body_done ||
769+ (req -> content_length > 0 && req -> bytes_sent >= req -> content_length ) ||
770+ (events & NRF_POLLHUP )) {
771+ http_finish_request (req );
772+ return ;
773+ }
774+
775+ req -> need_rearm_pollin = true;
776+ }
777+
665778/* Process HTTP request state machine (event-driven via XAPOLL) */
666779static void http_process_request (struct http_request * req , uint8_t events )
667780{
@@ -726,146 +839,62 @@ static void http_process_request(struct http_request *req, uint8_t events)
726839 break ;
727840
728841 case HTTP_STATE_RECEIVING_HEADERS :
729- case HTTP_STATE_RECEIVING_BODY :
730- /* POLLHUP without POLLIN before headers are received is an error:
731- * the server closed the connection before sending a valid response.
732- * When POLLIN co-fires, data may still be in the socket buffer (e.g.
733- * a server with Connection: close that sends headers and closes
734- * simultaneously); fall through to the POLLIN handler to drain it.
842+ /*
843+ * POLLHUP without POLLIN: closed before any response. When POLLIN
844+ * arrives at the same time, drain the socket buffer (e.g. Connection: close).
735845 */
736- if (events & NRF_POLLHUP ) {
737- if (!req -> headers_complete && !(events & NRF_POLLIN )) {
738- /* Server closed before headers arrived and no data to read */
739- LOG_ERR ("HTTP %d: Connection closed before headers (POLLHUP)" ,
740- req -> fd );
741- http_fail_request (req );
742- return ;
743- }
744- if (req -> headers_complete && !(events & NRF_POLLIN )) {
745- /* POLLHUP alone during body reception: server closed cleanly
746- * after all data. Treat as EOF.
747- */
748- if (req -> state == HTTP_STATE_RECEIVING_BODY ) {
749- if (req -> content_length > 0 &&
750- req -> total_received < req -> content_length &&
751- !req -> connection_close )
752- LOG_WRN ("HTTP %d: Incomplete - %d/%d bytes" ,
753- req -> fd , req -> total_received ,
754- req -> content_length );
755- http_finish_request (req );
756- return ;
757- }
758- /* In RECEIVING_HEADERS, fall through to the POLLIN
759- * handler which may drain remaining bytes.
760- */
761- }
846+ if ((events & NRF_POLLHUP ) && !(events & NRF_POLLIN )) {
847+ LOG_ERR ("HTTP %d: Connection closed before headers (POLLHUP)" ,
848+ req -> fd );
849+ http_fail_request (req );
850+ return ;
762851 }
763852
764- /* Handle POLLIN */
765- if (events & (NRF_POLLIN )) {
766- ret = nrf_recv (req -> fd , req -> recv_buf + req -> recv_buf_len ,
767- HTTP_RECV_BUF_SIZE - req -> recv_buf_len - 1 ,
768- NRF_MSG_DONTWAIT );
853+ if (!(events & NRF_POLLIN )) {
854+ break ;
855+ }
769856
770- if (ret < 0 ) {
771- if (errno == EAGAIN || errno == EWOULDBLOCK ) {
772- /* Socket buffer empty - re-arm POLLIN to wait for more data
773- */
774- set_xapoll_events (sock , NRF_POLLIN );
775- return ;
776- }
777- if (errno == ETIMEDOUT ) {
778- LOG_ERR ("Recv timed out" );
779- http_fail_request (req );
780- return ;
781- }
782- LOG_ERR ("Recv failed: %d" , errno );
857+ ret = http_recv_read (req , sock );
858+ if (ret < 0 ) {
859+ if (ret != -1 ) {
783860 http_fail_request (req );
784- return ;
785861 }
862+ return ;
863+ }
864+ if (ret == 0 ) {
865+ http_fail_request (req );
866+ return ;
867+ }
786868
787- if (ret == 0 ) {
788- /* Connection closed by server (EOF) */
789- if (!req -> headers_complete ) {
790- /* Closed before headers arrived */
791- http_fail_request (req );
792- return ;
793- }
794-
795- /* Check if we received all expected data */
796- if (req -> content_length > 0 &&
797- req -> total_received < req -> content_length &&
798- !req -> connection_close ) {
799- LOG_WRN ("HTTP %d: Incomplete transfer - received %d/%d "
800- "bytes" ,
801- req -> fd , req -> total_received ,
802- req -> content_length );
803- }
804-
805- http_finish_request (req );
806- return ;
807- }
808-
809- /* Data received - update idle timeout */
810- req -> recv_buf_len += ret ;
811- req -> recv_buf [req -> recv_buf_len ] = '\0' ;
812- req -> total_received += ret ;
813- req -> timeout_timestamp = k_uptime_get () + HTTP_RESPONSE_TIMEOUT_MS ;
814-
815- if (req -> state == HTTP_STATE_RECEIVING_HEADERS ) {
816- /* Look for end of headers */
817- char * header_end = strstr ((char * )req -> recv_buf , "\r\n\r\n" );
818-
819- if (header_end ) {
820- if (http_headers_complete (req , header_end , sock ,
821- events & NRF_POLLHUP )) {
822- return ;
823- }
824- req -> need_rearm_pollin = true;
825- return ;
826- }
869+ http_process_recv_headers (req , sock , events );
870+ break ;
827871
828- /* Check if buffer is full */
829- if (req -> recv_buf_len >= HTTP_RECV_BUF_SIZE - 1 ) {
830- LOG_ERR ("HTTP headers too large" );
831- http_fail_request (req );
832- return ;
833- }
834- } else {
835- /* Receiving body */
836- if (req -> manual_mode ) {
837- /*
838- * POLLIN fired in body state after xapoll_stop (race).
839- * nrf_recv already consumed bytes from the socket buffer
840- * into recv_buf and incremented total_received. Keep
841- * recv_buf intact so the host can pull it; do NOT reset
842- * recv_buf_len or the data is silently lost.
843- */
844- xapoll_stop (sock );
845- return ;
846- }
847- bool body_done = chunked_eof (req -> recv_buf , req -> recv_buf_len );
872+ case HTTP_STATE_RECEIVING_BODY :
873+ /* POLLHUP alone: server closed cleanly after sending body. */
874+ if ((events & NRF_POLLHUP ) && !(events & NRF_POLLIN )) {
875+ http_warn_incomplete_transfer (req );
876+ http_finish_request (req );
877+ return ;
878+ }
848879
849- http_send_data (req , req -> recv_buf , req -> recv_buf_len );
850- req -> recv_buf_len = 0 ;
880+ if (!(events & NRF_POLLIN )) {
881+ break ;
882+ }
851883
852- /* Finish if:
853- * - content-length satisfied (known-length transfer)
854- * - chunked terminator "0\r\n\r\n" just received
855- * - POLLHUP co-fired (connection closing)
856- */
857- if (body_done ||
858- (req -> content_length > 0 &&
859- req -> bytes_sent >= req -> content_length ) ||
860- (events & NRF_POLLHUP )) {
861- http_finish_request (req );
862- return ;
863- }
884+ ret = http_recv_read (req , sock );
885+ if (ret < 0 ) {
886+ if (ret != -1 ) {
887+ http_fail_request (req );
864888 }
865-
866- /* Set flag for socket layer to re-arm POLLIN for continuous reception */
867- req -> need_rearm_pollin = true;
889+ return ;
890+ }
891+ if (ret == 0 ) {
892+ http_warn_incomplete_transfer (req );
893+ http_finish_request (req );
894+ return ;
868895 }
896+
897+ http_process_recv_body (req , sock , events );
869898 break ;
870899
871900 case HTTP_STATE_IDLE :
0 commit comments