socketstreams.nim 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2021 Nim contributors
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## This module provides an implementation of the streams interface for sockets.
  10. ## It contains two separate implementations, a
  11. ## `ReadSocketStream <#ReadSocketStream>`_ and a
  12. ## `WriteSocketStream <#WriteSocketStream>`_.
  13. ##
  14. ## The `ReadSocketStream` only supports reading, peeking, and seeking.
  15. ## It reads into a buffer, so even by
  16. ## seeking backwards it will only read the same position a single time from the
  17. ## underlying socket. To clear the buffer and free the data read into it you
  18. ## can call `resetStream`, this will also reset the position back to 0 but
  19. ## won't do anything to the underlying socket.
  20. ##
  21. ## The `WriteSocketStream` allows both reading and writing, but it performs the
  22. ## reads on the internal buffer. So by writing to the buffer you can then read
  23. ## back what was written but without receiving anything from the socket. You
  24. ## can also set the position and overwrite parts of the buffer, and to send
  25. ## anything over the socket you need to call `flush` at which point you can't
  26. ## write anything to the buffer before the point of the flush (but it can still
  27. ## be read). Again to empty the underlying buffer you need to call
  28. ## `resetStream`.
  29. ##
  30. ## Examples
  31. ## ========
  32. ##
  33. ## ```Nim
  34. ## import std/socketstreams
  35. ##
  36. ## var
  37. ## socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
  38. ## stream = newReadSocketStream(socket)
  39. ## socket.sendTo("127.0.0.1", Port(12345), "SOME REQUEST")
  40. ## echo stream.readLine() # Will call `recv`
  41. ## stream.setPosition(0)
  42. ## echo stream.readLine() # Will return the read line from the buffer
  43. ## stream.resetStream() # Buffer is now empty, position is 0
  44. ## echo stream.readLine() # Will call `recv` again
  45. ## stream.close() # Closes the socket
  46. ## ```
  47. ##
  48. ## ```Nim
  49. ## import std/socketstreams
  50. ##
  51. ## var socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
  52. ## socket.connect("127.0.0.1", Port(12345))
  53. ## var sendStream = newWriteSocketStream(socket)
  54. ## sendStream.write "NOM"
  55. ## sendStream.setPosition(1)
  56. ## echo sendStream.peekStr(2) # OM
  57. ## sendStream.write "I"
  58. ## sendStream.setPosition(0)
  59. ## echo sendStream.readStr(3) # NIM
  60. ## echo sendStream.getPosition() # 3
  61. ## sendStream.flush() # This actually performs the writing to the socket
  62. ## sendStream.setPosition(1)
  63. ## sendStream.write "I" # Throws an error as we can't write into an already sent buffer
  64. ## ```
  65. import std/[net, streams]
  66. type
  67. ReadSocketStream* = ref ReadSocketStreamObj
  68. ReadSocketStreamObj* = object of StreamObj
  69. data: Socket
  70. pos: int
  71. buf: seq[byte]
  72. WriteSocketStream* = ref WriteSocketStreamObj
  73. WriteSocketStreamObj* = object of ReadSocketStreamObj
  74. lastFlush: int
  75. proc rsAtEnd(s: Stream): bool =
  76. return false
  77. proc rsSetPosition(s: Stream, pos: int) =
  78. var s = ReadSocketStream(s)
  79. s.pos = pos
  80. proc rsGetPosition(s: Stream): int =
  81. var s = ReadSocketStream(s)
  82. return s.pos
  83. proc rsPeekData(s: Stream, buffer: pointer, bufLen: int): int =
  84. let s = ReadSocketStream(s)
  85. if bufLen > 0:
  86. let oldLen = s.buf.len
  87. s.buf.setLen(max(s.pos + bufLen, s.buf.len))
  88. if s.pos + bufLen > oldLen:
  89. result = s.data.recv(s.buf[oldLen].addr, s.buf.len - oldLen)
  90. if result > 0:
  91. result += oldLen - s.pos
  92. else:
  93. result = bufLen
  94. copyMem(buffer, s.buf[s.pos].addr, result)
  95. proc rsReadData(s: Stream, buffer: pointer, bufLen: int): int =
  96. result = s.rsPeekData(buffer, bufLen)
  97. var s = ReadSocketStream(s)
  98. s.pos += bufLen
  99. proc rsReadDataStr(s: Stream, buffer: var string, slice: Slice[int]): int =
  100. var s = ReadSocketStream(s)
  101. result = slice.b + 1 - slice.a
  102. if result > 0:
  103. result = s.rsReadData(buffer[slice.a].addr, result)
  104. inc(s.pos, result)
  105. else:
  106. result = 0
  107. proc wsWriteData(s: Stream, buffer: pointer, bufLen: int) =
  108. var s = WriteSocketStream(s)
  109. if s.pos < s.lastFlush:
  110. raise newException(IOError, "Unable to write into buffer that has already been sent")
  111. if s.buf.len < s.pos + bufLen:
  112. s.buf.setLen(s.pos + bufLen)
  113. copyMem(s.buf[s.pos].addr, buffer, bufLen)
  114. s.pos += bufLen
  115. proc wsPeekData(s: Stream, buffer: pointer, bufLen: int): int =
  116. var s = WriteSocketStream(s)
  117. result = bufLen
  118. if result > 0:
  119. if s.pos > s.buf.len or s.pos == s.buf.len or s.pos + bufLen > s.buf.len:
  120. raise newException(IOError, "Unable to read past end of write buffer")
  121. else:
  122. copyMem(buffer, s.buf[s.pos].addr, bufLen)
  123. proc wsReadData(s: Stream, buffer: pointer, bufLen: int): int =
  124. result = s.wsPeekData(buffer, bufLen)
  125. var s = ReadSocketStream(s)
  126. s.pos += bufLen
  127. proc wsAtEnd(s: Stream): bool =
  128. var s = WriteSocketStream(s)
  129. return s.pos == s.buf.len
  130. proc wsFlush(s: Stream) =
  131. var s = WriteSocketStream(s)
  132. discard s.data.send(s.buf[s.lastFlush].addr, s.buf.len - s.lastFlush)
  133. s.lastFlush = s.buf.len
  134. proc rsClose(s: Stream) =
  135. {.cast(raises: [IOError, OSError]), cast(tags: []).}: # todo fixme maybe do something?
  136. var s = ReadSocketStream(s)
  137. s.data.close()
  138. proc newReadSocketStream*(s: Socket): owned ReadSocketStream =
  139. result = ReadSocketStream(data: s, pos: 0,
  140. closeImpl: rsClose,
  141. atEndImpl: rsAtEnd,
  142. setPositionImpl: rsSetPosition,
  143. getPositionImpl: rsGetPosition,
  144. readDataImpl: rsReadData,
  145. peekDataImpl: rsPeekData,
  146. readDataStrImpl: rsReadDataStr)
  147. proc resetStream*(s: ReadSocketStream) =
  148. s.buf = @[]
  149. s.pos = 0
  150. proc newWriteSocketStream*(s: Socket): owned WriteSocketStream =
  151. result = WriteSocketStream(data: s, pos: 0,
  152. closeImpl: rsClose,
  153. atEndImpl: wsAtEnd,
  154. setPositionImpl: rsSetPosition,
  155. getPositionImpl: rsGetPosition,
  156. writeDataImpl: wsWriteData,
  157. readDataImpl: wsReadData,
  158. peekDataImpl: wsPeekData,
  159. flushImpl: wsFlush)
  160. proc resetStream*(s: WriteSocketStream) =
  161. s.buf = @[]
  162. s.pos = 0
  163. s.lastFlush = 0