Fixes httpclient SSL issue. Implements unbuffered SSL recv. Ref #1487.
This commit is contained in:
parent
cda457c865
commit
bd542ebea3
3 changed files with 39 additions and 22 deletions
|
|
@ -449,6 +449,8 @@ when defined(windows) or defined(nimdoc):
|
||||||
## complete once all the data requested is read, a part of the data has been
|
## complete once all the data requested is read, a part of the data has been
|
||||||
## read, or the socket has disconnected in which case the future will
|
## read, or the socket has disconnected in which case the future will
|
||||||
## complete with a value of ``""``.
|
## complete with a value of ``""``.
|
||||||
|
##
|
||||||
|
## **Warning**: The ``Peek`` socket flag is not supported on Windows.
|
||||||
|
|
||||||
|
|
||||||
# Things to note:
|
# Things to note:
|
||||||
|
|
@ -458,6 +460,8 @@ when defined(windows) or defined(nimdoc):
|
||||||
# '\0' in the message currently signifies a socket disconnect. Who
|
# '\0' in the message currently signifies a socket disconnect. Who
|
||||||
# knows what will happen when someone sends that to our socket.
|
# knows what will happen when someone sends that to our socket.
|
||||||
verifyPresence(socket)
|
verifyPresence(socket)
|
||||||
|
assert SocketFlag.Peek notin flags, "Peek not supported on Windows."
|
||||||
|
|
||||||
var retFuture = newFuture[string]("recv")
|
var retFuture = newFuture[string]("recv")
|
||||||
var dataBuf: TWSABuf
|
var dataBuf: TWSABuf
|
||||||
dataBuf.buf = cast[cstring](alloc0(size))
|
dataBuf.buf = cast[cstring](alloc0(size))
|
||||||
|
|
|
||||||
|
|
@ -157,34 +157,43 @@ proc connect*(socket: PAsyncSocket, address: string, port: TPort,
|
||||||
sslSetConnectState(socket.sslHandle)
|
sslSetConnectState(socket.sslHandle)
|
||||||
sslLoop(socket, flags, sslDoHandshake(socket.sslHandle))
|
sslLoop(socket, flags, sslDoHandshake(socket.sslHandle))
|
||||||
|
|
||||||
proc readIntoBuf(socket: PAsyncSocket,
|
proc readInto(buf: cstring, size: int, socket: PAsyncSocket,
|
||||||
flags: set[TSocketFlags]): Future[int] {.async.} =
|
flags: set[TSocketFlags]): Future[int] {.async.} =
|
||||||
var data = await recv(socket.fd.TAsyncFD, BufferSize, flags)
|
|
||||||
if data.len != 0:
|
|
||||||
copyMem(addr socket.buffer[0], addr data[0], data.len)
|
|
||||||
if socket.isSsl:
|
if socket.isSsl:
|
||||||
when defined(ssl):
|
when defined(ssl):
|
||||||
# SSL mode.
|
# SSL mode.
|
||||||
let ret = bioWrite(socket.bioIn, addr socket.buffer[0], data.len.cint)
|
|
||||||
if ret < 0:
|
|
||||||
raiseSSLError()
|
|
||||||
sslLoop(socket, flags,
|
sslLoop(socket, flags,
|
||||||
sslRead(socket.sslHandle, addr socket.buffer[0], BufferSize.cint))
|
sslRead(socket.sslHandle, buf, size.cint))
|
||||||
socket.currPos = 0
|
|
||||||
socket.bufLen = opResult # Injected from sslLoop template.
|
|
||||||
result = opResult
|
result = opResult
|
||||||
else:
|
else:
|
||||||
|
var data = await recv(socket.fd.TAsyncFD, size, flags)
|
||||||
|
if data.len != 0:
|
||||||
|
copyMem(buf, addr data[0], data.len)
|
||||||
# Not in SSL mode.
|
# Not in SSL mode.
|
||||||
socket.bufLen = data.len
|
|
||||||
socket.currPos = 0
|
|
||||||
result = data.len
|
result = data.len
|
||||||
|
|
||||||
|
proc readIntoBuf(socket: PAsyncSocket,
|
||||||
|
flags: set[TSocketFlags]): Future[int] {.async.} =
|
||||||
|
result = await readInto(addr socket.buffer[0], BufferSize, socket, flags)
|
||||||
|
socket.currPos = 0
|
||||||
|
socket.bufLen = result
|
||||||
|
|
||||||
proc recv*(socket: PAsyncSocket, size: int,
|
proc recv*(socket: PAsyncSocket, size: int,
|
||||||
flags = {TSocketFlags.SafeDisconn}): Future[string] {.async.} =
|
flags = {TSocketFlags.SafeDisconn}): Future[string] {.async.} =
|
||||||
## Reads ``size`` bytes from ``socket``. Returned future will complete once
|
## Reads **up to** ``size`` bytes from ``socket``.
|
||||||
## all of the requested data is read. If socket is disconnected during the
|
##
|
||||||
|
## For buffered sockets this function will attempt to read all the requested
|
||||||
|
## data. It will read this data in ``BufferSize`` chunks.
|
||||||
|
##
|
||||||
|
## For unbuffered sockets this function makes no effort to read
|
||||||
|
## all the data requested. It will return as much data as the operating system
|
||||||
|
## gives it.
|
||||||
|
##
|
||||||
|
## If socket is disconnected during the
|
||||||
## recv operation then the future may complete with only a part of the
|
## recv operation then the future may complete with only a part of the
|
||||||
## requested data read. If socket is disconnected and no data is available
|
## requested data.
|
||||||
|
##
|
||||||
|
## If socket is disconnected and no data is available
|
||||||
## to be read then the future will complete with a value of ``""``.
|
## to be read then the future will complete with a value of ``""``.
|
||||||
if socket.isBuffered:
|
if socket.isBuffered:
|
||||||
result = newString(size)
|
result = newString(size)
|
||||||
|
|
@ -216,7 +225,9 @@ proc recv*(socket: PAsyncSocket, size: int,
|
||||||
socket.currPos = originalBufPos
|
socket.currPos = originalBufPos
|
||||||
result.setLen(read)
|
result.setLen(read)
|
||||||
else:
|
else:
|
||||||
result = await recv(socket.fd.TAsyncFD, size, flags)
|
result = newString(size)
|
||||||
|
let read = await readInto(addr result[0], size, socket, flags)
|
||||||
|
result.setLen(read)
|
||||||
|
|
||||||
proc send*(socket: PAsyncSocket, data: string,
|
proc send*(socket: PAsyncSocket, data: string,
|
||||||
flags = {TSocketFlags.SafeDisconn}) {.async.} =
|
flags = {TSocketFlags.SafeDisconn}) {.async.} =
|
||||||
|
|
@ -282,6 +293,9 @@ proc recvLine*(socket: PAsyncSocket,
|
||||||
## The partial line **will be lost**.
|
## The partial line **will be lost**.
|
||||||
##
|
##
|
||||||
## **Warning**: The ``Peek`` flag is not yet implemented.
|
## **Warning**: The ``Peek`` flag is not yet implemented.
|
||||||
|
##
|
||||||
|
## **Warning**: ``recvLine`` on unbuffered sockets assumes that the protocol
|
||||||
|
## uses ``\r\L`` to delimit a new line.
|
||||||
template addNLIfEmpty(): stmt =
|
template addNLIfEmpty(): stmt =
|
||||||
if result.len == 0:
|
if result.len == 0:
|
||||||
result.add("\c\L")
|
result.add("\c\L")
|
||||||
|
|
@ -324,10 +338,8 @@ proc recvLine*(socket: PAsyncSocket,
|
||||||
if c.len == 0:
|
if c.len == 0:
|
||||||
return ""
|
return ""
|
||||||
if c == "\r":
|
if c == "\r":
|
||||||
c = await recv(socket, 1, flags + {TSocketFlags.Peek})
|
c = await recv(socket, 1, flags) # Skip \L
|
||||||
if c.len > 0 and c == "\L":
|
assert c == "\L"
|
||||||
let dummy = await recv(socket, 1, flags)
|
|
||||||
assert dummy == "\L"
|
|
||||||
addNLIfEmpty()
|
addNLIfEmpty()
|
||||||
return
|
return
|
||||||
elif c == "\L":
|
elif c == "\L":
|
||||||
|
|
|
||||||
|
|
@ -466,6 +466,7 @@ proc newAsyncHttpClient*(userAgent = defUserAgent,
|
||||||
result.headers = newStringTable(modeCaseInsensitive)
|
result.headers = newStringTable(modeCaseInsensitive)
|
||||||
result.userAgent = defUserAgent
|
result.userAgent = defUserAgent
|
||||||
result.maxRedirects = maxRedirects
|
result.maxRedirects = maxRedirects
|
||||||
|
when defined(ssl):
|
||||||
result.sslContext = net.SslContext(sslContext)
|
result.sslContext = net.SslContext(sslContext)
|
||||||
|
|
||||||
proc close*(client: AsyncHttpClient) =
|
proc close*(client: AsyncHttpClient) =
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue