client.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. // This file is subject to a 1-clause BSD license.
  2. // Its contents can be found in the enclosed LICENSE file.
  3. package main
  4. import (
  5. "bufio"
  6. "bytes"
  7. "io"
  8. "net"
  9. "os"
  10. "time"
  11. )
  12. // PayloadHandler is a function type for handling incoming
  13. // server messages.
  14. type PayloadHandler func([]byte)
  15. // ConnectionTimeout is the deadline for a connection.
  16. const ConnectionTimeout = time.Minute * 3
  17. // Client is an IRC client for a single network connection.
  18. type Client struct {
  19. handler PayloadHandler
  20. conn net.Conn
  21. reader *bufio.Reader
  22. }
  23. // NewClient creates a new client for the given handler.
  24. func NewClient(handler PayloadHandler) *Client {
  25. return &Client{
  26. handler: handler,
  27. }
  28. }
  29. // Open creates a new tcp connection to the given address.
  30. func (c *Client) Open(address string) error {
  31. var err error
  32. c.conn, err = net.Dial("tcp", address)
  33. if err != nil {
  34. return err
  35. }
  36. c.reader = bufio.NewReader(c.conn)
  37. return nil
  38. }
  39. // OpenFd opens a new client using the given file descriptor.
  40. // This function is used to hand over the tcp connection between
  41. // parent and its fork.
  42. func (c *Client) OpenFd(file *os.File) error {
  43. var err error
  44. c.conn, err = net.FileConn(file)
  45. if err != nil {
  46. return err
  47. }
  48. c.reader = bufio.NewReader(c.conn)
  49. return nil
  50. }
  51. // Close closes the connection.
  52. func (c *Client) Close() error {
  53. return c.conn.Close()
  54. }
  55. // File returns a file descriptor representing the tcp connection.
  56. // This call is only valid if the connection is open.
  57. func (c *Client) File() (*os.File, error) {
  58. return c.conn.(*net.TCPConn).File()
  59. }
  60. // Run starts the message processing loop and does not return for as long
  61. // as there is an open connection.
  62. func (c *Client) Run() error {
  63. for {
  64. line, err := c.Read()
  65. if err != nil {
  66. return err
  67. }
  68. go c.handler(line)
  69. }
  70. }
  71. // Write writes the given message to the underlying stream.
  72. func (c *Client) Write(p []byte) (int, error) {
  73. if c.conn == nil || len(p) == 0 {
  74. return 0, io.EOF
  75. }
  76. n, err := c.conn.Write(p)
  77. if err == nil {
  78. c.conn.SetDeadline(time.Now().Add(ConnectionTimeout))
  79. }
  80. return n, err
  81. }
  82. // Read reads the next message from the connection.
  83. // This call blocks until enough data is available or an error occurs.
  84. func (c *Client) Read() ([]byte, error) {
  85. if c.conn == nil {
  86. return nil, io.EOF
  87. }
  88. data, err := c.reader.ReadBytes('\n')
  89. if err != nil {
  90. return nil, err
  91. }
  92. c.conn.SetDeadline(time.Now().Add(ConnectionTimeout))
  93. return bytes.TrimSpace(data), nil
  94. }