1 module soup.Socket; 2 3 private import gio.Cancellable; 4 private import gio.Socket: GIOSocket = Socket; 5 private import gio.IOStream; 6 private import gio.InitableIF; 7 private import gio.InitableT; 8 private import glib.ConstructionException; 9 private import glib.ErrorG; 10 private import glib.GException; 11 private import glib.Str; 12 private import gobject.ObjectG; 13 private import gobject.Signals; 14 private import soup.Address; 15 private import soup.c.functions; 16 public import soup.c.types; 17 private import std.algorithm; 18 19 20 /** 21 * #SoupSocket is libsoup's TCP socket type. While it is primarily 22 * intended for internal use, #SoupSocket<!-- -->s are exposed in the 23 * API in various places, and some of their methods (eg, 24 * soup_socket_get_remote_address()) may be useful to applications. 25 */ 26 public class Socket : ObjectG, InitableIF 27 { 28 /** the main Gtk struct */ 29 protected SoupSocket* soupSocket; 30 31 /** Get the main Gtk struct */ 32 public SoupSocket* getSocketStruct(bool transferOwnership = false) 33 { 34 if (transferOwnership) 35 ownedRef = false; 36 return soupSocket; 37 } 38 39 /** the main Gtk struct as a void* */ 40 protected override void* getStruct() 41 { 42 return cast(void*)soupSocket; 43 } 44 45 /** 46 * Sets our main struct and passes it to the parent class. 47 */ 48 public this (SoupSocket* soupSocket, bool ownedRef = false) 49 { 50 this.soupSocket = soupSocket; 51 super(cast(GObject*)soupSocket, ownedRef); 52 } 53 54 // add the Initable capabilities 55 mixin InitableT!(SoupSocket); 56 57 58 /** */ 59 public static GType getType() 60 { 61 return soup_socket_get_type(); 62 } 63 64 /** 65 * Begins asynchronously connecting to @sock's remote address. The 66 * socket will call @callback when it succeeds or fails (but not 67 * before returning from this function). 68 * 69 * If @cancellable is non-%NULL, it can be used to cancel the 70 * connection. @callback will still be invoked in this case, with a 71 * status of %SOUP_STATUS_CANCELLED. 72 * 73 * Params: 74 * cancellable = a #GCancellable, or %NULL 75 * callback = callback to call after connecting 76 * userData = data to pass to @callback 77 */ 78 public void connectAsync(Cancellable cancellable, SoupSocketCallback callback, void* userData) 79 { 80 soup_socket_connect_async(soupSocket, (cancellable is null) ? null : cancellable.getCancellableStruct(), callback, userData); 81 } 82 83 /** 84 * Attempt to synchronously connect @sock to its remote address. 85 * 86 * If @cancellable is non-%NULL, it can be used to cancel the 87 * connection, in which case soup_socket_connect_sync() will return 88 * %SOUP_STATUS_CANCELLED. 89 * 90 * Params: 91 * cancellable = a #GCancellable, or %NULL 92 * 93 * Returns: a success or failure code. 94 */ 95 public uint connectSync(Cancellable cancellable) 96 { 97 return soup_socket_connect_sync(soupSocket, (cancellable is null) ? null : cancellable.getCancellableStruct()); 98 } 99 100 /** 101 * Disconnects @sock. Any further read or write attempts on it will 102 * fail. 103 */ 104 public void disconnect() 105 { 106 soup_socket_disconnect(soupSocket); 107 } 108 109 /** 110 * Gets @sock's underlying file descriptor. 111 * 112 * Note that fiddling with the file descriptor may break the 113 * #SoupSocket. 114 * 115 * Returns: @sock's file descriptor. 116 */ 117 public int getFd() 118 { 119 return soup_socket_get_fd(soupSocket); 120 } 121 122 /** 123 * Returns the #SoupAddress corresponding to the local end of @sock. 124 * 125 * Calling this method on an unconnected socket is considered to be 126 * an error, and produces undefined results. 127 * 128 * Returns: the #SoupAddress 129 */ 130 public Address getLocalAddress() 131 { 132 auto __p = soup_socket_get_local_address(soupSocket); 133 134 if(__p is null) 135 { 136 return null; 137 } 138 139 return ObjectG.getDObject!(Address)(cast(SoupAddress*) __p); 140 } 141 142 /** 143 * Returns the #SoupAddress corresponding to the remote end of @sock. 144 * 145 * Calling this method on an unconnected socket is considered to be 146 * an error, and produces undefined results. 147 * 148 * Returns: the #SoupAddress 149 */ 150 public Address getRemoteAddress() 151 { 152 auto __p = soup_socket_get_remote_address(soupSocket); 153 154 if(__p is null) 155 { 156 return null; 157 } 158 159 return ObjectG.getDObject!(Address)(cast(SoupAddress*) __p); 160 } 161 162 /** 163 * Tests if @sock is connected to another host 164 * 165 * Returns: %TRUE or %FALSE. 166 */ 167 public bool isConnected() 168 { 169 return soup_socket_is_connected(soupSocket) != 0; 170 } 171 172 /** 173 * Tests if @sock is doing (or has attempted to do) SSL. 174 * 175 * Returns: %TRUE if @sock has SSL credentials set 176 */ 177 public bool isSsl() 178 { 179 return soup_socket_is_ssl(soupSocket) != 0; 180 } 181 182 /** 183 * Makes @sock start listening on its local address. When connections 184 * come in, @sock will emit #SoupSocket::new_connection. 185 * 186 * Returns: whether or not @sock is now listening. 187 */ 188 public bool listen() 189 { 190 return soup_socket_listen(soupSocket) != 0; 191 } 192 193 /** 194 * Attempts to read up to @len bytes from @sock into @buffer. If some 195 * data is successfully read, soup_socket_read() will return 196 * %SOUP_SOCKET_OK, and *@nread will contain the number of bytes 197 * actually read (which may be less than @len). 198 * 199 * If @sock is non-blocking, and no data is available, the return 200 * value will be %SOUP_SOCKET_WOULD_BLOCK. In this case, the caller 201 * can connect to the #SoupSocket::readable signal to know when there 202 * is more data to read. (NB: You MUST read all available data off the 203 * socket first. #SoupSocket::readable is only emitted after 204 * soup_socket_read() returns %SOUP_SOCKET_WOULD_BLOCK, and it is only 205 * emitted once. See the documentation for #SoupSocket:non-blocking.) 206 * 207 * Params: 208 * buffer = buffer to read 209 * into 210 * nread = on return, the number of bytes read into @buffer 211 * cancellable = a #GCancellable, or %NULL 212 * 213 * Returns: a #SoupSocketIOStatus, as described above (or 214 * %SOUP_SOCKET_EOF if the socket is no longer connected, or 215 * %SOUP_SOCKET_ERROR on any other error, in which case @error will 216 * also be set). 217 * 218 * Throws: GException on failure. 219 */ 220 public SoupSocketIOStatus read(ubyte[] buffer, out size_t nread, Cancellable cancellable) 221 { 222 GError* err = null; 223 224 auto __p = soup_socket_read(soupSocket, buffer.ptr, cast(size_t)buffer.length, &nread, (cancellable is null) ? null : cancellable.getCancellableStruct(), &err); 225 226 if (err !is null) 227 { 228 throw new GException( new ErrorG(err) ); 229 } 230 231 return __p; 232 } 233 234 /** 235 * Like soup_socket_read(), but reads no further than the first 236 * occurrence of @boundary. (If the boundary is found, it will be 237 * included in the returned data, and *@got_boundary will be set to 238 * %TRUE.) Any data after the boundary will returned in future reads. 239 * 240 * soup_socket_read_until() will almost always return fewer than @len 241 * bytes: if the boundary is found, then it will only return the bytes 242 * up until the end of the boundary, and if the boundary is not found, 243 * then it will leave the last <literal>(boundary_len - 1)</literal> 244 * bytes in its internal buffer, in case they form the start of the 245 * boundary string. Thus, @len normally needs to be at least 1 byte 246 * longer than @boundary_len if you want to make any progress at all. 247 * 248 * Params: 249 * buffer = buffer to read 250 * into 251 * boundary = boundary to read until 252 * boundaryLen = length of @boundary in bytes 253 * nread = on return, the number of bytes read into @buffer 254 * gotBoundary = on return, whether or not the data in @buffer 255 * ends with the boundary string 256 * cancellable = a #GCancellable, or %NULL 257 * 258 * Returns: as for soup_socket_read() 259 * 260 * Throws: GException on failure. 261 */ 262 public SoupSocketIOStatus readUntil(ubyte[] buffer, void* boundary, size_t boundaryLen, out size_t nread, bool* gotBoundary, Cancellable cancellable) 263 { 264 GError* err = null; 265 266 auto __p = soup_socket_read_until(soupSocket, buffer.ptr, cast(size_t)buffer.length, boundary, boundaryLen, &nread, cast(int*)gotBoundary, (cancellable is null) ? null : cancellable.getCancellableStruct(), &err); 267 268 if (err !is null) 269 { 270 throw new GException( new ErrorG(err) ); 271 } 272 273 return __p; 274 } 275 276 /** 277 * Starts using SSL on @socket, expecting to find a host named 278 * @ssl_host. 279 * 280 * Params: 281 * sslHost = hostname of the SSL server 282 * cancellable = a #GCancellable 283 * 284 * Returns: success or failure 285 */ 286 public bool startProxySsl(string sslHost, Cancellable cancellable) 287 { 288 return soup_socket_start_proxy_ssl(soupSocket, Str.toStringz(sslHost), (cancellable is null) ? null : cancellable.getCancellableStruct()) != 0; 289 } 290 291 /** 292 * Starts using SSL on @socket. 293 * 294 * Params: 295 * cancellable = a #GCancellable 296 * 297 * Returns: success or failure 298 */ 299 public bool startSsl(Cancellable cancellable) 300 { 301 return soup_socket_start_ssl(soupSocket, (cancellable is null) ? null : cancellable.getCancellableStruct()) != 0; 302 } 303 304 /** 305 * Attempts to write @len bytes from @buffer to @sock. If some data is 306 * successfully written, the return status will be %SOUP_SOCKET_OK, 307 * and *@nwrote will contain the number of bytes actually written 308 * (which may be less than @len). 309 * 310 * If @sock is non-blocking, and no data could be written right away, 311 * the return value will be %SOUP_SOCKET_WOULD_BLOCK. In this case, 312 * the caller can connect to the #SoupSocket::writable signal to know 313 * when more data can be written. (NB: #SoupSocket::writable is only 314 * emitted after soup_socket_write() returns %SOUP_SOCKET_WOULD_BLOCK, 315 * and it is only emitted once. See the documentation for 316 * #SoupSocket:non-blocking.) 317 * 318 * Params: 319 * buffer = data to write 320 * nwrote = on return, number of bytes written 321 * cancellable = a #GCancellable, or %NULL 322 * 323 * Returns: a #SoupSocketIOStatus, as described above (or 324 * %SOUP_SOCKET_EOF or %SOUP_SOCKET_ERROR. @error will be set if the 325 * return value is %SOUP_SOCKET_ERROR.) 326 * 327 * Throws: GException on failure. 328 */ 329 public SoupSocketIOStatus write(ubyte[] buffer, out size_t nwrote, Cancellable cancellable) 330 { 331 GError* err = null; 332 333 auto __p = soup_socket_write(soupSocket, buffer.ptr, cast(size_t)buffer.length, &nwrote, (cancellable is null) ? null : cancellable.getCancellableStruct(), &err); 334 335 if (err !is null) 336 { 337 throw new GException( new ErrorG(err) ); 338 } 339 340 return __p; 341 } 342 343 /** 344 * Emitted when the socket is disconnected, for whatever 345 * reason. 346 */ 347 gulong addOnDisconnected(void delegate(Socket) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 348 { 349 return Signals.connect(this, "disconnected", dlg, connectFlags ^ ConnectFlags.SWAPPED); 350 } 351 352 /** 353 * Emitted when a network-related event occurs. See 354 * #GSocketClient::event for more details. 355 * 356 * Params: 357 * event = the event that occurred 358 * connection = the current connection state 359 * 360 * Since: 2.38 361 */ 362 gulong addOnEvent(void delegate(GSocketClientEvent, IOStream, Socket) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 363 { 364 return Signals.connect(this, "event", dlg, connectFlags ^ ConnectFlags.SWAPPED); 365 } 366 367 /** 368 * Emitted when a listening socket (set up with 369 * soup_socket_listen()) receives a new connection. 370 * 371 * You must ref the @new if you want to keep it; otherwise it 372 * will be destroyed after the signal is emitted. 373 * 374 * Params: 375 * new_ = the new socket 376 */ 377 gulong addOnNewConnection(void delegate(Socket, Socket) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 378 { 379 return Signals.connect(this, "new-connection", dlg, connectFlags ^ ConnectFlags.SWAPPED); 380 } 381 382 /** 383 * Emitted when an async socket is readable. See 384 * soup_socket_read(), soup_socket_read_until() and 385 * #SoupSocket:non-blocking. 386 */ 387 gulong addOnReadable(void delegate(Socket) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 388 { 389 return Signals.connect(this, "readable", dlg, connectFlags ^ ConnectFlags.SWAPPED); 390 } 391 392 /** 393 * Emitted when an async socket is writable. See 394 * soup_socket_write() and #SoupSocket:non-blocking. 395 */ 396 gulong addOnWritable(void delegate(Socket) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 397 { 398 return Signals.connect(this, "writable", dlg, connectFlags ^ ConnectFlags.SWAPPED); 399 } 400 }