camfish/actors/http.go

133 lines
3.5 KiB
Go

package http
import "fmt"
import "log"
import "errors"
import "context"
import "net/http"
import cf "git.tebibyte.media/sashakoshka/camfish"
var _ cf.Actor = new(HTTP)
var _ cf.RunShutdownable = new(HTTP)
var _ cf.Configurable = new(HTTP)
var _ cf.Initializable = new(HTTP)
// HTTP is an actor providing an HTTP server. Its configuration options are
// as follows:
//
// - http.address: The host:port to serve on
// - http.cert-file: The location of the public TLS/SSL certificate
// - http.key-file: The location of the private TLS/SSL key
//
// If neither cert-file nor key-file are specified, the actor will serve
// over HTTPS instead of HTTP.
type HTTP struct {
// Typ determines the actor's type. If empty, it defaults to "http".
// Once the actor has been used in any way at all, this field must
// never be modified.
Typ string
// Handler is used to handle HTTP requests. If nil, the server will
// respond with a "404 Not found" error.
// Once the actor has been used in any way at all, this field must
// never be modified.
Handler http.Handler
// DefaultAddr specifies the default address to serve on if none is
// specified. This itself defaults to localhost:8080.
DefaultAddr
server *http.Server
address string
certFile string
keyFile string
}
// Type returns "http", or this.Typ if specified.
func (this *HTTP) Type() string {
if this.Typ == "" {
return "http"
}
return this.Typ
}
// Configure configures the actor.
func (this *HTTP) Configure(conf cf.Config) error {
{
value := conf.Get(this.Type() + ".address")
if value == "" {
if this.DefaultAddr == "" {
value = "localhost:8080"
} else {
value = this.DefaultAddr
}
this.address = value
}
{
certFile := this.Type() + "cert-file"
keyFile := this.Type() + "key-file"
this.certFile = conf.Get(certFile)
this.keyFile = conf.Get(keyFile)
if this.certFile == "" && this.keyFile != "" ||
this.certFile != "" && this.keyFile == "" {
return fmt.Errorf(
"both %s and %s http.key-file must be specified, or neither",
this.certFile,
this.keyFile)
}
}
return nil
}
// Init initializes the actor.
func (this *HTTP) Init(ctx context.Context) error {
this.server = &http.Server {
Addr: this.address,
Handler: this.Handler,
}
return ctx.Err()
}
// Run runs the actor.
func (this *HTTP) Run() error {
log.Printf("(i) [%s] listening on %s", this.Type(), this.address)
err := this.server.ListenAndServe()
if errors.Is(err, http.ErrServerClosed) { return nil }
return err
}
// Shutdown shuts down the actor.
func (this *HTTP) Shutdown(ctx context.Context) error {
return this.server.Shutdown(ctx)
}
var _ cf.Actor = new(HTTPServer)
var _ cf.RunShutdownable = new(HTTPServer)
var _ cf.Initializable = new(HTTPServer)
// HTTPServer lets you use a pre-existing [http.Server] as an actor. It has
// no configuration or anything.
type HTTPServer struct {
// Server must be non-nil.
*HTTP.Server
// Typ determines the actor's type. If empty, it defaults to "http".
// Once the actor has been used in any way at all, this field must
// never be modified.
Typ string
}
// Type returns "http", or this.Typ if specified.
func (this *HTTPServer) Type() string {
if this.Typ == "" {
return "http"
}
return this.Typ
}
// Run runs the actor.
func (this *HTTPServer) Run() error {
log.Printf("(i) [%s] listening on %s", this.Type(), this.Addr)
err := this.server.ListenAndServe()
if errors.Is(err, http.ErrServerClosed) { return nil }
return err
}