func (a *API) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.WithError(err).Error("api/roaming: read request body error")
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	var basePL backend.BasePayload
	if err := json.Unmarshal(b, &basePL); err != nil {
		log.WithError(err).Error("api/roaming: unmarshal request error")
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	ctxID, err := uuid.NewV4()
	if err != nil {
		log.WithError(err).Error("api/roaming: get uuid error")
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	ctx := context.WithValue(r.Context(), logging.ContextIDKey, ctxID)
	if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 {
		if r.TLS.PeerCertificates[0].Subject.CommonName != basePL.SenderID {
			log.WithFields(log.Fields{
				"ctx_id":      ctx.Value(logging.ContextIDKey),
				"sender_id":   basePL.SenderID,
				"common_name": r.TLS.PeerCertificates[0].Subject.CommonName,
			}).Error("roaming: SenderID does not match client-certificate CommonName")
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
	}
	if basePL.ReceiverID != a.netID.String() {
		log.WithFields(log.Fields{
			"ctx_id":      ctx.Value(logging.ContextIDKey),
			"receiver_id": basePL.ReceiverID,
			"net_id":      a.netID.String(),
		}).Error("roaming: ReceiverID does not match NetID of network-server")
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	var netID lorawan.NetID
	if err := netID.UnmarshalText([]byte(basePL.SenderID)); err != nil {
		log.WithFields(log.Fields{
			"ctx_id":    ctx.Value(logging.ContextIDKey),
			"sender_id": basePL.SenderID,
		}).WithError(err).Error("api/roaming: unmarshal SenderID as NetID error")
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	client, err := roaming.GetClientForNetID(netID)
	if err != nil {
		log.WithFields(log.Fields{
			"ctx_id":    ctx.Value(logging.ContextIDKey),
			"sender_id": basePL.SenderID,
		}).WithError(err).Error("api/roaming: get client for NetID error")
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	log.WithFields(log.Fields{
		"ctx_id":         ctx.Value(logging.ContextIDKey),
		"message_type":   basePL.MessageType,
		"sender_id":      basePL.SenderID,
		"receiver_id":    basePL.ReceiverID,
		"async_client":   client.IsAsync(),
		"transaction_id": basePL.TransactionID,
	}).Info("roaming: api request received")
	if client.IsAsync() {
		newCtx := context.Background()
		newCtx = context.WithValue(newCtx, logging.ContextIDKey, ctx.Value(logging.ContextIDKey))
		go a.handleAsync(newCtx, client, basePL, b)
	} else {
		a.handleSync(ctx, client, w, basePL, b)
	}
}
ServeHTTP handles a HTTP request.
package roaming
import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"
	"github.com/gofrs/uuid"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
	"github.com/brocaar/chirpstack-network-server/v3/internal/band"
	"github.com/brocaar/chirpstack-network-server/v3/internal/config"
	downdata "github.com/brocaar/chirpstack-network-server/v3/internal/downlink/data"
	"github.com/brocaar/chirpstack-network-server/v3/internal/logging"
	"github.com/brocaar/chirpstack-network-server/v3/internal/models"
	"github.com/brocaar/chirpstack-network-server/v3/internal/roaming"
	"github.com/brocaar/chirpstack-network-server/v3/internal/storage"
	updata "github.com/brocaar/chirpstack-network-server/v3/internal/uplink/data"
	"github.com/brocaar/chirpstack-network-server/v3/internal/uplink/join"
	"github.com/brocaar/lorawan"
	"github.com/brocaar/lorawan/backend"
)
func Setup(c config.Config) error {
	roamingConfig := c.Roaming
	if roamingConfig.API.Bind == "" {
		log.Debug("api/roaming: roaming is disabled")
		return nil
	}
	log.WithFields(log.Fields{
		"bind":     roamingConfig.API.Bind,
		"ca_cert":  roamingConfig.API.CACert,
		"tls_cert": roamingConfig.API.TLSCert,
		"tls_key":  roamingConfig.API.TLSKey,
	}).Info("api/roaming: starting roaming api")
	server := http.Server{
		Handler: &API{
			netID: c.NetworkServer.NetID,
		},
		Addr:      roamingConfig.API.Bind,
		TLSConfig: &tls.Config{},
	}
	if roamingConfig.API.CACert == "" && roamingConfig.API.TLSCert == "" && roamingConfig.API.TLSKey == "" {
		go func() {
			err := server.ListenAndServe()
			log.WithError(err).Fatal("api/roaming: api server error")
		}()
		return nil
	}
	if roamingConfig.API.CACert != "" {
		caCert, err := ioutil.ReadFile(roamingConfig.API.CACert)
		if err != nil {
			return errors.Wrap(err, "read ca certificate error")
		}
		caCertPool := x509.NewCertPool()
		if !caCertPool.AppendCertsFromPEM(caCert) {
			return errors.New("append ca certificate error")
		}
		server.TLSConfig.ClientCAs = caCertPool
		server.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert
		log.WithFields(log.Fields{
			"ca_cert": roamingConfig.API.CACert,
		}).Info("api/roaming: roaming api is configured with client-certificate authentication")
	}
	go func() {
		err := server.ListenAndServeTLS(roamingConfig.API.TLSCert, roamingConfig.API.TLSKey)
		log.WithError(err).Fatal("api/roaming: api server error")
	}()
	return nil
}
type API struct {
	netID lorawan.NetID
}
func NewAPI(netID lorawan.NetID) *API {
	return &API{
		netID: netID,
	}
}