Skip to content

Commit 40f9ffe

Browse files
author
Ognjen Sobajic
committed
SSL Linux after codereview - iteraion 2.
1 parent 546f598 commit 40f9ffe

File tree

3 files changed

+69
-59
lines changed

3 files changed

+69
-59
lines changed

Release/src/http/client/http_linux.cpp

Lines changed: 47 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -57,18 +57,25 @@ namespace web { namespace http
5757
}
5858

5959
std::unique_ptr<tcp::socket> m_socket;
60-
std::unique_ptr<boost::asio::ssl::stream<tcp::socket>> p_ssl_stream;
60+
std::unique_ptr<boost::asio::ssl::stream<tcp::socket>> m_ssl_stream;
6161

6262
uri m_what;
6363
size_t m_known_size;
6464
size_t m_current_size;
6565
bool m_needChunked;
6666
bool m_timedout;
67-
bool m_ssl;
6867
boost::asio::streambuf m_request_buf;
6968
boost::asio::streambuf m_response_buf;
7069
std::unique_ptr<boost::asio::deadline_timer> m_timer;
7170

71+
template <typename socket_type>
72+
void shutdown_socket(socket_type &socket)
73+
{
74+
boost::system::error_code ignore;
75+
socket.shutdown(tcp::socket::shutdown_both, ignore);
76+
socket.close();
77+
}
78+
7279
~linux_request_context()
7380
{
7481
if (m_timer)
@@ -77,17 +84,16 @@ namespace web { namespace http
7784
m_timer.reset();
7885
}
7986

80-
if (!m_ssl && m_socket)
87+
if (m_socket)
8188
{
82-
boost::system::error_code ignore;
83-
m_socket->shutdown(tcp::socket::shutdown_both, ignore);
84-
m_socket->close();
89+
shutdown_socket(*m_socket);
8590
m_socket.reset();
8691
}
8792

88-
if (m_ssl && p_ssl_stream)
93+
if (m_ssl_stream)
8994
{
90-
p_ssl_stream->shutdown();
95+
shutdown_socket(m_ssl_stream->lowest_layer());
96+
m_ssl_stream.reset();
9197
}
9298
}
9399

@@ -133,25 +139,27 @@ namespace web { namespace http
133139
auto what = ctx->m_what;
134140
auto resource = what.resource().to_string();
135141

136-
ctx->m_ssl = what.scheme() == "https";
137-
138-
if (ctx->m_ssl)
142+
if (what.scheme() == "https")
139143
{
140144
boost::asio::ssl::context context(boost::asio::ssl::context::sslv23);
141145
context.set_verify_mode(boost::asio::ssl::context::verify_none);
142-
ctx->p_ssl_stream.reset(new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(m_io_service, context));
146+
ctx->m_ssl_stream.reset(new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(m_io_service, context));
143147
}
144148
else
145149
ctx->m_socket.reset(new tcp::socket(m_io_service));
146150

147151
if (resource == "") resource = "/";
148152

149153
auto method = ctx->m_request.method();
154+
150155
// stop injection of headers via method
151156
// resource should be ok, since it's been encoded
152157
// and host won't resolve
153158
if (std::find(method.begin(), method.end(), '\r') != method.end())
154-
throw std::runtime_error("invalid method string");
159+
{
160+
ctx->report_exception(std::runtime_error("invalid method string"));
161+
return;
162+
}
155163

156164
auto host = what.host();
157165
std::ostream request_stream(&ctx->m_request_buf);
@@ -160,7 +168,7 @@ namespace web { namespace http
160168

161169
int port = what.port();
162170
if (port == 0)
163-
port = (ctx->m_ssl ? 443 : 80);
171+
port = (ctx->m_ssl_stream ? 443 : 80);
164172
request_stream << ":" << port << CRLF;
165173

166174
// Check user specified transfer-encoding
@@ -183,8 +191,7 @@ namespace web { namespace http
183191
else
184192
{
185193
has_body = false;
186-
if (!ctx->m_ssl)
187-
ctx->m_request.headers()[header_names::content_length] = U("0");
194+
ctx->m_request.headers()[header_names::content_length] = U("0");
188195
}
189196
}
190197

@@ -195,9 +202,9 @@ namespace web { namespace http
195202

196203
request_stream << flatten_http_headers(ctx->m_request.headers());
197204

198-
if (!ctx->m_ssl)
199-
request_stream << "Connection: close" << CRLF; // so we can just read to EOF
200-
205+
if (!ctx->m_ssl_stream)
206+
request_stream << "Connection: close" << CRLF; // so we can just read to EOF
207+
201208
request_stream << CRLF;
202209

203210
tcp::resolver::query query(host, utility::conversions::print_string(port));
@@ -206,13 +213,7 @@ namespace web { namespace http
206213
ctx->m_timer->expires_from_now(boost::posix_time::milliseconds(timeout));
207214
ctx->m_timer->async_wait(boost::bind(&linux_request_context::cancel, ctx, boost::asio::placeholders::error));
208215

209-
if (ctx->m_ssl)
210-
{
211-
boost::asio::ip::tcp::resolver::iterator iter = m_resolver.resolve(query);
212-
boost::asio::async_connect(ctx->p_ssl_stream->lowest_layer(), iter, boost::bind(&client::handle_connect, this, boost::asio::placeholders::error, boost::asio::placeholders::iterator, ctx));
213-
}
214-
else
215-
m_resolver.async_resolve(query, boost::bind(&client::handle_resolve, this, boost::asio::placeholders::error, boost::asio::placeholders::iterator, ctx));
216+
m_resolver.async_resolve(query, boost::bind(&client::handle_resolve, this, boost::asio::placeholders::error, boost::asio::placeholders::iterator, ctx));
216217
}
217218

218219
private:
@@ -246,11 +247,8 @@ namespace web { namespace http
246247
else
247248
{
248249
auto endpoint = *endpoints;
249-
if (ctx->m_ssl)
250-
{
251-
boost::asio::ip::tcp::resolver::iterator endpoint_iterator;
252-
boost::asio::async_connect((*(ctx->p_ssl_stream)).lowest_layer(), endpoint_iterator, boost::bind(&client::handle_connect, this, boost::asio::placeholders::error, ++endpoints, ctx));
253-
}
250+
if (ctx->m_ssl_stream)
251+
ctx->m_ssl_stream->lowest_layer().async_connect(endpoint, boost::bind(&client::handle_connect, this, boost::asio::placeholders::error, ++endpoints, ctx));
254252
else
255253
ctx->m_socket->async_connect(endpoint, boost::bind(&client::handle_connect, this, boost::asio::placeholders::error, ++endpoints, ctx));
256254
}
@@ -260,8 +258,8 @@ namespace web { namespace http
260258
{
261259
if (!ec)
262260
{
263-
if (ctx->m_ssl)
264-
ctx->p_ssl_stream->async_handshake(boost::asio::ssl::stream_base::client, boost::bind(&client::handle_handshake, this, boost::asio::placeholders::error, ctx));
261+
if (ctx->m_ssl_stream)
262+
ctx->m_ssl_stream->async_handshake(boost::asio::ssl::stream_base::client, boost::bind(&client::handle_handshake, this, boost::asio::placeholders::error, ctx));
265263
else
266264
boost::asio::async_write(*ctx->m_socket, ctx->m_request_buf, boost::bind(&client::handle_write_request, this, boost::asio::placeholders::error, ctx));
267265
}
@@ -271,31 +269,21 @@ namespace web { namespace http
271269
}
272270
else
273271
{
274-
if (ctx->m_ssl)
275-
{
276-
ctx->report_error("SSL Failed to connect to any resolved endpoint", ec);
277-
return;
278-
}
279-
280272
boost::system::error_code ignore;
281273
ctx->m_socket->shutdown(tcp::socket::shutdown_both, ignore);
282274
ctx->m_socket->close();
283275
ctx->m_socket.reset(new tcp::socket(m_io_service));
284276
auto endpoint = *endpoints;
285277
ctx->m_socket->async_connect(endpoint, boost::bind(&client::handle_connect, this, boost::asio::placeholders::error, ++endpoints, ctx));
286-
}
278+
}
287279
}
288280

289281
void handle_handshake(const boost::system::error_code& ec, linux_request_context* ctx)
290282
{
291283
if (!ec)
292-
{
293-
boost::asio::async_write(*ctx->p_ssl_stream, ctx->m_request_buf, boost::bind(&client::handle_write_request, this, boost::asio::placeholders::error, ctx));
294-
}
284+
boost::asio::async_write(*ctx->m_ssl_stream, ctx->m_request_buf, boost::bind(&client::handle_write_request, this, boost::asio::placeholders::error, ctx));
295285
else
296-
{
297-
std::cout << "Error code in handle_handshake is " << ec << std::endl;
298-
}
286+
ctx->report_error("Error code in handle_handshake is ", ec);
299287
}
300288

301289
void handle_write_chunked_body(const boost::system::error_code& ec, linux_request_context* ctx)
@@ -325,8 +313,8 @@ namespace web { namespace http
325313
ctx->m_request_buf.consume(offset);
326314
ctx->m_current_size += readSize;
327315
ctx->m_uploaded += (size64_t)readSize;
328-
if (ctx->m_ssl)
329-
boost::asio::async_write(*ctx->p_ssl_stream, ctx->m_request_buf,
316+
if (ctx->m_ssl_stream)
317+
boost::asio::async_write(*ctx->m_ssl_stream, ctx->m_request_buf,
330318
boost::bind(readSize != 0 ? &client::handle_write_chunked_body : &client::handle_write_body, this, boost::asio::placeholders::error, ctx));
331319
else
332320
boost::asio::async_write(*ctx->m_socket, ctx->m_request_buf,
@@ -362,8 +350,8 @@ namespace web { namespace http
362350
ctx->m_uploaded += (size64_t)actualSize;
363351
ctx->m_current_size += actualSize;
364352
ctx->m_request_buf.commit(actualSize);
365-
if (ctx->m_ssl)
366-
boost::asio::async_write(*ctx->p_ssl_stream, ctx->m_request_buf,
353+
if (ctx->m_ssl_stream)
354+
boost::asio::async_write(*ctx->m_ssl_stream, ctx->m_request_buf,
367355
boost::bind(&client::handle_write_large_body, this, boost::asio::placeholders::error, ctx));
368356
else
369357
boost::asio::async_write(*ctx->m_socket, ctx->m_request_buf,
@@ -399,8 +387,8 @@ namespace web { namespace http
399387
}
400388

401389
// Read until the end of entire headers
402-
if (ctx->m_ssl)
403-
boost::asio::async_read_until(*ctx->p_ssl_stream, ctx->m_response_buf, CRLF+CRLF,
390+
if (ctx->m_ssl_stream)
391+
boost::asio::async_read_until(*ctx->m_ssl_stream, ctx->m_response_buf, CRLF+CRLF,
404392
boost::bind(&client::handle_status_line, this, boost::asio::placeholders::error, ctx));
405393
else
406394
boost::asio::async_read_until(*ctx->m_socket, ctx->m_response_buf, CRLF+CRLF,
@@ -494,8 +482,8 @@ namespace web { namespace http
494482
boost::bind(&client::handle_read_content, this, boost::asio::placeholders::error, ctx), ctx);
495483
else
496484
{
497-
if (ctx->m_ssl)
498-
boost::asio::async_read_until(*ctx->p_ssl_stream, ctx->m_response_buf, CRLF,
485+
if (ctx->m_ssl_stream)
486+
boost::asio::async_read_until(*ctx->m_ssl_stream, ctx->m_response_buf, CRLF,
499487
boost::bind(&client::handle_chunk_header, this, boost::asio::placeholders::error, ctx));
500488
else
501489
boost::asio::async_read_until(*ctx->m_socket, ctx->m_response_buf, CRLF,
@@ -507,12 +495,12 @@ namespace web { namespace http
507495
template <typename ReadHandler>
508496
void async_read_until_buffersize(size_t size, ReadHandler handler, linux_request_context* ctx)
509497
{
510-
if (ctx->m_ssl)
498+
if (ctx->m_ssl_stream)
511499
{
512500
if (ctx->m_response_buf.size() >= size)
513-
boost::asio::async_read(*ctx->p_ssl_stream, ctx->m_response_buf, boost::asio::transfer_at_least(0), handler);
501+
boost::asio::async_read(*ctx->m_ssl_stream, ctx->m_response_buf, boost::asio::transfer_at_least(0), handler);
514502
else
515-
boost::asio::async_read(*ctx->p_ssl_stream, ctx->m_response_buf, boost::asio::transfer_at_least(size - ctx->m_response_buf.size()), handler);
503+
boost::asio::async_read(*ctx->m_ssl_stream, ctx->m_response_buf, boost::asio::transfer_at_least(size - ctx->m_response_buf.size()), handler);
516504
}
517505
else
518506
{
@@ -589,8 +577,8 @@ namespace web { namespace http
589577
}
590578
ctx->m_response_buf.consume(to_read + CRLF.size()); // consume crlf
591579

592-
if (ctx->m_ssl)
593-
boost::asio::async_read_until(*ctx->p_ssl_stream, ctx->m_response_buf, CRLF,
580+
if (ctx->m_ssl_stream)
581+
boost::asio::async_read_until(*ctx->m_ssl_stream, ctx->m_response_buf, CRLF,
594582
boost::bind(&client::handle_chunk_header, this, boost::asio::placeholders::error, ctx));
595583
else
596584
boost::asio::async_read_until(*ctx->m_socket, ctx->m_response_buf, CRLF,

Release/tests/Functional/http/client/connections_and_errors.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,27 @@ TEST_FIXTURE(uri_address, request_timeout, "Ignore:Linux", "TFS#612139")
134134
VERIFY_THROWS_HTTP_ERROR_CODE(client.request(methods::GET).get(), std::errc::timed_out);
135135
}
136136

137+
TEST_FIXTURE(uri_address, invalid_method)
138+
{
139+
web::http::uri uri(U("https://www.bing.com/"));
140+
http_client client(uri);
141+
VERIFY_THROWS(client.request("my\rmethod").get(), std::runtime_error);
142+
}
143+
144+
// This test sends an SSL request to a non-SSL server and should fail on handshaking
145+
TEST_FIXTURE(uri_address, handshake_fail, "Ignore:Linux", "TFS#747982")
146+
{
147+
web::http::uri ssl_uri(U("https://localhost:34568/"));
148+
149+
test_http_server server(m_uri);
150+
VERIFY_ARE_EQUAL(0u, server.open());
151+
152+
http_client client(ssl_uri);
153+
auto request = client.request(methods::GET);
154+
155+
VERIFY_THROWS(request.get(), std::exception);
156+
}
157+
137158
#if !defined(__cplusplus_winrt)
138159
// This test still sometimes segfaults on Linux, but I'm re-enabling it [AL]
139160
TEST_FIXTURE(uri_address, content_ready_timeout)

Release/tests/Functional/http/client/outside_tests.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ TEST_FIXTURE(uri_address, outside_ssl_json,
115115

116116
// Send request
117117
web::http::client::http_client playlistClient(playlistUri.to_uri());
118+
118119
playlistClient.request(methods::GET).then([=](http_response playlistResponse) -> pplx::task<json::value>
119120
{
120121
return playlistResponse.extract_json();

0 commit comments

Comments
 (0)