-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathconnection.cpp
More file actions
95 lines (73 loc) · 2.93 KB
/
connection.cpp
File metadata and controls
95 lines (73 loc) · 2.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include "connection.h"
#include <algorithm>
namespace forceinline {
http::connection::~connection( ) {
if ( !m_connected )
return;
close( );
}
void http::connection::connect( ) {
if ( WSAStartup( MAKEWORD( 2, 2 ), &m_wsa_data ) != 0 )
throw std::exception( "http::connection::connect: WSAStartup failed" );
addrinfo* result = nullptr, hints = { };
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
// Resolve the hostname
if ( getaddrinfo( m_hostname.data( ), "80", &hints, &result ) != 0 )
throw std::exception( "http::connection::connect: failed to resolve hostname" );
// Create a socket
m_socket = socket( result->ai_family, result->ai_socktype, result->ai_protocol );
if ( m_socket == INVALID_SOCKET )
throw std::exception( "http::connection::connect: failed to create socket" );
// Connect to the HTTP server
if ( ::connect( m_socket, result->ai_addr, result->ai_addrlen ) != 0 )
throw std::exception( "http::connection::connect: failed to connect to host" );
// Remember to free up the memory at addrinfo
freeaddrinfo( result );
// Mark us as connected
m_connected = true;
}
void http::connection::close( ) {
// Shutdown the connection properly
shutdown( m_socket, SD_BOTH );
m_socket = INVALID_SOCKET;
WSACleanup( );
m_connected = false;
}
void http::connection::make_request( http::request* request, std::function< void( const http::response& ) > callback ) {
if ( !request )
throw std::exception( "http::connection::make_request: request is nullptr" );
// Set the hostname because it's required in HTTP version 1.1
request->set_hostname( m_hostname );
// Get the finalized request
auto final_request = request->get( );
// Attempt to send the request
std::size_t total_bytes_sent = 0;
do {
int bytes_sent = send( m_socket, final_request.data( ) + total_bytes_sent, final_request.length( ) - total_bytes_sent, 0 );
// Some error occurred, notify the user
if ( bytes_sent <= 0 )
throw std::exception( "http::connection::make_request: error sending request" );
else
total_bytes_sent += bytes_sent;
} while ( total_bytes_sent < final_request.length( ) );
// Attempt to receive our response
int bytes_received = 0;
std::string response_body = { };
bool transmission_finished = false;
do {
bytes_received = recv( m_socket, m_buffer.data( ), m_buffer.capacity( ), 0 );
if ( bytes_received <= 0 )
throw std::exception( "connection::make_request: error receiving response" );
response_body.insert( response_body.end( ), m_buffer.begin( ), m_buffer.begin( ) + bytes_received );
if ( auto header_end = response_body.find( "\r\n\r\n" ); header_end != std::string::npos ) {
http::response response( response_body, &transmission_finished );
if ( transmission_finished && callback ) {
callback( response );
break;
}
}
} while ( !transmission_finished );
}
} // namespace forceinline