Commit 7fc6ef28 authored by Georges Oates_Larsen's avatar Georges Oates_Larsen Committed by Mahesh Mahadevan
Browse files

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: default avatarGeorges Oates_Larsen <georges.larsen@nordicsemi.no>
parent e3549278
Loading
Loading
Loading
Loading
+58 −47
Original line number Diff line number Diff line
@@ -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);