Add optional subscription ID
[ric-plt/xapp-frame.git] / pkg / restapi / server.go
1 // Code generated by go-swagger; DO NOT EDIT.
2
3 package restapi
4
5 import (
6         "context"
7         "crypto/tls"
8         "crypto/x509"
9         "errors"
10         "fmt"
11         "io/ioutil"
12         "log"
13         "net"
14         "net/http"
15         "os"
16         "os/signal"
17         "strconv"
18         "sync"
19         "sync/atomic"
20         "syscall"
21         "time"
22
23         "github.com/go-openapi/runtime/flagext"
24         "github.com/go-openapi/swag"
25         flags "github.com/jessevdk/go-flags"
26         "golang.org/x/net/netutil"
27
28         "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/restapi/operations"
29 )
30
31 const (
32         schemeHTTP  = "http"
33         schemeHTTPS = "https"
34         schemeUnix  = "unix"
35 )
36
37 var defaultSchemes []string
38
39 func init() {
40         defaultSchemes = []string{
41                 schemeHTTP,
42         }
43 }
44
45 // NewServer creates a new api xapp framework server but does not configure it
46 func NewServer(api *operations.XappFrameworkAPI) *Server {
47         s := new(Server)
48
49         s.shutdown = make(chan struct{})
50         s.api = api
51         s.interrupt = make(chan os.Signal, 1)
52         return s
53 }
54
55 // ConfigureAPI configures the API and handlers.
56 func (s *Server) ConfigureAPI() {
57         if s.api != nil {
58                 s.handler = configureAPI(s.api)
59         }
60 }
61
62 // ConfigureFlags configures the additional flags defined by the handlers. Needs to be called before the parser.Parse
63 func (s *Server) ConfigureFlags() {
64         if s.api != nil {
65                 configureFlags(s.api)
66         }
67 }
68
69 // Server for the xapp framework API
70 type Server struct {
71         EnabledListeners []string         `long:"scheme" description:"the listeners to enable, this can be repeated and defaults to the schemes in the swagger spec"`
72         CleanupTimeout   time.Duration    `long:"cleanup-timeout" description:"grace period for which to wait before killing idle connections" default:"10s"`
73         GracefulTimeout  time.Duration    `long:"graceful-timeout" description:"grace period for which to wait before shutting down the server" default:"15s"`
74         MaxHeaderSize    flagext.ByteSize `long:"max-header-size" description:"controls the maximum number of bytes the server will read parsing the request header's keys and values, including the request line. It does not limit the size of the request body." default:"1MiB"`
75
76         SocketPath    flags.Filename `long:"socket-path" description:"the unix socket to listen on" default:"/var/run/xapp-framework.sock"`
77         domainSocketL net.Listener
78
79         Host         string        `long:"host" description:"the IP to listen on" default:"localhost" env:"HOST"`
80         Port         int           `long:"port" description:"the port to listen on for insecure connections, defaults to a random value" env:"PORT"`
81         ListenLimit  int           `long:"listen-limit" description:"limit the number of outstanding requests"`
82         KeepAlive    time.Duration `long:"keep-alive" description:"sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)" default:"3m"`
83         ReadTimeout  time.Duration `long:"read-timeout" description:"maximum duration before timing out read of the request" default:"30s"`
84         WriteTimeout time.Duration `long:"write-timeout" description:"maximum duration before timing out write of the response" default:"60s"`
85         httpServerL  net.Listener
86
87         TLSHost           string         `long:"tls-host" description:"the IP to listen on for tls, when not specified it's the same as --host" env:"TLS_HOST"`
88         TLSPort           int            `long:"tls-port" description:"the port to listen on for secure connections, defaults to a random value" env:"TLS_PORT"`
89         TLSCertificate    flags.Filename `long:"tls-certificate" description:"the certificate to use for secure connections" env:"TLS_CERTIFICATE"`
90         TLSCertificateKey flags.Filename `long:"tls-key" description:"the private key to use for secure connections" env:"TLS_PRIVATE_KEY"`
91         TLSCACertificate  flags.Filename `long:"tls-ca" description:"the certificate authority file to be used with mutual tls auth" env:"TLS_CA_CERTIFICATE"`
92         TLSListenLimit    int            `long:"tls-listen-limit" description:"limit the number of outstanding requests"`
93         TLSKeepAlive      time.Duration  `long:"tls-keep-alive" description:"sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)"`
94         TLSReadTimeout    time.Duration  `long:"tls-read-timeout" description:"maximum duration before timing out read of the request"`
95         TLSWriteTimeout   time.Duration  `long:"tls-write-timeout" description:"maximum duration before timing out write of the response"`
96         httpsServerL      net.Listener
97
98         api          *operations.XappFrameworkAPI
99         handler      http.Handler
100         hasListeners bool
101         shutdown     chan struct{}
102         shuttingDown int32
103         interrupted  bool
104         interrupt    chan os.Signal
105 }
106
107 // Logf logs message either via defined user logger or via system one if no user logger is defined.
108 func (s *Server) Logf(f string, args ...interface{}) {
109         if s.api != nil && s.api.Logger != nil {
110                 s.api.Logger(f, args...)
111         } else {
112                 log.Printf(f, args...)
113         }
114 }
115
116 // Fatalf logs message either via defined user logger or via system one if no user logger is defined.
117 // Exits with non-zero status after printing
118 func (s *Server) Fatalf(f string, args ...interface{}) {
119         if s.api != nil && s.api.Logger != nil {
120                 s.api.Logger(f, args...)
121                 os.Exit(1)
122         } else {
123                 log.Fatalf(f, args...)
124         }
125 }
126
127 // SetAPI configures the server with the specified API. Needs to be called before Serve
128 func (s *Server) SetAPI(api *operations.XappFrameworkAPI) {
129         if api == nil {
130                 s.api = nil
131                 s.handler = nil
132                 return
133         }
134
135         s.api = api
136         s.handler = configureAPI(api)
137 }
138
139 func (s *Server) hasScheme(scheme string) bool {
140         schemes := s.EnabledListeners
141         if len(schemes) == 0 {
142                 schemes = defaultSchemes
143         }
144
145         for _, v := range schemes {
146                 if v == scheme {
147                         return true
148                 }
149         }
150         return false
151 }
152
153 // Serve the api
154 func (s *Server) Serve() (err error) {
155         if !s.hasListeners {
156                 if err = s.Listen(); err != nil {
157                         return err
158                 }
159         }
160
161         // set default handler, if none is set
162         if s.handler == nil {
163                 if s.api == nil {
164                         return errors.New("can't create the default handler, as no api is set")
165                 }
166
167                 s.SetHandler(s.api.Serve(nil))
168         }
169
170         wg := new(sync.WaitGroup)
171         once := new(sync.Once)
172         signalNotify(s.interrupt)
173         go handleInterrupt(once, s)
174
175         servers := []*http.Server{}
176
177         if s.hasScheme(schemeUnix) {
178                 domainSocket := new(http.Server)
179                 domainSocket.MaxHeaderBytes = int(s.MaxHeaderSize)
180                 domainSocket.Handler = s.handler
181                 if int64(s.CleanupTimeout) > 0 {
182                         domainSocket.IdleTimeout = s.CleanupTimeout
183                 }
184
185                 configureServer(domainSocket, "unix", string(s.SocketPath))
186
187                 servers = append(servers, domainSocket)
188                 wg.Add(1)
189                 s.Logf("Serving xapp framework at unix://%s", s.SocketPath)
190                 go func(l net.Listener) {
191                         defer wg.Done()
192                         if err := domainSocket.Serve(l); err != nil && err != http.ErrServerClosed {
193                                 s.Fatalf("%v", err)
194                         }
195                         s.Logf("Stopped serving xapp framework at unix://%s", s.SocketPath)
196                 }(s.domainSocketL)
197         }
198
199         if s.hasScheme(schemeHTTP) {
200                 httpServer := new(http.Server)
201                 httpServer.MaxHeaderBytes = int(s.MaxHeaderSize)
202                 httpServer.ReadTimeout = s.ReadTimeout
203                 httpServer.WriteTimeout = s.WriteTimeout
204                 httpServer.SetKeepAlivesEnabled(int64(s.KeepAlive) > 0)
205                 if s.ListenLimit > 0 {
206                         s.httpServerL = netutil.LimitListener(s.httpServerL, s.ListenLimit)
207                 }
208
209                 if int64(s.CleanupTimeout) > 0 {
210                         httpServer.IdleTimeout = s.CleanupTimeout
211                 }
212
213                 httpServer.Handler = s.handler
214
215                 configureServer(httpServer, "http", s.httpServerL.Addr().String())
216
217                 servers = append(servers, httpServer)
218                 wg.Add(1)
219                 s.Logf("Serving xapp framework at http://%s", s.httpServerL.Addr())
220                 go func(l net.Listener) {
221                         defer wg.Done()
222                         if err := httpServer.Serve(l); err != nil && err != http.ErrServerClosed {
223                                 s.Fatalf("%v", err)
224                         }
225                         s.Logf("Stopped serving xapp framework at http://%s", l.Addr())
226                 }(s.httpServerL)
227         }
228
229         if s.hasScheme(schemeHTTPS) {
230                 httpsServer := new(http.Server)
231                 httpsServer.MaxHeaderBytes = int(s.MaxHeaderSize)
232                 httpsServer.ReadTimeout = s.TLSReadTimeout
233                 httpsServer.WriteTimeout = s.TLSWriteTimeout
234                 httpsServer.SetKeepAlivesEnabled(int64(s.TLSKeepAlive) > 0)
235                 if s.TLSListenLimit > 0 {
236                         s.httpsServerL = netutil.LimitListener(s.httpsServerL, s.TLSListenLimit)
237                 }
238                 if int64(s.CleanupTimeout) > 0 {
239                         httpsServer.IdleTimeout = s.CleanupTimeout
240                 }
241                 httpsServer.Handler = s.handler
242
243                 // Inspired by https://blog.bracebin.com/achieving-perfect-ssl-labs-score-with-go
244                 httpsServer.TLSConfig = &tls.Config{
245                         // Causes servers to use Go's default ciphersuite preferences,
246                         // which are tuned to avoid attacks. Does nothing on clients.
247                         PreferServerCipherSuites: true,
248                         // Only use curves which have assembly implementations
249                         // https://github.com/golang/go/tree/master/src/crypto/elliptic
250                         CurvePreferences: []tls.CurveID{tls.CurveP256},
251                         // Use modern tls mode https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility
252                         NextProtos: []string{"h2", "http/1.1"},
253                         // https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet#Rule_-_Only_Support_Strong_Protocols
254                         MinVersion: tls.VersionTLS12,
255                         // These ciphersuites support Forward Secrecy: https://en.wikipedia.org/wiki/Forward_secrecy
256                         CipherSuites: []uint16{
257                                 tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
258                                 tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
259                                 tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
260                                 tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
261                                 tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
262                                 tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
263                         },
264                 }
265
266                 // build standard config from server options
267                 if s.TLSCertificate != "" && s.TLSCertificateKey != "" {
268                         httpsServer.TLSConfig.Certificates = make([]tls.Certificate, 1)
269                         httpsServer.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(string(s.TLSCertificate), string(s.TLSCertificateKey))
270                         if err != nil {
271                                 return err
272                         }
273                 }
274
275                 if s.TLSCACertificate != "" {
276                         // include specified CA certificate
277                         caCert, caCertErr := ioutil.ReadFile(string(s.TLSCACertificate))
278                         if caCertErr != nil {
279                                 return caCertErr
280                         }
281                         caCertPool := x509.NewCertPool()
282                         ok := caCertPool.AppendCertsFromPEM(caCert)
283                         if !ok {
284                                 return fmt.Errorf("cannot parse CA certificate")
285                         }
286                         httpsServer.TLSConfig.ClientCAs = caCertPool
287                         httpsServer.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert
288                 }
289
290                 // call custom TLS configurator
291                 configureTLS(httpsServer.TLSConfig)
292
293                 if len(httpsServer.TLSConfig.Certificates) == 0 && httpsServer.TLSConfig.GetCertificate == nil {
294                         // after standard and custom config are passed, this ends up with no certificate
295                         if s.TLSCertificate == "" {
296                                 if s.TLSCertificateKey == "" {
297                                         s.Fatalf("the required flags `--tls-certificate` and `--tls-key` were not specified")
298                                 }
299                                 s.Fatalf("the required flag `--tls-certificate` was not specified")
300                         }
301                         if s.TLSCertificateKey == "" {
302                                 s.Fatalf("the required flag `--tls-key` was not specified")
303                         }
304                         // this happens with a wrong custom TLS configurator
305                         s.Fatalf("no certificate was configured for TLS")
306                 }
307
308                 // must have at least one certificate or panics
309                 httpsServer.TLSConfig.BuildNameToCertificate()
310
311                 configureServer(httpsServer, "https", s.httpsServerL.Addr().String())
312
313                 servers = append(servers, httpsServer)
314                 wg.Add(1)
315                 s.Logf("Serving xapp framework at https://%s", s.httpsServerL.Addr())
316                 go func(l net.Listener) {
317                         defer wg.Done()
318                         if err := httpsServer.Serve(l); err != nil && err != http.ErrServerClosed {
319                                 s.Fatalf("%v", err)
320                         }
321                         s.Logf("Stopped serving xapp framework at https://%s", l.Addr())
322                 }(tls.NewListener(s.httpsServerL, httpsServer.TLSConfig))
323         }
324
325         wg.Add(1)
326         go s.handleShutdown(wg, &servers)
327
328         wg.Wait()
329         return nil
330 }
331
332 // Listen creates the listeners for the server
333 func (s *Server) Listen() error {
334         if s.hasListeners { // already done this
335                 return nil
336         }
337
338         if s.hasScheme(schemeHTTPS) {
339                 // Use http host if https host wasn't defined
340                 if s.TLSHost == "" {
341                         s.TLSHost = s.Host
342                 }
343                 // Use http listen limit if https listen limit wasn't defined
344                 if s.TLSListenLimit == 0 {
345                         s.TLSListenLimit = s.ListenLimit
346                 }
347                 // Use http tcp keep alive if https tcp keep alive wasn't defined
348                 if int64(s.TLSKeepAlive) == 0 {
349                         s.TLSKeepAlive = s.KeepAlive
350                 }
351                 // Use http read timeout if https read timeout wasn't defined
352                 if int64(s.TLSReadTimeout) == 0 {
353                         s.TLSReadTimeout = s.ReadTimeout
354                 }
355                 // Use http write timeout if https write timeout wasn't defined
356                 if int64(s.TLSWriteTimeout) == 0 {
357                         s.TLSWriteTimeout = s.WriteTimeout
358                 }
359         }
360
361         if s.hasScheme(schemeUnix) {
362                 domSockListener, err := net.Listen("unix", string(s.SocketPath))
363                 if err != nil {
364                         return err
365                 }
366                 s.domainSocketL = domSockListener
367         }
368
369         if s.hasScheme(schemeHTTP) {
370                 listener, err := net.Listen("tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port)))
371                 if err != nil {
372                         return err
373                 }
374
375                 h, p, err := swag.SplitHostPort(listener.Addr().String())
376                 if err != nil {
377                         return err
378                 }
379                 s.Host = h
380                 s.Port = p
381                 s.httpServerL = listener
382         }
383
384         if s.hasScheme(schemeHTTPS) {
385                 tlsListener, err := net.Listen("tcp", net.JoinHostPort(s.TLSHost, strconv.Itoa(s.TLSPort)))
386                 if err != nil {
387                         return err
388                 }
389
390                 sh, sp, err := swag.SplitHostPort(tlsListener.Addr().String())
391                 if err != nil {
392                         return err
393                 }
394                 s.TLSHost = sh
395                 s.TLSPort = sp
396                 s.httpsServerL = tlsListener
397         }
398
399         s.hasListeners = true
400         return nil
401 }
402
403 // Shutdown server and clean up resources
404 func (s *Server) Shutdown() error {
405         if atomic.CompareAndSwapInt32(&s.shuttingDown, 0, 1) {
406                 close(s.shutdown)
407         }
408         return nil
409 }
410
411 func (s *Server) handleShutdown(wg *sync.WaitGroup, serversPtr *[]*http.Server) {
412         // wg.Done must occur last, after s.api.ServerShutdown()
413         // (to preserve old behaviour)
414         defer wg.Done()
415
416         <-s.shutdown
417
418         servers := *serversPtr
419
420         ctx, cancel := context.WithTimeout(context.TODO(), s.GracefulTimeout)
421         defer cancel()
422
423         // first execute the pre-shutdown hook
424         s.api.PreServerShutdown()
425
426         shutdownChan := make(chan bool)
427         for i := range servers {
428                 server := servers[i]
429                 go func() {
430                         var success bool
431                         defer func() {
432                                 shutdownChan <- success
433                         }()
434                         if err := server.Shutdown(ctx); err != nil {
435                                 // Error from closing listeners, or context timeout:
436                                 s.Logf("HTTP server Shutdown: %v", err)
437                         } else {
438                                 success = true
439                         }
440                 }()
441         }
442
443         // Wait until all listeners have successfully shut down before calling ServerShutdown
444         success := true
445         for range servers {
446                 success = success && <-shutdownChan
447         }
448         if success {
449                 s.api.ServerShutdown()
450         }
451 }
452
453 // GetHandler returns a handler useful for testing
454 func (s *Server) GetHandler() http.Handler {
455         return s.handler
456 }
457
458 // SetHandler allows for setting a http handler on this server
459 func (s *Server) SetHandler(handler http.Handler) {
460         s.handler = handler
461 }
462
463 // UnixListener returns the domain socket listener
464 func (s *Server) UnixListener() (net.Listener, error) {
465         if !s.hasListeners {
466                 if err := s.Listen(); err != nil {
467                         return nil, err
468                 }
469         }
470         return s.domainSocketL, nil
471 }
472
473 // HTTPListener returns the http listener
474 func (s *Server) HTTPListener() (net.Listener, error) {
475         if !s.hasListeners {
476                 if err := s.Listen(); err != nil {
477                         return nil, err
478                 }
479         }
480         return s.httpServerL, nil
481 }
482
483 // TLSListener returns the https listener
484 func (s *Server) TLSListener() (net.Listener, error) {
485         if !s.hasListeners {
486                 if err := s.Listen(); err != nil {
487                         return nil, err
488                 }
489         }
490         return s.httpsServerL, nil
491 }
492
493 func handleInterrupt(once *sync.Once, s *Server) {
494         once.Do(func() {
495                 for range s.interrupt {
496                         if s.interrupted {
497                                 s.Logf("Server already shutting down")
498                                 continue
499                         }
500                         s.interrupted = true
501                         s.Logf("Shutting down... ")
502                         if err := s.Shutdown(); err != nil {
503                                 s.Logf("HTTP server Shutdown: %v", err)
504                         }
505                 }
506         })
507 }
508
509 func signalNotify(interrupt chan<- os.Signal) {
510         signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
511 }