net: lib: http: Correct http_client edge-case behavior
Correct various small edge-case behaviors that have been accidentally introduced in the http_client. - http_client_req no longer incorrectly returns -ETIMEDOUT on NULL HTTP resonse. -ETIMEDOUT is now only returned when the underlying TLS socket times out. - http_client_req now returns -ECONRESET upon incomplete (but non-NULL) HTTP response. The request callback is no longer called in this case (as with any other error state). - http_wait_data has been refactored slightly to increase clarity. Signed-off-by: Georges Oates_Larsen <georges.larsen@nordicsemi.no>
This commit is contained in:
parent
e354927895
commit
7fc6ef28d3
|
@ -392,7 +392,11 @@ static void http_client_init_parser(struct http_parser *parser,
|
|||
settings->on_url = on_url;
|
||||
}
|
||||
|
||||
static void http_data_final_null_resp(struct http_request *req)
|
||||
/* Report a NULL HTTP response to the caller.
|
||||
* A NULL response is when the HTTP server intentionally closes the TLS socket (using FINACK)
|
||||
* without sending any HTTP payload.
|
||||
*/
|
||||
static void http_report_null(struct http_request *req)
|
||||
{
|
||||
if (req->internal.response.cb) {
|
||||
NET_DBG("Calling callback for Final Data"
|
||||
|
@ -413,6 +417,28 @@ static void http_data_final_null_resp(struct http_request *req)
|
|||
}
|
||||
}
|
||||
|
||||
/* Report a completed HTTP transaction (with no error) to the caller */
|
||||
static void http_report_complete(struct http_request *req)
|
||||
{
|
||||
if (req->internal.response.cb) {
|
||||
NET_DBG("Calling callback for %zd len data", req->internal.response.data_len);
|
||||
req->internal.response.cb(&req->internal.response, HTTP_DATA_FINAL,
|
||||
req->internal.user_data);
|
||||
}
|
||||
}
|
||||
|
||||
/* Report that some data has been received, but the HTTP transaction is still ongoing. */
|
||||
static void http_report_progress(struct http_request *req)
|
||||
{
|
||||
if (req->internal.response.cb) {
|
||||
NET_DBG("Calling callback for partitioned %zd len data",
|
||||
req->internal.response.data_len);
|
||||
|
||||
req->internal.response.cb(&req->internal.response, HTTP_DATA_MORE,
|
||||
req->internal.user_data);
|
||||
}
|
||||
}
|
||||
|
||||
static int http_wait_data(int sock, struct http_request *req, int32_t timeout)
|
||||
{
|
||||
int total_received = 0;
|
||||
|
@ -438,24 +464,26 @@ static int http_wait_data(int sock, struct http_request *req, int32_t timeout)
|
|||
ret = zsock_poll(fds, nfds, remaining_time);
|
||||
if (ret == 0) {
|
||||
LOG_DBG("Timeout");
|
||||
goto finalize_data;
|
||||
ret = -ETIMEDOUT;
|
||||
goto error;
|
||||
} else if (ret < 0) {
|
||||
ret = -errno;
|
||||
goto error;
|
||||
}
|
||||
if (fds[0].revents & (ZSOCK_POLLERR | ZSOCK_POLLNVAL)) {
|
||||
ret = -errno;
|
||||
goto error;
|
||||
} else if (fds[0].revents & ZSOCK_POLLHUP) {
|
||||
/* Connection closed */
|
||||
LOG_DBG("Connection closed");
|
||||
goto finalize_data;
|
||||
goto closed;
|
||||
} else if (fds[0].revents & ZSOCK_POLLIN) {
|
||||
received = zsock_recv(sock, req->internal.response.recv_buf + offset,
|
||||
req->internal.response.recv_buf_len - offset, 0);
|
||||
if (received == 0) {
|
||||
/* Connection closed */
|
||||
LOG_DBG("Connection closed");
|
||||
goto finalize_data;
|
||||
goto closed;
|
||||
} else if (received < 0) {
|
||||
ret = -errno;
|
||||
goto error;
|
||||
} else {
|
||||
req->internal.response.data_len += received;
|
||||
|
@ -472,27 +500,11 @@ static int http_wait_data(int sock, struct http_request *req, int32_t timeout)
|
|||
offset = 0;
|
||||
}
|
||||
|
||||
if (req->internal.response.cb) {
|
||||
bool notify = false;
|
||||
enum http_final_call event;
|
||||
|
||||
if (req->internal.response.message_complete) {
|
||||
NET_DBG("Calling callback for %zd len data",
|
||||
req->internal.response.data_len);
|
||||
|
||||
notify = true;
|
||||
event = HTTP_DATA_FINAL;
|
||||
http_report_complete(req);
|
||||
break;
|
||||
} else if (offset == 0) {
|
||||
NET_DBG("Calling callback for partitioned %zd len data",
|
||||
req->internal.response.data_len);
|
||||
|
||||
notify = true;
|
||||
event = HTTP_DATA_MORE;
|
||||
}
|
||||
|
||||
if (notify) {
|
||||
req->internal.response.cb(&req->internal.response, event,
|
||||
req->internal.user_data);
|
||||
http_report_progress(req);
|
||||
|
||||
/* Re-use the result buffer and start to fill it again */
|
||||
req->internal.response.data_len = 0;
|
||||
|
@ -501,25 +513,28 @@ static int http_wait_data(int sock, struct http_request *req, int32_t timeout)
|
|||
}
|
||||
}
|
||||
|
||||
if (req->internal.response.message_complete) {
|
||||
ret = total_received;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} while (true);
|
||||
|
||||
return ret;
|
||||
return total_received;
|
||||
|
||||
finalize_data:
|
||||
ret = total_received;
|
||||
closed:
|
||||
LOG_DBG("Connection closed");
|
||||
|
||||
http_data_final_null_resp(req);
|
||||
return ret;
|
||||
/* If connection was closed with no data sent, this is a NULL response, and is a special
|
||||
* case valid response.
|
||||
*/
|
||||
if (total_received == 0) {
|
||||
http_report_null(req);
|
||||
return total_received;
|
||||
}
|
||||
|
||||
/* Otherwise, connection was closed mid-way through response, and this should be
|
||||
* considered an error.
|
||||
*/
|
||||
ret = -ECONNRESET;
|
||||
|
||||
error:
|
||||
LOG_DBG("Connection error (%d)", errno);
|
||||
ret = -errno;
|
||||
LOG_DBG("Connection error (%d)", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -719,10 +734,6 @@ int http_client_req(int sock, struct http_request *req,
|
|||
NET_DBG("Wait data failure (%d)", total_recv);
|
||||
ret = total_recv;
|
||||
goto out;
|
||||
} else if (total_recv == 0) {
|
||||
NET_DBG("Timeout while waiting data");
|
||||
ret = -ETIMEDOUT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
NET_DBG("Received %d bytes", total_recv);
|
||||
|
|
Loading…
Reference in a new issue