github.com/wangyougui/gf/v2@v2.6.5/net/ghttp/ghttp_server_config.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/wangyougui/gf.
     6  
     7  package ghttp
     8  
     9  import (
    10  	"context"
    11  	"crypto/tls"
    12  	"fmt"
    13  	"net"
    14  	"net/http"
    15  	"strconv"
    16  	"strings"
    17  	"time"
    18  
    19  	"github.com/wangyougui/gf/v2/errors/gcode"
    20  	"github.com/wangyougui/gf/v2/errors/gerror"
    21  	"github.com/wangyougui/gf/v2/internal/intlog"
    22  	"github.com/wangyougui/gf/v2/net/gsvc"
    23  	"github.com/wangyougui/gf/v2/os/gfile"
    24  	"github.com/wangyougui/gf/v2/os/glog"
    25  	"github.com/wangyougui/gf/v2/os/gres"
    26  	"github.com/wangyougui/gf/v2/os/gsession"
    27  	"github.com/wangyougui/gf/v2/os/gview"
    28  	"github.com/wangyougui/gf/v2/text/gstr"
    29  	"github.com/wangyougui/gf/v2/util/gconv"
    30  	"github.com/wangyougui/gf/v2/util/gutil"
    31  )
    32  
    33  const (
    34  	defaultHttpAddr  = ":80"  // Default listening port for HTTP.
    35  	defaultHttpsAddr = ":443" // Default listening port for HTTPS.
    36  
    37  )
    38  
    39  const (
    40  	UriTypeDefault  = iota // Method names to the URI converting type, which converts name to its lower case and joins the words using char '-'.
    41  	UriTypeFullName        // Method names to the URI converting type, which does not convert to the method name.
    42  	UriTypeAllLower        // Method names to the URI converting type, which converts name to its lower case.
    43  	UriTypeCamel           // Method names to the URI converting type, which converts name to its camel case.
    44  )
    45  
    46  // ServerConfig is the HTTP Server configuration manager.
    47  type ServerConfig struct {
    48  	// ======================================================================================================
    49  	// Basic.
    50  	// ======================================================================================================
    51  
    52  	// Service name, which is for service registry and discovery.
    53  	Name string `json:"name"`
    54  
    55  	// Address specifies the server listening address like "port" or ":port",
    56  	// multiple addresses joined using ','.
    57  	Address string `json:"address"`
    58  
    59  	// HTTPSAddr specifies the HTTPS addresses, multiple addresses joined using char ','.
    60  	HTTPSAddr string `json:"httpsAddr"`
    61  
    62  	// Listeners specifies the custom listeners.
    63  	Listeners []net.Listener `json:"listeners"`
    64  
    65  	// Endpoints are custom endpoints for service register, it uses Address if empty.
    66  	Endpoints []string `json:"endpoints"`
    67  
    68  	// HTTPSCertPath specifies certification file path for HTTPS service.
    69  	HTTPSCertPath string `json:"httpsCertPath"`
    70  
    71  	// HTTPSKeyPath specifies the key file path for HTTPS service.
    72  	HTTPSKeyPath string `json:"httpsKeyPath"`
    73  
    74  	// TLSConfig optionally provides a TLS configuration for use
    75  	// by ServeTLS and ListenAndServeTLS. Note that this value is
    76  	// cloned by ServeTLS and ListenAndServeTLS, so it's not
    77  	// possible to modify the configuration with methods like
    78  	// tls.Config.SetSessionTicketKeys. To use
    79  	// SetSessionTicketKeys, use Server.Serve with a TLS Listener
    80  	// instead.
    81  	TLSConfig *tls.Config `json:"tlsConfig"`
    82  
    83  	// Handler the handler for HTTP request.
    84  	Handler func(w http.ResponseWriter, r *http.Request) `json:"-"`
    85  
    86  	// ReadTimeout is the maximum duration for reading the entire
    87  	// request, including the body.
    88  	//
    89  	// Because ReadTimeout does not let Handlers make per-request
    90  	// decisions on each request body's acceptable deadline or
    91  	// upload rate, most users will prefer to use
    92  	// ReadHeaderTimeout. It is valid to use them both.
    93  	ReadTimeout time.Duration `json:"readTimeout"`
    94  
    95  	// WriteTimeout is the maximum duration before timing out
    96  	// writes of the response. It is reset whenever a new
    97  	// request's header is read. Like ReadTimeout, it does not
    98  	// let Handlers make decisions on a per-request basis.
    99  	WriteTimeout time.Duration `json:"writeTimeout"`
   100  
   101  	// IdleTimeout is the maximum amount of time to wait for the
   102  	// next request when keep-alive are enabled. If IdleTimeout
   103  	// is zero, the value of ReadTimeout is used. If both are
   104  	// zero, there is no timeout.
   105  	IdleTimeout time.Duration `json:"idleTimeout"`
   106  
   107  	// MaxHeaderBytes controls the maximum number of bytes the
   108  	// server will read parsing the request header's keys and
   109  	// values, including the request line. It does not limit the
   110  	// size of the request body.
   111  	//
   112  	// It can be configured in configuration file using string like: 1m, 10m, 500kb etc.
   113  	// It's 10240 bytes in default.
   114  	MaxHeaderBytes int `json:"maxHeaderBytes"`
   115  
   116  	// KeepAlive enables HTTP keep-alive.
   117  	KeepAlive bool `json:"keepAlive"`
   118  
   119  	// ServerAgent specifies the server agent information, which is wrote to
   120  	// HTTP response header as "Server".
   121  	ServerAgent string `json:"serverAgent"`
   122  
   123  	// View specifies the default template view object for the server.
   124  	View *gview.View `json:"view"`
   125  
   126  	// ======================================================================================================
   127  	// Static.
   128  	// ======================================================================================================
   129  
   130  	// Rewrites specifies the URI rewrite rules map.
   131  	Rewrites map[string]string `json:"rewrites"`
   132  
   133  	// IndexFiles specifies the index files for static folder.
   134  	IndexFiles []string `json:"indexFiles"`
   135  
   136  	// IndexFolder specifies if listing sub-files when requesting folder.
   137  	// The server responses HTTP status code 403 if it is false.
   138  	IndexFolder bool `json:"indexFolder"`
   139  
   140  	// ServerRoot specifies the root directory for static service.
   141  	ServerRoot string `json:"serverRoot"`
   142  
   143  	// SearchPaths specifies additional searching directories for static service.
   144  	SearchPaths []string `json:"searchPaths"`
   145  
   146  	// StaticPaths specifies URI to directory mapping array.
   147  	StaticPaths []staticPathItem `json:"staticPaths"`
   148  
   149  	// FileServerEnabled is the global switch for static service.
   150  	// It is automatically set enabled if any static path is set.
   151  	FileServerEnabled bool `json:"fileServerEnabled"`
   152  
   153  	// ======================================================================================================
   154  	// Cookie.
   155  	// ======================================================================================================
   156  
   157  	// CookieMaxAge specifies the max TTL for cookie items.
   158  	CookieMaxAge time.Duration `json:"cookieMaxAge"`
   159  
   160  	// CookiePath specifies cookie path.
   161  	// It also affects the default storage for session id.
   162  	CookiePath string `json:"cookiePath"`
   163  
   164  	// CookieDomain specifies cookie domain.
   165  	// It also affects the default storage for session id.
   166  	CookieDomain string `json:"cookieDomain"`
   167  
   168  	// CookieSameSite specifies cookie SameSite property.
   169  	// It also affects the default storage for session id.
   170  	CookieSameSite string `json:"cookieSameSite"`
   171  
   172  	// CookieSameSite specifies cookie Secure property.
   173  	// It also affects the default storage for session id.
   174  	CookieSecure bool `json:"cookieSecure"`
   175  
   176  	// CookieSameSite specifies cookie HttpOnly property.
   177  	// It also affects the default storage for session id.
   178  	CookieHttpOnly bool `json:"cookieHttpOnly"`
   179  
   180  	// ======================================================================================================
   181  	// Session.
   182  	// ======================================================================================================
   183  
   184  	// SessionIdName specifies the session id name.
   185  	SessionIdName string `json:"sessionIdName"`
   186  
   187  	// SessionMaxAge specifies max TTL for session items.
   188  	SessionMaxAge time.Duration `json:"sessionMaxAge"`
   189  
   190  	// SessionPath specifies the session storage directory path for storing session files.
   191  	// It only makes sense if the session storage is type of file storage.
   192  	SessionPath string `json:"sessionPath"`
   193  
   194  	// SessionStorage specifies the session storage.
   195  	SessionStorage gsession.Storage `json:"sessionStorage"`
   196  
   197  	// SessionCookieMaxAge specifies the cookie ttl for session id.
   198  	// If it is set 0, it means it expires along with browser session.
   199  	SessionCookieMaxAge time.Duration `json:"sessionCookieMaxAge"`
   200  
   201  	// SessionCookieOutput specifies whether automatic outputting session id to cookie.
   202  	SessionCookieOutput bool `json:"sessionCookieOutput"`
   203  
   204  	// ======================================================================================================
   205  	// Logging.
   206  	// ======================================================================================================
   207  
   208  	Logger           *glog.Logger `json:"logger"`           // Logger specifies the logger for server.
   209  	LogPath          string       `json:"logPath"`          // LogPath specifies the directory for storing logging files.
   210  	LogLevel         string       `json:"logLevel"`         // LogLevel specifies the logging level for logger.
   211  	LogStdout        bool         `json:"logStdout"`        // LogStdout specifies whether printing logging content to stdout.
   212  	ErrorStack       bool         `json:"errorStack"`       // ErrorStack specifies whether logging stack information when error.
   213  	ErrorLogEnabled  bool         `json:"errorLogEnabled"`  // ErrorLogEnabled enables error logging content to files.
   214  	ErrorLogPattern  string       `json:"errorLogPattern"`  // ErrorLogPattern specifies the error log file pattern like: error-{Ymd}.log
   215  	AccessLogEnabled bool         `json:"accessLogEnabled"` // AccessLogEnabled enables access logging content to files.
   216  	AccessLogPattern string       `json:"accessLogPattern"` // AccessLogPattern specifies the error log file pattern like: access-{Ymd}.log
   217  
   218  	// ======================================================================================================
   219  	// PProf.
   220  	// ======================================================================================================
   221  
   222  	PProfEnabled bool   `json:"pprofEnabled"` // PProfEnabled enables PProf feature.
   223  	PProfPattern string `json:"pprofPattern"` // PProfPattern specifies the PProf service pattern for router.
   224  
   225  	// ======================================================================================================
   226  	// API & Swagger.
   227  	// ======================================================================================================
   228  
   229  	OpenApiPath       string `json:"openapiPath"`       // OpenApiPath specifies the OpenApi specification file path.
   230  	SwaggerPath       string `json:"swaggerPath"`       // SwaggerPath specifies the swagger UI path for route registering.
   231  	SwaggerUITemplate string `json:"swaggerUITemplate"` // SwaggerUITemplate specifies the swagger UI custom template
   232  
   233  	// ======================================================================================================
   234  	// Other.
   235  	// ======================================================================================================
   236  
   237  	// ClientMaxBodySize specifies the max body size limit in bytes for client request.
   238  	// It can be configured in configuration file using string like: 1m, 10m, 500kb etc.
   239  	// It's `8MB` in default.
   240  	ClientMaxBodySize int64 `json:"clientMaxBodySize"`
   241  
   242  	// FormParsingMemory specifies max memory buffer size in bytes which can be used for
   243  	// parsing multimedia form.
   244  	// It can be configured in configuration file using string like: 1m, 10m, 500kb etc.
   245  	// It's 1MB in default.
   246  	FormParsingMemory int64 `json:"formParsingMemory"`
   247  
   248  	// NameToUriType specifies the type for converting struct method name to URI when
   249  	// registering routes.
   250  	NameToUriType int `json:"nameToUriType"`
   251  
   252  	// RouteOverWrite allows to overwrite the route if duplicated.
   253  	RouteOverWrite bool `json:"routeOverWrite"`
   254  
   255  	// DumpRouterMap specifies whether automatically dumps router map when server starts.
   256  	DumpRouterMap bool `json:"dumpRouterMap"`
   257  
   258  	// Graceful enables graceful reload feature for all servers of the process.
   259  	Graceful bool `json:"graceful"`
   260  
   261  	// GracefulTimeout set the maximum survival time (seconds) of the parent process.
   262  	GracefulTimeout uint8 `json:"gracefulTimeout"`
   263  
   264  	// GracefulShutdownTimeout set the maximum survival time (seconds) before stopping the server.
   265  	GracefulShutdownTimeout uint8 `json:"gracefulShutdownTimeout"`
   266  }
   267  
   268  // NewConfig creates and returns a ServerConfig object with default configurations.
   269  // Note that, do not define this default configuration to local package variable, as there are
   270  // some pointer attributes that may be shared in different servers.
   271  func NewConfig() ServerConfig {
   272  	return ServerConfig{
   273  		Name:                    DefaultServerName,
   274  		Address:                 ":0",
   275  		HTTPSAddr:               "",
   276  		Listeners:               nil,
   277  		Handler:                 nil,
   278  		ReadTimeout:             60 * time.Second,
   279  		WriteTimeout:            0, // No timeout.
   280  		IdleTimeout:             60 * time.Second,
   281  		MaxHeaderBytes:          10240, // 10KB
   282  		KeepAlive:               true,
   283  		IndexFiles:              []string{"index.html", "index.htm"},
   284  		IndexFolder:             false,
   285  		ServerAgent:             "GoFrame HTTP Server",
   286  		ServerRoot:              "",
   287  		StaticPaths:             make([]staticPathItem, 0),
   288  		FileServerEnabled:       false,
   289  		CookieMaxAge:            time.Hour * 24 * 365,
   290  		CookiePath:              "/",
   291  		CookieDomain:            "",
   292  		SessionIdName:           "gfsessionid",
   293  		SessionPath:             gsession.DefaultStorageFilePath,
   294  		SessionMaxAge:           time.Hour * 24,
   295  		SessionCookieOutput:     true,
   296  		SessionCookieMaxAge:     time.Hour * 24,
   297  		Logger:                  glog.New(),
   298  		LogLevel:                "all",
   299  		LogStdout:               true,
   300  		ErrorStack:              true,
   301  		ErrorLogEnabled:         true,
   302  		ErrorLogPattern:         "error-{Ymd}.log",
   303  		AccessLogEnabled:        false,
   304  		AccessLogPattern:        "access-{Ymd}.log",
   305  		DumpRouterMap:           true,
   306  		ClientMaxBodySize:       8 * 1024 * 1024, // 8MB
   307  		FormParsingMemory:       1024 * 1024,     // 1MB
   308  		Rewrites:                make(map[string]string),
   309  		Graceful:                false,
   310  		GracefulTimeout:         2, // seconds
   311  		GracefulShutdownTimeout: 5, // seconds
   312  	}
   313  }
   314  
   315  // ConfigFromMap creates and returns a ServerConfig object with given map and
   316  // default configuration object.
   317  func ConfigFromMap(m map[string]interface{}) (ServerConfig, error) {
   318  	config := NewConfig()
   319  	if err := gconv.Struct(m, &config); err != nil {
   320  		return config, err
   321  	}
   322  	return config, nil
   323  }
   324  
   325  // SetConfigWithMap sets the configuration for the server using map.
   326  func (s *Server) SetConfigWithMap(m map[string]interface{}) error {
   327  	// The m now is a shallow copy of m.
   328  	// Any changes to m does not affect the original one.
   329  	// A little tricky, isn't it?
   330  	m = gutil.MapCopy(m)
   331  	// Allow setting the size configuration items using string size like:
   332  	// 1m, 100mb, 512kb, etc.
   333  	if k, v := gutil.MapPossibleItemByKey(m, "MaxHeaderBytes"); k != "" {
   334  		m[k] = gfile.StrToSize(gconv.String(v))
   335  	}
   336  	if k, v := gutil.MapPossibleItemByKey(m, "ClientMaxBodySize"); k != "" {
   337  		m[k] = gfile.StrToSize(gconv.String(v))
   338  	}
   339  	if k, v := gutil.MapPossibleItemByKey(m, "FormParsingMemory"); k != "" {
   340  		m[k] = gfile.StrToSize(gconv.String(v))
   341  	}
   342  	// Update the current configuration object.
   343  	// It only updates the configured keys not all the object.
   344  	if err := gconv.Struct(m, &s.config); err != nil {
   345  		return err
   346  	}
   347  	return s.SetConfig(s.config)
   348  }
   349  
   350  // SetConfig sets the configuration for the server.
   351  func (s *Server) SetConfig(c ServerConfig) error {
   352  	s.config = c
   353  	// Automatically add ':' prefix for address if it is missed.
   354  	if s.config.Address != "" && !gstr.Contains(s.config.Address, ":") {
   355  		s.config.Address = ":" + s.config.Address
   356  	}
   357  	// Static files root.
   358  	if c.ServerRoot != "" {
   359  		s.SetServerRoot(c.ServerRoot)
   360  	}
   361  	if len(c.SearchPaths) > 0 {
   362  		paths := c.SearchPaths
   363  		c.SearchPaths = []string{}
   364  		for _, v := range paths {
   365  			s.AddSearchPath(v)
   366  		}
   367  	}
   368  	// HTTPS.
   369  	if c.TLSConfig == nil && c.HTTPSCertPath != "" {
   370  		s.EnableHTTPS(c.HTTPSCertPath, c.HTTPSKeyPath)
   371  	}
   372  	// Logging.
   373  	if s.config.LogPath != "" && s.config.LogPath != s.config.Logger.GetPath() {
   374  		if err := s.config.Logger.SetPath(s.config.LogPath); err != nil {
   375  			return err
   376  		}
   377  	}
   378  	if err := s.config.Logger.SetLevelStr(s.config.LogLevel); err != nil {
   379  		intlog.Errorf(context.TODO(), `%+v`, err)
   380  	}
   381  	gracefulEnabled = c.Graceful
   382  	intlog.Printf(context.TODO(), "SetConfig: %+v", s.config)
   383  	return nil
   384  }
   385  
   386  // SetAddr sets the listening address for the server.
   387  // The address is like ':80', '0.0.0.0:80', '127.0.0.1:80', '180.18.99.10:80', etc.
   388  func (s *Server) SetAddr(address string) {
   389  	s.config.Address = address
   390  }
   391  
   392  // SetPort sets the listening ports for the server.
   393  // The listening ports can be multiple like: SetPort(80, 8080).
   394  func (s *Server) SetPort(port ...int) {
   395  	if len(port) > 0 {
   396  		s.config.Address = ""
   397  		for _, v := range port {
   398  			if len(s.config.Address) > 0 {
   399  				s.config.Address += ","
   400  			}
   401  			s.config.Address += ":" + strconv.Itoa(v)
   402  		}
   403  	}
   404  }
   405  
   406  // SetHTTPSAddr sets the HTTPS listening ports for the server.
   407  func (s *Server) SetHTTPSAddr(address string) {
   408  	s.config.HTTPSAddr = address
   409  }
   410  
   411  // SetHTTPSPort sets the HTTPS listening ports for the server.
   412  // The listening ports can be multiple like: SetHTTPSPort(443, 500).
   413  func (s *Server) SetHTTPSPort(port ...int) {
   414  	if len(port) > 0 {
   415  		s.config.HTTPSAddr = ""
   416  		for _, v := range port {
   417  			if len(s.config.HTTPSAddr) > 0 {
   418  				s.config.HTTPSAddr += ","
   419  			}
   420  			s.config.HTTPSAddr += ":" + strconv.Itoa(v)
   421  		}
   422  	}
   423  }
   424  
   425  // SetListener set the custom listener for the server.
   426  func (s *Server) SetListener(listeners ...net.Listener) error {
   427  	if listeners == nil {
   428  		return gerror.NewCodef(gcode.CodeInvalidParameter, "SetListener failed: listener can not be nil")
   429  	}
   430  	if len(listeners) > 0 {
   431  		ports := make([]string, len(listeners))
   432  		for k, v := range listeners {
   433  			if v == nil {
   434  				return gerror.NewCodef(gcode.CodeInvalidParameter, "SetListener failed: listener can not be nil")
   435  			}
   436  			ports[k] = fmt.Sprintf(":%d", (v.Addr().(*net.TCPAddr)).Port)
   437  		}
   438  		s.config.Address = strings.Join(ports, ",")
   439  		s.config.Listeners = listeners
   440  	}
   441  	return nil
   442  }
   443  
   444  // EnableHTTPS enables HTTPS with given certification and key files for the server.
   445  // The optional parameter `tlsConfig` specifies custom TLS configuration.
   446  func (s *Server) EnableHTTPS(certFile, keyFile string, tlsConfig ...*tls.Config) {
   447  	var ctx = context.TODO()
   448  	certFileRealPath := gfile.RealPath(certFile)
   449  	if certFileRealPath == "" {
   450  		certFileRealPath = gfile.RealPath(gfile.Pwd() + gfile.Separator + certFile)
   451  		if certFileRealPath == "" {
   452  			certFileRealPath = gfile.RealPath(gfile.MainPkgPath() + gfile.Separator + certFile)
   453  		}
   454  	}
   455  	// Resource.
   456  	if certFileRealPath == "" && gres.Contains(certFile) {
   457  		certFileRealPath = certFile
   458  	}
   459  	if certFileRealPath == "" {
   460  		s.Logger().Fatalf(ctx, `EnableHTTPS failed: certFile "%s" does not exist`, certFile)
   461  	}
   462  	keyFileRealPath := gfile.RealPath(keyFile)
   463  	if keyFileRealPath == "" {
   464  		keyFileRealPath = gfile.RealPath(gfile.Pwd() + gfile.Separator + keyFile)
   465  		if keyFileRealPath == "" {
   466  			keyFileRealPath = gfile.RealPath(gfile.MainPkgPath() + gfile.Separator + keyFile)
   467  		}
   468  	}
   469  	// Resource.
   470  	if keyFileRealPath == "" && gres.Contains(keyFile) {
   471  		keyFileRealPath = keyFile
   472  	}
   473  	if keyFileRealPath == "" {
   474  		s.Logger().Fatal(ctx, `EnableHTTPS failed: keyFile "%s" does not exist`, keyFile)
   475  	}
   476  	s.config.HTTPSCertPath = certFileRealPath
   477  	s.config.HTTPSKeyPath = keyFileRealPath
   478  	if len(tlsConfig) > 0 {
   479  		s.config.TLSConfig = tlsConfig[0]
   480  	}
   481  }
   482  
   483  // SetTLSConfig sets custom TLS configuration and enables HTTPS feature for the server.
   484  func (s *Server) SetTLSConfig(tlsConfig *tls.Config) {
   485  	s.config.TLSConfig = tlsConfig
   486  }
   487  
   488  // SetReadTimeout sets the ReadTimeout for the server.
   489  func (s *Server) SetReadTimeout(t time.Duration) {
   490  	s.config.ReadTimeout = t
   491  }
   492  
   493  // SetWriteTimeout sets the WriteTimeout for the server.
   494  func (s *Server) SetWriteTimeout(t time.Duration) {
   495  	s.config.WriteTimeout = t
   496  }
   497  
   498  // SetIdleTimeout sets the IdleTimeout for the server.
   499  func (s *Server) SetIdleTimeout(t time.Duration) {
   500  	s.config.IdleTimeout = t
   501  }
   502  
   503  // SetMaxHeaderBytes sets the MaxHeaderBytes for the server.
   504  func (s *Server) SetMaxHeaderBytes(b int) {
   505  	s.config.MaxHeaderBytes = b
   506  }
   507  
   508  // SetServerAgent sets the ServerAgent for the server.
   509  func (s *Server) SetServerAgent(agent string) {
   510  	s.config.ServerAgent = agent
   511  }
   512  
   513  // SetKeepAlive sets the KeepAlive for the server.
   514  func (s *Server) SetKeepAlive(enabled bool) {
   515  	s.config.KeepAlive = enabled
   516  }
   517  
   518  // SetView sets the View for the server.
   519  func (s *Server) SetView(view *gview.View) {
   520  	s.config.View = view
   521  }
   522  
   523  // GetName returns the name of the server.
   524  func (s *Server) GetName() string {
   525  	return s.config.Name
   526  }
   527  
   528  // SetName sets the name for the server.
   529  func (s *Server) SetName(name string) {
   530  	s.config.Name = name
   531  }
   532  
   533  // SetEndpoints sets the Endpoints for the server.
   534  func (s *Server) SetEndpoints(endpoints []string) {
   535  	s.config.Endpoints = endpoints
   536  }
   537  
   538  // SetHandler sets the request handler for server.
   539  func (s *Server) SetHandler(h func(w http.ResponseWriter, r *http.Request)) {
   540  	s.config.Handler = h
   541  }
   542  
   543  // GetHandler returns the request handler of the server.
   544  func (s *Server) GetHandler() func(w http.ResponseWriter, r *http.Request) {
   545  	if s.config.Handler == nil {
   546  		return s.ServeHTTP
   547  	}
   548  	return s.config.Handler
   549  }
   550  
   551  // SetRegistrar sets the Registrar for server.
   552  func (s *Server) SetRegistrar(registrar gsvc.Registrar) {
   553  	s.registrar = registrar
   554  }
   555  
   556  // GetRegistrar returns the Registrar of server.
   557  func (s *Server) GetRegistrar() gsvc.Registrar {
   558  	return s.registrar
   559  }