Browse Source

Add basic ClientSocket work

tags/v0.1.0
Daniel Oaks 8 years ago
parent
commit
3a5314bd8e
2 changed files with 174 additions and 0 deletions
  1. 129
    0
      irc/clientsocket.go
  2. 45
    0
      irc/messages.go

+ 129
- 0
irc/clientsocket.go View File

@@ -0,0 +1,129 @@
1
+// Copyright (c) 2016- Daniel Oaks <daniel@danieloaks.net>
2
+// released under the MIT license
3
+
4
+package irc
5
+
6
+import (
7
+	"fmt"
8
+	"net"
9
+	"strings"
10
+
11
+	"github.com/DanielOaks/girc-go/ircmsg"
12
+)
13
+
14
+// ClientSocket listens to a socket using the IRC protocol, processes events,
15
+// and also sends IRC lines out of that socket.
16
+type ClientSocket struct {
17
+	receiveLines  chan string
18
+	ReceiveEvents chan Message
19
+	SendLines     chan string
20
+	socket        Socket
21
+	client        Client
22
+}
23
+
24
+// NewClientSocket returns a new ClientSocket.
25
+func NewClientSocket(conn net.Conn, client Client) ClientSocket {
26
+	return ClientSocket{
27
+		receiveLines:  make(chan string),
28
+		ReceiveEvents: make(chan Message),
29
+		SendLines:     make(chan string),
30
+		socket:        NewSocket(conn),
31
+		client:        client,
32
+	}
33
+}
34
+
35
+// Start creates and starts running the necessary event loops.
36
+func (cs *ClientSocket) Start() {
37
+	go cs.RunEvents()
38
+	go cs.RunSocketSender()
39
+	go cs.RunSocketListener()
40
+}
41
+
42
+// RunEvents handles received IRC lines and processes incoming commands.
43
+func (cs *ClientSocket) RunEvents() {
44
+	var exiting bool
45
+	var line string
46
+	for {
47
+		select {
48
+		case line = <-cs.receiveLines:
49
+			if line != "" {
50
+				fmt.Println("<- ", strings.TrimRight(line, "\r\n"))
51
+				exiting = cs.processIncomingLine(line)
52
+				if exiting {
53
+					cs.socket.Close()
54
+					break
55
+				}
56
+			}
57
+		}
58
+	}
59
+	// empty the receiveLines queue
60
+	select {
61
+	case <-cs.receiveLines:
62
+		// empty
63
+	default:
64
+		// empty
65
+	}
66
+}
67
+
68
+// RunSocketSender sends lines to the IRC socket.
69
+func (cs *ClientSocket) RunSocketSender() {
70
+	var err error
71
+	var line string
72
+	for {
73
+		line = <-cs.SendLines
74
+		err = cs.socket.Write(line)
75
+		fmt.Println(" ->", strings.TrimRight(line, "\r\n"))
76
+		if err != nil {
77
+			break
78
+		}
79
+	}
80
+}
81
+
82
+// RunSocketListener receives lines from the IRC socket.
83
+func (cs *ClientSocket) RunSocketListener() {
84
+	var errConn error
85
+	var line string
86
+
87
+	for {
88
+		line, errConn = cs.socket.Read()
89
+		cs.receiveLines <- line
90
+		if errConn != nil {
91
+			break
92
+		}
93
+	}
94
+	if !cs.socket.Closed {
95
+		cs.Send(nil, "", "ERROR", "Closing connection")
96
+		cs.socket.Close()
97
+	}
98
+}
99
+
100
+// Send sends an IRC line to the listener.
101
+func (cs *ClientSocket) Send(tags *map[string]ircmsg.TagValue, prefix string, command string, params ...string) error {
102
+	ircmsg := ircmsg.MakeMessage(tags, prefix, command, params...)
103
+	line, err := ircmsg.Line()
104
+	if err != nil {
105
+		return err
106
+	}
107
+	cs.SendLines <- line
108
+	return nil
109
+}
110
+
111
+// processIncomingLine splits and handles the given command line.
112
+// Returns true if client is exiting (sent a QUIT command, etc).
113
+func (cs *ClientSocket) processIncomingLine(line string) bool {
114
+	msg, err := ircmsg.ParseLine(line)
115
+	if err != nil {
116
+		cs.Send(nil, "", "ERROR", "Your client sent a malformed line")
117
+		return true
118
+	}
119
+
120
+	command, canBeParsed := Commands[msg.Command]
121
+
122
+	if canBeParsed {
123
+		return command.Run(cs, msg)
124
+	}
125
+	//TODO(dan): This is an error+disconnect purely for reasons of testing.
126
+	// Later it may be downgraded to not-that-bad.
127
+	cs.Send(nil, "", "ERROR", fmt.Sprintf("Your client sent a command that could not be parsed [%s]", msg.Command))
128
+	return true
129
+}

+ 45
- 0
irc/messages.go View File

@@ -0,0 +1,45 @@
1
+// Copyright (c) 2016- Daniel Oaks <daniel@danieloaks.net>
2
+// released under the MIT license
3
+
4
+package irc
5
+
6
+// Message represents an internal message passed by oragono.
7
+type Message struct {
8
+	Type MessageType
9
+	Verb MessageVerb
10
+	Info map[MessageInfoKey]interface{}
11
+}
12
+
13
+// NewMessage returns a new Message.
14
+// This is purely a convenience function.
15
+func NewMessage(mt MessageType, mv MessageVerb) Message {
16
+	var message Message
17
+	message.Type = mt
18
+	message.Verb = mv
19
+	message.Info = make(map[MessageInfoKey]interface{})
20
+	return message
21
+}
22
+
23
+// MessageType represents the type of message it is.
24
+type MessageType int
25
+
26
+const (
27
+	// LineMT represents an IRC line Message Type
28
+	LineMT MessageType = iota
29
+)
30
+
31
+// MessageVerb represents the verb (i.e. the specific command, etc) of a message.
32
+type MessageVerb int
33
+
34
+const (
35
+	// NoMV represents no Message Verb
36
+	NoMV MessageVerb = iota
37
+)
38
+
39
+// MessageInfoKey represents a key in the Info attribute of a Message.
40
+type MessageInfoKey int
41
+
42
+const (
43
+	// LineIK represents an IRC line message info key
44
+	LineIK MessageInfoKey = iota
45
+)

Loading…
Cancel
Save