Improve net and ssl module docs.

This commit is contained in:
Dominik Picheta 2016-04-04 20:47:47 +01:00
commit 8e6f5f20f6
2 changed files with 80 additions and 20 deletions

View file

@ -9,6 +9,9 @@
## This module provides an easy to use sockets-style ## This module provides an easy to use sockets-style
## nim interface to the OpenSSL library. ## nim interface to the OpenSSL library.
##
## **Warning:** This module is deprecated, use the SSL procedures defined in
## the ``net`` module instead.
{.deprecated.} {.deprecated.}

View file

@ -8,19 +8,76 @@
# #
## This module implements a high-level cross-platform sockets interface. ## This module implements a high-level cross-platform sockets interface.
## The procedures implemented in this module are primarily for blocking sockets.
## For asynchronous non-blocking sockets use the ``asyncnet`` module together
## with the ``asyncdispatch`` module.
##
## The first thing you will always need to do in order to start using sockets,
## is to create a new instance of the ``Socket`` type using the ``newSocket``
## procedure.
##
## SSL
## ====
##
## In order to use the SSL procedures defined in this module, you will need to
## compile your application with the ``-d:ssl`` flag.
##
## Examples
## ========
##
## Connecting to a server
## ----------------------
##
## After you create a socket with the ``newSocket`` procedure, you can easily
## connect it to a server running at a known hostname (or IP address) and port.
## To do so over TCP, use the example below.
##
## .. code-block:: Nim
## var socket = newSocket()
## socket.connect("google.com", Port(80))
##
## UDP is a connectionless protocol, so UDP sockets don't have to explicitly
## call the ``connect`` procedure. They can simply start sending data
## immediately.
##
## .. code-block:: Nim
## var socket = newSocket()
## socket.sendTo("192.168.0.1", Port(27960), "status\n")
##
## Creating a server
## -----------------
##
## After you create a socket with the ``newSocket`` procedure, you can create a
## TCP server by calling the ``bindAddr`` and ``listen`` procedures.
##
## .. code-block:: Nim
## var socket = newSocket()
## socket.bindAddr(Port(1234))
## socket.listen()
##
## You can then begin accepting connections using the ``accept`` procedure.
##
## .. code-block:: Nim
## var client = newSocket()
## var address = ""
## while true:
## socket.acceptAddr(client, address)
## echo("Client connected from: ", address)
##
{.deadCodeElim: on.} {.deadCodeElim: on.}
import nativesockets, os, strutils, parseutils, times import nativesockets, os, strutils, parseutils, times
export Port, `$`, `==` export Port, `$`, `==`
const useWinVersion = defined(Windows) or defined(nimdoc) const useWinVersion = defined(Windows) or defined(nimdoc)
const defineSsl = defined(ssl) or defined(nimdoc)
when defined(ssl): when defineSsl:
import openssl import openssl
# Note: The enumerations are mapped to Window's constants. # Note: The enumerations are mapped to Window's constants.
when defined(ssl): when defineSsl:
type type
SslError* = object of Exception SslError* = object of Exception
@ -54,7 +111,7 @@ type
currPos: int # current index in buffer currPos: int # current index in buffer
bufLen: int # current length of buffer bufLen: int # current length of buffer
of false: nil of false: nil
when defined(ssl): when defineSsl:
case isSsl: bool case isSsl: bool
of true: of true:
sslHandle: SSLPtr sslHandle: SSLPtr
@ -160,7 +217,7 @@ proc newSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM,
raiseOSError(osLastError()) raiseOSError(osLastError())
result = newSocket(fd, domain, sockType, protocol, buffered) result = newSocket(fd, domain, sockType, protocol, buffered)
when defined(ssl): when defineSsl:
CRYPTO_malloc_init() CRYPTO_malloc_init()
SslLibraryInit() SslLibraryInit()
SslLoadErrorStrings() SslLoadErrorStrings()
@ -301,7 +358,7 @@ proc socketError*(socket: Socket, err: int = -1, async = false,
## error was caused by no data being available to be read. ## error was caused by no data being available to be read.
## ##
## If ``err`` is not lower than 0 no exception will be raised. ## If ``err`` is not lower than 0 no exception will be raised.
when defined(ssl): when defineSsl:
if socket.isSSL: if socket.isSSL:
if err <= 0: if err <= 0:
var ret = SSLGetError(socket.sslHandle, err.cint) var ret = SSLGetError(socket.sslHandle, err.cint)
@ -334,7 +391,7 @@ proc socketError*(socket: Socket, err: int = -1, async = false,
raiseSSLError() raiseSSLError()
else: raiseSSLError("Unknown Error") else: raiseSSLError("Unknown Error")
if err == -1 and not (when defined(ssl): socket.isSSL else: false): if err == -1 and not (when defineSsl: socket.isSSL else: false):
var lastE = if lastError.int == -1: getSocketError(socket) else: lastError var lastE = if lastError.int == -1: getSocketError(socket) else: lastError
if async: if async:
when useWinVersion: when useWinVersion:
@ -414,7 +471,7 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string,
client.isBuffered = server.isBuffered client.isBuffered = server.isBuffered
# Handle SSL. # Handle SSL.
when defined(ssl): when defineSsl:
if server.isSSL: if server.isSSL:
# We must wrap the client sock in a ssl context. # We must wrap the client sock in a ssl context.
@ -425,7 +482,7 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string,
# Client socket is set above. # Client socket is set above.
address = $inet_ntoa(sockAddress.sin_addr) address = $inet_ntoa(sockAddress.sin_addr)
when false: #defined(ssl): when false: #defineSsl:
proc acceptAddrSSL*(server: Socket, client: var Socket, proc acceptAddrSSL*(server: Socket, client: var Socket,
address: var string): SSLAcceptResult {. address: var string): SSLAcceptResult {.
tags: [ReadIOEffect].} = tags: [ReadIOEffect].} =
@ -444,7 +501,7 @@ when false: #defined(ssl):
## ``AcceptNoClient`` will be returned when no client is currently attempting ## ``AcceptNoClient`` will be returned when no client is currently attempting
## to connect. ## to connect.
template doHandshake(): stmt = template doHandshake(): stmt =
when defined(ssl): when defineSsl:
if server.isSSL: if server.isSSL:
client.setBlocking(false) client.setBlocking(false)
# We must wrap the client sock in a ssl context. # We must wrap the client sock in a ssl context.
@ -495,7 +552,7 @@ proc accept*(server: Socket, client: var Socket,
proc close*(socket: Socket) = proc close*(socket: Socket) =
## Closes a socket. ## Closes a socket.
try: try:
when defined(ssl): when defineSsl:
if socket.isSSL: if socket.isSSL:
ErrClearError() ErrClearError()
# As we are closing the underlying socket immediately afterwards, # As we are closing the underlying socket immediately afterwards,
@ -547,7 +604,7 @@ proc setSockOpt*(socket: Socket, opt: SOBool, value: bool, level = SOL_SOCKET) {
var valuei = cint(if value: 1 else: 0) var valuei = cint(if value: 1 else: 0)
setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei) setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei)
when defined(ssl): when defineSsl:
proc handshake*(socket: Socket): bool {.tags: [ReadIOEffect, WriteIOEffect].} = proc handshake*(socket: Socket): bool {.tags: [ReadIOEffect, WriteIOEffect].} =
## This proc needs to be called on a socket after it connects. This is ## This proc needs to be called on a socket after it connects. This is
## only applicable when using ``connectAsync``. ## only applicable when using ``connectAsync``.
@ -594,7 +651,7 @@ proc hasDataBuffered*(s: Socket): bool =
if s.isBuffered: if s.isBuffered:
result = s.bufLen > 0 and s.currPos != s.bufLen result = s.bufLen > 0 and s.currPos != s.bufLen
when defined(ssl): when defineSsl:
if s.isSSL and not result: if s.isSSL and not result:
result = s.sslHasPeekChar result = s.sslHasPeekChar
@ -608,7 +665,7 @@ proc select(readfd: Socket, timeout = 500): int =
proc readIntoBuf(socket: Socket, flags: int32): int = proc readIntoBuf(socket: Socket, flags: int32): int =
result = 0 result = 0
when defined(ssl): when defineSsl:
if socket.isSSL: if socket.isSSL:
result = SSLRead(socket.sslHandle, addr(socket.buffer), int(socket.buffer.high)) result = SSLRead(socket.sslHandle, addr(socket.buffer), int(socket.buffer.high))
else: else:
@ -658,7 +715,7 @@ proc recv*(socket: Socket, data: pointer, size: int): int {.tags: [ReadIOEffect]
result = read result = read
else: else:
when defined(ssl): when defineSsl:
if socket.isSSL: if socket.isSSL:
if socket.sslHasPeekChar: if socket.sslHasPeekChar:
copyMem(data, addr(socket.sslPeekChar), 1) copyMem(data, addr(socket.sslPeekChar), 1)
@ -696,7 +753,7 @@ proc waitFor(socket: Socket, waited: var float, timeout, size: int,
if timeout - int(waited * 1000.0) < 1: if timeout - int(waited * 1000.0) < 1:
raise newException(TimeoutError, "Call to '" & funcName & "' timed out.") raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
when defined(ssl): when defineSsl:
if socket.isSSL: if socket.isSSL:
if socket.hasDataBuffered: if socket.hasDataBuffered:
# sslPeekChar is present. # sslPeekChar is present.
@ -764,7 +821,7 @@ proc peekChar(socket: Socket, c: var char): int {.tags: [ReadIOEffect].} =
c = socket.buffer[socket.currPos] c = socket.buffer[socket.currPos]
else: else:
when defined(ssl): when defineSsl:
if socket.isSSL: if socket.isSSL:
if not socket.sslHasPeekChar: if not socket.sslHasPeekChar:
result = SSLRead(socket.sslHandle, addr(socket.sslPeekChar), 1) result = SSLRead(socket.sslHandle, addr(socket.sslPeekChar), 1)
@ -872,7 +929,7 @@ proc send*(socket: Socket, data: pointer, size: int): int {.
## ##
## **Note**: This is a low-level version of ``send``. You likely should use ## **Note**: This is a low-level version of ``send``. You likely should use
## the version below. ## the version below.
when defined(ssl): when defineSsl:
if socket.isSSL: if socket.isSSL:
return SSLWrite(socket.sslHandle, cast[cstring](data), size) return SSLWrite(socket.sslHandle, cast[cstring](data), size)
@ -943,7 +1000,7 @@ proc sendTo*(socket: Socket, address: string, port: Port,
proc isSsl*(socket: Socket): bool = proc isSsl*(socket: Socket): bool =
## Determines whether ``socket`` is a SSL socket. ## Determines whether ``socket`` is a SSL socket.
when defined(ssl): when defineSsl:
result = socket.isSSL result = socket.isSSL
else: else:
result = false result = false
@ -1253,7 +1310,7 @@ proc connect*(socket: Socket, address: string,
dealloc(aiList) dealloc(aiList)
if not success: raiseOSError(lastError) if not success: raiseOSError(lastError)
when defined(ssl): when defineSsl:
if socket.isSSL: if socket.isSSL:
# RFC3546 for SNI specifies that IP addresses are not allowed. # RFC3546 for SNI specifies that IP addresses are not allowed.
if not isIpAddress(address): if not isIpAddress(address):
@ -1314,7 +1371,7 @@ proc connect*(socket: Socket, address: string, port = Port(0),
if selectWrite(s, timeout) != 1: if selectWrite(s, timeout) != 1:
raise newException(TimeoutError, "Call to 'connect' timed out.") raise newException(TimeoutError, "Call to 'connect' timed out.")
else: else:
when defined(ssl): when defineSsl:
if socket.isSSL: if socket.isSSL:
socket.fd.setBlocking(true) socket.fd.setBlocking(true)
doAssert socket.handshake() doAssert socket.handshake()