1 // Copyright (c) 2013, Peter Wood. 2 // See license.txt for licensing details. 3 module stalkd.connection; 4 5 import std.socket; 6 import stalkd.exceptions; 7 import stalkd.server; 8 import stalkd.tube; 9 10 /** 11 * The Connection class represents a connection to a single Beanstalkd server. 12 * A Connection instance should not be shared between multiple tubes. 13 * 14 */ 15 class Connection { 16 /** 17 * Constructor for the Connection class. 18 * 19 * Params: 20 * server = A reference to the Server the connection will attach to. 21 */ 22 this(Server server) { 23 _server = server; 24 } 25 26 /** 27 * Constructor for the Connection class. 28 */ 29 this(string host, ushort port=Server.DEFAULT_BEANSTALKD_PORT) { 30 _server = Server(host, port); 31 } 32 33 /** 34 * This function attempts to establish a connection to the server identified 35 * by a Connection objects settings. 36 */ 37 void open() { 38 auto addresses = getAddress(_server.host, _server.port); 39 40 if(addresses.length == 0) { 41 throw(new StalkdException("Unable to determine a network address for the server.")); 42 } 43 44 try { 45 _socket = new TcpSocket; 46 _socket.connect(addresses[0]); 47 } catch(Exception exception) { 48 throw(new StalkdException("Failed to open connection to server.", exception)); 49 } 50 } 51 52 /** 53 * This function closes a connection if it is open. 54 */ 55 void close() { 56 if(_socket !is null) { 57 _socket.close(); 58 _socket = null; 59 } 60 } 61 62 /** 63 * This function is used to test whether a connection is open. 64 */ 65 const @property bool isOpen() { 66 return(_socket !is null && _socket.isAlive); 67 } 68 69 /** 70 * Getter for the server property. 71 */ 72 @property Server server() { 73 return(_server); 74 } 75 76 /** 77 * This function provides package level access to the Socket held within a 78 * Connection object. Asking for a socket before a Connection has been 79 * explicitly opened implies an implicit call to the open() function. 80 */ 81 @property package Socket socket() { 82 if(_socket is null) { 83 this.open(); 84 } 85 return(_socket); 86 } 87 88 /** 89 * This function retrieves a Tube object from a Connection. 90 * 91 * Params: 92 * name = The name of the tube that the Tube object will use. Defaults 93 * to Tube.DEFAULT_TUBE_NAME. 94 */ 95 Tube getTube(string name=Tube.DEFAULT_TUBE_NAME) { 96 Tube tube = new Tube(this); 97 98 if(name !is Tube.DEFAULT_TUBE_NAME) { 99 tube.use(name); 100 } 101 102 return(tube); 103 } 104 105 private Server _server; 106 private Socket _socket; 107 } 108 109 //------------------------------------------------------------------------------ 110 // Unit Tests 111 //------------------------------------------------------------------------------ 112 /* 113 * NOTE: There is a limit to the amount of unit testing that can be performed 114 * without an actual server connection. For this reason, the test below 115 * check for the presence of an available test Beanstalkd instance via 116 * the existence of the BEANSTALKD_TEST_HOST environment variable. If 117 * this is set then an attempt will be made to connect to it to perform 118 * an additional series of tests. You can specify the port for this test 119 * server using the BEANSTALKD_TEST_PORT environment variable. As the 120 * queues on this server will be added to, deleted from and cleared of 121 * content as part of the tests this server should not be used for any 122 * other purpose! 123 */ 124 unittest { 125 import std.stdio; 126 import std.conv; 127 import std.process; 128 import std.exception; 129 130 auto server = Server("localhost"); 131 auto connection = new Connection(server); 132 133 assert(connection.server is server); 134 assert(connection.isOpen == false); 135 136 auto tube = connection.getTube(); 137 assert(tube !is null); 138 assert(tube.connection is connection); 139 140 auto host = environment.get("BEANSTALKD_TEST_HOST"); 141 if(host !is null) { 142 writeln("The BEANSTALKD_TEST_HOST environment variable is set, conducting advanced tests for the Connection class."); 143 ushort port = Server.DEFAULT_BEANSTALKD_PORT; 144 if(environment.get("BEANSTALKD_TEST_PORT") !is null) { 145 port = to!ushort(environment.get("BEANSTALKD_TEST_PORT")); 146 } 147 148 connection = new Connection(host, port); 149 150 void testOpen() { 151 connection.open(); 152 } 153 assertNotThrown!StalkdException(testOpen); 154 assert(connection.isOpen == true); 155 156 void testClose() { 157 connection.close(); 158 } 159 assertNotThrown!StalkdException(testClose); 160 assert(connection.isOpen == false); 161 } else { 162 writeln("The BEANSTALKD_TEST_HOST environment variable is not set, advanced tests for the Connection class skipped."); 163 } 164 } 165 166