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 }