Commit 25280667 authored by Sietse Ringers's avatar Sietse Ringers
Browse files

Support for separate listen address and port for requestor and client servers

parent ce17e1a0
......@@ -31,7 +31,7 @@ func StartIrmaServer(configuration *irmaserver.Configuration) {
}
func StopIrmaServer() {
irmaserver.Stop()
_ = irmaserver.Stop()
}
var IrmaServerConfiguration = &irmaserver.Configuration{
......
......@@ -2,6 +2,7 @@ package server
import (
"encoding/json"
"fmt"
"net"
"net/http"
"os"
......@@ -65,7 +66,7 @@ const (
// RemoteError converts an error and an explaining message to an *irma.RemoteError.
func RemoteError(err Error, message string) *irma.RemoteError {
stack := string(debug.Stack())
Logger.Warn("Session error: %d %s %s\n%s", err.Status, err.Type, message, stack)
Logger.Warnf("Session error: %d %s %s\n%s", err.Status, err.Type, message, stack)
return &irma.RemoteError{
Status: err.Status,
Description: err.Description,
......@@ -210,24 +211,26 @@ func Verbosity(level int) logrus.Level {
}
}
func LogError(err error) error {
Logger.Error(err.Error())
if e, ok := err.(*errors.Error); ok {
Logger.Error(e.ErrorStack())
func log(level logrus.Level, err error) error {
writer := Logger.WriterLevel(level)
if e, ok := err.(*errors.Error); ok && Logger.IsLevelEnabled(logrus.TraceLevel) {
_, _ = writer.Write([]byte(e.ErrorStack()))
} else {
Logger.Errorf("%s %s", reflect.TypeOf(err).String(), err.Error())
_, _ = writer.Write([]byte(fmt.Sprintf("%s %s", reflect.TypeOf(err).String(), err.Error())))
}
return err
}
func LogFatal(err error) error {
return log(logrus.FatalLevel, err)
}
func LogError(err error) error {
return log(logrus.ErrorLevel, err)
}
func LogWarning(err error) error {
Logger.Warn(err.Error())
if e, ok := err.(*errors.Error); ok {
Logger.Warn(e.ErrorStack())
} else {
Logger.Warn("%s %s", reflect.TypeOf(err).String(), err.Error())
}
return err
return log(logrus.WarnLevel, err)
}
func ToJson(o interface{}) string {
......
......@@ -28,6 +28,11 @@ type Configuration struct {
// Port to listen at
Port int `json:"port" mapstructure:"port"`
// If specified, start a separate server for the IRMA app at his port
ClientPort int `json:"clientport" mapstructure:"clientport"`
// If clientport is specified, the server for the IRMA app listens at this address
ClientListenAddress string `json:"clientlistenaddr" mapstructure:"clientlistenaddr"`
// Requestor-specific permission and authentication configuration
RequestorsString string `json:"-" mapstructure:"requestors"`
Requestors map[string]Requestor `json:"requestors"`
......@@ -153,6 +158,16 @@ func (conf *Configuration) initialize() error {
return errors.Errorf("Port must be between 1 and 65535 (was %d)", conf.Port)
}
if conf.ClientPort != 0 && conf.ClientPort == conf.Port {
return errors.New("If clientport is given it must be different from port")
}
if conf.ClientPort < 0 || conf.ClientPort > 65535 {
return errors.Errorf("clientport must be between 0 and 65535 (was %d)", conf.ClientPort)
}
if conf.ClientListenAddress != "" && conf.ClientPort == 0 {
return errors.New("clientlistenaddr must be combined with a nonzero clientport")
}
if err := conf.validatePermissions(); err != nil {
return err
}
......@@ -163,7 +178,11 @@ func (conf *Configuration) initialize() error {
}
conf.URL = conf.URL + "irma/"
// replace "port" in url with actual port
replace := "$1:" + strconv.Itoa(conf.Port)
port := conf.ClientPort
if port == 0 {
port = conf.Port
}
replace := "$1:" + strconv.Itoa(port)
conf.URL = string(regexp.MustCompile("(https?://[^/]*):port").ReplaceAll([]byte(conf.URL), []byte(replace)))
}
......@@ -261,6 +280,10 @@ func (conf *Configuration) readPrivateKey() error {
return err
}
func (conf *Configuration) separateClientServer() bool {
return conf.ClientPort != 0
}
// Return true iff query equals an element of strings.
func contains(strings []string, query string) bool {
for _, s := range strings {
......
......@@ -78,6 +78,8 @@ func setFlags(cmd *cobra.Command) error {
flags.StringP("url", "u", defaulturl, "External URL to server to which the IRMA client connects")
flags.StringP("listenaddr", "l", "0.0.0.0", "Address at which to listen")
flags.IntP("port", "p", 8088, "Port at which to listen")
flags.Int("clientport", 0, "If specified, start a separate server for the IRMA app at his port")
flags.String("clientlistenaddr", "0.0.0.0", "Address at which server for IRMA app listens")
flags.Bool("noauth", false, "Whether or not to authenticate requestors")
flags.String("requestors", "", "Requestor configuration (in JSON)")
......@@ -139,6 +141,8 @@ func configure() error {
},
ListenAddress: viper.GetString("listenaddr"),
Port: viper.GetInt("port"),
ClientListenAddress: viper.GetString("clientlistenaddr"),
ClientPort: viper.GetInt("clientport"),
DisableRequestorAuthentication: viper.GetBool("noauth"),
Requestors: make(map[string]irmaserver.Requestor),
GlobalPermissions: irmaserver.Permissions{},
......
......@@ -16,54 +16,101 @@ import (
)
var (
s *http.Server
serv, clientserv *http.Server
conf *Configuration
)
// Start the server. If successful then it will not return until Stop() is called.
func Start(config *Configuration) error {
handler, err := Handler(config)
if err != nil {
if err := Initialize(config); err != nil {
return err
}
// Start server
addr := fmt.Sprintf("%s:%d", conf.ListenAddress, conf.Port)
config.Logger.Info("Listening at ", addr)
s = &http.Server{Addr: addr, Handler: handler}
err = s.ListenAndServe()
if err == http.ErrServerClosed {
return nil // Server was closed normally
// Start server(s)
if conf.separateClientServer() {
go startClientServer()
}
startRequestorServer()
return server.LogError(err)
return nil
}
func Stop() {
s.Close()
func startRequestorServer() {
serv = &http.Server{}
startServer(serv, Handler(), "Server", conf.ListenAddress, conf.Port)
}
// Handler returns a http.Handler that handles all IRMA requestor messages
// and IRMA client messages.
func Handler(config *Configuration) (http.Handler, error) {
func startClientServer() {
clientserv = &http.Server{}
startServer(clientserv, ClientHandler(), "Client server", conf.ClientListenAddress, conf.ClientPort)
}
func startServer(s *http.Server, handler http.Handler, name, addr string, port int) {
fulladdr := fmt.Sprintf("%s:%d", addr, port)
conf.Logger.Info(name, " listening at ", fulladdr)
s.Addr = fulladdr
s.Handler = handler
if err := s.ListenAndServe(); err != http.ErrServerClosed {
_ = server.LogFatal(err)
}
}
func Stop() error {
var err1, err2 error
// Even if closing serv fails, we want to try closing clientserv
err1 = serv.Close()
if clientserv != nil {
err2 = clientserv.Close()
}
// Now check errors
if err1 != nil {
return err1
}
if err2 != nil {
return err2
}
return nil
}
func Initialize(config *Configuration) error {
conf = config
if err := irmarequestor.Initialize(conf.Configuration); err != nil {
return nil, err
return err
}
if err := conf.initialize(); err != nil {
return nil, err
return err
}
return nil
}
func ClientHandler() http.Handler {
router := chi.NewRouter()
router.Use(cors.New(cors.Options{
AllowedOrigins: []string{"*"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type"},
AllowedMethods: []string{http.MethodGet, http.MethodPost, http.MethodDelete},
}).Handler)
router.Mount("/irma/", irmarequestor.HttpHandlerFunc())
return router
}
// Handler returns a http.Handler that handles all IRMA requestor messages
// and IRMA client messages.
func Handler() http.Handler {
router := chi.NewRouter()
router.Use(cors.New(cors.Options{
AllowedOrigins: []string{"*"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type"},
AllowedMethods: []string{http.MethodGet, http.MethodPost, http.MethodDelete},
}).Handler)
if !conf.separateClientServer() {
// Mount server for irmaclient
router.Mount("/irma/", irmarequestor.HttpHandlerFunc())
}
// Server routes
router.Post("/session", handleCreate)
......@@ -75,7 +122,7 @@ func Handler(config *Configuration) (http.Handler, error) {
router.Get("/session/{token}/result-jwt", handleJwtResult)
router.Get("/session/{token}/getproof", handleJwtProofs) // irma_api_server-compatible JWT
return router, nil
return router
}
func handleCreate(w http.ResponseWriter, r *http.Request) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment