Skip to content

Commit

Permalink
add support for pure http request
Browse files Browse the repository at this point in the history
  • Loading branch information
lry127 committed Feb 2, 2023
1 parent 3c23b27 commit 7437421
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 42 deletions.
2 changes: 1 addition & 1 deletion gfw_proxy/client_session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,5 +210,5 @@ void Client_session::destroy()
out_socket_.close(ec);
}

std::cerr << "connection to " << ssl_socket_.next_layer().remote_endpoint(ec).address() << " closed.\n";
std::cerr << "connection to " << request_.get_host() + ":" + request_.get_port() << " closed.\n";
}
100 changes: 65 additions & 35 deletions gfw_proxy/httprequest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,16 @@ void HttpRequest::read_head(const std::string& head_data)
if (method == "CONNECT")
method_ = CONNECT;
else
{
method_ = OTHERS;
valid_proxy_request_ = false;
return;
}

// now read the target
auto pos = target.find(':');
if (pos == std::string::npos)
{
valid_proxy_request_ = false;
return;
}
host_ = target.substr(0, pos);
port_ = target.substr(pos + 1);

method_str_ = method;
target_ = target;

if (version == "HTTP/1.1")
{
version_ = HTTP_1_1;
}
else
{
version_ = HTTP_OTHERS;
valid_proxy_request_ = false;
return;
}

Expand Down Expand Up @@ -74,10 +60,6 @@ bool HttpRequest::read_fields(const std::string& fields_data)

bool HttpRequest::check_valid_proxy_request(const std::string& password)
{
// first check the format of the request
if (!valid_proxy_request_)
return false;

/* check if the request contains proper password
note that though here we use "Proxy-Authorization" field
it doesn't necessarily mean that we'll strict follow the
Expand All @@ -86,9 +68,13 @@ bool HttpRequest::check_valid_proxy_request(const std::string& password)
well as security reasons (the transfer is encrypted
under secure tls1.3 traffic)
*/
if (get_field("Proxy-Authorization") != password)
if (!valid_proxy_request_ || get_field("Proxy-Authorization") != password)
return false;

// now this is a valid proxy request
// we need to know which server it wants to connect to
read_host_port_info();

// check finished, everything's ok
return true;

Expand All @@ -113,21 +99,65 @@ std::string HttpRequest::get_405_not_allowed_message()

std::string HttpRequest::get_proxy_message(const std::string& password)
{
if (host_.empty() && port_.empty())
read_host_port_info();
std::string req;

if (port_ == "80")
{
std::string host_port_data = get_field("Host");
auto pos = host_port_data.find(":");
if (pos == std::string::npos)
{
host_ = host_port_data;
port_ = "80";
}
// since the request will be sent to the real server
// other than only other proxy server, we need to
// add the original field info to the req
req = std::string(method_str_);

auto pos = target_.find("://");
pos = target_.find('/', pos + 3);
req += " " + target_.substr(pos);
req += " HTTP/1.1\r\n";

for (auto& [field, data] : fields_)
if (field != "Proxy-Connection")
req += field + ": " + data + "\r\n";
else
req += "Connection: " + data + "\r\n";
}
else
{
req = std::string("CONNECT ");
req += get_host() + ":" + get_port() + " ";
req += "HTTP/1.1\r\n";
req += "Host: " + get_host() + ":" + get_port() + "\r\n";
}

std::string req("CONNECT ");
req += get_host() + ":" + get_port() + " ";
req += "HTTP/1.1\r\n";
req += "Proxy-Authorization: " + password;
req += "\r\n\r\n";
return req;
req += "\r\n";

return req + "\r\n";
}

void HttpRequest::read_host_port_info()
{
std::string host_port_data = get_field("Host");

auto pos = host_port_data.find(':');
if (pos == std::string::npos)
{
// there's no ':' in Host field, default to 80
host_ = host_port_data;
port_ = "80";
}
else
{
host_ = host_port_data.substr(0, pos);
port_ = host_port_data.substr(pos + 1);
}
}

std::string HttpRequest::parse_plain_http_request()
{
std::string req(method_str_);
req += " " + target_ + " HTTP/1.1\r\n";
for (auto& [field, data] : fields_)
if (field != "Proxy-Authorization")
req += field + ": " + data + "\r\n";
return req + "\r\n";
}
4 changes: 4 additions & 0 deletions gfw_proxy/httprequest.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,17 @@ class HttpRequest
bool is_valid_proxy_request(const std::string& password) { return check_valid_proxy_request(password); }
const std::string& get_field(const std::string& key) { return fields_[key]; } // todo: add boundary checking
std::string get_proxy_message(const std::string&);
std::string parse_plain_http_request();
static std::string get_200_ok_message();
static std::string get_405_not_allowed_message();
static std::string get_404_not_found_message();
private:
bool check_valid_proxy_request(const std::string& password);
bool valid_proxy_request_ = false;
void read_host_port_info();
method method_;
std::string method_str_;
std::string target_;
http_version version_;
std::string host_;
std::string port_;
Expand Down
23 changes: 17 additions & 6 deletions gfw_proxy/server_session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ void Server_session::on_in_received(raw_data_ptr data)
std::shared_ptr<boost::asio::ip::tcp::resolver::query> query;
if (!request_.is_valid_proxy_request(config_.get_password()))
{
// the proxy request in invalid (eg. either not CONNECT method or incorrect password
// the proxy request in invalid (provided incorrect password or no password)
// we forword this request to a pre-configured real http server to provide service
query.reset(new boost::asio::ip::tcp::resolver::query(config_.get_http_service_address(), std::to_string(config_.get_http_service_port())));
}
Expand All @@ -75,20 +75,31 @@ void Server_session::on_in_received(raw_data_ptr data)

if (request_.is_valid_proxy_request(config_.get_password()))
{
read_state_ = HEAD_FINISHED;
std::cerr << "accepted connection to " << request_.get_host() << ":" << request_.get_port() << std::endl;
data_ptr ok_data_str = std::make_shared <std::string>(HttpRequest::get_200_ok_message());
boost::asio::async_write(ssl_socket_, boost::asio::buffer(*ok_data_str), [this, self, ok_data_str](const boost::system::error_code& ec, size_t len) {
if (request_.get_port() == "80")
{
data_ptr original_data_ptr = std::make_shared<std::string>(request_.parse_plain_http_request());
boost::asio::async_write(out_socket_, boost::asio::buffer(*original_data_ptr), [this, self, original_data_ptr](const boost::system::error_code& ec, size_t len) {
read_state_ = FORWARD;
on_out_sent();
});
}
else
{
data_ptr ok_data_str = std::make_shared <std::string>(HttpRequest::get_200_ok_message());
boost::asio::async_write(ssl_socket_, boost::asio::buffer(*ok_data_str), [this, self, ok_data_str](const boost::system::error_code& ec, size_t len) {
read_state_ = HEAD_FINISHED;
on_in_sent();
});
});
}
}
else
{
read_state_ = FORWARD;
std::cerr << "unrecongnized http request, redirecting to http sercive... (from: " << ssl_socket_.next_layer().remote_endpoint().address() << ")\n";
// now forward the original http message to the real http server
data_ptr original_data_ptr = std::make_shared<std::string>(conn_esta_msg_);
boost::asio::async_write(out_socket_, boost::asio::buffer(*original_data_ptr), [this, self, original_data_ptr](const boost::system::error_code& ec, size_t len) {
read_state_ = FORWARD;
on_out_sent();
});
}
Expand Down

0 comments on commit 7437421

Please sign in to comment.