Commit | Line | Data |
---|---|---|
6650af14 BS |
1 | /** @file |
2 | * @brief TCPService class header. This class represents a TCP client. | |
3 | * | |
4 | * | |
5 | * | |
6 | * @copyright Intra2net AG | |
7 | * @license GPLv2 | |
8 | */ | |
9 | ||
4de6a9b8 | 10 | #include "tcp_service.hpp" |
6650af14 BS |
11 | |
12 | typedef boost::shared_ptr<SSLStream> SocketPtr; | |
13 | ||
14 | using namespace std; | |
15 | ||
16 | ||
17 | /** | |
18 | * Constructor | |
19 | * @param _log Logger object. | |
20 | * @param io_service Service peer information. | |
21 | */ | |
22 | TCPService::TCPService() | |
23 | : Socket() | |
24 | , IOService() | |
25 | { | |
26 | } | |
27 | ||
28 | ||
29 | /** | |
30 | * Default destrucotr. | |
31 | */ | |
32 | TCPService::~TCPService() | |
33 | { | |
34 | } | |
35 | ||
36 | ||
37 | /** | |
38 | * Initiate a new session with the peer. | |
39 | * @param endpoint_iterator The enpoint iterator. | |
c9d27cb6 | 40 | * @return throws boost::syste::system_error if something went wrong. |
6650af14 | 41 | */ |
557b6f56 | 42 | void TCPService::connect(const std::string& _host, const std::string& _port) |
6650af14 BS |
43 | { |
44 | // Init boost::system::error_code | |
c730deea | 45 | boost::system::error_code err_code; |
6650af14 BS |
46 | |
47 | // Create service object. The IO_Service has to be a member | |
48 | //boost::asio::io_service io_service; | |
49 | ||
50 | // Init DNS query object | |
c730deea BS |
51 | boost::asio::ip::tcp::resolver service_resolver(IOService); |
52 | boost::asio::ip::tcp::resolver::query dns_query(_host, _port); | |
53 | boost::asio::ip::tcp::resolver::iterator end_iter; | |
6650af14 BS |
54 | |
55 | // Perform the DNS query | |
c730deea BS |
56 | boost::asio::ip::tcp::resolver::iterator endpoint_iterator = service_resolver.resolve(dns_query, err_code); |
57 | if ( err_code ) | |
58 | throw boost::system::system_error(err_code); | |
6650af14 BS |
59 | |
60 | // Init ssl context for service object | |
61 | boost::asio::ssl::context ctx(IOService, boost::asio::ssl::context::sslv23); | |
62 | ctx.set_verify_mode(boost::asio::ssl::context::verify_none); | |
63 | ||
64 | // Initialize the member Socket | |
c3c84086 | 65 | Socket = SocketPtr(new SSLStream(IOService,ctx)); |
6650af14 BS |
66 | |
67 | // Try to connect the Socket through all available endpoints | |
c730deea | 68 | if (endpoint_iterator != end_iter) |
6650af14 BS |
69 | { |
70 | do | |
71 | { | |
557b6f56 BS |
72 | Socket->lowest_layer().close(); /*lint !e534 */ |
73 | Socket->lowest_layer().connect(*endpoint_iterator++, err_code); /*lint !e534 */ | |
c730deea | 74 | } while ( err_code && (endpoint_iterator != end_iter) ); |
6650af14 | 75 | } |
c730deea BS |
76 | if ( err_code ) |
77 | throw boost::system::system_error(err_code); | |
6650af14 BS |
78 | |
79 | // Perform SSL handshake | |
557b6f56 | 80 | Socket->handshake(boost::asio::ssl::stream_base::client,err_code); /*lint !e534*/ |
c730deea BS |
81 | if ( err_code ) |
82 | throw boost::system::system_error(err_code); | |
6650af14 | 83 | |
c9d27cb6 | 84 | return; |
6650af14 BS |
85 | } |
86 | ||
87 | ||
88 | /** | |
89 | * Will read all available data and return it as a string. | |
90 | * @return The data read from the socket. Throws boost::syste::system_error if something went wrong. | |
91 | */ | |
557b6f56 | 92 | std::string TCPService::read_from_socket() |
6650af14 BS |
93 | { |
94 | // Limit stream buffer to 1MB | |
95 | //unsigned int max_buff_size = 1024*1024; | |
96 | //boost::asio::streambuf stream_buffer(max_buff_size); | |
97 | ||
98 | // Do not limit the receive buffer size for reusability. By default, max_size == 18446744073709551615 | |
99 | boost::asio::streambuf stream_buffer; | |
100 | ||
c730deea | 101 | // Init error_code |
6650af14 BS |
102 | boost::system::error_code error_code; |
103 | ||
104 | // Is blocking until all available data was read or buffer is full | |
08a5a621 | 105 | size_t bytes_read = boost::asio::read(*Socket.get(),stream_buffer,boost::asio::transfer_at_least(1),error_code); /*lint !e747 */ |
6650af14 | 106 | if ( error_code == boost::asio::error::eof ) |
83a271a9 | 107 | throw boost::system::system_error(boost::system::error_code(ECONNABORTED,boost::system::system_category()),"Connection closed by peer."); |
6650af14 BS |
108 | else if (error_code) |
109 | throw boost::system::system_error(error_code); | |
110 | else if ( bytes_read == stream_buffer.max_size() ) | |
83a271a9 | 111 | throw boost::system::system_error(boost::system::error_code(ENOBUFS,boost::system::system_category()),"Could not read all available data into streambuf."); |
6650af14 | 112 | else if ( bytes_read == 0 ) |
83a271a9 | 113 | throw boost::system::system_error(boost::system::error_code(ENODATA,boost::system::system_category()),"No data available on socket."); |
6650af14 BS |
114 | |
115 | // Copy data from stream buffer to a constant buffer and create a string object from the constant buffer | |
116 | boost::asio::streambuf::const_buffers_type const_buffer = stream_buffer.data(); | |
08a5a621 | 117 | std::string data_read(boost::asio::buffers_begin(const_buffer), boost::asio::buffers_begin(const_buffer) + bytes_read); /*lint !e713*/ |
6650af14 BS |
118 | |
119 | return data_read; | |
120 | } | |
121 | ||
122 | ||
123 | /** | |
124 | * Writes out given data to the socket. | |
125 | * @param data The data which will be written to the socket. | |
83ae2edf | 126 | * @return Throws boost::syste::system_error if something went wrong. |
6650af14 | 127 | */ |
557b6f56 | 128 | void TCPService::write_to_socket(const string& data) |
6650af14 BS |
129 | { |
130 | // Get the data size which will be written | |
c730deea | 131 | size_t data_size = data.size(); |
6650af14 BS |
132 | |
133 | // Init stream_buffer with data | |
134 | boost::asio::streambuf stream_buffer(data_size); | |
135 | std::ostream request_stream(&stream_buffer); | |
136 | request_stream << data; | |
137 | ||
08a5a621 | 138 | // Init error_code |
6650af14 BS |
139 | boost::system::error_code error_code; |
140 | ||
08a5a621 | 141 | size_t bytes_wrote = boost::asio::write(*Socket.get(), stream_buffer, boost::asio::transfer_at_least(data_size), error_code); |
6650af14 BS |
142 | if (error_code) |
143 | throw boost::system::system_error(error_code); | |
144 | else if ( bytes_wrote != data_size ) | |
83a271a9 | 145 | throw boost::system::system_error(boost::system::error_code(ENOBUFS,boost::system::system_category()),"Could not write all data to the socket."); |
6650af14 | 146 | |
83ae2edf | 147 | return; |
6650af14 BS |
148 | } |
149 | ||
150 | ||
151 | /** | |
152 | * Will close the session. | |
153 | * @return 0 if all is fine, throws boost::syste::system_error if something went wrong. | |
154 | */ | |
557b6f56 | 155 | void TCPService::close() |
6650af14 BS |
156 | { |
157 | boost::system::error_code error_code; | |
158 | ||
159 | // Shutdown send and receive channel on socket. This results in sending FIN flag. | |
557b6f56 | 160 | Socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both,error_code); /*lint !e534*/ |
6650af14 BS |
161 | if ( error_code ) |
162 | throw boost::system::system_error(error_code); | |
163 | ||
164 | // Closing the session. | |
557b6f56 | 165 | Socket->lowest_layer().close( error_code ); /*lint !e534*/ |
6650af14 BS |
166 | if ( error_code ) |
167 | throw boost::system::system_error(error_code); | |
168 | ||
31beb90a | 169 | return; |
6650af14 | 170 | } |