libnmdc: optional synchronous-based API instead of channel-based API

This commit is contained in:
mappu 2016-04-03 18:52:19 +12:00
parent e3a92da5f6
commit ba378a8245

View File

@ -75,11 +75,17 @@ func init() {
} }
type HubConnectionOptions struct { type HubConnectionOptions struct {
Address HubAddress Address HubAddress
SkipVerifyTLS bool // using a negative verb, because bools default to false SkipVerifyTLS bool // using a negative verb, because bools default to false
Self UserInfo Self UserInfo
NickPassword string NickPassword string
// Returning messages in async mode
NumEventsToBuffer uint NumEventsToBuffer uint
OnEvent chan HubEvent
// Returning messages in sync mode
OnEventSync func(HubEvent)
} }
type HubConnection struct { type HubConnection struct {
@ -92,7 +98,7 @@ type HubConnection struct {
Users map[string]UserInfo Users map[string]UserInfo
// Streamed events // Streamed events
OnEvent chan HubEvent processEvent func(HubEvent)
// Private state // Private state
conn net.Conn // this is an interface conn net.Conn // this is an interface
@ -148,7 +154,7 @@ func (this *HubConnection) userJoined_NameOnly(nick string) {
_, already_existed := this.Users[nick] _, already_existed := this.Users[nick]
if !already_existed { if !already_existed {
this.Users[nick] = *NewUserInfo(nick) this.Users[nick] = *NewUserInfo(nick)
this.OnEvent <- HubEvent{EventType: EVENT_USER_JOINED, Nick: nick} this.processEvent(HubEvent{EventType: EVENT_USER_JOINED, Nick: nick})
} }
} }
@ -156,7 +162,7 @@ func (this *HubConnection) userJoined_Full(uinf *UserInfo) {
_, already_existed := this.Users[uinf.Nick] _, already_existed := this.Users[uinf.Nick]
if !already_existed { if !already_existed {
this.Users[uinf.Nick] = *uinf this.Users[uinf.Nick] = *uinf
this.OnEvent <- HubEvent{EventType: EVENT_USER_JOINED, Nick: uinf.Nick} this.processEvent(HubEvent{EventType: EVENT_USER_JOINED, Nick: uinf.Nick})
} }
} }
@ -205,14 +211,14 @@ func (this *HubConnection) processProtocolMessage(message string) {
// ``````````` // ```````````
if rx_publicChat.MatchString(message) { if rx_publicChat.MatchString(message) {
pubchat_parts := rx_publicChat.FindStringSubmatch(message) pubchat_parts := rx_publicChat.FindStringSubmatch(message)
this.OnEvent <- HubEvent{EventType: EVENT_PUBLIC, Nick: pubchat_parts[1], Message: NMDCUnescape(pubchat_parts[2])} this.processEvent(HubEvent{EventType: EVENT_PUBLIC, Nick: pubchat_parts[1], Message: NMDCUnescape(pubchat_parts[2])})
return return
} }
// System messages // System messages
// ``````````````` // ```````````````
if message[0] != '$' { if message[0] != '$' {
this.OnEvent <- HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_HUB, Nick: this.HubName, Message: NMDCUnescape(message)} this.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_HUB, Nick: this.HubName, Message: NMDCUnescape(message)})
return return
} }
@ -242,27 +248,27 @@ func (this *HubConnection) processProtocolMessage(message string) {
case "$HubName": case "$HubName":
this.HubName = commandParts[1] this.HubName = commandParts[1]
this.OnEvent <- HubEvent{EventType: EVENT_HUBNAME_CHANGED, Nick: commandParts[1]} this.processEvent(HubEvent{EventType: EVENT_HUBNAME_CHANGED, Nick: commandParts[1]})
case "$ValidateDenide": // sic case "$ValidateDenide": // sic
if len(this.Hco.NickPassword) > 0 { if len(this.Hco.NickPassword) > 0 {
this.OnEvent <- HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Incorrect password."} this.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Incorrect password."})
} else { } else {
this.OnEvent <- HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Nick already in use."} this.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Nick already in use."})
} }
case "$HubIsFull": case "$HubIsFull":
this.OnEvent <- HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Hub is full."} this.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Hub is full."})
case "$BadPass": case "$BadPass":
this.OnEvent <- HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Incorrect password."} this.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Incorrect password."})
case "$GetPass": case "$GetPass":
this.SayRaw("$MyPass " + NMDCEscape(this.Hco.NickPassword) + "|") this.SayRaw("$MyPass " + NMDCEscape(this.Hco.NickPassword) + "|")
case "$Quit": case "$Quit":
delete(this.Users, commandParts[1]) delete(this.Users, commandParts[1])
this.OnEvent <- HubEvent{EventType: EVENT_USER_PART, Nick: commandParts[1]} this.processEvent(HubEvent{EventType: EVENT_USER_PART, Nick: commandParts[1]})
case "$MyINFO": case "$MyINFO":
u := UserInfo{} u := UserInfo{}
@ -270,7 +276,7 @@ func (this *HubConnection) processProtocolMessage(message string) {
if err == nil { if err == nil {
this.userJoined_Full(&u) this.userJoined_Full(&u)
} else { } else {
this.OnEvent <- HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: err.Error()} this.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: err.Error()})
} }
case "$NickList": case "$NickList":
@ -286,20 +292,20 @@ func (this *HubConnection) processProtocolMessage(message string) {
if rx_incomingTo.MatchString(commandParts[1]) { if rx_incomingTo.MatchString(commandParts[1]) {
txparts := rx_incomingTo.FindStringSubmatch(commandParts[1]) txparts := rx_incomingTo.FindStringSubmatch(commandParts[1])
if txparts[1] == this.Hco.Self.Nick && txparts[2] == txparts[3] { if txparts[1] == this.Hco.Self.Nick && txparts[2] == txparts[3] {
this.OnEvent <- HubEvent{EventType: EVENT_PRIVATE, Nick: txparts[2], Message: txparts[4]} this.processEvent(HubEvent{EventType: EVENT_PRIVATE, Nick: txparts[2], Message: txparts[4]})
valid = true valid = true
} }
} }
if !valid { if !valid {
this.OnEvent <- HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: "Malformed private message '" + commandParts[1] + "'"} this.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: "Malformed private message '" + commandParts[1] + "'"})
} }
case "$UserIP": case "$UserIP":
// Final message in PtokaX connection handshake - trigger connection callback. // Final message in PtokaX connection handshake - trigger connection callback.
// This might not be the case for other hubsofts, though // This might not be the case for other hubsofts, though
if this.State != CONNECTIONSTATE_CONNECTED { if this.State != CONNECTIONSTATE_CONNECTED {
this.OnEvent <- HubEvent{EventType: EVENT_CONNECTION_STATE_CHANGED, StateChange: CONNECTIONSTATE_CONNECTED} this.processEvent(HubEvent{EventType: EVENT_CONNECTION_STATE_CHANGED, StateChange: CONNECTIONSTATE_CONNECTED})
this.State = CONNECTIONSTATE_CONNECTED this.State = CONNECTIONSTATE_CONNECTED
} }
@ -318,7 +324,7 @@ func (this *HubConnection) processProtocolMessage(message string) {
case "$ConnectToMe": case "$ConnectToMe":
default: default:
this.OnEvent <- HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: "Unhandled protocol command '" + commandParts[0] + "'"} this.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: "Unhandled protocol command '" + commandParts[0] + "'"})
} }
} }
@ -350,7 +356,7 @@ func (this *HubConnection) worker() {
} else { } else {
this.State = CONNECTIONSTATE_CONNECTING this.State = CONNECTIONSTATE_CONNECTING
this.connValid = true this.connValid = true
this.OnEvent <- HubEvent{EventType: EVENT_CONNECTION_STATE_CHANGED, StateChange: CONNECTIONSTATE_CONNECTING} this.processEvent(HubEvent{EventType: EVENT_CONNECTION_STATE_CHANGED, StateChange: CONNECTIONSTATE_CONNECTING})
} }
} }
@ -369,7 +375,7 @@ func (this *HubConnection) worker() {
this.State = CONNECTIONSTATE_DISCONNECTED this.State = CONNECTIONSTATE_DISCONNECTED
this.conn = nil this.conn = nil
this.connValid = false this.connValid = false
this.OnEvent <- HubEvent{EventType: EVENT_CONNECTION_STATE_CHANGED, StateChange: CONNECTIONSTATE_DISCONNECTED, Message: err.Error()} this.processEvent(HubEvent{EventType: EVENT_CONNECTION_STATE_CHANGED, StateChange: CONNECTIONSTATE_DISCONNECTED, Message: err.Error()})
time.Sleep(30 * time.Second) // Wait before reconnect time.Sleep(30 * time.Second) // Wait before reconnect
continue continue
@ -393,26 +399,45 @@ func (this *HubConnection) worker() {
} }
// Connects to an NMDC server, and spawns a background goroutine to handle func (this *HubConnectionOptions) prepareConnection() *HubConnection {
// protocol messages. Client code should select on all the interface channels.
func (this *HubConnectionOptions) Connect() *HubConnection {
if this.Self.ClientTag == "" { if this.Self.ClientTag == "" {
this.Self.ClientTag = "libnmdc.go" this.Self.ClientTag = "libnmdc.go"
} }
if this.NumEventsToBuffer < 1 {
this.NumEventsToBuffer = 1
}
hc := HubConnection{ hc := HubConnection{
Hco: this, Hco: this,
HubName: "(unknown)", HubName: "(unknown)",
State: CONNECTIONSTATE_DISCONNECTED, State: CONNECTIONSTATE_DISCONNECTED,
Users: make(map[string]UserInfo), Users: make(map[string]UserInfo),
OnEvent: make(chan HubEvent, this.NumEventsToBuffer),
} }
go hc.worker()
return &hc return &hc
} }
// Connects to an NMDC server, and spawns a background goroutine to handle
// protocol messages. Client code should select on all the interface channels.
func (this *HubConnectionOptions) Connect() *HubConnection {
if this.NumEventsToBuffer < 1 {
this.NumEventsToBuffer = 1
}
if this.OnEvent == nil {
this.OnEvent = make(chan HubEvent, this.NumEventsToBuffer)
}
hc := this.prepareConnection()
hc.processEvent = func(ev HubEvent) {
this.OnEvent <- ev
}
go hc.worker()
return hc
}
// Connects to an NMDC server, and blocks forever to handle protocol messages.
// Client code should supply an event handling function.
func (this *HubConnectionOptions) ConnectSync() {
hc := this.prepareConnection()
hc.worker()
}