net: http: Fix HTTP_DATA_FINAL notification

HTTP_DATA_FINAL was incorrectly notified in case Content Length field
was present in the HTTP respone - in such case it was set for every
response fragment, not only the last one.

Fix this by relying on `message_complete` flag instead of
`http_should_keep_alive()` function to determine whether to notify
HTTP_DATA_FINAL or not. As the HTTP parser calls the
`on_message_complete()` callback in either case (response is chunked or
not), this seems to be a more reasonable apporach to determine whether
the fragment is final or not.

Additinally, instead of calling response callback for
`on_body`/`on_message_complete` separately, call it directly from
`http_wait_data()` function, after the parsing round. This fixes the
case when headers were not reported correctly when the provided buffer
was smaller than the total headers length, resulting in corrupted data
being reported to the user.

Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
This commit is contained in:
Robert Lubos 2021-09-14 17:01:32 +02:00 committed by Anas Nashif
commit be7faf7c08

View file

@ -269,28 +269,6 @@ static int on_body(struct http_parser *parser, const char *at, size_t length)
req->internal.response.body_start = (uint8_t *)at;
}
if (req->internal.response.cb) {
if (http_should_keep_alive(parser)) {
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);
} else {
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);
}
/* Re-use the result buffer and start to fill it again */
req->internal.response.data_len = 0;
req->internal.response.body_start = NULL;
}
return 0;
}
@ -354,12 +332,6 @@ static int on_message_complete(struct http_parser *parser)
req->internal.response.message_complete = 1;
if (req->internal.response.cb) {
req->internal.response.cb(&req->internal.response,
HTTP_DATA_FINAL,
req->internal.user_data);
}
return 0;
}
@ -416,12 +388,21 @@ static int http_wait_data(int sock, struct http_request *req)
do {
received = zsock_recv(sock, req->internal.response.recv_buf + offset,
req->internal.response.recv_buf_len - offset,
0);
req->internal.response.recv_buf_len - offset,
0);
if (received == 0) {
/* Connection closed */
LOG_DBG("Connection closed");
ret = total_received;
if (req->internal.response.cb) {
NET_DBG("Calling callback for closed connection");
req->internal.response.cb(&req->internal.response,
HTTP_DATA_FINAL,
req->internal.user_data);
}
break;
} else if (received < 0) {
/* Socket error */
@ -445,6 +426,35 @@ static int http_wait_data(int sock, struct http_request *req)
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;
} 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);
/* Re-use the result buffer and start to fill it again */
req->internal.response.data_len = 0;
req->internal.response.body_start = NULL;
}
}
if (req->internal.response.message_complete) {
ret = total_received;
break;