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_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_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) @safe; 31 alias OpenSocketCallbackVibeD = PlainVibeDSocket function(string,ushort) @safe; 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 @safe: 40 void close(); 41 @property bool connected() const; 42 void read(ubyte[] dst); 43 void write(in ubyte[] bytes); 44 45 void acquire(); 46 void release(); 47 bool isOwner(); 48 bool amOwner(); 49 } 50 51 /// Wraps a Phobos socket with the common interface 52 class MySQLSocketPhobos : MySQLSocket 53 { 54 @safe: 55 private PlainPhobosSocket socket; 56 57 /// The socket should already be open 58 this(PlainPhobosSocket socket) 59 { 60 enforce!MYX(socket, "Tried to use a null Phobos socket - Maybe the 'openSocket' callback returned null?"); 61 enforce!MYX(socket.isAlive, "Tried to use a closed Phobos socket - Maybe the 'openSocket' callback created a socket but forgot to open it?"); 62 this.socket = socket; 63 } 64 65 invariant() 66 { 67 assert(socket !is null); 68 } 69 70 void close() 71 { 72 socket.shutdown(SocketShutdown.BOTH); 73 socket.close(); 74 } 75 76 @property bool connected() const 77 { 78 return socket.isAlive; 79 } 80 81 void read(ubyte[] dst) 82 { 83 // Note: I'm a little uncomfortable with this line as it doesn't 84 // (and can't) update Connection._open. Not sure what can be done, 85 // but perhaps Connection._open should be eliminated in favor of 86 // querying the socket's opened/closed state directly. 87 scope(failure) socket.close(); 88 89 for (size_t off, len; off < dst.length; off += len) { 90 len = socket.receive(dst[off..$]); 91 enforce!MYX(len != 0, "Server closed the connection"); 92 enforce!MYX(len != socket.ERROR, "Received std.socket.Socket.ERROR"); 93 } 94 } 95 96 void write(in ubyte[] bytes) 97 { 98 socket.send(bytes); 99 } 100 101 void acquire() { /+ Do nothing +/ } 102 void release() { /+ Do nothing +/ } 103 bool isOwner() { return true; } 104 bool amOwner() { return true; } 105 } 106 107 version(Have_vibe_core) { 108 /// Wraps a Vibe.d socket with the common interface 109 class MySQLSocketVibeD : MySQLSocket 110 { 111 @safe: 112 private PlainVibeDSocket socket; 113 114 /// The socket should already be open 115 this(PlainVibeDSocket socket) 116 { 117 enforce!MYX(socket, "Tried to use a null Vibe.d socket - Maybe the 'openSocket' callback returned null?"); 118 enforce!MYX(socket.connected, "Tried to use a closed Vibe.d socket - Maybe the 'openSocket' callback created a socket but forgot to open it?"); 119 this.socket = socket; 120 } 121 122 invariant() 123 { 124 // vibe-core (used by default as of vibe.d v0.8.3) changed 125 // TCPConnection from an interface to a struct, so checking 126 // for null is no longer needed. 127 static if(is(socket == interface)) 128 assert(socket !is null); 129 } 130 131 void close() 132 { 133 socket.close(); 134 } 135 136 @property bool connected() const 137 { 138 return socket.connected; 139 } 140 141 void read(ubyte[] dst) 142 { 143 socket.read(dst); 144 } 145 146 void write(in ubyte[] bytes) 147 { 148 socket.write(bytes); 149 } 150 151 static if (is(typeof(&TCPConnection.isOwner))) { 152 void acquire() { socket.acquire(); } 153 void release() { socket.release(); } 154 bool isOwner() { return socket.isOwner(); } 155 bool amOwner() { return socket.isOwner(); } 156 } else { 157 void acquire() { /+ Do nothing +/ } 158 void release() { /+ Do nothing +/ } 159 bool isOwner() { return true; } 160 bool amOwner() { return true; } 161 } 162 } 163 }