@@ -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