1 /**
2 Default socket type for Connection.
3 
4 Copyright: Copyright Boris-Barboris 2017.
5 License: MIT
6 Authors: Boris-Barboris
7 */
8 
9 module dpeq.socket;
10 
11 import core.time: Duration;
12 import std.socket;
13 
14 import dpeq.exceptions;
15 
16 
17 
18 /**
19 $(D std.socket.Socket) wrapper wich is compatible with PSQLConnection.
20 If you want to use custom socket type (vibe-d or any other), make it
21 duck-type and exception-compatible with this one.
22 */
23 final class StdSocket
24 {
25     private Socket m_socket;
26 
27     /// Underlying socket instance.
28     @property Socket socket() @safe pure { return m_socket; }
29 
30     /// Establish connection to backend. Constructor is expected to throw Exception
31     /// if anything goes wrong.
32     this(string host, ushort port, Duration timeout) @trusted
33     {
34         if (host[0] == '/')
35         {
36             // Unix socket
37             version(Posix)
38             {
39                 Address addr = new UnixAddress(host);
40                 m_socket = new Socket(AddressFamily.UNIX, SocketType.STREAM);
41                 m_socket.connect(addr);
42             }
43             else
44                 assert(0, "Cannot connect using UNIX sockets on non-Posix OS");
45         }
46         else
47         {
48             m_socket = new TcpSocket();
49             // 20 minutes for both tcp_keepalive_time and tcp_keepalive_intvl
50             m_socket.setKeepAlive(1200, 1200);
51             // example of receive timeout:
52             // m_socket.setOption(SocketOptionLevel.SOCKET, SocketOption.RCVTIMEO, seconds(120));
53             m_socket.connect(new InternetAddress(host, port));
54         }
55     }
56 
57     void close() nothrow @nogc @trusted
58     {
59         m_socket.shutdown(SocketShutdown.BOTH);
60         m_socket.close();
61     }
62 
63     /// Send whole byte buffer or throw. Throws: $(D PsqlSocketException).
64     auto send(const(ubyte)[] buf) @trusted
65     {
66         auto r = m_socket.send(buf);
67         if (r == Socket.ERROR)
68             throw new PsqlSocketException("Socket.ERROR on send");
69         return r;
70     }
71 
72     /// Fill byte buffer from the socket completely or throw.
73     /// Throws: $(D PsqlSocketException).
74     auto receive(ubyte[] buf) @trusted
75     {
76         auto r = m_socket.receive(buf);
77         if (r == 0 && buf.length > 0)
78             throw new PsqlSocketException("Connection closed");
79         if (r == Socket.ERROR)
80             throw new PsqlSocketException("Socket.ERROR on receive: " ~ m_socket.getErrorText());
81         return r;
82     }
83 }