@@ -105,6 +105,22 @@ target(urls::url_view url) noexcept
105
105
return url.encoded_target ();
106
106
}
107
107
108
+ core::string_view
109
+ effective_port (urls::url_view url)
110
+ {
111
+ if (url.has_port ())
112
+ return url.port ();
113
+
114
+ if (url.scheme () == " https" )
115
+ return " 443" ;
116
+
117
+ if (url.scheme () == " http" )
118
+ return " 80" ;
119
+
120
+ throw std::runtime_error{
121
+ " Unsupported scheme" };
122
+ }
123
+
108
124
struct is_redirect_result
109
125
{
110
126
bool is_redirect = false ;
@@ -705,31 +721,97 @@ class message
705
721
};
706
722
707
723
asio::awaitable<any_stream>
708
- connect (ssl::context& ssl_ctx, urls::url_view url)
724
+ connect (
725
+ const po::variables_map& vm,
726
+ ssl::context& ssl_ctx,
727
+ http_proto::context& http_proto_ctx,
728
+ urls::url_view url)
709
729
{
710
730
auto executor = co_await asio::this_coro::executor;
711
731
auto resolver = asio::ip::tcp::resolver{ executor };
712
- auto service = url.has_port () ? url.port () : url.scheme ();
713
- auto rresults = co_await resolver.async_resolve (url.host (), service);
732
+ auto stream = asio::ip::tcp::socket{ executor };
733
+
734
+ if (vm.count (" proxy" ))
735
+ {
736
+ auto proxy_url = urls::parse_uri (vm.at (" proxy" ).as <std::string>());
737
+
738
+ if (proxy_url.has_error ())
739
+ throw system_error{ proxy_url.error (), " Failed to parse proxy" };
740
+
741
+ if (proxy_url->scheme () != " http" )
742
+ throw std::runtime_error{ " only HTTP proxies are supported" };
743
+
744
+ // Connect to the HTTP proxy server
745
+ auto rr = co_await resolver.async_resolve (
746
+ proxy_url->host (), effective_port (proxy_url.value ()));
747
+ co_await asio::async_connect (stream, rr);
748
+
749
+ {
750
+ using http_proto::field;
751
+ auto request = http_proto::request{};
752
+ auto host = std::string{ url.encoded_host () };
753
+
754
+ host.push_back (' :' );
755
+ host.append (effective_port (url));
756
+
757
+ request.set_method (http_proto::method::connect);
758
+ request.set_target (host);
759
+ request.set (field::host, host);
760
+ request.set (field::proxy_connection, " keep-alive" );
761
+
762
+ if (vm.count (" user-agent" ))
763
+ {
764
+ request.set (
765
+ field::user_agent,
766
+ vm.at (" user-agent" ).as <std::string>());
767
+ }
768
+ else
769
+ {
770
+ request.set (field::user_agent, " Boost.Http.Io" );
771
+ }
772
+
773
+ // TODO
774
+ // request.set(field::proxy_authorization, "");
775
+
776
+ auto serializer = http_proto::serializer{ http_proto_ctx };
777
+ serializer.start (request);
778
+ co_await http_io::async_write (stream, serializer);
779
+ }
780
+
781
+ {
782
+ auto parser = http_proto::response_parser{ http_proto_ctx };
783
+ parser.reset ();
784
+ parser.start ();
785
+ co_await http_io::async_read_header (stream, parser);
786
+ if (parser.get ().status () != http_proto::status::ok)
787
+ throw std::runtime_error{
788
+ " Proxy server rejected the connection" };
789
+ }
790
+ }
791
+ else // no proxy
792
+ {
793
+ auto rr = co_await resolver.async_resolve (
794
+ url.host (), effective_port (url));
795
+ co_await asio::async_connect (stream, rr);
796
+ }
714
797
715
798
if (url.scheme () == " https" )
716
799
{
717
- auto stream = ssl::stream<asio::ip::tcp::socket>{ executor, ssl_ctx };
718
- co_await asio::async_connect (stream. lowest_layer ( ), rresults) ;
800
+ auto ssl_stream = ssl::stream<asio::ip::tcp::socket>{
801
+ std::move (stream), ssl_ctx } ;
719
802
720
- if (auto host_s = std::string{ url.host () };
721
- !SSL_set_tlsext_host_name (stream.native_handle (), host_s.c_str ()))
803
+ auto host = std::string{ url.host () };
804
+ if (!SSL_set_tlsext_host_name (
805
+ ssl_stream.native_handle (), host.c_str ()))
722
806
{
723
807
throw system_error{ static_cast <int >(::ERR_get_error ()),
724
808
asio::error::get_ssl_category () };
725
809
}
726
810
727
- co_await stream .async_handshake (ssl::stream_base::client);
728
- co_return stream ;
811
+ co_await ssl_stream .async_handshake (ssl::stream_base::client);
812
+ co_return ssl_stream ;
729
813
}
730
814
731
- auto stream = asio::ip::tcp::socket{ executor };
732
- co_await asio::async_connect (stream, rresults);
733
815
co_return stream;
734
816
}
735
817
@@ -813,7 +895,7 @@ request(
813
895
http_proto::request request,
814
896
urls::url_view url)
815
897
{
816
- auto stream = co_await connect (ssl_ctx, url);
898
+ auto stream = co_await connect (vm, ssl_ctx, http_proto_ctx , url);
817
899
auto parser = http_proto::response_parser{ http_proto_ctx };
818
900
auto serializer = http_proto::serializer{ http_proto_ctx };
819
901
@@ -838,15 +920,18 @@ request(
838
920
if (auto it = response.find (http_proto::field::location);
839
921
it != response.end ())
840
922
{
841
- auto redirect = urls::parse_uri (it->value ).value ();
923
+ auto location = urls::parse_uri (it->value ).value ();
842
924
843
925
// Consume the body
844
926
co_await http_io::async_read (stream, parser);
845
927
846
- if (!can_reuse_connection (response, referer, redirect ))
928
+ if (!can_reuse_connection (response, referer, location ))
847
929
{
848
- co_await stream.async_shutdown (asio::as_tuple);
849
- stream = co_await connect (ssl_ctx, redirect);
930
+ if (!vm.count (" proxy" ))
931
+ co_await stream.async_shutdown (asio::as_tuple);
932
+
933
+ stream = co_await connect (
934
+ vm, ssl_ctx, http_proto_ctx, location);
850
935
}
851
936
852
937
// Change the method according to RFC 9110, Section 15.4.4.
@@ -857,11 +942,11 @@ request(
857
942
request.erase (http_proto::field::content_type);
858
943
msg = {}; // drop the body
859
944
}
860
- request.set_target (target (redirect ));
861
- request.set (http_proto::field::host, redirect .host ());
862
- request.set (http_proto::field::referer, redirect );
945
+ request.set_target (target (location ));
946
+ request.set (http_proto::field::host, location .host ());
947
+ request.set (http_proto::field::referer, location );
863
948
864
- referer = redirect ;
949
+ referer = location ;
865
950
866
951
serializer.reset ();
867
952
msg.start_serializer (serializer, request);
@@ -904,9 +989,12 @@ request(
904
989
}
905
990
906
991
// clean shutdown
907
- auto [ec] = co_await stream.async_shutdown (asio::as_tuple);
908
- if (ec && ec != ssl::error::stream_truncated)
909
- throw system_error{ ec };
992
+ if (!vm.count (" proxy" ))
993
+ {
994
+ auto [ec] = co_await stream.async_shutdown (asio::as_tuple);
995
+ if (ec && ec != ssl::error::stream_truncated)
996
+ throw system_error{ ec };
997
+ }
910
998
};
911
999
912
1000
int
@@ -936,6 +1024,9 @@ main(int argc, char* argv[])
936
1024
(" output,o" ,
937
1025
po::value<std::string>()->value_name (" <file>" ),
938
1026
" Write to file instead of stdout" )
1027
+ (" proxy,x" ,
1028
+ po::value<std::string>()->value_name (" <url>" ),
1029
+ " Use this proxy" )
939
1030
(" range,r" ,
940
1031
po::value<std::string>()->value_name (" <range>" ),
941
1032
" Retrieve only the bytes within range" )
0 commit comments