Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/HttpClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -779,7 +779,8 @@ int HttpClient::readHeader()
case eStatusCodeRead:
// We're at the start of a line, or somewhere in the middle of reading
// the Content-Length prefix
if (*iContentLengthPtr == c)
if ((iTransferEncodingChunkedPtr == kTransferEncodingChunked) &&
(tolower((unsigned char)*iContentLengthPtr) == tolower((unsigned char)c)))
{
// This character matches, just move along
iContentLengthPtr++;
Expand All @@ -793,7 +794,8 @@ int HttpClient::readHeader()
iBodyLengthConsumed = 0;
}
}
else if (*iTransferEncodingChunkedPtr == c)
else if ((iContentLengthPtr == kContentLengthPrefix) &&
(tolower((unsigned char)*iTransferEncodingChunkedPtr) == tolower((unsigned char)c)))
{
// This character matches, just move along
iTransferEncodingChunkedPtr++;
Expand Down
4 changes: 4 additions & 0 deletions tests/host/Arduino.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#pragma once
#include <ArduinoAPI.h>
#include <Client.h>
using namespace arduino;
22 changes: 22 additions & 0 deletions tests/host/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Response parser regression

Use a local checkout of [ArduinoCore-API](https://github.com/arduino/ArduinoCore-API)
for the production String/Stream/Client interfaces and its host conversion helpers.
The test transport supplies in-memory HTTP response bytes; it does not emulate a
Wi-Fi chip or prove behavior on physical hardware.

From this repository root, set `CORE_API` to that checkout and run:

```sh
c++ -std=c++11 -fsanitize=address,undefined -Itests/host -I"$CORE_API" \
-idirafter "$CORE_API/api" -Isrc src/HttpClient.cpp src/b64.cpp \
tests/host/response_headers.cpp "$CORE_API"/api/String.cpp \
"$CORE_API"/api/Print.cpp "$CORE_API"/api/Stream.cpp \
"$CORE_API"/api/IPAddress.cpp "$CORE_API"/test/src/itoa.cpp \
"$CORE_API"/test/src/dtostrf.cpp -o /tmp/http-response-headers
/tmp/http-response-headers
```

`-idirafter` avoids shadowing the system `string.h` with Arduino's `String.h`
on case-insensitive filesystems. The Core API emits existing weak-main warnings
with Clang on macOS.
50 changes: 50 additions & 0 deletions tests/host/response_headers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include "HttpClient.h"
#include <assert.h>
#include <stdio.h>
#include <string>

unsigned long millis() { static unsigned long clock = 0; return ++clock; }
void delay(unsigned long) {}

class ResponseClient : public Client {
std::string response;
size_t position = 0;
public:
explicit ResponseClient(const std::string& value) : response(value) {}
int connect(IPAddress, uint16_t) override { return 1; }
int connect(const char*, uint16_t) override { return 1; }
size_t write(uint8_t) override { return 1; }
size_t write(const uint8_t*, size_t size) override { return size; }
int available() override { return response.size() - position; }
int read() override { return available() ? (unsigned char)response[position++] : -1; }
int read(uint8_t* data, size_t size) override {
size_t count = 0;
while (count < size && available()) data[count++] = read();
return count;
}
int peek() override { return available() ? (unsigned char)response[position] : -1; }
void flush() override {}
void stop() override {}
uint8_t connected() override { return available() > 0; }
operator bool() override { return true; }
};
int main() {
for (const char* name : {"Content-Length", "content-length", "CONTENT-LENGTH", "cOnTeNt-LeNgTh"}) {
ResponseClient transport(std::string("HTTP/1.1 200 OK\r\nX-Test: ignored\r\n") + name + ": 5\r\n\r\nhello");
HttpClient http(transport, "example.test");
assert(http.get("/") == 0);
assert(http.responseStatusCode() == 200);
assert(http.contentLength() == 5);
assert(http.responseBody() == "hello");
}
for (const char* field : {"Transfer-Encoding: chunked", "transfer-encoding: chunked", "TRANSFER-ENCODING: CHUNKED", "tRaNsFeR-EnCoDiNg: ChUnKeD"}) {
ResponseClient transport(std::string("HTTP/1.1 200 OK\r\n") + field + "\r\n\r\n5\r\nhello\r\n0\r\n\r\n");
HttpClient http(transport, "example.test");
assert(http.get("/") == 0);
assert(http.responseStatusCode() == 200);
assert(http.skipResponseHeaders() == 0);
assert(http.isResponseChunked());
assert(http.responseBody() == "hello");
}
puts("eight response-header casing cases passed");
}
Loading