Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Burl: HTTP and SOCKS5 proxy support #20

Merged
merged 4 commits into from
Nov 4, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 100 additions & 86 deletions example/client/burl/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ target(urls::url_view url) noexcept

struct is_redirect_result
{
bool is_redirect;
bool need_method_change;
bool is_redirect = false;
bool need_method_change = false;
};

is_redirect_result
Expand All @@ -119,18 +119,36 @@ is_redirect(http_proto::status status) noexcept
// user agents do change the method in practice.
switch(status)
{
case http_proto::status::moved_permanently:
case http_proto::status::found:
case http_proto::status::see_other:
return { true, true };
case http_proto::status::temporary_redirect:
case http_proto::status::permanent_redirect:
return { true, false };
default:
return { false, false };
case http_proto::status::moved_permanently:
case http_proto::status::found:
case http_proto::status::see_other:
return { true, true };
case http_proto::status::temporary_redirect:
case http_proto::status::permanent_redirect:
return { true, false };
default:
return { false, false };
}
}

bool
can_reuse_connection(
http_proto::response_view response,
urls::url_view a,
urls::url_view b) noexcept
{
if(a.encoded_origin() != b.encoded_origin())
return false;

if(response.version() != http_proto::version::http_1_1)
return false;

if(response.metadata().connection.close)
return false;

return true;
}

class any_stream
{
public:
Expand Down Expand Up @@ -378,8 +396,7 @@ class multipart_form
std::optional<std::uint64_t> file_size;
};

// storage_ containts boundary with extra "--" prefix and postfix.
// This reduces the number of steps needed during serialization.
// boundary with extra "--" prefix and postfix.
std::array<char, 2 + 46 + 2> storage_{ generate_boundary() };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this braced-initialization is going to eventually mess you up

std::vector<part_t> parts_;

Expand Down Expand Up @@ -554,61 +571,61 @@ class multipart_form::source
{
switch(step_)
{
case 0:
// --boundary
if(!copy({ form_->storage_.data(),
form_->storage_.size() - 2 })) return rs;
++step_;
case 1:
if(!copy(content_disposition_)) return rs;
++step_;
case 2:
if(!copy(it_->name)) return rs;
++step_;
case 3:
if(!copy("\"")) return rs;
++step_;
case 4:
if(!it_->file_size.has_value())
goto content_type;
if(!copy(filename_)) return rs;
++step_;
case 5:
if(!copy(filename(it_->value_or_path))) return rs;
++step_;
case 6:
if(!copy("\"")) return rs;
++step_;
case 7:
content_type:
if(it_->content_type.empty())
goto end_of_header;
if(!copy(content_type_)) return rs;
++step_;
case 8:
if(!copy(it_->content_type)) return rs;
++step_;
case 9:
end_of_header:
if(!copy("\r\n\r\n")) return rs;
++step_;
case 10:
if(it_->file_size)
{
if(!read(
it_->value_or_path,
it_->file_size.value())) return rs;
}
else
{
if(!copy(it_->value_or_path)) return rs;
}
++step_;
case 11:
if(!copy("\r\n"))
return rs;
step_ = 0;
++it_;
case 0:
// --boundary
if(!copy({ form_->storage_.data(),
form_->storage_.size() - 2 })) return rs;
++step_;
case 1:
if(!copy(content_disposition_)) return rs;
++step_;
case 2:
if(!copy(it_->name)) return rs;
++step_;
case 3:
if(!copy("\"")) return rs;
++step_;
case 4:
if(!it_->file_size.has_value())
goto content_type;
if(!copy(filename_)) return rs;
++step_;
case 5:
if(!copy(filename(it_->value_or_path))) return rs;
++step_;
case 6:
if(!copy("\"")) return rs;
++step_;
case 7:
content_type:
if(it_->content_type.empty())
goto end_of_header;
if(!copy(content_type_)) return rs;
++step_;
case 8:
if(!copy(it_->content_type)) return rs;
++step_;
case 9:
end_of_header:
if(!copy("\r\n\r\n")) return rs;
++step_;
case 10:
if(it_->file_size)
{
if(!read(
it_->value_or_path,
it_->file_size.value())) return rs;
}
else
{
if(!copy(it_->value_or_path)) return rs;
}
++step_;
case 11:
if(!copy("\r\n"))
return rs;
step_ = 0;
++it_;
}
}

Expand Down Expand Up @@ -687,7 +704,6 @@ class message
}
};


asio::awaitable<any_stream>
connect(ssl::context& ssl_ctx, urls::url_view url)
{
Expand Down Expand Up @@ -809,7 +825,7 @@ request(
co_await http_io::async_read_header(stream, parser);

// handle redirects
auto referer_url = urls::url{ url };
auto referer = urls::url{ url };
for(;;)
{
auto [is_redirect, need_method_change] =
Expand All @@ -822,11 +838,16 @@ request(
if(auto it = response.find(http_proto::field::location);
it != response.end())
{
auto redirect_url = urls::parse_uri(it->value).value();
auto redirect = urls::parse_uri(it->value).value();

// Consume the body
co_await http_io::async_read(stream, parser);

// TODO: reuse the established connection when possible
co_await stream.async_shutdown(asio::as_tuple);
stream = co_await connect(ssl_ctx, redirect_url);
if(!can_reuse_connection(response, referer, redirect))
{
co_await stream.async_shutdown(asio::as_tuple);
stream = co_await connect(ssl_ctx, redirect);
}

// Change the method according to RFC 9110, Section 15.4.4.
if(need_method_change && !vm.count("head"))
Expand All @@ -836,11 +857,11 @@ request(
request.erase(http_proto::field::content_type);
msg = {}; // drop the body
}
request.set_target(target(redirect_url));
request.set(http_proto::field::host, redirect_url.host());
request.set(http_proto::field::referer, referer_url);
request.set_target(target(redirect));
request.set(http_proto::field::host, redirect.host());
request.set(http_proto::field::referer, redirect);

referer_url = redirect_url;
referer = redirect;

serializer.reset();
msg.start_serializer(serializer, request);
Expand Down Expand Up @@ -891,8 +912,6 @@ request(
int
main(int argc, char* argv[])
{
int co_main(int argc, char* argv[]);
//return co_main(argc, argv);
try
{
auto odesc = po::options_description{"Options"};
Expand Down Expand Up @@ -963,12 +982,7 @@ main(int argc, char* argv[])

auto url = urls::parse_uri(vm.at("url").as<std::string>());
if(url.has_error())
{
std::cerr
<< "Failed to parse URL\n"
<< "Error: " << url.error().what() << std::endl;
return EXIT_FAILURE;
}
throw system_error{ url.error(), "Failed to parse URL" };

auto ioc = asio::io_context{};
auto ssl_ctx = ssl::context{ ssl::context::tlsv12_client };
Expand Down