You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ircconn.go 4.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. package irc
  2. import (
  3. "bytes"
  4. "errors"
  5. "io"
  6. "net"
  7. "unicode/utf8"
  8. "github.com/gorilla/websocket"
  9. "github.com/goshuirc/irc-go/ircmsg"
  10. "github.com/oragono/oragono/irc/utils"
  11. )
  12. const (
  13. maxReadQBytes = ircmsg.MaxlenTagsFromClient + MaxLineLen + 1024
  14. initialBufferSize = 1024
  15. )
  16. var (
  17. crlf = []byte{'\r', '\n'}
  18. errReadQ = errors.New("ReadQ Exceeded")
  19. )
  20. // IRCConn abstracts away the distinction between a regular
  21. // net.Conn (which includes both raw TCP and TLS) and a websocket.
  22. // it doesn't expose the net.Conn, io.Reader, or io.Writer interfaces
  23. // because websockets are message-oriented, not stream-oriented, and
  24. // therefore this abstraction is message-oriented as well.
  25. type IRCConn interface {
  26. UnderlyingConn() *utils.WrappedConn
  27. // these take an IRC line or lines, correctly terminated with CRLF:
  28. WriteLine([]byte) error
  29. WriteLines([][]byte) error
  30. // this returns an IRC line, possibly terminated with CRLF, LF, or nothing:
  31. ReadLine() (line []byte, err error)
  32. Close() error
  33. }
  34. // IRCStreamConn is an IRCConn over a regular stream connection.
  35. type IRCStreamConn struct {
  36. conn *utils.WrappedConn
  37. buf []byte
  38. start int // start of valid (i.e., read but not yet consumed) data in the buffer
  39. end int // end of valid data in the buffer
  40. searchFrom int // start of valid data in the buffer not yet searched for \n
  41. eof bool
  42. }
  43. func NewIRCStreamConn(conn *utils.WrappedConn) *IRCStreamConn {
  44. return &IRCStreamConn{
  45. conn: conn,
  46. }
  47. }
  48. func (cc *IRCStreamConn) UnderlyingConn() *utils.WrappedConn {
  49. return cc.conn
  50. }
  51. func (cc *IRCStreamConn) WriteLine(buf []byte) (err error) {
  52. _, err = cc.conn.Write(buf)
  53. return
  54. }
  55. func (cc *IRCStreamConn) WriteLines(buffers [][]byte) (err error) {
  56. // on Linux, with a plaintext TCP or Unix domain socket,
  57. // the Go runtime will optimize this into a single writev(2) call:
  58. _, err = (*net.Buffers)(&buffers).WriteTo(cc.conn)
  59. return
  60. }
  61. func (cc *IRCStreamConn) ReadLine() ([]byte, error) {
  62. for {
  63. // try to find a terminated line in the buffered data already read
  64. nlidx := bytes.IndexByte(cc.buf[cc.searchFrom:cc.end], '\n')
  65. if nlidx != -1 {
  66. // got a complete line
  67. line := cc.buf[cc.start : cc.searchFrom+nlidx]
  68. cc.start = cc.searchFrom + nlidx + 1
  69. cc.searchFrom = cc.start
  70. if globalUtf8EnforcementSetting && !utf8.Valid(line) {
  71. return line, errInvalidUtf8
  72. } else {
  73. return line, nil
  74. }
  75. }
  76. if cc.start == 0 && len(cc.buf) == maxReadQBytes {
  77. return nil, errReadQ // out of space, can't expand or slide
  78. }
  79. if cc.eof {
  80. return nil, io.EOF
  81. }
  82. if len(cc.buf) < maxReadQBytes && (len(cc.buf)-(cc.end-cc.start) < initialBufferSize/2) {
  83. // allocate a new buffer, copy any remaining data
  84. newLen := utils.RoundUpToPowerOfTwo(len(cc.buf) + 1)
  85. if newLen > maxReadQBytes {
  86. newLen = maxReadQBytes
  87. } else if newLen < initialBufferSize {
  88. newLen = initialBufferSize
  89. }
  90. newBuf := make([]byte, newLen)
  91. copy(newBuf, cc.buf[cc.start:cc.end])
  92. cc.buf = newBuf
  93. } else if cc.start != 0 {
  94. // slide remaining data back to the front of the buffer
  95. copy(cc.buf, cc.buf[cc.start:cc.end])
  96. }
  97. cc.end = cc.end - cc.start
  98. cc.start = 0
  99. cc.searchFrom = cc.end
  100. n, err := cc.conn.Read(cc.buf[cc.end:])
  101. cc.end += n
  102. if n != 0 && err == io.EOF {
  103. // we may have received new \n-terminated lines, try to parse them
  104. cc.eof = true
  105. } else if err != nil {
  106. return nil, err
  107. }
  108. }
  109. }
  110. func (cc *IRCStreamConn) Close() (err error) {
  111. return cc.conn.Close()
  112. }
  113. // IRCWSConn is an IRCConn over a websocket.
  114. type IRCWSConn struct {
  115. conn *websocket.Conn
  116. }
  117. func NewIRCWSConn(conn *websocket.Conn) IRCWSConn {
  118. return IRCWSConn{conn: conn}
  119. }
  120. func (wc IRCWSConn) UnderlyingConn() *utils.WrappedConn {
  121. // just assume that the type is OK
  122. wConn, _ := wc.conn.UnderlyingConn().(*utils.WrappedConn)
  123. return wConn
  124. }
  125. func (wc IRCWSConn) WriteLine(buf []byte) (err error) {
  126. buf = bytes.TrimSuffix(buf, crlf)
  127. if !globalUtf8EnforcementSetting && !utf8.Valid(buf) {
  128. // there's not much we can do about this;
  129. // silently drop the message
  130. return nil
  131. }
  132. return wc.conn.WriteMessage(websocket.TextMessage, buf)
  133. }
  134. func (wc IRCWSConn) WriteLines(buffers [][]byte) (err error) {
  135. for _, buf := range buffers {
  136. err = wc.WriteLine(buf)
  137. if err != nil {
  138. return
  139. }
  140. }
  141. return
  142. }
  143. func (wc IRCWSConn) ReadLine() (line []byte, err error) {
  144. messageType, line, err := wc.conn.ReadMessage()
  145. if err == nil {
  146. if messageType == websocket.TextMessage {
  147. return line, nil
  148. } else {
  149. // for purposes of fakelag, treat non-text message as an empty line
  150. return nil, nil
  151. }
  152. } else if err == websocket.ErrReadLimit {
  153. return line, errReadQ
  154. } else {
  155. return line, err
  156. }
  157. }
  158. func (wc IRCWSConn) Close() (err error) {
  159. return wc.conn.Close()
  160. }