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 }