1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
3 * Data Differential YATL (i.e. libtest) library
5 * Copyright (C) 2012 Data Differential, http://datadifferential.com/
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following disclaimer
16 * in the documentation and/or other materials provided with the
19 * * The names of its contributors may not be used to endorse or
20 * promote products derived from this software without specific prior
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 #include "libtest/yatlcon.h"
38 #include <libtest/common.h>
40 #include <sys/types.h>
41 #include <sys/socket.h>
49 #ifndef HAVE_MSG_NOSIGNAL
50 # define MSG_NOSIGNAL 0
55 SimpleClient::SimpleClient(const std::string
& hostname_
, in_port_t port_
) :
59 sock_fd(INVALID_SOCKET
),
64 bool SimpleClient::ready(int event_
)
68 fds
[0].events
= event_
;
72 if (_is_connected
== false)
74 timeout
= timeout
* 30;
77 int ready_fds
= poll(fds
, 1, timeout
);
81 _error
= strerror(errno
);
84 else if (ready_fds
== 1)
86 if (fds
[0].revents
& (POLLERR
| POLLHUP
| POLLNVAL
))
89 socklen_t len
= sizeof (err
);
90 // We replace errno with err if getsockopt() passes, but err has been
92 if (getsockopt(fds
[0].fd
, SOL_SOCKET
, SO_ERROR
, &err
, &len
) == 0)
94 // We check the value to see what happened wth the socket.
97 _error
= "getsockopt() returned no error but poll() indicated one existed";
102 _error
= strerror(errno
);
108 if (fds
[0].revents
& event_
)
114 fatal_assert(ready_fds
== 0);
120 struct addrinfo
* SimpleClient::lookup()
122 struct addrinfo
*ai
= NULL
;
123 struct addrinfo hints
;
124 memset(&hints
, 0, sizeof(struct addrinfo
));
125 hints
.ai_socktype
= SOCK_STREAM
;
126 hints
.ai_protocol
= IPPROTO_TCP
;
128 libtest::vchar_t service
;
129 service
.resize(NI_MAXSERV
);
130 (void)snprintf(&service
[0], service
.size(), "%d", _port
);
132 int getaddrinfo_error
;
133 if ((getaddrinfo_error
= getaddrinfo(_hostname
.c_str(), &service
[0], &hints
, &ai
)) != 0)
135 if (getaddrinfo_error
!= EAI_SYSTEM
)
137 _error
= gai_strerror(getaddrinfo_error
);
142 _error
= strerror(getaddrinfo_error
);
150 SimpleClient::~SimpleClient()
155 void SimpleClient::close_socket()
157 if (sock_fd
!= INVALID_SOCKET
)
160 sock_fd
= INVALID_SOCKET
;
164 bool SimpleClient::instance_connect()
166 _is_connected
= false;
171 struct addrinfo
* address_info_next
= ai
;
173 while (address_info_next
and sock_fd
== INVALID_SOCKET
)
175 if ((sock_fd
= socket(address_info_next
->ai_family
, address_info_next
->ai_socktype
, address_info_next
->ai_protocol
)) != SOCKET_ERROR
)
177 if (connect(sock_fd
, address_info_next
->ai_addr
, address_info_next
->ai_addrlen
) == SOCKET_ERROR
)
185 case EINPROGRESS
: // nonblocking mode - first return
186 case EALREADY
: // nonblocking mode - subsequent returns
187 continue; // Jump to while() and continue on
196 _error
= strerror(errno
);
201 fatal_message(strerror(errno
));
203 address_info_next
= address_info_next
->ai_next
;
209 if (sock_fd
== INVALID_SOCKET
)
211 fatal_assert(_error
.size());
214 return bool(sock_fd
!= INVALID_SOCKET
);
220 bool SimpleClient::is_valid()
223 if (sock_fd
== INVALID_SOCKET
)
225 return instance_connect();
231 bool SimpleClient::message(const char* ptr
, const size_t len
)
240 ssize_t nw
= send(sock_fd
, ptr
+ offset
, len
- offset
, MSG_NOSIGNAL
);
245 _error
= strerror(errno
);
253 } while (offset
< ssize_t(len
));
259 fatal_assert(_error
.size());
264 bool SimpleClient::send_message(const std::string
& arg
)
266 if (message(arg
.c_str(), arg
.size()) == true)
268 return message("\r\n", 2);
274 bool SimpleClient::send_data(const libtest::vchar_t
& message_
, libtest::vchar_t
& response_
)
277 if (message(&message_
[0], message_
.size()))
279 return response(response_
);
285 bool SimpleClient::send_message(const std::string
& message_
, std::string
& response_
)
288 if (send_message(message_
))
290 return response(response_
);
296 bool SimpleClient::response(libtest::vchar_t
& response_
)
309 ssize_t nr
= recv(sock_fd
, buffer
, 1, MSG_NOSIGNAL
);
314 _error
= strerror(errno
);
325 response_
.reserve(response_
.size() + nr
+1);
326 fatal_assert(nr
== 1);
327 if (buffer
[0] == '\n')
331 response_
.insert(response_
.end(), buffer
, buffer
+nr
);
335 return response_
.size();
339 fatal_assert(_error
.size());
343 bool SimpleClient::response(std::string
& response_
)
356 ssize_t nr
= recv(sock_fd
, buffer
, 1, MSG_NOSIGNAL
);
361 _error
= strerror(errno
);
372 fatal_assert(nr
== 1);
373 if (buffer
[0] == '\n')
377 response_
.append(buffer
);
381 return response_
.size();
385 fatal_assert(_error
.size());
389 } // namespace libtest