Skip to content

Commit ae407bd

Browse files
committed
Implement BodyReader::read() method and add corresponding tests for content-length reading
1 parent 6e7dfd3 commit ae407bd

File tree

2 files changed

+135
-4
lines changed

2 files changed

+135
-4
lines changed

httplib.h

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7177,11 +7177,34 @@ inline ssize_t Stream::write(const std::string &s) {
71777177
return write(s.data(), s.size());
71787178
}
71797179

7180-
// BodyReader implementation (stub for Phase 2.2, full impl in Phase 2.3)
7180+
// BodyReader implementation
71817181
inline ssize_t detail::BodyReader::read(char *buf, size_t len) {
7182-
(void)buf;
7183-
(void)len;
7184-
// TODO: Implement in Phase 2.3
7182+
if (!stream) { return -1; }
7183+
if (eof) { return 0; }
7184+
7185+
if (!chunked) {
7186+
// Content-Length based reading
7187+
if (bytes_read >= content_length) {
7188+
eof = true;
7189+
return 0;
7190+
}
7191+
7192+
auto remaining = content_length - bytes_read;
7193+
auto to_read = (std::min)(len, remaining);
7194+
auto n = stream->read(buf, to_read);
7195+
7196+
if (n <= 0) {
7197+
eof = true;
7198+
return n;
7199+
}
7200+
7201+
bytes_read += static_cast<size_t>(n);
7202+
if (bytes_read >= content_length) { eof = true; }
7203+
return n;
7204+
}
7205+
7206+
// TODO: Chunked transfer encoding (Phase 2.3 extension)
7207+
// For now, return error for chunked
71857208
return -1;
71867209
}
71877210

test/test20.cc

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,3 +585,111 @@ TEST(BodyReaderTest, InitializeAsChunked) {
585585
EXPECT_TRUE(reader.chunked);
586586
EXPECT_EQ(0u, reader.content_length);
587587
}
588+
589+
//------------------------------------------------------------------------------
590+
// Phase 2.3: BodyReader::read() tests
591+
//------------------------------------------------------------------------------
592+
593+
// Mock stream for testing BodyReader
594+
class MockStream : public httplib::Stream {
595+
public:
596+
explicit MockStream(const std::string &data) : data_(data), pos_(0) {}
597+
598+
bool is_readable() const override { return pos_ < data_.size(); }
599+
bool wait_readable() const override { return is_readable(); }
600+
bool wait_writable() const override { return true; }
601+
602+
ssize_t read(char *ptr, size_t size) override {
603+
if (pos_ >= data_.size()) return 0;
604+
size_t to_read = std::min(size, data_.size() - pos_);
605+
std::memcpy(ptr, data_.data() + pos_, to_read);
606+
pos_ += to_read;
607+
return static_cast<ssize_t>(to_read);
608+
}
609+
610+
ssize_t write(const char *, size_t) override { return -1; }
611+
612+
void get_remote_ip_and_port(std::string &ip, int &port) const override {
613+
ip = "127.0.0.1";
614+
port = 0;
615+
}
616+
617+
void get_local_ip_and_port(std::string &ip, int &port) const override {
618+
ip = "127.0.0.1";
619+
port = 0;
620+
}
621+
622+
socket_t socket() const override { return INVALID_SOCKET; }
623+
time_t duration() const override { return 0; }
624+
625+
private:
626+
std::string data_;
627+
size_t pos_;
628+
};
629+
630+
TEST(BodyReaderTest, ReadWithContentLength) {
631+
std::string body = "Hello, World!";
632+
MockStream stream(body);
633+
634+
httplib::detail::BodyReader reader;
635+
reader.stream = &stream;
636+
reader.content_length = body.size();
637+
reader.chunked = false;
638+
639+
char buf[32];
640+
auto n = reader.read(buf, sizeof(buf));
641+
642+
EXPECT_EQ(static_cast<ssize_t>(body.size()), n);
643+
EXPECT_EQ(body, std::string(buf, static_cast<size_t>(n)));
644+
EXPECT_EQ(body.size(), reader.bytes_read);
645+
}
646+
647+
TEST(BodyReaderTest, ReadWithContentLengthPartial) {
648+
std::string body = "Hello, World!";
649+
MockStream stream(body);
650+
651+
httplib::detail::BodyReader reader;
652+
reader.stream = &stream;
653+
reader.content_length = body.size();
654+
reader.chunked = false;
655+
656+
// Read in small chunks
657+
char buf[5];
658+
std::string result;
659+
660+
ssize_t n;
661+
while ((n = reader.read(buf, sizeof(buf))) > 0) {
662+
result.append(buf, static_cast<size_t>(n));
663+
}
664+
665+
EXPECT_EQ(body, result);
666+
EXPECT_TRUE(reader.eof);
667+
}
668+
669+
TEST(BodyReaderTest, ReadReturnsZeroAtEOF) {
670+
std::string body = "Hi";
671+
MockStream stream(body);
672+
673+
httplib::detail::BodyReader reader;
674+
reader.stream = &stream;
675+
reader.content_length = body.size();
676+
reader.chunked = false;
677+
678+
char buf[32];
679+
reader.read(buf, sizeof(buf)); // Read all
680+
681+
// Next read should return 0
682+
auto n = reader.read(buf, sizeof(buf));
683+
EXPECT_EQ(0, n);
684+
EXPECT_TRUE(reader.eof);
685+
}
686+
687+
TEST(BodyReaderTest, ReadWithoutStream) {
688+
httplib::detail::BodyReader reader;
689+
reader.stream = nullptr;
690+
691+
char buf[32];
692+
auto n = reader.read(buf, sizeof(buf));
693+
694+
EXPECT_EQ(-1, n);
695+
}

0 commit comments

Comments
 (0)