1 /// Internal - Phobos and vibe.d sockets. 2 module mysql.protocol.sockets; 3 4 import std.exception; 5 import std.socket; 6 7 import mysql.exceptions; 8 9 version(Have_vibe_d_core) 10 { 11 static if(__traits(compiles, (){ import vibe.core.net; } )) 12 import vibe.core.net; 13 else 14 static assert(false, "mysql-native can't find Vibe.d's 'vibe.core.net'."); 15 } 16 17 /// Phobos/Vibe.d type aliases 18 alias PlainPhobosSocket = std.socket.TcpSocket; 19 version(Have_vibe_d_core) 20 { 21 static import vibe.core.net; 22 alias PlainVibeDSocket = vibe.core.net.TCPConnection; ///ditto 23 } 24 else 25 { 26 // Dummy types 27 alias PlainVibeDSocket = Object; ///ditto 28 } 29 30 alias OpenSocketCallbackPhobos = PlainPhobosSocket function(string,ushort); 31 alias OpenSocketCallbackVibeD = PlainVibeDSocket function(string,ushort); 32 33 enum MySQLSocketType { phobos, vibed } 34 35 /// A minimal socket interface similar to Vibe.d's TCPConnection. 36 /// Used to wrap both Phobos and Vibe.d sockets with a common interface. 37 interface MySQLSocket 38 { 39 void close(); 40 @property bool connected() const; 41 void read(ubyte[] dst); 42 void write(in ubyte[] bytes); 43 44 void acquire(); 45 void release(); 46 bool isOwner(); 47 bool amOwner(); 48 } 49 50 /// Wraps a Phobos socket with the common interface 51 class MySQLSocketPhobos : MySQLSocket 52 { 53 private PlainPhobosSocket socket; 54 55 /// The socket should already be open 56 this(PlainPhobosSocket socket) 57 { 58 enforceEx!MYX(socket, "Tried to use a null Phobos socket - Maybe the 'openSocket' callback returned null?"); 59 enforceEx!MYX(socket.isAlive, "Tried to use a closed Phobos socket - Maybe the 'openSocket' callback created a socket but forgot to open it?"); 60 this.socket = socket; 61 } 62 63 invariant() 64 { 65 assert(!!socket); 66 } 67 68 void close() 69 { 70 socket.shutdown(SocketShutdown.BOTH); 71 socket.close(); 72 } 73 74 @property bool connected() const 75 { 76 return socket.isAlive; 77 } 78 79 void read(ubyte[] dst) 80 { 81 // Note: I'm a little uncomfortable with this line as it doesn't 82 // (and can't) update Connection._open. Not sure what can be done, 83 // but perhaps Connection._open should be eliminated in favor of 84 // querying the socket's opened/closed state directly. 85 scope(failure) socket.close(); 86 87 for (size_t off, len; off < dst.length; off += len) { 88 len = socket.receive(dst[off..$]); 89 enforceEx!MYX(len != 0, "Server closed the connection"); 90 enforceEx!MYX(len != socket.ERROR, "Received std.socket.Socket.ERROR"); 91 } 92 } 93 94 void write(in ubyte[] bytes) 95 { 96 socket.send(bytes); 97 } 98 99 void acquire() { /+ Do nothing +/ } 100 void release() { /+ Do nothing +/ } 101 bool isOwner() { return true; } 102 bool amOwner() { return true; } 103 } 104 105 version(Have_vibe_d_core) { 106 /// Wraps a Vibe.d socket with the common interface 107 class MySQLSocketVibeD : MySQLSocket 108 { 109 private PlainVibeDSocket socket; 110 111 /// The socket should already be open 112 this(PlainVibeDSocket socket) 113 { 114 enforceEx!MYX(socket, "Tried to use a null Vibe.d socket - Maybe the 'openSocket' callback returned null?"); 115 enforceEx!MYX(socket.connected, "Tried to use a closed Vibe.d socket - Maybe the 'openSocket' callback created a socket but forgot to open it?"); 116 this.socket = socket; 117 } 118 119 invariant() 120 { 121 assert(!!socket); 122 } 123 124 void close() 125 { 126 socket.close(); 127 } 128 129 @property bool connected() const 130 { 131 return socket.connected; 132 } 133 134 void read(ubyte[] dst) 135 { 136 socket.read(dst); 137 } 138 139 void write(in ubyte[] bytes) 140 { 141 socket.write(bytes); 142 } 143 144 static if (is(typeof(&TCPConnection.isOwner))) { 145 void acquire() { socket.acquire(); } 146 void release() { socket.release(); } 147 bool isOwner() { return socket.isOwner(); } 148 bool amOwner() { return socket.isOwner(); } 149 } else { 150 void acquire() { /+ Do nothing +/ } 151 void release() { /+ Do nothing +/ } 152 bool isOwner() { return true; } 153 bool amOwner() { return true; } 154 } 155 } 156 }