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 }