Skip to content

Commit 06756c7

Browse files
committed
Merge #126 'Optionally listen on a UNIX socket instead of TCP'
2 parents 7e25289 + edb9060 commit 06756c7

File tree

7 files changed

+260
-51
lines changed

7 files changed

+260
-51
lines changed

.travis.yml

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ addons:
2525
- libboost-system-dev
2626
- libboost-regex-dev
2727
- libboost-date-time-dev
28+
- libboost-filesystem-dev
2829
- libboost-program-options-dev
2930
- libboost-test-dev
3031
- google-mock

CMakeLists.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ else()
4040
set(BOOST_MIN_VERSION "1.40")
4141
endif()
4242

43-
set(CUKE_CORE_BOOST_LIBS thread system regex date_time program_options)
43+
set(CUKE_CORE_BOOST_LIBS thread system regex date_time program_options filesystem)
4444
if(NOT CUKE_DISABLE_BOOST_TEST)
4545
set(CUKE_TEST_BOOST_LIBS unit_test_framework)
4646
endif()
@@ -72,7 +72,7 @@ endif()
7272

7373
if(Boost_FOUND)
7474
include_directories(${Boost_INCLUDE_DIRS})
75-
set(CUKE_EXTRA_LIBRARIES ${CUKE_EXTRA_LIBRARIES} ${Boost_THREAD_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_REGEX_LIBRARY} ${Boost_DATE_TIME_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY})
75+
set(CUKE_EXTRA_LIBRARIES ${CUKE_EXTRA_LIBRARIES} ${Boost_THREAD_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_REGEX_LIBRARY} ${Boost_DATE_TIME_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_FILESYSTEM_LIBRARY})
7676
endif()
7777

7878
#

HISTORY.md

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
### New Features
44

5+
* Add support for Unix sockets ([#126](https://github.com./cucumber/cucumber-cpp/pull/126) Giel van Schijndel)
56
* Add support for using ephemeral ports ([#131](https://github.com./cucumber/cucumber-cpp/pull/131) Giel van Schijndel)
67
* Removed CppSpec support ([#118](https://github.com./cucumber/cucumber-cpp/pull/118) Paolo Ambrosio)
78
* Support for GoogleTest 1.8 ([#120](https://github.com./cucumber/cucumber-cpp/pull/120) Kamil Strzempowicz)

include/cucumber-cpp/internal/connectors/wire/WireServer.hpp

+68-13
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,41 @@ namespace internal {
1212

1313
using namespace boost::asio;
1414
using namespace boost::asio::ip;
15+
#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
16+
using namespace boost::asio::local;
17+
#endif
1518

1619
/**
1720
* Socket server that calls a protocol handler line by line
1821
*/
1922
class SocketServer {
23+
public:
24+
/**
25+
* Constructor for DI
26+
*/
27+
SocketServer(const ProtocolHandler *protocolHandler);
28+
29+
/**
30+
* Accept one connection
31+
*/
32+
virtual void acceptOnce() = 0;
33+
34+
protected:
35+
const ProtocolHandler *protocolHandler;
36+
io_service ios;
37+
38+
template <typename Protocol, typename Service>
39+
void doListen(basic_socket_acceptor<Protocol, Service>& acceptor,
40+
const typename Protocol::endpoint& endpoint);
41+
template <typename Protocol, typename Service>
42+
void doAcceptOnce(basic_socket_acceptor<Protocol, Service>& acceptor);
43+
void processStream(std::iostream &stream);
44+
};
45+
46+
/**
47+
* Socket server that calls a protocol handler line by line
48+
*/
49+
class TCPSocketServer : public SocketServer {
2050
public:
2151
/**
2252
* Type definition for TCP port
@@ -26,35 +56,60 @@ class SocketServer {
2656
/**
2757
* Constructor for DI
2858
*/
29-
SocketServer(const ProtocolHandler *protocolHandler);
59+
TCPSocketServer(const ProtocolHandler *protocolHandler);
3060

3161
/**
3262
* Bind and listen to a TCP port
3363
*/
3464
void listen(const port_type port);
3565

3666
/**
37-
* Port number that this server is currently listening on.
67+
* Endpoint (IP address and port number) that this server is currently
68+
* listening on.
3869
*
39-
* @throw boost::system::system_error when not listening on any TCP port or
40-
* the port cannot be determined.
70+
* @throw boost::system::system_error when not listening on any socket or
71+
* the endpoint cannot be determined.
72+
*/
73+
tcp::endpoint listenEndpoint() const;
74+
75+
virtual void acceptOnce();
76+
77+
private:
78+
tcp::acceptor acceptor;
79+
};
80+
81+
#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
82+
/**
83+
* Socket server that calls a protocol handler line by line
84+
*/
85+
class UnixSocketServer : public SocketServer {
86+
public:
87+
/**
88+
* Constructor for DI
89+
*/
90+
UnixSocketServer(const ProtocolHandler *protocolHandler);
91+
92+
/**
93+
* Bind and listen on a local stream socket
4194
*/
42-
port_type listenPort() const;
95+
void listen(const std::string& unixPath);
4396

4497
/**
45-
* Accept one connection
98+
* Port number that this server is currently listening on.
99+
*
100+
* @throw boost::system::system_error when not listening on any socket or
101+
* the endpoint cannot be determined.
46102
*/
47-
void acceptOnce();
103+
stream_protocol::endpoint listenEndpoint() const;
48104

49-
~SocketServer() {}; // Forbid inheritance
105+
virtual void acceptOnce();
50106

51-
private:
52-
const ProtocolHandler *protocolHandler;
53-
io_service ios;
54-
tcp::acceptor acceptor;
107+
~UnixSocketServer();
55108

56-
void processStream(tcp::iostream &stream);
109+
private:
110+
stream_protocol::acceptor acceptor;
57111
};
112+
#endif
58113

59114
}
60115
}

src/connectors/wire/WireServer.cpp

+65-14
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,91 @@
11
#include <cucumber-cpp/internal/connectors/wire/WireServer.hpp>
2+
#include <boost/filesystem/operations.hpp>
23

34
namespace cucumber {
45
namespace internal {
56

7+
using namespace boost::asio::ip;
8+
#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
9+
using namespace boost::asio::local;
10+
#endif
11+
612
SocketServer::SocketServer(const ProtocolHandler *protocolHandler) :
713
protocolHandler(protocolHandler),
8-
ios(),
9-
acceptor(ios) {
14+
ios() {
1015
}
1116

12-
void SocketServer::listen(const port_type port) {
13-
tcp::endpoint endpoint(tcp::v4(), port);
17+
template <typename Protocol, typename Service>
18+
void SocketServer::doListen(basic_socket_acceptor<Protocol, Service>& acceptor,
19+
const typename Protocol::endpoint& endpoint) {
20+
if (acceptor.is_open())
21+
throw boost::system::system_error(boost::asio::error::already_open);
1422
acceptor.open(endpoint.protocol());
15-
acceptor.set_option(tcp::acceptor::reuse_address(true));
16-
acceptor.set_option(tcp::no_delay(true));
23+
acceptor.set_option(typename Protocol::acceptor::reuse_address(true));
1724
acceptor.bind(endpoint);
1825
acceptor.listen(1);
1926
}
2027

21-
SocketServer::port_type SocketServer::listenPort() const {
22-
const tcp::endpoint ep(acceptor.local_endpoint());
23-
return ep.port();
24-
}
25-
26-
void SocketServer::acceptOnce() {
27-
tcp::iostream stream;
28+
template <typename Protocol, typename Service>
29+
void SocketServer::doAcceptOnce(basic_socket_acceptor<Protocol, Service>& acceptor) {
30+
typename Protocol::iostream stream;
2831
acceptor.accept(*stream.rdbuf());
2932
processStream(stream);
3033
}
3134

32-
void SocketServer::processStream(tcp::iostream &stream) {
35+
void SocketServer::processStream(std::iostream& stream) {
3336
std::string request;
3437
while (getline(stream, request)) {
3538
stream << protocolHandler->handle(request) << std::endl << std::flush;
3639
}
3740
}
3841

42+
TCPSocketServer::TCPSocketServer(const ProtocolHandler *protocolHandler) :
43+
SocketServer(protocolHandler),
44+
acceptor(ios) {
45+
}
46+
47+
void TCPSocketServer::listen(const port_type port) {
48+
doListen(acceptor, tcp::endpoint(tcp::v4(), port));
49+
acceptor.set_option(tcp::no_delay(true));
50+
}
51+
52+
tcp::endpoint TCPSocketServer::listenEndpoint() const {
53+
return acceptor.local_endpoint();
54+
}
55+
56+
void TCPSocketServer::acceptOnce() {
57+
doAcceptOnce(acceptor);
58+
}
59+
60+
#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
61+
UnixSocketServer::UnixSocketServer(const ProtocolHandler *protocolHandler) :
62+
SocketServer(protocolHandler),
63+
acceptor(ios) {
64+
}
65+
66+
void UnixSocketServer::listen(const std::string& unixPath) {
67+
if (boost::filesystem::status(unixPath).type() == boost::filesystem::socket_file)
68+
boost::filesystem::remove(unixPath);
69+
70+
doListen(acceptor, stream_protocol::endpoint(unixPath));
71+
}
72+
73+
stream_protocol::endpoint UnixSocketServer::listenEndpoint() const {
74+
return acceptor.local_endpoint();
75+
}
76+
77+
void UnixSocketServer::acceptOnce() {
78+
doAcceptOnce(acceptor);
79+
}
80+
81+
UnixSocketServer::~UnixSocketServer() {
82+
if (!acceptor.is_open())
83+
return;
84+
std::string path = acceptor.local_endpoint().path();
85+
// NOTE: this will fail if this path got deleted manually or represents an abstract-namespace socket
86+
boost::filesystem::remove(path);
87+
}
88+
#endif
89+
3990
}
4091
}

src/main.cpp

+33-7
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,35 @@
33
#include <cucumber-cpp/internal/connectors/wire/WireProtocol.hpp>
44
#include <iostream>
55
#include <boost/program_options.hpp>
6+
#include <boost/scoped_ptr.hpp>
67

78
namespace {
89

9-
void acceptWireProtocol(int port, bool verbose) {
10+
void acceptWireProtocol(int port, const std::string& unixPath, bool verbose) {
1011
using namespace ::cucumber::internal;
1112
CukeEngineImpl cukeEngine;
1213
JsonSpiritWireMessageCodec wireCodec;
1314
WireProtocolHandler protocolHandler(&wireCodec, &cukeEngine);
14-
SocketServer server(&protocolHandler);
15-
server.listen(port);
16-
if (verbose)
17-
std::clog << "Listening on port " << server.listenPort() << std::endl;
18-
server.acceptOnce();
15+
boost::scoped_ptr<SocketServer> server;
16+
#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
17+
if (!unixPath.empty())
18+
{
19+
UnixSocketServer* const unixServer = new UnixSocketServer(&protocolHandler);
20+
server.reset(unixServer);
21+
unixServer->listen(unixPath);
22+
if (verbose)
23+
std::clog << "Listening on socket " << unixServer->listenEndpoint() << std::endl;
24+
}
25+
else
26+
#endif
27+
{
28+
TCPSocketServer* const tcpServer = new TCPSocketServer(&protocolHandler);
29+
server.reset(tcpServer);
30+
tcpServer->listen(port);
31+
if (verbose)
32+
std::clog << "Listening on port " << tcpServer->listenEndpoint() << std::endl;
33+
}
34+
server->acceptOnce();
1935
}
2036

2137
}
@@ -27,6 +43,9 @@ int main(int argc, char **argv) {
2743
("help,h", "help for cucumber-cpp")
2844
("verbose,v", "verbose output")
2945
("port,p", value<int>(), "listening port of wireserver, use '0' (zero) to select an ephemeral port")
46+
#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
47+
("unix,u", value<std::string>(), "listening unix socket of wireserver (disables listening on port)")
48+
#endif
3049
;
3150
boost::program_options::variables_map optionVariableMap;
3251
boost::program_options::store(boost::program_options::parse_command_line(argc, argv, optionDescription), optionVariableMap);
@@ -42,13 +61,20 @@ int main(int argc, char **argv) {
4261
port = optionVariableMap["port"].as<int>();
4362
}
4463

64+
std::string unixPath;
65+
#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
66+
if (optionVariableMap.count("unix")) {
67+
unixPath = optionVariableMap["unix"].as<std::string>();
68+
}
69+
#endif
70+
4571
bool verbose = false;
4672
if (optionVariableMap.count("verbose")) {
4773
verbose = true;
4874
}
4975

5076
try {
51-
acceptWireProtocol(port, verbose);
77+
acceptWireProtocol(port, unixPath, verbose);
5278
} catch (std::exception &e) {
5379
std::cerr << e.what() << std::endl;
5480
exit(1);

0 commit comments

Comments
 (0)