evio/evio.go

206 lines
6.4 KiB
Go
Raw Normal View History

2017-11-02 18:08:18 +03:00
// Copyright 2017 Joshua J Baker. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
2017-10-29 00:58:59 +03:00
package evio
2017-10-26 23:43:31 +03:00
import (
"io"
"net"
"os"
"strings"
"time"
)
2017-10-30 00:31:03 +03:00
// Action is an action that occurs after the completion of an event.
2017-10-26 23:43:31 +03:00
type Action int
const (
2017-10-30 00:31:03 +03:00
// None indicates that no action should occur following an event.
2017-10-26 23:43:31 +03:00
None Action = iota
2017-10-30 00:31:03 +03:00
// Detach detaches the client.
2017-10-26 23:43:31 +03:00
Detach
2017-10-30 00:31:03 +03:00
// Close closes the client.
2017-10-26 23:43:31 +03:00
Close
2017-10-30 00:31:03 +03:00
// Shutdown shutdowns the server.
2017-10-26 23:43:31 +03:00
Shutdown
)
2017-10-30 00:31:03 +03:00
// Options are set when the client opens.
2017-10-26 23:43:31 +03:00
type Options struct {
2017-10-30 00:31:03 +03:00
// TCPKeepAlive (SO_KEEPALIVE) socket option.
2017-10-26 23:43:31 +03:00
TCPKeepAlive time.Duration
}
2017-11-07 19:52:16 +03:00
// Info represents a information about the connection
type Info struct {
2017-11-07 16:49:33 +03:00
// Closing is true when the connection is about to close. Expect a Closed
// event to fire soon.
Closing bool
// AddrIndex is the index of server address that was passed to the Serve call.
AddrIndex int
// LocalAddr is the connection's local socket address.
LocalAddr net.Addr
// RemoteAddr is the connection's remote peer address.
RemoteAddr net.Addr
2017-10-30 00:05:23 +03:00
}
2017-11-07 16:49:33 +03:00
// Server represents a server context which provides information about the
2017-11-07 19:52:16 +03:00
// running server and has control functions for managing state.
2017-11-07 16:49:33 +03:00
type Server struct {
// The addrs parameter is an array of listening addresses that align
// with the addr strings passed to the Serve function.
Addrs []net.Addr
// Wake is a goroutine-safe function that triggers a Data event
// (with a nil `in` parameter) for the specified id.
2017-11-07 19:52:16 +03:00
Wake func(id int) (ok bool)
// Dial is a goroutine-safe function makes a connection to an external
// server and returns a new connection id. The new connection is added
// to the event loop and is managed exactly the same way as all the
// other connections. This operation only fails if the server/loop has
// been shut down. An `id` that is not zero means the operation succeeded
// and then there always be exactly one Opened and one Closed event
// following this call. Look for socket errors from the Closed event.
Dial func(addr string, timeout time.Duration) (id int)
2017-11-06 13:58:06 +03:00
}
2017-10-30 00:31:03 +03:00
// Events represents the server events for the Serve call.
// Each event has an Action return value that is used manage the state
// of the connection and server.
2017-10-26 23:43:31 +03:00
type Events struct {
2017-11-07 19:52:16 +03:00
// Serving fires when the server can accept connections. The server
// parameter has information and various utilities.
Serving func(server Server) (action Action)
2017-10-26 23:43:31 +03:00
// Opened fires when a new connection has opened.
2017-11-07 19:52:16 +03:00
// The info parameter has information about the connection such as
// it's local and remote address.
2017-10-26 23:43:31 +03:00
// Use the out return value to write data to the connection.
2017-10-30 00:31:03 +03:00
// The opts return value is used to set connection options.
2017-11-07 19:52:16 +03:00
Opened func(id int, info Info) (out []byte, opts Options, action Action)
2017-11-06 13:58:06 +03:00
// Closed fires when a connection has closed.
2017-11-07 19:52:16 +03:00
// The err parameter is the last known connection error.
2017-11-01 03:44:57 +03:00
Closed func(id int, err error) (action Action)
2017-10-26 23:43:31 +03:00
// Detached fires when a connection has been previously detached.
2017-10-30 00:31:03 +03:00
// Once detached it's up to the receiver of this event to manage the
// state of the connection. The Closed event will not be called for
// this connection.
// The conn parameter is a ReadWriteCloser that represents the
// underlying socket connection. It can be freely used in goroutines
2017-11-02 03:39:18 +03:00
// and should be closed when it's no longer needed.
2017-11-01 03:44:57 +03:00
Detached func(id int, rwc io.ReadWriteCloser) (action Action)
2017-10-26 23:43:31 +03:00
// Data fires when a connection sends the server data.
2017-10-30 00:31:03 +03:00
// The in parameter is the incoming data.
2017-10-26 23:43:31 +03:00
// Use the out return value to write data to the connection.
Data func(id int, in []byte) (out []byte, action Action)
// Prewrite fires prior to every write attempt.
// The amount parameter is the number of bytes that will be attempted
// to be written to the connection.
Prewrite func(id int, amount int) (action Action)
// Postwrite fires immediately after every write attempt.
// The amount parameter is the number of bytes that was written to the
// connection.
// The remaining parameter is the number of bytes that still remain in
// the buffer scheduled to be written.
Postwrite func(id int, amount, remaining int) (action Action)
// Tick fires immediately after the server starts and will fire again
// following the duration specified by the delay return value.
Tick func() (delay time.Duration, action Action)
}
// Serve starts handling events for the specified addresses.
2017-10-30 00:31:03 +03:00
//
2017-11-02 03:36:35 +03:00
// Addresses should use a scheme prefix and be formatted
2017-10-30 00:31:03 +03:00
// like `tcp://192.168.0.10:9851` or `unix://socket`.
2017-11-02 03:36:35 +03:00
// Valid network schemes:
// tcp - bind to both IPv4 and IPv6
2017-10-30 00:31:03 +03:00
// tcp4 - IPv4
// tcp6 - IPv6
// unix - Unix Domain Socket
//
2017-11-02 03:36:35 +03:00
// The "tcp" network scheme is assumed when one is not specified.
2017-10-26 23:43:31 +03:00
func Serve(events Events, addr ...string) error {
var lns []*listener
defer func() {
for _, ln := range lns {
ln.close()
}
}()
2017-10-28 03:01:03 +03:00
var stdlib bool
2017-10-26 23:43:31 +03:00
for _, addr := range addr {
2017-11-07 16:49:33 +03:00
var ln listener
var stdlibt bool
ln.network, ln.addr, stdlibt = parseAddr(addr)
if stdlibt {
2017-10-28 03:01:03 +03:00
stdlib = true
2017-10-26 23:43:31 +03:00
}
if ln.network == "unix" {
os.RemoveAll(ln.addr)
}
var err error
ln.ln, err = net.Listen(ln.network, ln.addr)
if err != nil {
return err
}
2017-11-02 03:36:35 +03:00
ln.naddr = ln.ln.Addr()
2017-10-28 03:01:03 +03:00
if !stdlib {
2017-10-26 23:43:31 +03:00
if err := ln.system(); err != nil {
return err
}
}
lns = append(lns, &ln)
}
2017-10-28 03:01:03 +03:00
if stdlib {
2017-10-30 00:05:23 +03:00
return servenet(events, lns)
2017-10-26 23:43:31 +03:00
}
return serve(events, lns)
}
2017-10-28 22:23:13 +03:00
// InputStream is a helper type for managing input streams inside the
// Data event.
type InputStream struct{ b []byte }
// Begin accepts a new packet and returns a working sequence of
// unprocessed bytes.
func (is *InputStream) Begin(packet []byte) (data []byte) {
data = packet
if len(is.b) > 0 {
is.b = append(is.b, data...)
data = is.b
}
return data
}
// End shift the stream to match the unprocessed data.
func (is *InputStream) End(data []byte) {
if len(data) > 0 {
if len(data) != len(is.b) {
is.b = append(is.b[:0], data...)
}
} else if len(is.b) > 0 {
is.b = is.b[:0]
}
}
type listener struct {
ln net.Listener
f *os.File
fd int
network string
addr string
2017-11-02 03:36:35 +03:00
naddr net.Addr
2017-10-26 23:43:31 +03:00
}
2017-11-07 16:49:33 +03:00
func parseAddr(addr string) (network, address string, stdlib bool) {
network = "tcp"
address = addr
if strings.Contains(address, "://") {
network = strings.Split(address, "://")[0]
address = strings.Split(address, "://")[1]
}
if strings.HasSuffix(network, "-net") {
stdlib = true
network = network[:len(network)-4]
}
return
}