@@ -679,6 +679,109 @@ struct socket_test
679679 s2.close ();
680680 }
681681
682+ // Shutdown
683+
684+ void
685+ testShutdownSend ()
686+ {
687+ io_context ioc;
688+ auto [s1, s2] = test::make_socket_pair (ioc);
689+
690+ auto task = [](socket& a, socket& b) -> capy::task<>
691+ {
692+ // Write data then shutdown send
693+ co_await a.write_some (capy::const_buffer (" hello" , 5 ));
694+ a.shutdown (socket::shutdown_send);
695+
696+ // Read the data
697+ char buf[32 ] = {};
698+ auto [ec1, n1] = co_await b.read_some (
699+ capy::mutable_buffer (buf, sizeof (buf)));
700+ BOOST_TEST (!ec1);
701+ BOOST_TEST_EQ (std::string_view (buf, n1), " hello" );
702+
703+ // Next read should get EOF
704+ auto [ec2, n2] = co_await b.read_some (
705+ capy::mutable_buffer (buf, sizeof (buf)));
706+ BOOST_TEST (ec2 == capy::cond::eof);
707+ };
708+ capy::run_async (ioc.get_executor ())(task (s1, s2));
709+
710+ ioc.run ();
711+ s1.close ();
712+ s2.close ();
713+ }
714+
715+ void
716+ testShutdownReceive ()
717+ {
718+ io_context ioc;
719+ auto [s1, s2] = test::make_socket_pair (ioc);
720+
721+ auto task = [](socket& a, socket& b) -> capy::task<>
722+ {
723+ // Shutdown receive on b
724+ b.shutdown (socket::shutdown_receive);
725+
726+ // b can still send
727+ co_await b.write_some (capy::const_buffer (" from_b" , 6 ));
728+
729+ char buf[32 ] = {};
730+ auto [ec, n] = co_await a.read_some (
731+ capy::mutable_buffer (buf, sizeof (buf)));
732+ BOOST_TEST (!ec);
733+ BOOST_TEST_EQ (std::string_view (buf, n), " from_b" );
734+ };
735+ capy::run_async (ioc.get_executor ())(task (s1, s2));
736+
737+ ioc.run ();
738+ s1.close ();
739+ s2.close ();
740+ }
741+
742+ void
743+ testShutdownOnClosedSocket ()
744+ {
745+ io_context ioc;
746+ socket sock (ioc);
747+
748+ // Shutdown on closed socket should not crash
749+ sock.shutdown (socket::shutdown_send);
750+ sock.shutdown (socket::shutdown_receive);
751+ sock.shutdown (socket::shutdown_both);
752+ }
753+
754+ void
755+ testShutdownBothSendDirection ()
756+ {
757+ io_context ioc;
758+ auto [s1, s2] = test::make_socket_pair (ioc);
759+
760+ auto task = [](socket& a, socket& b) -> capy::task<>
761+ {
762+ // Write data then shutdown both
763+ co_await a.write_some (capy::const_buffer (" goodbye" , 7 ));
764+ a.shutdown (socket::shutdown_both);
765+
766+ // Peer should receive the data
767+ char buf[32 ] = {};
768+ auto [ec1, n1] = co_await b.read_some (
769+ capy::mutable_buffer (buf, sizeof (buf)));
770+ BOOST_TEST (!ec1);
771+ BOOST_TEST_EQ (std::string_view (buf, n1), " goodbye" );
772+
773+ // Next read should get EOF
774+ auto [ec2, n2] = co_await b.read_some (
775+ capy::mutable_buffer (buf, sizeof (buf)));
776+ BOOST_TEST (ec2 == capy::cond::eof);
777+ };
778+ capy::run_async (ioc.get_executor ())(task (s1, s2));
779+
780+ ioc.run ();
781+ s1.close ();
782+ s2.close ();
783+ }
784+
682785 // Data Integrity
683786
684787 void
@@ -770,6 +873,12 @@ struct socket_test
770873 testReadAfterPeerClose ();
771874 testWriteAfterPeerClose ();
772875
876+ // Shutdown
877+ testShutdownSend ();
878+ testShutdownReceive ();
879+ testShutdownOnClosedSocket ();
880+ testShutdownBothSendDirection ();
881+
773882 // Cancellation
774883 testCancelRead ();
775884 testCloseWhileReading ();
0 commit comments