github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/http/fast/fast.go (about)

     1  // 🚀 Fast is an Express inspired web framework written in Go.
     2  
     3  package fast
     4  
     5  import (
     6  	"bufio"
     7  	"crypto/tls"
     8  	"fmt"
     9  	"log"
    10  	"net"
    11  	"net/http"
    12  	"net/http/httputil"
    13  	"os"
    14  	"os/exec"
    15  	"reflect"
    16  	"runtime"
    17  	"strconv"
    18  	"strings"
    19  	"time"
    20  
    21  	"github.com/valyala/fasthttp"
    22  )
    23  
    24  // Version of current package
    25  const Version = "1.0.0"
    26  
    27  // Fast denotes the Fast application.
    28  type Fast struct {
    29  	server   *fasthttp.Server // FastHTTP server
    30  	routes   []*Route         // Route stack
    31  	Settings *Settings        // Fast settings
    32  }
    33  
    34  // Settings holds is a struct holding the server settings
    35  type Settings struct {
    36  	// This will spawn multiple Go processes listening on the same port
    37  	Prefork bool // default: false
    38  	// Enable strict routing. When enabled, the router treats "/foo" and "/foo/" as different.
    39  	StrictRouting bool // default: false
    40  	// Enable case sensitivity. When enabled, "/Foo" and "/foo" are different routes.
    41  	CaseSensitive bool // default: false
    42  	// Enables the "Server: value" HTTP header.
    43  	ServerHeader string // default: ""
    44  	// Enables handler values to be immutable even if you return from handler
    45  	Immutable bool // default: false
    46  	// Max body size that the server accepts
    47  	BodyLimit int // default: 4 * 1024 * 1024
    48  	// Folder containing template files
    49  	TemplateFolder string // default: ""
    50  	// Template engine: html, amber, handlebars , mustache or pug
    51  	TemplateEngine func(raw string, bind interface{}) (string, error) // default: nil
    52  	// Extension for the template files
    53  	TemplateExtension string // default: ""
    54  	// The amount of time allowed to read the full request including body.
    55  	ReadTimeout time.Duration // default: unlimited
    56  	// The maximum duration before timing out writes of the response.
    57  	WriteTimeout time.Duration // default: unlimited
    58  	// The maximum amount of time to wait for the next request when keep-alive is enabled.
    59  	IdleTimeout time.Duration // default: unlimited
    60  }
    61  
    62  // Group struct
    63  type Group struct {
    64  	prefix string
    65  	app    *Fast
    66  }
    67  
    68  // New creates a new Fast named instance.
    69  // You can pass optional settings when creating a new instance.
    70  func New(settings ...*Settings) *Fast {
    71  	// Parse arguments
    72  	for _, v := range os.Args[1:] {
    73  		if v == "-prefork" || v == "-fork" {
    74  			isPrefork = true
    75  		} else if v == "-child" {
    76  			isChild = true
    77  		}
    78  	}
    79  	// Create app
    80  	app := new(Fast)
    81  	// Create settings
    82  	app.Settings = new(Settings)
    83  	// SetHeader default settings
    84  	app.Settings.Prefork = isPrefork
    85  	app.Settings.BodyLimit = 4 * 1024 * 1024
    86  	// If settings exist, set defaults
    87  	if len(settings) > 0 {
    88  		app.Settings = settings[0] // SetHeader custom settings
    89  		if !app.Settings.Prefork { // Default to -prefork flag if false
    90  			app.Settings.Prefork = isPrefork
    91  		}
    92  		if app.Settings.BodyLimit == 0 { // Default MaxRequestBodySize
    93  			app.Settings.BodyLimit = 4 * 1024 * 1024
    94  		}
    95  		if app.Settings.Immutable { // Replace unsafe conversion funcs
    96  			GetString = getStringImmutable
    97  		}
    98  	}
    99  	return app
   100  }
   101  
   102  // Group is used for Routes with common prefix to define a new sub-router with optional middleware.
   103  func (app *Fast) Group(prefix string, handlers ...func(*Ctx)) *Group {
   104  	if len(handlers) > 0 {
   105  		app.registerMethod("USE", prefix, handlers...)
   106  	}
   107  	return &Group{
   108  		prefix: prefix,
   109  		app:    app,
   110  	}
   111  }
   112  
   113  // Static struct
   114  type Static struct {
   115  	// Transparently compresses responses if set to true
   116  	// This works differently than the compression middleware
   117  	// The server tries minimizing CPU usage by caching compressed files.
   118  	// It adds ".gz" suffix to the original file name.
   119  	// Optional. Default value false
   120  	Compress bool
   121  	// Enables byte range requests if set to true.
   122  	// Optional. Default value false
   123  	ByteRange bool
   124  	// Enable directory browsing.
   125  	// Optional. Default value false.
   126  	Browse bool
   127  	// Index file for serving a directory.
   128  	// Optional. Default value "index.html".
   129  	Index string
   130  }
   131  
   132  // Static registers a new route with path prefix to serve static files from the provided root directory.
   133  func (app *Fast) Static(prefix, root string, config ...Static) *Fast {
   134  	app.registerStatic(prefix, root, config...)
   135  	return app
   136  }
   137  
   138  // Use registers a middleware route.
   139  // Middleware matches requests beginning with the provided prefix.
   140  // Providing a prefix is optional, it defaults to "/"
   141  func (app *Fast) Use(args ...interface{}) *Fast {
   142  	var path = ""
   143  	var handlers []func(*Ctx)
   144  	for i := 0; i < len(args); i++ {
   145  		switch arg := args[i].(type) {
   146  		case string:
   147  			path = arg
   148  		case func(*Ctx):
   149  			handlers = append(handlers, arg)
   150  		default:
   151  			log.Fatalf("Invalid handler: %v", reflect.TypeOf(arg))
   152  		}
   153  	}
   154  	app.registerMethod("USE", path, handlers...)
   155  	return app
   156  }
   157  
   158  // Connect http method handler.
   159  func (app *Fast) Connect(path string, handlers ...func(*Ctx)) *Fast {
   160  	app.registerMethod("CONNECT", path, handlers...)
   161  	return app
   162  }
   163  
   164  // Put http method handler.
   165  func (app *Fast) Put(path string, handlers ...func(*Ctx)) *Fast {
   166  	app.registerMethod("PUT", path, handlers...)
   167  	return app
   168  }
   169  
   170  // Post http method handler.
   171  func (app *Fast) Post(path string, handlers ...func(*Ctx)) *Fast {
   172  	app.registerMethod("POST", path, handlers...)
   173  	return app
   174  }
   175  
   176  // Delete http method handler.
   177  func (app *Fast) Delete(path string, handlers ...func(*Ctx)) *Fast {
   178  	app.registerMethod("DELETE", path, handlers...)
   179  	return app
   180  }
   181  
   182  // Head http method handler.
   183  func (app *Fast) Head(path string, handlers ...func(*Ctx)) *Fast {
   184  	app.registerMethod("HEAD", path, handlers...)
   185  	return app
   186  }
   187  
   188  // Patch http method handler.
   189  func (app *Fast) Patch(path string, handlers ...func(*Ctx)) *Fast {
   190  	app.registerMethod("PATCH", path, handlers...)
   191  	return app
   192  }
   193  
   194  // Options http method handler.
   195  func (app *Fast) Options(path string, handlers ...func(*Ctx)) *Fast {
   196  	app.registerMethod("OPTIONS", path, handlers...)
   197  	return app
   198  }
   199  
   200  // Trace http method handler.
   201  func (app *Fast) Trace(path string, handlers ...func(*Ctx)) *Fast {
   202  	app.registerMethod("TRACE", path, handlers...)
   203  	return app
   204  }
   205  
   206  // GetHeader http method handler.
   207  func (app *Fast) Get(path string, handlers ...func(*Ctx)) *Fast {
   208  	app.registerMethod("GET", path, handlers...)
   209  	return app
   210  }
   211  
   212  // All matches all HTTP methods and complete paths
   213  func (app *Fast) All(path string, handlers ...func(*Ctx)) *Fast {
   214  	app.registerMethod("ALL", path, handlers...)
   215  	return app
   216  }
   217  
   218  // Group is used for Routes with common prefix to define a new sub-router with optional middleware.
   219  func (grp *Group) Group(prefix string, handlers ...func(*Ctx)) *Group {
   220  	prefix = groupPaths(grp.prefix, prefix)
   221  	if len(handlers) > 0 {
   222  		grp.app.registerMethod("USE", prefix, handlers...)
   223  	}
   224  	return &Group{
   225  		prefix: prefix,
   226  		app:    grp.app,
   227  	}
   228  }
   229  
   230  // Static : https://fiber.wiki/application#static
   231  func (grp *Group) Static(prefix, root string, config ...Static) *Group {
   232  	prefix = groupPaths(grp.prefix, prefix)
   233  	grp.app.registerStatic(prefix, root, config...)
   234  	return grp
   235  }
   236  
   237  // Use registers a middleware route.
   238  // Middleware matches requests beginning with the provided prefix.
   239  // Providing a prefix is optional, it defaults to "/"
   240  func (grp *Group) Use(args ...interface{}) *Group {
   241  	var path = ""
   242  	var handlers []func(*Ctx)
   243  	for i := 0; i < len(args); i++ {
   244  		switch arg := args[i].(type) {
   245  		case string:
   246  			path = arg
   247  		case func(*Ctx):
   248  			handlers = append(handlers, arg)
   249  		default:
   250  			log.Fatalf("Invalid Use() arguments, must be (prefix, handler) or (handler)")
   251  		}
   252  	}
   253  	grp.app.registerMethod("USE", groupPaths(grp.prefix, path), handlers...)
   254  	return grp
   255  }
   256  
   257  // Connect http method handler.
   258  func (grp *Group) Connect(path string, handlers ...func(*Ctx)) *Group {
   259  	grp.app.registerMethod("CONNECT", groupPaths(grp.prefix, path), handlers...)
   260  	return grp
   261  }
   262  
   263  // Put http method handler.
   264  func (grp *Group) Put(path string, handlers ...func(*Ctx)) *Group {
   265  	grp.app.registerMethod("PUT", groupPaths(grp.prefix, path), handlers...)
   266  	return grp
   267  }
   268  
   269  // Post http method handler.
   270  func (grp *Group) Post(path string, handlers ...func(*Ctx)) *Group {
   271  	grp.app.registerMethod("POST", groupPaths(grp.prefix, path), handlers...)
   272  	return grp
   273  }
   274  
   275  // Delete http method handler.
   276  func (grp *Group) Delete(path string, handlers ...func(*Ctx)) *Group {
   277  	grp.app.registerMethod("DELETE", groupPaths(grp.prefix, path), handlers...)
   278  	return grp
   279  }
   280  
   281  // Head http method handler.
   282  func (grp *Group) Head(path string, handlers ...func(*Ctx)) *Group {
   283  	grp.app.registerMethod("HEAD", groupPaths(grp.prefix, path), handlers...)
   284  	return grp
   285  }
   286  
   287  // Patch http method handler.
   288  func (grp *Group) Patch(path string, handlers ...func(*Ctx)) *Group {
   289  	grp.app.registerMethod("PATCH", groupPaths(grp.prefix, path), handlers...)
   290  	return grp
   291  }
   292  
   293  // Options http method handler.
   294  func (grp *Group) Options(path string, handlers ...func(*Ctx)) *Group {
   295  	grp.app.registerMethod("OPTIONS", groupPaths(grp.prefix, path), handlers...)
   296  	return grp
   297  }
   298  
   299  // Trace http method handler.
   300  func (grp *Group) Trace(path string, handlers ...func(*Ctx)) *Group {
   301  	grp.app.registerMethod("TRACE", groupPaths(grp.prefix, path), handlers...)
   302  	return grp
   303  }
   304  
   305  // GetHeader http method handler.
   306  func (grp *Group) Get(path string, handlers ...func(*Ctx)) *Group {
   307  	grp.app.registerMethod("GET", groupPaths(grp.prefix, path), handlers...)
   308  	return grp
   309  }
   310  
   311  // All matches all HTTP methods and complete paths
   312  func (grp *Group) All(path string, handlers ...func(*Ctx)) *Group {
   313  	grp.app.registerMethod("ALL", groupPaths(grp.prefix, path), handlers...)
   314  	return grp
   315  }
   316  
   317  // Listen serves HTTP requests from the given addr or port.
   318  // You can pass an optional *tls.Config to enable TLS.
   319  func (app *Fast) Listen(address interface{}, tlsconfig ...*tls.Config) error {
   320  	addr, ok := address.(string)
   321  	if !ok {
   322  		port, ok := address.(int)
   323  		if !ok {
   324  			port = 80
   325  		}
   326  		addr = strconv.Itoa(port)
   327  	}
   328  	if !strings.Contains(addr, ":") {
   329  		addr = ":" + addr
   330  	}
   331  	// Create fasthttp server
   332  	app.server = app.newServer()
   333  	// Print listening message
   334  	if !isChild {
   335  		fmt.Printf("Fast v%s listening on %s\n", Version, addr)
   336  	}
   337  	var ln net.Listener
   338  	var err error
   339  	// Prefork enabled
   340  	if app.Settings.Prefork && runtime.NumCPU() > 1 {
   341  		if ln, err = app.prefork(addr); err != nil {
   342  			return err
   343  		}
   344  	} else {
   345  		if ln, err = net.Listen("tcp4", addr); err != nil {
   346  			return err
   347  		}
   348  	}
   349  
   350  	// TLS config
   351  	if len(tlsconfig) > 0 {
   352  		ln = tls.NewListener(ln, tlsconfig[0])
   353  	}
   354  	return app.server.Serve(ln)
   355  }
   356  
   357  // Shutdown gracefully shuts down the server without interrupting any active connections.
   358  // Shutdown works by first closing all open listeners and then waiting indefinitely for all connections to return to idle and then shut down.
   359  //
   360  // When Shutdown is called, Serve, ListenAndServe, and ListenAndServeTLS immediately return nil.
   361  // Make sure the program doesn't exit and waits instead for Shutdown to return.
   362  //
   363  // Shutdown does not close keepalive connections so its recommended to set ReadTimeout to something else than 0.
   364  func (app *Fast) Shutdown() error {
   365  	return app.server.Shutdown()
   366  }
   367  
   368  // Test is used for internal debugging by passing a *http.Request.
   369  // Timeout is optional and defaults to 200ms, -1 to 10s.
   370  func (app *Fast) Test(request *http.Request, msTimeout ...int) (*http.Response, error) {
   371  	timeout := 200
   372  	if len(msTimeout) > 0 {
   373  		timeout = msTimeout[0]
   374  	}
   375  	if timeout < 0 {
   376  		timeout = 10000
   377  	}
   378  	// Dump raw http request
   379  	dump, err := httputil.DumpRequest(request, true)
   380  	if err != nil {
   381  		return nil, err
   382  	}
   383  	// Setup server
   384  	app.server = app.newServer()
   385  	// Create conn
   386  	conn := new(testConn)
   387  	// Write raw http request
   388  	if _, err = conn.r.Write(dump); err != nil {
   389  		return nil, err
   390  	}
   391  	// Serve conn to server
   392  	channel := make(chan error)
   393  	go func() {
   394  		channel <- app.server.ServeConn(conn)
   395  	}()
   396  	// Wait for callback
   397  	select {
   398  	case err := <-channel:
   399  		if err != nil {
   400  			return nil, err
   401  		}
   402  	case <-time.After(time.Duration(timeout) * time.Millisecond):
   403  		return nil, fmt.Errorf("timeout error")
   404  	}
   405  	// Read response
   406  	buffer := bufio.NewReader(&conn.w)
   407  	// Convert raw http response to *http.Response
   408  	resp, err := http.ReadResponse(buffer, request)
   409  	if err != nil {
   410  		return nil, err
   411  	}
   412  	// Return *http.Response
   413  	return resp, nil
   414  }
   415  
   416  // Sharding: https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/
   417  func (app *Fast) prefork(address string) (ln net.Listener, err error) {
   418  	// Master proc
   419  	if !isChild {
   420  		addr, err := net.ResolveTCPAddr("tcp", address)
   421  		if err != nil {
   422  			return ln, err
   423  		}
   424  		tcplistener, err := net.ListenTCP("tcp", addr)
   425  		if err != nil {
   426  			return ln, err
   427  		}
   428  		fl, err := tcplistener.File()
   429  		if err != nil {
   430  			return ln, err
   431  		}
   432  		files := []*os.File{fl}
   433  		childs := make([]*exec.Cmd, runtime.NumCPU()/2)
   434  		// #nosec G204
   435  		for i := range childs {
   436  			childs[i] = exec.Command(os.Args[0], append(os.Args[1:], "-prefork", "-child")...)
   437  			childs[i].Stdout = os.Stdout
   438  			childs[i].Stderr = os.Stderr
   439  			childs[i].ExtraFiles = files
   440  			if err := childs[i].Start(); err != nil {
   441  				return ln, err
   442  			}
   443  		}
   444  
   445  		for _, child := range childs {
   446  			if err := child.Wait(); err != nil {
   447  				return ln, err
   448  			}
   449  		}
   450  		os.Exit(0)
   451  	} else {
   452  		// 1 core per child
   453  		runtime.GOMAXPROCS(1)
   454  		ln, err = net.FileListener(os.NewFile(3, ""))
   455  	}
   456  	return ln, err
   457  }
   458  
   459  func (app *Fast) newServer() *fasthttp.Server {
   460  	return &fasthttp.Server{
   461  		Handler:               app.handler,
   462  		Name:                  app.Settings.ServerHeader,
   463  		MaxRequestBodySize:    app.Settings.BodyLimit,
   464  		NoDefaultServerHeader: app.Settings.ServerHeader == "",
   465  		ReadTimeout:           app.Settings.ReadTimeout,
   466  		WriteTimeout:          app.Settings.WriteTimeout,
   467  		IdleTimeout:           app.Settings.IdleTimeout,
   468  		LogAllErrors:          false,
   469  		ErrorHandler: func(ctx *fasthttp.RequestCtx, err error) {
   470  			if err.Error() == "body size exceeds the given limit" {
   471  				ctx.Response.SetStatusCode(413)
   472  				ctx.Response.SetBodyString("Request Entity Too Large")
   473  			} else {
   474  				ctx.Response.SetStatusCode(400)
   475  				ctx.Response.SetBodyString("Bad Request")
   476  			}
   477  		},
   478  	}
   479  }