github.com/gogf/gf/v2@v2.7.4/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/gogf/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/gogf/gf/v2/errors/gcode"
    20  	"github.com/gogf/gf/v2/errors/gerror"
    21  	"github.com/gogf/gf/v2/internal/intlog"
    22  	"github.com/gogf/gf/v2/net/gsvc"
    23  	"github.com/gogf/gf/v2/os/gfile"
    24  	"github.com/gogf/gf/v2/os/glog"
    25  	"github.com/gogf/gf/v2/os/gres"
    26  	"github.com/gogf/gf/v2/os/gsession"
    27  	"github.com/gogf/gf/v2/os/gview"
    28  	"github.com/gogf/gf/v2/text/gstr"
    29  	"github.com/gogf/gf/v2/util/gconv"
    30  	"github.com/gogf/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  	// Graceful reload & shutdown.
   235  	// ======================================================================================================
   236  
   237  	// Graceful enables graceful reload feature for all servers of the process.
   238  	Graceful bool `json:"graceful"`
   239  
   240  	// GracefulTimeout set the maximum survival time (seconds) of the parent process.
   241  	GracefulTimeout int `json:"gracefulTimeout"`
   242  
   243  	// GracefulShutdownTimeout set the maximum survival time (seconds) before stopping the server.
   244  	GracefulShutdownTimeout int `json:"gracefulShutdownTimeout"`
   245  
   246  	// ======================================================================================================
   247  	// Other.
   248  	// ======================================================================================================
   249  
   250  	// ClientMaxBodySize specifies the max body size limit in bytes for client request.
   251  	// It can be configured in configuration file using string like: 1m, 10m, 500kb etc.
   252  	// It's `8MB` in default.
   253  	ClientMaxBodySize int64 `json:"clientMaxBodySize"`
   254  
   255  	// FormParsingMemory specifies max memory buffer size in bytes which can be used for
   256  	// parsing multimedia form.
   257  	// It can be configured in configuration file using string like: 1m, 10m, 500kb etc.
   258  	// It's 1MB in default.
   259  	FormParsingMemory int64 `json:"formParsingMemory"`
   260  
   261  	// NameToUriType specifies the type for converting struct method name to URI when
   262  	// registering routes.
   263  	NameToUriType int `json:"nameToUriType"`
   264  
   265  	// RouteOverWrite allows to overwrite the route if duplicated.
   266  	RouteOverWrite bool `json:"routeOverWrite"`
   267  
   268  	// DumpRouterMap specifies whether automatically dumps router map when server starts.
   269  	DumpRouterMap bool `json:"dumpRouterMap"`
   270  }
   271  
   272  // NewConfig creates and returns a ServerConfig object with default configurations.
   273  // Note that, do not define this default configuration to local package variable, as there are
   274  // some pointer attributes that may be shared in different servers.
   275  func NewConfig() ServerConfig {
   276  	return ServerConfig{
   277  		Name:                    DefaultServerName,
   278  		Address:                 ":0",
   279  		HTTPSAddr:               "",
   280  		Listeners:               nil,
   281  		Handler:                 nil,
   282  		ReadTimeout:             60 * time.Second,
   283  		WriteTimeout:            0, // No timeout.
   284  		IdleTimeout:             60 * time.Second,
   285  		MaxHeaderBytes:          10240, // 10KB
   286  		KeepAlive:               true,
   287  		IndexFiles:              []string{"index.html", "index.htm"},
   288  		IndexFolder:             false,
   289  		ServerAgent:             "GoFrame HTTP Server",
   290  		ServerRoot:              "",
   291  		StaticPaths:             make([]staticPathItem, 0),
   292  		FileServerEnabled:       false,
   293  		CookieMaxAge:            time.Hour * 24 * 365,
   294  		CookiePath:              "/",
   295  		CookieDomain:            "",
   296  		SessionIdName:           "gfsessionid",
   297  		SessionPath:             gsession.DefaultStorageFilePath,
   298  		SessionMaxAge:           time.Hour * 24,
   299  		SessionCookieOutput:     true,
   300  		SessionCookieMaxAge:     time.Hour * 24,
   301  		Logger:                  glog.New(),
   302  		LogLevel:                "all",
   303  		LogStdout:               true,
   304  		ErrorStack:              true,
   305  		ErrorLogEnabled:         true,
   306  		ErrorLogPattern:         "error-{Ymd}.log",
   307  		AccessLogEnabled:        false,
   308  		AccessLogPattern:        "access-{Ymd}.log",
   309  		DumpRouterMap:           true,
   310  		ClientMaxBodySize:       8 * 1024 * 1024, // 8MB
   311  		FormParsingMemory:       1024 * 1024,     // 1MB
   312  		Rewrites:                make(map[string]string),
   313  		Graceful:                false,
   314  		GracefulTimeout:         2, // seconds
   315  		GracefulShutdownTimeout: 5, // seconds
   316  	}
   317  }
   318  
   319  // ConfigFromMap creates and returns a ServerConfig object with given map and
   320  // default configuration object.
   321  func ConfigFromMap(m map[string]interface{}) (ServerConfig, error) {
   322  	config := NewConfig()
   323  	if err := gconv.Struct(m, &config); err != nil {
   324  		return config, err
   325  	}
   326  	return config, nil
   327  }
   328  
   329  // SetConfigWithMap sets the configuration for the server using map.
   330  func (s *Server) SetConfigWithMap(m map[string]interface{}) error {
   331  	// The m now is a shallow copy of m.
   332  	// Any changes to m does not affect the original one.
   333  	// A little tricky, isn't it?
   334  	m = gutil.MapCopy(m)
   335  	// Allow setting the size configuration items using string size like:
   336  	// 1m, 100mb, 512kb, etc.
   337  	if k, v := gutil.MapPossibleItemByKey(m, "MaxHeaderBytes"); k != "" {
   338  		m[k] = gfile.StrToSize(gconv.String(v))
   339  	}
   340  	if k, v := gutil.MapPossibleItemByKey(m, "ClientMaxBodySize"); k != "" {
   341  		m[k] = gfile.StrToSize(gconv.String(v))
   342  	}
   343  	if k, v := gutil.MapPossibleItemByKey(m, "FormParsingMemory"); k != "" {
   344  		m[k] = gfile.StrToSize(gconv.String(v))
   345  	}
   346  	// Update the current configuration object.
   347  	// It only updates the configured keys not all the object.
   348  	if err := gconv.Struct(m, &s.config); err != nil {
   349  		return err
   350  	}
   351  	return s.SetConfig(s.config)
   352  }
   353  
   354  // SetConfig sets the configuration for the server.
   355  func (s *Server) SetConfig(c ServerConfig) error {
   356  	s.config = c
   357  	// Automatically add ':' prefix for address if it is missed.
   358  	if s.config.Address != "" && !gstr.Contains(s.config.Address, ":") {
   359  		s.config.Address = ":" + s.config.Address
   360  	}
   361  	// Static files root.
   362  	if c.ServerRoot != "" {
   363  		s.SetServerRoot(c.ServerRoot)
   364  	}
   365  	if len(c.SearchPaths) > 0 {
   366  		paths := c.SearchPaths
   367  		c.SearchPaths = []string{}
   368  		for _, v := range paths {
   369  			s.AddSearchPath(v)
   370  		}
   371  	}
   372  	// HTTPS.
   373  	if c.TLSConfig == nil && c.HTTPSCertPath != "" {
   374  		s.EnableHTTPS(c.HTTPSCertPath, c.HTTPSKeyPath)
   375  	}
   376  	// Logging.
   377  	if s.config.LogPath != "" && s.config.LogPath != s.config.Logger.GetPath() {
   378  		if err := s.config.Logger.SetPath(s.config.LogPath); err != nil {
   379  			return err
   380  		}
   381  	}
   382  	if err := s.config.Logger.SetLevelStr(s.config.LogLevel); err != nil {
   383  		intlog.Errorf(context.TODO(), `%+v`, err)
   384  	}
   385  	gracefulEnabled = c.Graceful
   386  	intlog.Printf(context.TODO(), "SetConfig: %+v", s.config)
   387  	return nil
   388  }
   389  
   390  // SetAddr sets the listening address for the server.
   391  // The address is like ':80', '0.0.0.0:80', '127.0.0.1:80', '180.18.99.10:80', etc.
   392  func (s *Server) SetAddr(address string) {
   393  	s.config.Address = address
   394  }
   395  
   396  // SetPort sets the listening ports for the server.
   397  // The listening ports can be multiple like: SetPort(80, 8080).
   398  func (s *Server) SetPort(port ...int) {
   399  	if len(port) > 0 {
   400  		s.config.Address = ""
   401  		for _, v := range port {
   402  			if len(s.config.Address) > 0 {
   403  				s.config.Address += ","
   404  			}
   405  			s.config.Address += ":" + strconv.Itoa(v)
   406  		}
   407  	}
   408  }
   409  
   410  // SetHTTPSAddr sets the HTTPS listening ports for the server.
   411  func (s *Server) SetHTTPSAddr(address string) {
   412  	s.config.HTTPSAddr = address
   413  }
   414  
   415  // SetHTTPSPort sets the HTTPS listening ports for the server.
   416  // The listening ports can be multiple like: SetHTTPSPort(443, 500).
   417  func (s *Server) SetHTTPSPort(port ...int) {
   418  	if len(port) > 0 {
   419  		s.config.HTTPSAddr = ""
   420  		for _, v := range port {
   421  			if len(s.config.HTTPSAddr) > 0 {
   422  				s.config.HTTPSAddr += ","
   423  			}
   424  			s.config.HTTPSAddr += ":" + strconv.Itoa(v)
   425  		}
   426  	}
   427  }
   428  
   429  // SetListener set the custom listener for the server.
   430  func (s *Server) SetListener(listeners ...net.Listener) error {
   431  	if listeners == nil {
   432  		return gerror.NewCodef(gcode.CodeInvalidParameter, "SetListener failed: listener can not be nil")
   433  	}
   434  	if len(listeners) > 0 {
   435  		ports := make([]string, len(listeners))
   436  		for k, v := range listeners {
   437  			if v == nil {
   438  				return gerror.NewCodef(gcode.CodeInvalidParameter, "SetListener failed: listener can not be nil")
   439  			}
   440  			ports[k] = fmt.Sprintf(":%d", (v.Addr().(*net.TCPAddr)).Port)
   441  		}
   442  		s.config.Address = strings.Join(ports, ",")
   443  		s.config.Listeners = listeners
   444  	}
   445  	return nil
   446  }
   447  
   448  // EnableHTTPS enables HTTPS with given certification and key files for the server.
   449  // The optional parameter `tlsConfig` specifies custom TLS configuration.
   450  func (s *Server) EnableHTTPS(certFile, keyFile string, tlsConfig ...*tls.Config) {
   451  	var ctx = context.TODO()
   452  	certFileRealPath := gfile.RealPath(certFile)
   453  	if certFileRealPath == "" {
   454  		certFileRealPath = gfile.RealPath(gfile.Pwd() + gfile.Separator + certFile)
   455  		if certFileRealPath == "" {
   456  			certFileRealPath = gfile.RealPath(gfile.MainPkgPath() + gfile.Separator + certFile)
   457  		}
   458  	}
   459  	// Resource.
   460  	if certFileRealPath == "" && gres.Contains(certFile) {
   461  		certFileRealPath = certFile
   462  	}
   463  	if certFileRealPath == "" {
   464  		s.Logger().Fatalf(ctx, `EnableHTTPS failed: certFile "%s" does not exist`, certFile)
   465  	}
   466  	keyFileRealPath := gfile.RealPath(keyFile)
   467  	if keyFileRealPath == "" {
   468  		keyFileRealPath = gfile.RealPath(gfile.Pwd() + gfile.Separator + keyFile)
   469  		if keyFileRealPath == "" {
   470  			keyFileRealPath = gfile.RealPath(gfile.MainPkgPath() + gfile.Separator + keyFile)
   471  		}
   472  	}
   473  	// Resource.
   474  	if keyFileRealPath == "" && gres.Contains(keyFile) {
   475  		keyFileRealPath = keyFile
   476  	}
   477  	if keyFileRealPath == "" {
   478  		s.Logger().Fatal(ctx, `EnableHTTPS failed: keyFile "%s" does not exist`, keyFile)
   479  	}
   480  	s.config.HTTPSCertPath = certFileRealPath
   481  	s.config.HTTPSKeyPath = keyFileRealPath
   482  	if len(tlsConfig) > 0 {
   483  		s.config.TLSConfig = tlsConfig[0]
   484  	}
   485  }
   486  
   487  // SetTLSConfig sets custom TLS configuration and enables HTTPS feature for the server.
   488  func (s *Server) SetTLSConfig(tlsConfig *tls.Config) {
   489  	s.config.TLSConfig = tlsConfig
   490  }
   491  
   492  // SetReadTimeout sets the ReadTimeout for the server.
   493  func (s *Server) SetReadTimeout(t time.Duration) {
   494  	s.config.ReadTimeout = t
   495  }
   496  
   497  // SetWriteTimeout sets the WriteTimeout for the server.
   498  func (s *Server) SetWriteTimeout(t time.Duration) {
   499  	s.config.WriteTimeout = t
   500  }
   501  
   502  // SetIdleTimeout sets the IdleTimeout for the server.
   503  func (s *Server) SetIdleTimeout(t time.Duration) {
   504  	s.config.IdleTimeout = t
   505  }
   506  
   507  // SetMaxHeaderBytes sets the MaxHeaderBytes for the server.
   508  func (s *Server) SetMaxHeaderBytes(b int) {
   509  	s.config.MaxHeaderBytes = b
   510  }
   511  
   512  // SetServerAgent sets the ServerAgent for the server.
   513  func (s *Server) SetServerAgent(agent string) {
   514  	s.config.ServerAgent = agent
   515  }
   516  
   517  // SetKeepAlive sets the KeepAlive for the server.
   518  func (s *Server) SetKeepAlive(enabled bool) {
   519  	s.config.KeepAlive = enabled
   520  }
   521  
   522  // SetView sets the View for the server.
   523  func (s *Server) SetView(view *gview.View) {
   524  	s.config.View = view
   525  }
   526  
   527  // GetName returns the name of the server.
   528  func (s *Server) GetName() string {
   529  	return s.config.Name
   530  }
   531  
   532  // SetName sets the name for the server.
   533  func (s *Server) SetName(name string) {
   534  	s.config.Name = name
   535  }
   536  
   537  // SetEndpoints sets the Endpoints for the server.
   538  func (s *Server) SetEndpoints(endpoints []string) {
   539  	s.config.Endpoints = endpoints
   540  }
   541  
   542  // SetHandler sets the request handler for server.
   543  func (s *Server) SetHandler(h func(w http.ResponseWriter, r *http.Request)) {
   544  	s.config.Handler = h
   545  }
   546  
   547  // GetHandler returns the request handler of the server.
   548  func (s *Server) GetHandler() func(w http.ResponseWriter, r *http.Request) {
   549  	if s.config.Handler == nil {
   550  		return s.ServeHTTP
   551  	}
   552  	return s.config.Handler
   553  }
   554  
   555  // SetRegistrar sets the Registrar for server.
   556  func (s *Server) SetRegistrar(registrar gsvc.Registrar) {
   557  	s.registrar = registrar
   558  }
   559  
   560  // GetRegistrar returns the Registrar of server.
   561  func (s *Server) GetRegistrar() gsvc.Registrar {
   562  	return s.registrar
   563  }