@@ -67,6 +67,11 @@ class HttpClientTest : public Aws::Testing::AwsCppSdkGTestSuite
6767{
6868};
6969
70+ class CURLHttpClientTest : public Aws ::Testing::AwsCppSdkGTestSuite
71+ {
72+ };
73+
74+
7075TEST_F (HttpClientTest, TestRandomURLWithNoProxy)
7176{
7277 auto httpClient = CreateHttpClient (Aws::Client::ClientConfiguration ());
@@ -382,7 +387,97 @@ TEST_F(CURLHttpClientTest, TestHttpRequestWorksFine)
382387 EXPECT_EQ (Aws::Http::HttpResponseCode::OK, response->GetResponseCode ());
383388 EXPECT_EQ (" " , response->GetClientErrorMessage ());
384389}
390+
391+ #include < aws/core/utils/memory/stl/AWSVector.h>
392+ #include < streambuf>
393+
394+ // A streambuf that supports writing but does NOT support seeking.
395+ // This reproduces the behavior of many filtering / transforming streams.
396+ class NonSeekableWriteBuf final : public std::streambuf
397+ {
398+ public:
399+ explicit NonSeekableWriteBuf (Aws::Vector<char >& out) : m_out(out) {}
400+
401+ protected:
402+ std::streamsize xsputn (const char * s, std::streamsize n) override
403+ {
404+ if (n > 0 )
405+ {
406+ m_out.insert (m_out.end (), s, s + static_cast <size_t >(n));
407+ }
408+ return n;
409+ }
410+
411+ int overflow (int ch) override
412+ {
413+ if (ch == traits_type::eof ())
414+ {
415+ return traits_type::not_eof (ch);
416+ }
417+ m_out.push_back (static_cast <char >(ch));
418+ return ch;
419+ }
420+
421+ // Disallow positioning (seek/tell)
422+ pos_type seekoff (off_type, std::ios_base::seekdir, std::ios_base::openmode) override
423+ {
424+ return pos_type (off_type (-1 ));
425+ }
426+
427+ pos_type seekpos (pos_type, std::ios_base::openmode) override
428+ {
429+ return pos_type (off_type (-1 ));
430+ }
431+
432+ private:
433+ Aws::Vector<char >& m_out;
434+ };
435+
436+ class NonSeekableIOStream final : public Aws::IOStream
437+ {
438+ public:
439+ NonSeekableIOStream (const Aws::String& /* allocationTag*/ , Aws::Vector<char >& out)
440+ : Aws::IOStream(nullptr ), m_buf(out)
441+ {
442+ rdbuf (&m_buf);
443+ }
444+
445+ private:
446+ NonSeekableWriteBuf m_buf;
447+ };
448+
449+ // Regression test:
450+ // Ensure CurlHttpClient can write response bodies into a non-seekable output stream.
451+ // Older implementations that call tellp() as part of the write callback may fail here.
452+ TEST_F (CURLHttpClientTest, TestNonSeekableResponseStreamDoesNotAbortTransfer)
453+ {
454+ Aws::Vector<char > captured;
455+
456+ auto request = CreateHttpRequest (
457+ Aws::String (" http://127.0.0.1:8778" ),
458+ HttpMethod::HTTP_GET,
459+ Aws::Utils::Stream::DefaultResponseStreamFactoryMethod);
460+
461+ request->SetHeaderValue (" WaitSeconds" , " 1" );
462+
463+ request->SetResponseStreamFactory ([&captured]() -> Aws::IOStream*
464+ {
465+ return Aws::New<NonSeekableIOStream>(ALLOCATION_TAG, ALLOCATION_TAG, captured);
466+ });
467+
468+ Aws::Client::ClientConfiguration config;
469+ config.requestTimeoutMs = 10000 ;
470+
471+ auto httpClient = CreateHttpClient (config);
472+ auto response = httpClient->MakeRequest (request);
473+
474+ ASSERT_NE (nullptr , response);
475+
476+ ASSERT_FALSE (response->HasClientError ()) << response->GetClientErrorMessage ();
477+ EXPECT_EQ (Aws::Http::HttpResponseCode::OK, response->GetResponseCode ());
478+
479+ }
385480#endif // ENABLE_CURL_CLIENT
386481#endif // ENABLE_HTTP_CLIENT_TESTING
387482#endif // NO_HTTP_CLIENT
388- #endif // DISABLE_DNS_REQUIRED_TESTS
483+ #endif // DISABLE_DNS_REQUIRED_TESTS
0 commit comments