hnakra/service/http.go

101 lines
2.5 KiB
Go

package service
import "log"
import "net"
import "sync"
import "bufio"
import "errors"
import "net/http"
import "hnakra/protocol"
// HTTP is an https:// mount.
type HTTP struct {
Mount
// AllowInsecure allows this mount to respond to plain-text HTTP
// requests. You can get a TLS cert for free nowadays so there are very
// few cases where you'd want this on.
AllowInsecure bool
// Handler specifies the handler to invoke. If it is nil,
// http.DefaultServeMux is used.
Handler http.Handler
conn net.Conn
connLock sync.Mutex
connReadWriter *bufio.ReadWriter
requests requestManager
}
// Close closes the mount abruptly, interrupting any active connections.
func (mount *HTTP) Close () error {
mount.connLock.Lock()
defer mount.connLock.Unlock()
return mount.conn.Close()
}
// Shutdown gracefully shuts down the service without interrupting any active
// connections.
func (mount *HTTP) Shutdown () error {
// TODO
return mount.Close()
}
// Run connects to the router, and blocks while fulfilling requests. This method
// will only return when the connection to the router has been closed.
func (mount *HTTP) Run () (err error) {
if mount.AllowInsecure {
mount.conn, err = mount.connect("http")
} else {
mount.conn, err = mount.connect("https")
}
if err != nil { return }
mount.connReadWriter = bufio.NewReadWriter (
bufio.NewReader(mount.conn),
bufio.NewWriter(mount.conn))
mount.requests.init()
for {
message, err := protocol.ReadMessage(mount.connReadWriter)
if err != nil { return err }
switch message.(type) {
case protocol.MessageHTTPRequest:
request := message.(protocol.MessageHTTPRequest)
mount.requests.add(request.ID)
go mount.handle(request)
case protocol.MessageHTTPBodySegment:
segment := message.(protocol.MessageHTTPBodySegment)
mount.requests.feed(segment.ID, segment.Data)
case protocol.MessageHTTPBodyEnd:
end := message.(protocol.MessageHTTPBodyEnd)
mount.requests.end(end.ID)
case protocol.MessageStatus:
status := message.(protocol.MessageStatus)
log.Println("router says:", status.Status)
default:
mount.Close()
return errors.New("router sent unknown type code")
}
}
}
// NewHTTP creates a very basic https:// mount with the specified name and
// description.
func NewHTTP (name, description string) HTTP {
return HTTP { Mount: M(name, description) }
}
func (mount *HTTP) send (message protocol.Message) (err error) {
mount.connLock.Lock()
defer mount.connLock.Unlock()
err = message.Send(mount.connReadWriter)
if err != nil { return }
return mount.connReadWriter.Flush()
}