1 /** 2 * Socket encoding/decoding functions 3 */ 4 module bformat.client; 5 6 import std.socket : Socket; 7 import river.core; 8 import river.impls.sock : SockStream; 9 import niknaks.bits : bytesToIntegral, order, Order; 10 11 /** 12 * Bformat client to encode and decode via a 13 * `Socket` or river-based `Stream` 14 */ 15 public class BClient 16 { 17 /** 18 * Underlying stream 19 */ 20 private Stream stream; 21 22 /** 23 * Constructs a new `BClient` for encoding and decoding 24 * to and from the provided `Socket` 25 * 26 * Params: 27 * socket = the `Socket` to use for writing and reading 28 */ 29 this(Socket socket) 30 { 31 this(new SockStream(socket)); 32 } 33 34 /** 35 * Constructs a new `BClient` for encoding and decoding 36 * to and from the provided river-based `Stream` 37 * 38 * Params: 39 * stream = the `Stream` to use for writing and reading 40 */ 41 this(Stream stream) 42 { 43 this.stream = stream; 44 } 45 46 /** 47 * Receives a message from the provided socket 48 * by decoding the streamed bytes into bformat 49 * and finally placing the resulting payload in 50 * the provided array 51 * 52 * Params: 53 * originator = the socket to receive from 54 * receiveMessage = the nbuffer to receive into 55 * 56 * Returns: true if the receive succeeded, false otheriwse 57 */ 58 public bool receiveMessage(ref byte[] receiveMessage) 59 { 60 /* Construct a buffer to receive into */ 61 byte[] receiveBuffer; 62 63 /* Get the length of the message */ 64 byte[4] messageLengthBytes; 65 66 try 67 { 68 stream.readFully(messageLengthBytes); 69 } 70 catch(StreamException streamErr) 71 { 72 /* If there was an error reading from the socket */ 73 return false; 74 } 75 76 77 /* Response message length */ 78 uint messageLength; 79 80 /* Order the bytes into Little endian (only flips if host order doesn't match LE) */ 81 messageLength = order(bytesToIntegral!(uint)(cast(ubyte[])messageLengthBytes), Order.LE); 82 83 /* Read the full message */ 84 receiveBuffer.length = messageLength; 85 try 86 { 87 stream.readFully(receiveBuffer); 88 receiveMessage = receiveBuffer; 89 90 /* If there was no error receiving the message */ 91 return true; 92 } 93 catch(StreamException streamErr) 94 { 95 /* If there was an error reading from the socket */ 96 return false; 97 } 98 } 99 100 /** 101 * Encodes the provided message into the bformat format 102 * and sends it over the provided socket 103 * 104 * Params: 105 * recipient = the socket to send over 106 * message = the message to encode and send 107 * 108 * Returns: true if the send succeeded, false otherwise 109 */ 110 public bool sendMessage(byte[] message) 111 { 112 /* The message buffer */ 113 byte[] messageBuffer; 114 115 import bformat.marshall : encodeBformat; 116 messageBuffer = encodeBformat(message); 117 118 try 119 { 120 /* Send the message */ 121 stream.writeFully(messageBuffer); 122 123 return true; 124 } 125 catch(StreamException streamError) 126 { 127 return false; 128 } 129 } 130 131 /** 132 * Closes the client 133 */ 134 public void close() 135 { 136 /* Close the underlying stream */ 137 stream.close(); 138 } 139 } 140 141 version(unittest) 142 { 143 import std.socket; 144 import core.thread; 145 import std.stdio; 146 } 147 148 /** 149 * Create a server that encodes a message to the client 150 * and then let the client decode it from us; both making 151 * use of `BClient` to accomplish this 152 */ 153 unittest 154 { 155 UnixAddress unixAddr = new UnixAddress("/tmp/bformatServer.sock"); 156 157 scope(exit) 158 { 159 import std.stdio; 160 remove(cast(char*)unixAddr.path()); 161 } 162 163 Socket serverSocket = new Socket(AddressFamily.UNIX, SocketType.STREAM); 164 serverSocket.bind(unixAddr); 165 serverSocket.listen(0); 166 167 class ServerThread : Thread 168 { 169 private Socket servSock; 170 171 this(Socket servSock) 172 { 173 this.servSock = servSock; 174 super(&worker); 175 } 176 177 private void worker() 178 { 179 Socket clientSock = servSock.accept(); 180 181 BClient bClient = new BClient(clientSock); 182 183 byte[] message = cast(byte[])"ABBA"; 184 bClient.sendMessage(message); 185 } 186 } 187 188 Thread serverThread = new ServerThread(serverSocket); 189 serverThread.start(); 190 191 Socket client = new Socket(AddressFamily.UNIX, SocketType.STREAM); 192 client.connect(unixAddr); 193 BClient bClient = new BClient(client); 194 195 byte[] receivedMessage; 196 bClient.receiveMessage(receivedMessage); 197 assert(receivedMessage == "ABBA"); 198 writeln(receivedMessage); 199 writeln(cast(string)receivedMessage); 200 201 bClient.close(); 202 }