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