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 }