| package fasthttp |
| |
| import ( |
| "bufio" |
| "crypto/tls" |
| "errors" |
| "fmt" |
| "io" |
| "log" |
| "mime/multipart" |
| "net" |
| "os" |
| "strings" |
| "sync" |
| "sync/atomic" |
| "time" |
| ) |
| |
| // ServeConn serves HTTP requests from the given connection |
| // using the given handler. |
| // |
| // ServeConn returns nil if all requests from the c are successfully served. |
| // It returns non-nil error otherwise. |
| // |
| // Connection c must immediately propagate all the data passed to Write() |
| // to the client. Otherwise requests' processing may hang. |
| // |
| // ServeConn closes c before returning. |
| func ServeConn(c net.Conn, handler RequestHandler) error { |
| v := serverPool.Get() |
| if v == nil { |
| v = &Server{} |
| } |
| s := v.(*Server) |
| s.Handler = handler |
| err := s.ServeConn(c) |
| s.Handler = nil |
| serverPool.Put(v) |
| return err |
| } |
| |
| var serverPool sync.Pool |
| |
| // Serve serves incoming connections from the given listener |
| // using the given handler. |
| // |
| // Serve blocks until the given listener returns permanent error. |
| func Serve(ln net.Listener, handler RequestHandler) error { |
| s := &Server{ |
| Handler: handler, |
| } |
| return s.Serve(ln) |
| } |
| |
| // ServeTLS serves HTTPS requests from the given net.Listener |
| // using the given handler. |
| // |
| // certFile and keyFile are paths to TLS certificate and key files. |
| func ServeTLS(ln net.Listener, certFile, keyFile string, handler RequestHandler) error { |
| s := &Server{ |
| Handler: handler, |
| } |
| return s.ServeTLS(ln, certFile, keyFile) |
| } |
| |
| // ServeTLSEmbed serves HTTPS requests from the given net.Listener |
| // using the given handler. |
| // |
| // certData and keyData must contain valid TLS certificate and key data. |
| func ServeTLSEmbed(ln net.Listener, certData, keyData []byte, handler RequestHandler) error { |
| s := &Server{ |
| Handler: handler, |
| } |
| return s.ServeTLSEmbed(ln, certData, keyData) |
| } |
| |
| // ListenAndServe serves HTTP requests from the given TCP addr |
| // using the given handler. |
| func ListenAndServe(addr string, handler RequestHandler) error { |
| s := &Server{ |
| Handler: handler, |
| } |
| return s.ListenAndServe(addr) |
| } |
| |
| // ListenAndServeUNIX serves HTTP requests from the given UNIX addr |
| // using the given handler. |
| // |
| // The function deletes existing file at addr before starting serving. |
| // |
| // The server sets the given file mode for the UNIX addr. |
| func ListenAndServeUNIX(addr string, mode os.FileMode, handler RequestHandler) error { |
| s := &Server{ |
| Handler: handler, |
| } |
| return s.ListenAndServeUNIX(addr, mode) |
| } |
| |
| // ListenAndServeTLS serves HTTPS requests from the given TCP addr |
| // using the given handler. |
| // |
| // certFile and keyFile are paths to TLS certificate and key files. |
| func ListenAndServeTLS(addr, certFile, keyFile string, handler RequestHandler) error { |
| s := &Server{ |
| Handler: handler, |
| } |
| return s.ListenAndServeTLS(addr, certFile, keyFile) |
| } |
| |
| // ListenAndServeTLSEmbed serves HTTPS requests from the given TCP addr |
| // using the given handler. |
| // |
| // certData and keyData must contain valid TLS certificate and key data. |
| func ListenAndServeTLSEmbed(addr string, certData, keyData []byte, handler RequestHandler) error { |
| s := &Server{ |
| Handler: handler, |
| } |
| return s.ListenAndServeTLSEmbed(addr, certData, keyData) |
| } |
| |
| // RequestHandler must process incoming requests. |
| // |
| // RequestHandler must call ctx.TimeoutError() before returning |
| // if it keeps references to ctx and/or its' members after the return. |
| // Consider wrapping RequestHandler into TimeoutHandler if response time |
| // must be limited. |
| type RequestHandler func(ctx *RequestCtx) |
| |
| // Server implements HTTP server. |
| // |
| // Default Server settings should satisfy the majority of Server users. |
| // Adjust Server settings only if you really understand the consequences. |
| // |
| // It is forbidden copying Server instances. Create new Server instances |
| // instead. |
| // |
| // It is safe to call Server methods from concurrently running goroutines. |
| type Server struct { |
| noCopy noCopy |
| |
| // Handler for processing incoming requests. |
| Handler RequestHandler |
| |
| // Server name for sending in response headers. |
| // |
| // Default server name is used if left blank. |
| Name string |
| |
| // The maximum number of concurrent connections the server may serve. |
| // |
| // DefaultConcurrency is used if not set. |
| Concurrency int |
| |
| // Whether to disable keep-alive connections. |
| // |
| // The server will close all the incoming connections after sending |
| // the first response to client if this option is set to true. |
| // |
| // By default keep-alive connections are enabled. |
| DisableKeepalive bool |
| |
| // Per-connection buffer size for requests' reading. |
| // This also limits the maximum header size. |
| // |
| // Increase this buffer if your clients send multi-KB RequestURIs |
| // and/or multi-KB headers (for example, BIG cookies). |
| // |
| // Default buffer size is used if not set. |
| ReadBufferSize int |
| |
| // Per-connection buffer size for responses' writing. |
| // |
| // Default buffer size is used if not set. |
| WriteBufferSize int |
| |
| // Maximum duration for reading the full request (including body). |
| // |
| // This also limits the maximum duration for idle keep-alive |
| // connections. |
| // |
| // By default request read timeout is unlimited. |
| ReadTimeout time.Duration |
| |
| // Maximum duration for writing the full response (including body). |
| // |
| // By default response write timeout is unlimited. |
| WriteTimeout time.Duration |
| |
| // Maximum number of concurrent client connections allowed per IP. |
| // |
| // By default unlimited number of concurrent connections |
| // may be established to the server from a single IP address. |
| MaxConnsPerIP int |
| |
| // Maximum number of requests served per connection. |
| // |
| // The server closes connection after the last request. |
| // 'Connection: close' header is added to the last response. |
| // |
| // By default unlimited number of requests may be served per connection. |
| MaxRequestsPerConn int |
| |
| // Maximum keep-alive connection lifetime. |
| // |
| // The server closes keep-alive connection after its' lifetime |
| // expiration. |
| // |
| // See also ReadTimeout for limiting the duration of idle keep-alive |
| // connections. |
| // |
| // By default keep-alive connection lifetime is unlimited. |
| MaxKeepaliveDuration time.Duration |
| |
| // Maximum request body size. |
| // |
| // The server rejects requests with bodies exceeding this limit. |
| // |
| // Request body size is limited by DefaultMaxRequestBodySize by default. |
| MaxRequestBodySize int |
| |
| // Aggressively reduces memory usage at the cost of higher CPU usage |
| // if set to true. |
| // |
| // Try enabling this option only if the server consumes too much memory |
| // serving mostly idle keep-alive connections. This may reduce memory |
| // usage by more than 50%. |
| // |
| // Aggressive memory usage reduction is disabled by default. |
| ReduceMemoryUsage bool |
| |
| // Rejects all non-GET requests if set to true. |
| // |
| // This option is useful as anti-DoS protection for servers |
| // accepting only GET requests. The request size is limited |
| // by ReadBufferSize if GetOnly is set. |
| // |
| // Server accepts all the requests by default. |
| GetOnly bool |
| |
| // Logs all errors, including the most frequent |
| // 'connection reset by peer', 'broken pipe' and 'connection timeout' |
| // errors. Such errors are common in production serving real-world |
| // clients. |
| // |
| // By default the most frequent errors such as |
| // 'connection reset by peer', 'broken pipe' and 'connection timeout' |
| // are suppressed in order to limit output log traffic. |
| LogAllErrors bool |
| |
| // Header names are passed as-is without normalization |
| // if this option is set. |
| // |
| // Disabled header names' normalization may be useful only for proxying |
| // incoming requests to other servers expecting case-sensitive |
| // header names. See https://github.com/valyala/fasthttp/issues/57 |
| // for details. |
| // |
| // By default request and response header names are normalized, i.e. |
| // The first letter and the first letters following dashes |
| // are uppercased, while all the other letters are lowercased. |
| // Examples: |
| // |
| // * HOST -> Host |
| // * content-type -> Content-Type |
| // * cONTENT-lenGTH -> Content-Length |
| DisableHeaderNamesNormalizing bool |
| |
| // Logger, which is used by RequestCtx.Logger(). |
| // |
| // By default standard logger from log package is used. |
| Logger Logger |
| |
| concurrency uint32 |
| concurrencyCh chan struct{} |
| perIPConnCounter perIPConnCounter |
| serverName atomic.Value |
| |
| ctxPool sync.Pool |
| readerPool sync.Pool |
| writerPool sync.Pool |
| hijackConnPool sync.Pool |
| bytePool sync.Pool |
| } |
| |
| // TimeoutHandler creates RequestHandler, which returns StatusRequestTimeout |
| // error with the given msg to the client if h didn't return during |
| // the given duration. |
| // |
| // The returned handler may return StatusTooManyRequests error with the given |
| // msg to the client if there are more than Server.Concurrency concurrent |
| // handlers h are running at the moment. |
| func TimeoutHandler(h RequestHandler, timeout time.Duration, msg string) RequestHandler { |
| if timeout <= 0 { |
| return h |
| } |
| |
| return func(ctx *RequestCtx) { |
| concurrencyCh := ctx.s.concurrencyCh |
| select { |
| case concurrencyCh <- struct{}{}: |
| default: |
| ctx.Error(msg, StatusTooManyRequests) |
| return |
| } |
| |
| ch := ctx.timeoutCh |
| if ch == nil { |
| ch = make(chan struct{}, 1) |
| ctx.timeoutCh = ch |
| } |
| go func() { |
| h(ctx) |
| ch <- struct{}{} |
| <-concurrencyCh |
| }() |
| ctx.timeoutTimer = initTimer(ctx.timeoutTimer, timeout) |
| select { |
| case <-ch: |
| case <-ctx.timeoutTimer.C: |
| ctx.TimeoutError(msg) |
| } |
| stopTimer(ctx.timeoutTimer) |
| } |
| } |
| |
| // CompressHandler returns RequestHandler that transparently compresses |
| // response body generated by h if the request contains 'gzip' or 'deflate' |
| // 'Accept-Encoding' header. |
| func CompressHandler(h RequestHandler) RequestHandler { |
| return CompressHandlerLevel(h, CompressDefaultCompression) |
| } |
| |
| // CompressHandlerLevel returns RequestHandler that transparently compresses |
| // response body generated by h if the request contains 'gzip' or 'deflate' |
| // 'Accept-Encoding' header. |
| // |
| // Level is the desired compression level: |
| // |
| // * CompressNoCompression |
| // * CompressBestSpeed |
| // * CompressBestCompression |
| // * CompressDefaultCompression |
| // * CompressHuffmanOnly |
| func CompressHandlerLevel(h RequestHandler, level int) RequestHandler { |
| return func(ctx *RequestCtx) { |
| h(ctx) |
| ce := ctx.Response.Header.PeekBytes(strContentEncoding) |
| if len(ce) > 0 { |
| // Do not compress responses with non-empty |
| // Content-Encoding. |
| return |
| } |
| if ctx.Request.Header.HasAcceptEncodingBytes(strGzip) { |
| ctx.Response.gzipBody(level) |
| } else if ctx.Request.Header.HasAcceptEncodingBytes(strDeflate) { |
| ctx.Response.deflateBody(level) |
| } |
| } |
| } |
| |
| // RequestCtx contains incoming request and manages outgoing response. |
| // |
| // It is forbidden copying RequestCtx instances. |
| // |
| // RequestHandler should avoid holding references to incoming RequestCtx and/or |
| // its' members after the return. |
| // If holding RequestCtx references after the return is unavoidable |
| // (for instance, ctx is passed to a separate goroutine and ctx lifetime cannot |
| // be controlled), then the RequestHandler MUST call ctx.TimeoutError() |
| // before return. |
| // |
| // It is unsafe modifying/reading RequestCtx instance from concurrently |
| // running goroutines. The only exception is TimeoutError*, which may be called |
| // while other goroutines accessing RequestCtx. |
| type RequestCtx struct { |
| noCopy noCopy |
| |
| // Incoming request. |
| // |
| // Copying Request by value is forbidden. Use pointer to Request instead. |
| Request Request |
| |
| // Outgoing response. |
| // |
| // Copying Response by value is forbidden. Use pointer to Response instead. |
| Response Response |
| |
| userValues userData |
| |
| lastReadDuration time.Duration |
| |
| connID uint64 |
| connRequestNum uint64 |
| connTime time.Time |
| |
| time time.Time |
| |
| logger ctxLogger |
| s *Server |
| c net.Conn |
| fbr firstByteReader |
| |
| timeoutResponse *Response |
| timeoutCh chan struct{} |
| timeoutTimer *time.Timer |
| |
| hijackHandler HijackHandler |
| } |
| |
| // HijackHandler must process the hijacked connection c. |
| // |
| // The connection c is automatically closed after returning from HijackHandler. |
| // |
| // The connection c must not be used after returning from the handler. |
| type HijackHandler func(c net.Conn) |
| |
| // Hijack registers the given handler for connection hijacking. |
| // |
| // The handler is called after returning from RequestHandler |
| // and sending http response. The current connection is passed |
| // to the handler. The connection is automatically closed after |
| // returning from the handler. |
| // |
| // The server skips calling the handler in the following cases: |
| // |
| // * 'Connection: close' header exists in either request or response. |
| // * Unexpected error during response writing to the connection. |
| // |
| // The server stops processing requests from hijacked connections. |
| // Server limits such as Concurrency, ReadTimeout, WriteTimeout, etc. |
| // aren't applied to hijacked connections. |
| // |
| // The handler must not retain references to ctx members. |
| // |
| // Arbitrary 'Connection: Upgrade' protocols may be implemented |
| // with HijackHandler. For instance, |
| // |
| // * WebSocket ( https://en.wikipedia.org/wiki/WebSocket ) |
| // * HTTP/2.0 ( https://en.wikipedia.org/wiki/HTTP/2 ) |
| // |
| func (ctx *RequestCtx) Hijack(handler HijackHandler) { |
| ctx.hijackHandler = handler |
| } |
| |
| // Hijacked returns true after Hijack is called. |
| func (ctx *RequestCtx) Hijacked() bool { |
| return ctx.hijackHandler != nil |
| } |
| |
| // SetUserValue stores the given value (arbitrary object) |
| // under the given key in ctx. |
| // |
| // The value stored in ctx may be obtained by UserValue*. |
| // |
| // This functionality may be useful for passing arbitrary values between |
| // functions involved in request processing. |
| // |
| // All the values are removed from ctx after returning from the top |
| // RequestHandler. Additionally, Close method is called on each value |
| // implementing io.Closer before removing the value from ctx. |
| func (ctx *RequestCtx) SetUserValue(key string, value interface{}) { |
| ctx.userValues.Set(key, value) |
| } |
| |
| // SetUserValueBytes stores the given value (arbitrary object) |
| // under the given key in ctx. |
| // |
| // The value stored in ctx may be obtained by UserValue*. |
| // |
| // This functionality may be useful for passing arbitrary values between |
| // functions involved in request processing. |
| // |
| // All the values stored in ctx are deleted after returning from RequestHandler. |
| func (ctx *RequestCtx) SetUserValueBytes(key []byte, value interface{}) { |
| ctx.userValues.SetBytes(key, value) |
| } |
| |
| // UserValue returns the value stored via SetUserValue* under the given key. |
| func (ctx *RequestCtx) UserValue(key string) interface{} { |
| return ctx.userValues.Get(key) |
| } |
| |
| // UserValueBytes returns the value stored via SetUserValue* |
| // under the given key. |
| func (ctx *RequestCtx) UserValueBytes(key []byte) interface{} { |
| return ctx.userValues.GetBytes(key) |
| } |
| |
| // VisitUserValues calls visitor for each existing userValue. |
| // |
| // visitor must not retain references to key and value after returning. |
| // Make key and/or value copies if you need storing them after returning. |
| func (ctx *RequestCtx) VisitUserValues(visitor func([]byte, interface{})) { |
| for i, n := 0, len(ctx.userValues); i < n; i++ { |
| kv := &ctx.userValues[i] |
| visitor(kv.key, kv.value) |
| } |
| } |
| |
| type connTLSer interface { |
| ConnectionState() tls.ConnectionState |
| } |
| |
| // IsTLS returns true if the underlying connection is tls.Conn. |
| // |
| // tls.Conn is an encrypted connection (aka SSL, HTTPS). |
| func (ctx *RequestCtx) IsTLS() bool { |
| // cast to (connTLSer) instead of (*tls.Conn), since it catches |
| // cases with overridden tls.Conn such as: |
| // |
| // type customConn struct { |
| // *tls.Conn |
| // |
| // // other custom fields here |
| // } |
| _, ok := ctx.c.(connTLSer) |
| return ok |
| } |
| |
| // TLSConnectionState returns TLS connection state. |
| // |
| // The function returns nil if the underlying connection isn't tls.Conn. |
| // |
| // The returned state may be used for verifying TLS version, client certificates, |
| // etc. |
| func (ctx *RequestCtx) TLSConnectionState() *tls.ConnectionState { |
| tlsConn, ok := ctx.c.(connTLSer) |
| if !ok { |
| return nil |
| } |
| state := tlsConn.ConnectionState() |
| return &state |
| } |
| |
| type firstByteReader struct { |
| c net.Conn |
| ch byte |
| byteRead bool |
| } |
| |
| func (r *firstByteReader) Read(b []byte) (int, error) { |
| if len(b) == 0 { |
| return 0, nil |
| } |
| nn := 0 |
| if !r.byteRead { |
| b[0] = r.ch |
| b = b[1:] |
| r.byteRead = true |
| nn = 1 |
| } |
| n, err := r.c.Read(b) |
| return n + nn, err |
| } |
| |
| // Logger is used for logging formatted messages. |
| type Logger interface { |
| // Printf must have the same semantics as log.Printf. |
| Printf(format string, args ...interface{}) |
| } |
| |
| var ctxLoggerLock sync.Mutex |
| |
| type ctxLogger struct { |
| ctx *RequestCtx |
| logger Logger |
| } |
| |
| func (cl *ctxLogger) Printf(format string, args ...interface{}) { |
| ctxLoggerLock.Lock() |
| msg := fmt.Sprintf(format, args...) |
| ctx := cl.ctx |
| cl.logger.Printf("%.3f %s - %s", time.Since(ctx.Time()).Seconds(), ctx.String(), msg) |
| ctxLoggerLock.Unlock() |
| } |
| |
| var zeroTCPAddr = &net.TCPAddr{ |
| IP: net.IPv4zero, |
| } |
| |
| // String returns unique string representation of the ctx. |
| // |
| // The returned value may be useful for logging. |
| func (ctx *RequestCtx) String() string { |
| return fmt.Sprintf("#%016X - %s<->%s - %s %s", ctx.ID(), ctx.LocalAddr(), ctx.RemoteAddr(), ctx.Request.Header.Method(), ctx.URI().FullURI()) |
| } |
| |
| // ID returns unique ID of the request. |
| func (ctx *RequestCtx) ID() uint64 { |
| return (ctx.connID << 32) | ctx.connRequestNum |
| } |
| |
| // ConnID returns unique connection ID. |
| // |
| // This ID may be used to match distinct requests to the same incoming |
| // connection. |
| func (ctx *RequestCtx) ConnID() uint64 { |
| return ctx.connID |
| } |
| |
| // Time returns RequestHandler call time truncated to the nearest second. |
| // |
| // Call time.Now() at the beginning of RequestHandler in order to obtain |
| // percise RequestHandler call time. |
| func (ctx *RequestCtx) Time() time.Time { |
| return ctx.time |
| } |
| |
| // ConnTime returns the time server starts serving the connection |
| // the current request came from. |
| // |
| // The returned time is truncated to the nearest second. |
| func (ctx *RequestCtx) ConnTime() time.Time { |
| return ctx.connTime |
| } |
| |
| // ConnRequestNum returns request sequence number |
| // for the current connection. |
| // |
| // Sequence starts with 1. |
| func (ctx *RequestCtx) ConnRequestNum() uint64 { |
| return ctx.connRequestNum |
| } |
| |
| // SetConnectionClose sets 'Connection: close' response header and closes |
| // connection after the RequestHandler returns. |
| func (ctx *RequestCtx) SetConnectionClose() { |
| ctx.Response.SetConnectionClose() |
| } |
| |
| // SetStatusCode sets response status code. |
| func (ctx *RequestCtx) SetStatusCode(statusCode int) { |
| ctx.Response.SetStatusCode(statusCode) |
| } |
| |
| // SetContentType sets response Content-Type. |
| func (ctx *RequestCtx) SetContentType(contentType string) { |
| ctx.Response.Header.SetContentType(contentType) |
| } |
| |
| // SetContentTypeBytes sets response Content-Type. |
| // |
| // It is safe modifying contentType buffer after function return. |
| func (ctx *RequestCtx) SetContentTypeBytes(contentType []byte) { |
| ctx.Response.Header.SetContentTypeBytes(contentType) |
| } |
| |
| // RequestURI returns RequestURI. |
| // |
| // This uri is valid until returning from RequestHandler. |
| func (ctx *RequestCtx) RequestURI() []byte { |
| return ctx.Request.Header.RequestURI() |
| } |
| |
| // URI returns requested uri. |
| // |
| // The uri is valid until returning from RequestHandler. |
| func (ctx *RequestCtx) URI() *URI { |
| return ctx.Request.URI() |
| } |
| |
| // Referer returns request referer. |
| // |
| // The referer is valid until returning from RequestHandler. |
| func (ctx *RequestCtx) Referer() []byte { |
| return ctx.Request.Header.Referer() |
| } |
| |
| // UserAgent returns User-Agent header value from the request. |
| func (ctx *RequestCtx) UserAgent() []byte { |
| return ctx.Request.Header.UserAgent() |
| } |
| |
| // Path returns requested path. |
| // |
| // The path is valid until returning from RequestHandler. |
| func (ctx *RequestCtx) Path() []byte { |
| return ctx.URI().Path() |
| } |
| |
| // Host returns requested host. |
| // |
| // The host is valid until returning from RequestHandler. |
| func (ctx *RequestCtx) Host() []byte { |
| return ctx.URI().Host() |
| } |
| |
| // QueryArgs returns query arguments from RequestURI. |
| // |
| // It doesn't return POST'ed arguments - use PostArgs() for this. |
| // |
| // Returned arguments are valid until returning from RequestHandler. |
| // |
| // See also PostArgs, FormValue and FormFile. |
| func (ctx *RequestCtx) QueryArgs() *Args { |
| return ctx.URI().QueryArgs() |
| } |
| |
| // PostArgs returns POST arguments. |
| // |
| // It doesn't return query arguments from RequestURI - use QueryArgs for this. |
| // |
| // Returned arguments are valid until returning from RequestHandler. |
| // |
| // See also QueryArgs, FormValue and FormFile. |
| func (ctx *RequestCtx) PostArgs() *Args { |
| return ctx.Request.PostArgs() |
| } |
| |
| // MultipartForm returns requests's multipart form. |
| // |
| // Returns ErrNoMultipartForm if request's content-type |
| // isn't 'multipart/form-data'. |
| // |
| // All uploaded temporary files are automatically deleted after |
| // returning from RequestHandler. Either move or copy uploaded files |
| // into new place if you want retaining them. |
| // |
| // Use SaveMultipartFile function for permanently saving uploaded file. |
| // |
| // The returned form is valid until returning from RequestHandler. |
| // |
| // See also FormFile and FormValue. |
| func (ctx *RequestCtx) MultipartForm() (*multipart.Form, error) { |
| return ctx.Request.MultipartForm() |
| } |
| |
| // FormFile returns uploaded file associated with the given multipart form key. |
| // |
| // The file is automatically deleted after returning from RequestHandler, |
| // so either move or copy uploaded file into new place if you want retaining it. |
| // |
| // Use SaveMultipartFile function for permanently saving uploaded file. |
| // |
| // The returned file header is valid until returning from RequestHandler. |
| func (ctx *RequestCtx) FormFile(key string) (*multipart.FileHeader, error) { |
| mf, err := ctx.MultipartForm() |
| if err != nil { |
| return nil, err |
| } |
| if mf.File == nil { |
| return nil, err |
| } |
| fhh := mf.File[key] |
| if fhh == nil { |
| return nil, ErrMissingFile |
| } |
| return fhh[0], nil |
| } |
| |
| // ErrMissingFile may be returned from FormFile when the is no uploaded file |
| // associated with the given multipart form key. |
| var ErrMissingFile = errors.New("there is no uploaded file associated with the given key") |
| |
| // SaveMultipartFile saves multipart file fh under the given filename path. |
| func SaveMultipartFile(fh *multipart.FileHeader, path string) error { |
| f, err := fh.Open() |
| if err != nil { |
| return err |
| } |
| defer f.Close() |
| |
| if ff, ok := f.(*os.File); ok { |
| return os.Rename(ff.Name(), path) |
| } |
| |
| ff, err := os.Create(path) |
| if err != nil { |
| return err |
| } |
| defer ff.Close() |
| _, err = copyZeroAlloc(ff, f) |
| return err |
| } |
| |
| // FormValue returns form value associated with the given key. |
| // |
| // The value is searched in the following places: |
| // |
| // * Query string. |
| // * POST or PUT body. |
| // |
| // There are more fine-grained methods for obtaining form values: |
| // |
| // * QueryArgs for obtaining values from query string. |
| // * PostArgs for obtaining values from POST or PUT body. |
| // * MultipartForm for obtaining values from multipart form. |
| // * FormFile for obtaining uploaded files. |
| // |
| // The returned value is valid until returning from RequestHandler. |
| func (ctx *RequestCtx) FormValue(key string) []byte { |
| v := ctx.QueryArgs().Peek(key) |
| if len(v) > 0 { |
| return v |
| } |
| v = ctx.PostArgs().Peek(key) |
| if len(v) > 0 { |
| return v |
| } |
| mf, err := ctx.MultipartForm() |
| if err == nil && mf.Value != nil { |
| vv := mf.Value[key] |
| if len(vv) > 0 { |
| return []byte(vv[0]) |
| } |
| } |
| return nil |
| } |
| |
| // IsGet returns true if request method is GET. |
| func (ctx *RequestCtx) IsGet() bool { |
| return ctx.Request.Header.IsGet() |
| } |
| |
| // IsPost returns true if request method is POST. |
| func (ctx *RequestCtx) IsPost() bool { |
| return ctx.Request.Header.IsPost() |
| } |
| |
| // IsPut returns true if request method is PUT. |
| func (ctx *RequestCtx) IsPut() bool { |
| return ctx.Request.Header.IsPut() |
| } |
| |
| // IsDelete returns true if request method is DELETE. |
| func (ctx *RequestCtx) IsDelete() bool { |
| return ctx.Request.Header.IsDelete() |
| } |
| |
| // Method return request method. |
| // |
| // Returned value is valid until returning from RequestHandler. |
| func (ctx *RequestCtx) Method() []byte { |
| return ctx.Request.Header.Method() |
| } |
| |
| // IsHead returns true if request method is HEAD. |
| func (ctx *RequestCtx) IsHead() bool { |
| return ctx.Request.Header.IsHead() |
| } |
| |
| // RemoteAddr returns client address for the given request. |
| // |
| // Always returns non-nil result. |
| func (ctx *RequestCtx) RemoteAddr() net.Addr { |
| if ctx.c == nil { |
| return zeroTCPAddr |
| } |
| addr := ctx.c.RemoteAddr() |
| if addr == nil { |
| return zeroTCPAddr |
| } |
| return addr |
| } |
| |
| // LocalAddr returns server address for the given request. |
| // |
| // Always returns non-nil result. |
| func (ctx *RequestCtx) LocalAddr() net.Addr { |
| if ctx.c == nil { |
| return zeroTCPAddr |
| } |
| addr := ctx.c.LocalAddr() |
| if addr == nil { |
| return zeroTCPAddr |
| } |
| return addr |
| } |
| |
| // RemoteIP returns the client ip the request came from. |
| // |
| // Always returns non-nil result. |
| func (ctx *RequestCtx) RemoteIP() net.IP { |
| return addrToIP(ctx.RemoteAddr()) |
| } |
| |
| // LocalIP returns the server ip the request came to. |
| // |
| // Always returns non-nil result. |
| func (ctx *RequestCtx) LocalIP() net.IP { |
| return addrToIP(ctx.LocalAddr()) |
| } |
| |
| func addrToIP(addr net.Addr) net.IP { |
| x, ok := addr.(*net.TCPAddr) |
| if !ok { |
| return net.IPv4zero |
| } |
| return x.IP |
| } |
| |
| // Error sets response status code to the given value and sets response body |
| // to the given message. |
| func (ctx *RequestCtx) Error(msg string, statusCode int) { |
| ctx.Response.Reset() |
| ctx.SetStatusCode(statusCode) |
| ctx.SetContentTypeBytes(defaultContentType) |
| ctx.SetBodyString(msg) |
| } |
| |
| // Success sets response Content-Type and body to the given values. |
| func (ctx *RequestCtx) Success(contentType string, body []byte) { |
| ctx.SetContentType(contentType) |
| ctx.SetBody(body) |
| } |
| |
| // SuccessString sets response Content-Type and body to the given values. |
| func (ctx *RequestCtx) SuccessString(contentType, body string) { |
| ctx.SetContentType(contentType) |
| ctx.SetBodyString(body) |
| } |
| |
| // Redirect sets 'Location: uri' response header and sets the given statusCode. |
| // |
| // statusCode must have one of the following values: |
| // |
| // * StatusMovedPermanently (301) |
| // * StatusFound (302) |
| // * StatusSeeOther (303) |
| // * StatusTemporaryRedirect (307) |
| // |
| // All other statusCode values are replaced by StatusFound (302). |
| // |
| // The redirect uri may be either absolute or relative to the current |
| // request uri. |
| func (ctx *RequestCtx) Redirect(uri string, statusCode int) { |
| u := AcquireURI() |
| ctx.URI().CopyTo(u) |
| u.Update(uri) |
| ctx.redirect(u.FullURI(), statusCode) |
| ReleaseURI(u) |
| } |
| |
| // RedirectBytes sets 'Location: uri' response header and sets |
| // the given statusCode. |
| // |
| // statusCode must have one of the following values: |
| // |
| // * StatusMovedPermanently (301) |
| // * StatusFound (302) |
| // * StatusSeeOther (303) |
| // * StatusTemporaryRedirect (307) |
| // |
| // All other statusCode values are replaced by StatusFound (302). |
| // |
| // The redirect uri may be either absolute or relative to the current |
| // request uri. |
| func (ctx *RequestCtx) RedirectBytes(uri []byte, statusCode int) { |
| s := b2s(uri) |
| ctx.Redirect(s, statusCode) |
| } |
| |
| func (ctx *RequestCtx) redirect(uri []byte, statusCode int) { |
| ctx.Response.Header.SetCanonical(strLocation, uri) |
| statusCode = getRedirectStatusCode(statusCode) |
| ctx.Response.SetStatusCode(statusCode) |
| } |
| |
| func getRedirectStatusCode(statusCode int) int { |
| if statusCode == StatusMovedPermanently || statusCode == StatusFound || |
| statusCode == StatusSeeOther || statusCode == StatusTemporaryRedirect { |
| return statusCode |
| } |
| return StatusFound |
| } |
| |
| // SetBody sets response body to the given value. |
| // |
| // It is safe re-using body argument after the function returns. |
| func (ctx *RequestCtx) SetBody(body []byte) { |
| ctx.Response.SetBody(body) |
| } |
| |
| // SetBodyString sets response body to the given value. |
| func (ctx *RequestCtx) SetBodyString(body string) { |
| ctx.Response.SetBodyString(body) |
| } |
| |
| // ResetBody resets response body contents. |
| func (ctx *RequestCtx) ResetBody() { |
| ctx.Response.ResetBody() |
| } |
| |
| // SendFile sends local file contents from the given path as response body. |
| // |
| // This is a shortcut to ServeFile(ctx, path). |
| // |
| // SendFile logs all the errors via ctx.Logger. |
| // |
| // See also ServeFile, FSHandler and FS. |
| func (ctx *RequestCtx) SendFile(path string) { |
| ServeFile(ctx, path) |
| } |
| |
| // SendFileBytes sends local file contents from the given path as response body. |
| // |
| // This is a shortcut to ServeFileBytes(ctx, path). |
| // |
| // SendFileBytes logs all the errors via ctx.Logger. |
| // |
| // See also ServeFileBytes, FSHandler and FS. |
| func (ctx *RequestCtx) SendFileBytes(path []byte) { |
| ServeFileBytes(ctx, path) |
| } |
| |
| // IfModifiedSince returns true if lastModified exceeds 'If-Modified-Since' |
| // value from the request header. |
| // |
| // The function returns true also 'If-Modified-Since' request header is missing. |
| func (ctx *RequestCtx) IfModifiedSince(lastModified time.Time) bool { |
| ifModStr := ctx.Request.Header.peek(strIfModifiedSince) |
| if len(ifModStr) == 0 { |
| return true |
| } |
| ifMod, err := ParseHTTPDate(ifModStr) |
| if err != nil { |
| return true |
| } |
| lastModified = lastModified.Truncate(time.Second) |
| return ifMod.Before(lastModified) |
| } |
| |
| // NotModified resets response and sets '304 Not Modified' response status code. |
| func (ctx *RequestCtx) NotModified() { |
| ctx.Response.Reset() |
| ctx.SetStatusCode(StatusNotModified) |
| } |
| |
| // NotFound resets response and sets '404 Not Found' response status code. |
| func (ctx *RequestCtx) NotFound() { |
| ctx.Response.Reset() |
| ctx.SetStatusCode(StatusNotFound) |
| ctx.SetBodyString("404 Page not found") |
| } |
| |
| // Write writes p into response body. |
| func (ctx *RequestCtx) Write(p []byte) (int, error) { |
| ctx.Response.AppendBody(p) |
| return len(p), nil |
| } |
| |
| // WriteString appends s to response body. |
| func (ctx *RequestCtx) WriteString(s string) (int, error) { |
| ctx.Response.AppendBodyString(s) |
| return len(s), nil |
| } |
| |
| // PostBody returns POST request body. |
| // |
| // The returned value is valid until RequestHandler return. |
| func (ctx *RequestCtx) PostBody() []byte { |
| return ctx.Request.Body() |
| } |
| |
| // SetBodyStream sets response body stream and, optionally body size. |
| // |
| // bodyStream.Close() is called after finishing reading all body data |
| // if it implements io.Closer. |
| // |
| // If bodySize is >= 0, then bodySize bytes must be provided by bodyStream |
| // before returning io.EOF. |
| // |
| // If bodySize < 0, then bodyStream is read until io.EOF. |
| // |
| // See also SetBodyStreamWriter. |
| func (ctx *RequestCtx) SetBodyStream(bodyStream io.Reader, bodySize int) { |
| ctx.Response.SetBodyStream(bodyStream, bodySize) |
| } |
| |
| // SetBodyStreamWriter registers the given stream writer for populating |
| // response body. |
| // |
| // Access to RequestCtx and/or its' members is forbidden from sw. |
| // |
| // This function may be used in the following cases: |
| // |
| // * if response body is too big (more than 10MB). |
| // * if response body is streamed from slow external sources. |
| // * if response body must be streamed to the client in chunks. |
| // (aka `http server push`). |
| func (ctx *RequestCtx) SetBodyStreamWriter(sw StreamWriter) { |
| ctx.Response.SetBodyStreamWriter(sw) |
| } |
| |
| // IsBodyStream returns true if response body is set via SetBodyStream*. |
| func (ctx *RequestCtx) IsBodyStream() bool { |
| return ctx.Response.IsBodyStream() |
| } |
| |
| // Logger returns logger, which may be used for logging arbitrary |
| // request-specific messages inside RequestHandler. |
| // |
| // Each message logged via returned logger contains request-specific information |
| // such as request id, request duration, local address, remote address, |
| // request method and request url. |
| // |
| // It is safe re-using returned logger for logging multiple messages |
| // for the current request. |
| // |
| // The returned logger is valid until returning from RequestHandler. |
| func (ctx *RequestCtx) Logger() Logger { |
| if ctx.logger.ctx == nil { |
| ctx.logger.ctx = ctx |
| } |
| if ctx.logger.logger == nil { |
| ctx.logger.logger = ctx.s.logger() |
| } |
| return &ctx.logger |
| } |
| |
| // TimeoutError sets response status code to StatusRequestTimeout and sets |
| // body to the given msg. |
| // |
| // All response modifications after TimeoutError call are ignored. |
| // |
| // TimeoutError MUST be called before returning from RequestHandler if there are |
| // references to ctx and/or its members in other goroutines remain. |
| // |
| // Usage of this function is discouraged. Prefer eliminating ctx references |
| // from pending goroutines instead of using this function. |
| func (ctx *RequestCtx) TimeoutError(msg string) { |
| ctx.TimeoutErrorWithCode(msg, StatusRequestTimeout) |
| } |
| |
| // TimeoutErrorWithCode sets response body to msg and response status |
| // code to statusCode. |
| // |
| // All response modifications after TimeoutErrorWithCode call are ignored. |
| // |
| // TimeoutErrorWithCode MUST be called before returning from RequestHandler |
| // if there are references to ctx and/or its members in other goroutines remain. |
| // |
| // Usage of this function is discouraged. Prefer eliminating ctx references |
| // from pending goroutines instead of using this function. |
| func (ctx *RequestCtx) TimeoutErrorWithCode(msg string, statusCode int) { |
| var resp Response |
| resp.SetStatusCode(statusCode) |
| resp.SetBodyString(msg) |
| ctx.TimeoutErrorWithResponse(&resp) |
| } |
| |
| // TimeoutErrorWithResponse marks the ctx as timed out and sends the given |
| // response to the client. |
| // |
| // All ctx modifications after TimeoutErrorWithResponse call are ignored. |
| // |
| // TimeoutErrorWithResponse MUST be called before returning from RequestHandler |
| // if there are references to ctx and/or its members in other goroutines remain. |
| // |
| // Usage of this function is discouraged. Prefer eliminating ctx references |
| // from pending goroutines instead of using this function. |
| func (ctx *RequestCtx) TimeoutErrorWithResponse(resp *Response) { |
| respCopy := &Response{} |
| resp.CopyTo(respCopy) |
| ctx.timeoutResponse = respCopy |
| } |
| |
| // ListenAndServe serves HTTP requests from the given TCP4 addr. |
| // |
| // Pass custom listener to Serve if you need listening on non-TCP4 media |
| // such as IPv6. |
| func (s *Server) ListenAndServe(addr string) error { |
| ln, err := net.Listen("tcp4", addr) |
| if err != nil { |
| return err |
| } |
| return s.Serve(ln) |
| } |
| |
| // ListenAndServeUNIX serves HTTP requests from the given UNIX addr. |
| // |
| // The function deletes existing file at addr before starting serving. |
| // |
| // The server sets the given file mode for the UNIX addr. |
| func (s *Server) ListenAndServeUNIX(addr string, mode os.FileMode) error { |
| if err := os.Remove(addr); err != nil && !os.IsNotExist(err) { |
| return fmt.Errorf("unexpected error when trying to remove unix socket file %q: %s", addr, err) |
| } |
| ln, err := net.Listen("unix", addr) |
| if err != nil { |
| return err |
| } |
| if err = os.Chmod(addr, mode); err != nil { |
| return fmt.Errorf("cannot chmod %#o for %q: %s", mode, addr, err) |
| } |
| return s.Serve(ln) |
| } |
| |
| // ListenAndServeTLS serves HTTPS requests from the given TCP4 addr. |
| // |
| // certFile and keyFile are paths to TLS certificate and key files. |
| // |
| // Pass custom listener to Serve if you need listening on non-TCP4 media |
| // such as IPv6. |
| func (s *Server) ListenAndServeTLS(addr, certFile, keyFile string) error { |
| ln, err := net.Listen("tcp4", addr) |
| if err != nil { |
| return err |
| } |
| return s.ServeTLS(ln, certFile, keyFile) |
| } |
| |
| // ListenAndServeTLSEmbed serves HTTPS requests from the given TCP4 addr. |
| // |
| // certData and keyData must contain valid TLS certificate and key data. |
| // |
| // Pass custom listener to Serve if you need listening on arbitrary media |
| // such as IPv6. |
| func (s *Server) ListenAndServeTLSEmbed(addr string, certData, keyData []byte) error { |
| ln, err := net.Listen("tcp4", addr) |
| if err != nil { |
| return err |
| } |
| return s.ServeTLSEmbed(ln, certData, keyData) |
| } |
| |
| // ServeTLS serves HTTPS requests from the given listener. |
| // |
| // certFile and keyFile are paths to TLS certificate and key files. |
| func (s *Server) ServeTLS(ln net.Listener, certFile, keyFile string) error { |
| lnTLS, err := newTLSListener(ln, certFile, keyFile) |
| if err != nil { |
| return err |
| } |
| return s.Serve(lnTLS) |
| } |
| |
| // ServeTLSEmbed serves HTTPS requests from the given listener. |
| // |
| // certData and keyData must contain valid TLS certificate and key data. |
| func (s *Server) ServeTLSEmbed(ln net.Listener, certData, keyData []byte) error { |
| lnTLS, err := newTLSListenerEmbed(ln, certData, keyData) |
| if err != nil { |
| return err |
| } |
| return s.Serve(lnTLS) |
| } |
| |
| func newTLSListener(ln net.Listener, certFile, keyFile string) (net.Listener, error) { |
| cert, err := tls.LoadX509KeyPair(certFile, keyFile) |
| if err != nil { |
| return nil, fmt.Errorf("cannot load TLS key pair from certFile=%q and keyFile=%q: %s", certFile, keyFile, err) |
| } |
| return newCertListener(ln, &cert), nil |
| } |
| |
| func newTLSListenerEmbed(ln net.Listener, certData, keyData []byte) (net.Listener, error) { |
| cert, err := tls.X509KeyPair(certData, keyData) |
| if err != nil { |
| return nil, fmt.Errorf("cannot load TLS key pair from the provided certData(%d) and keyData(%d): %s", |
| len(certData), len(keyData), err) |
| } |
| return newCertListener(ln, &cert), nil |
| } |
| |
| func newCertListener(ln net.Listener, cert *tls.Certificate) net.Listener { |
| tlsConfig := &tls.Config{ |
| Certificates: []tls.Certificate{*cert}, |
| PreferServerCipherSuites: true, |
| } |
| return tls.NewListener(ln, tlsConfig) |
| } |
| |
| // DefaultConcurrency is the maximum number of concurrent connections |
| // the Server may serve by default (i.e. if Server.Concurrency isn't set). |
| const DefaultConcurrency = 256 * 1024 |
| |
| // Serve serves incoming connections from the given listener. |
| // |
| // Serve blocks until the given listener returns permanent error. |
| func (s *Server) Serve(ln net.Listener) error { |
| var lastOverflowErrorTime time.Time |
| var lastPerIPErrorTime time.Time |
| var c net.Conn |
| var err error |
| |
| maxWorkersCount := s.getConcurrency() |
| s.concurrencyCh = make(chan struct{}, maxWorkersCount) |
| wp := &workerPool{ |
| WorkerFunc: s.serveConn, |
| MaxWorkersCount: maxWorkersCount, |
| LogAllErrors: s.LogAllErrors, |
| Logger: s.logger(), |
| } |
| wp.Start() |
| |
| for { |
| if c, err = acceptConn(s, ln, &lastPerIPErrorTime); err != nil { |
| wp.Stop() |
| if err == io.EOF { |
| return nil |
| } |
| return err |
| } |
| if !wp.Serve(c) { |
| s.writeFastError(c, StatusServiceUnavailable, |
| "The connection cannot be served because Server.Concurrency limit exceeded") |
| c.Close() |
| if time.Since(lastOverflowErrorTime) > time.Minute { |
| s.logger().Printf("The incoming connection cannot be served, because %d concurrent connections are served. "+ |
| "Try increasing Server.Concurrency", maxWorkersCount) |
| lastOverflowErrorTime = CoarseTimeNow() |
| } |
| |
| // The current server reached concurrency limit, |
| // so give other concurrently running servers a chance |
| // accepting incoming connections on the same address. |
| // |
| // There is a hope other servers didn't reach their |
| // concurrency limits yet :) |
| time.Sleep(100 * time.Millisecond) |
| } |
| c = nil |
| } |
| } |
| |
| func acceptConn(s *Server, ln net.Listener, lastPerIPErrorTime *time.Time) (net.Conn, error) { |
| for { |
| c, err := ln.Accept() |
| if err != nil { |
| if c != nil { |
| panic("BUG: net.Listener returned non-nil conn and non-nil error") |
| } |
| if netErr, ok := err.(net.Error); ok && netErr.Temporary() { |
| s.logger().Printf("Temporary error when accepting new connections: %s", netErr) |
| time.Sleep(time.Second) |
| continue |
| } |
| if err != io.EOF && !strings.Contains(err.Error(), "use of closed network connection") { |
| s.logger().Printf("Permanent error when accepting new connections: %s", err) |
| return nil, err |
| } |
| return nil, io.EOF |
| } |
| if c == nil { |
| panic("BUG: net.Listener returned (nil, nil)") |
| } |
| if s.MaxConnsPerIP > 0 { |
| pic := wrapPerIPConn(s, c) |
| if pic == nil { |
| if time.Since(*lastPerIPErrorTime) > time.Minute { |
| s.logger().Printf("The number of connections from %s exceeds MaxConnsPerIP=%d", |
| getConnIP4(c), s.MaxConnsPerIP) |
| *lastPerIPErrorTime = CoarseTimeNow() |
| } |
| continue |
| } |
| c = pic |
| } |
| return c, nil |
| } |
| } |
| |
| func wrapPerIPConn(s *Server, c net.Conn) net.Conn { |
| ip := getUint32IP(c) |
| if ip == 0 { |
| return c |
| } |
| n := s.perIPConnCounter.Register(ip) |
| if n > s.MaxConnsPerIP { |
| s.perIPConnCounter.Unregister(ip) |
| s.writeFastError(c, StatusTooManyRequests, "The number of connections from your ip exceeds MaxConnsPerIP") |
| c.Close() |
| return nil |
| } |
| return acquirePerIPConn(c, ip, &s.perIPConnCounter) |
| } |
| |
| var defaultLogger = Logger(log.New(os.Stderr, "", log.LstdFlags)) |
| |
| func (s *Server) logger() Logger { |
| if s.Logger != nil { |
| return s.Logger |
| } |
| return defaultLogger |
| } |
| |
| var ( |
| // ErrPerIPConnLimit may be returned from ServeConn if the number of connections |
| // per ip exceeds Server.MaxConnsPerIP. |
| ErrPerIPConnLimit = errors.New("too many connections per ip") |
| |
| // ErrConcurrencyLimit may be returned from ServeConn if the number |
| // of concurrenty served connections exceeds Server.Concurrency. |
| ErrConcurrencyLimit = errors.New("canot serve the connection because Server.Concurrency concurrent connections are served") |
| |
| // ErrKeepaliveTimeout is returned from ServeConn |
| // if the connection lifetime exceeds MaxKeepaliveDuration. |
| ErrKeepaliveTimeout = errors.New("exceeded MaxKeepaliveDuration") |
| ) |
| |
| // ServeConn serves HTTP requests from the given connection. |
| // |
| // ServeConn returns nil if all requests from the c are successfully served. |
| // It returns non-nil error otherwise. |
| // |
| // Connection c must immediately propagate all the data passed to Write() |
| // to the client. Otherwise requests' processing may hang. |
| // |
| // ServeConn closes c before returning. |
| func (s *Server) ServeConn(c net.Conn) error { |
| if s.MaxConnsPerIP > 0 { |
| pic := wrapPerIPConn(s, c) |
| if pic == nil { |
| return ErrPerIPConnLimit |
| } |
| c = pic |
| } |
| |
| n := atomic.AddUint32(&s.concurrency, 1) |
| if n > uint32(s.getConcurrency()) { |
| atomic.AddUint32(&s.concurrency, ^uint32(0)) |
| s.writeFastError(c, StatusServiceUnavailable, "The connection cannot be served because Server.Concurrency limit exceeded") |
| c.Close() |
| return ErrConcurrencyLimit |
| } |
| |
| err := s.serveConn(c) |
| |
| atomic.AddUint32(&s.concurrency, ^uint32(0)) |
| |
| if err != errHijacked { |
| err1 := c.Close() |
| if err == nil { |
| err = err1 |
| } |
| } else { |
| err = nil |
| } |
| return err |
| } |
| |
| var errHijacked = errors.New("connection has been hijacked") |
| |
| func (s *Server) getConcurrency() int { |
| n := s.Concurrency |
| if n <= 0 { |
| n = DefaultConcurrency |
| } |
| return n |
| } |
| |
| var globalConnID uint64 |
| |
| func nextConnID() uint64 { |
| return atomic.AddUint64(&globalConnID, 1) |
| } |
| |
| // DefaultMaxRequestBodySize is the maximum request body size the server |
| // reads by default. |
| // |
| // See Server.MaxRequestBodySize for details. |
| const DefaultMaxRequestBodySize = 4 * 1024 * 1024 |
| |
| func (s *Server) serveConn(c net.Conn) error { |
| serverName := s.getServerName() |
| connRequestNum := uint64(0) |
| connID := nextConnID() |
| currentTime := CoarseTimeNow() |
| connTime := currentTime |
| maxRequestBodySize := s.MaxRequestBodySize |
| if maxRequestBodySize <= 0 { |
| maxRequestBodySize = DefaultMaxRequestBodySize |
| } |
| |
| ctx := s.acquireCtx(c) |
| ctx.connTime = connTime |
| isTLS := ctx.IsTLS() |
| var ( |
| br *bufio.Reader |
| bw *bufio.Writer |
| |
| err error |
| timeoutResponse *Response |
| hijackHandler HijackHandler |
| |
| lastReadDeadlineTime time.Time |
| lastWriteDeadlineTime time.Time |
| |
| connectionClose bool |
| isHTTP11 bool |
| ) |
| for { |
| connRequestNum++ |
| ctx.time = currentTime |
| |
| if s.ReadTimeout > 0 || s.MaxKeepaliveDuration > 0 { |
| lastReadDeadlineTime = s.updateReadDeadline(c, ctx, lastReadDeadlineTime) |
| if lastReadDeadlineTime.IsZero() { |
| err = ErrKeepaliveTimeout |
| break |
| } |
| } |
| |
| if !(s.ReduceMemoryUsage || ctx.lastReadDuration > time.Second) || br != nil { |
| if br == nil { |
| br = acquireReader(ctx) |
| } |
| } else { |
| br, err = acquireByteReader(&ctx) |
| } |
| ctx.Request.isTLS = isTLS |
| |
| if err == nil { |
| if s.DisableHeaderNamesNormalizing { |
| ctx.Request.Header.DisableNormalizing() |
| ctx.Response.Header.DisableNormalizing() |
| } |
| err = ctx.Request.readLimitBody(br, maxRequestBodySize, s.GetOnly) |
| if br.Buffered() == 0 || err != nil { |
| releaseReader(s, br) |
| br = nil |
| } |
| } |
| |
| currentTime = CoarseTimeNow() |
| ctx.lastReadDuration = currentTime.Sub(ctx.time) |
| |
| if err != nil { |
| if err == io.EOF { |
| err = nil |
| } else { |
| bw = writeErrorResponse(bw, ctx, err) |
| } |
| break |
| } |
| |
| // 'Expect: 100-continue' request handling. |
| // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html for details. |
| if !ctx.Request.Header.noBody() && ctx.Request.MayContinue() { |
| // Send 'HTTP/1.1 100 Continue' response. |
| if bw == nil { |
| bw = acquireWriter(ctx) |
| } |
| bw.Write(strResponseContinue) |
| err = bw.Flush() |
| releaseWriter(s, bw) |
| bw = nil |
| if err != nil { |
| break |
| } |
| |
| // Read request body. |
| if br == nil { |
| br = acquireReader(ctx) |
| } |
| err = ctx.Request.ContinueReadBody(br, maxRequestBodySize) |
| if br.Buffered() == 0 || err != nil { |
| releaseReader(s, br) |
| br = nil |
| } |
| if err != nil { |
| bw = writeErrorResponse(bw, ctx, err) |
| break |
| } |
| } |
| |
| connectionClose = s.DisableKeepalive || ctx.Request.Header.connectionCloseFast() |
| isHTTP11 = ctx.Request.Header.IsHTTP11() |
| |
| ctx.Response.Header.SetServerBytes(serverName) |
| ctx.connID = connID |
| ctx.connRequestNum = connRequestNum |
| ctx.connTime = connTime |
| ctx.time = currentTime |
| s.Handler(ctx) |
| |
| timeoutResponse = ctx.timeoutResponse |
| if timeoutResponse != nil { |
| ctx = s.acquireCtx(c) |
| timeoutResponse.CopyTo(&ctx.Response) |
| if br != nil { |
| // Close connection, since br may be attached to the old ctx via ctx.fbr. |
| ctx.SetConnectionClose() |
| } |
| } |
| |
| if !ctx.IsGet() && ctx.IsHead() { |
| ctx.Response.SkipBody = true |
| } |
| ctx.Request.Reset() |
| |
| hijackHandler = ctx.hijackHandler |
| ctx.hijackHandler = nil |
| |
| ctx.userValues.Reset() |
| |
| if s.MaxRequestsPerConn > 0 && connRequestNum >= uint64(s.MaxRequestsPerConn) { |
| ctx.SetConnectionClose() |
| } |
| |
| if s.WriteTimeout > 0 || s.MaxKeepaliveDuration > 0 { |
| lastWriteDeadlineTime = s.updateWriteDeadline(c, ctx, lastWriteDeadlineTime) |
| } |
| |
| // Verify Request.Header.connectionCloseFast() again, |
| // since request handler might trigger full headers' parsing. |
| connectionClose = connectionClose || ctx.Request.Header.connectionCloseFast() || ctx.Response.ConnectionClose() |
| if connectionClose { |
| ctx.Response.Header.SetCanonical(strConnection, strClose) |
| } else if !isHTTP11 { |
| // Set 'Connection: keep-alive' response header for non-HTTP/1.1 request. |
| // There is no need in setting this header for http/1.1, since in http/1.1 |
| // connections are keep-alive by default. |
| ctx.Response.Header.SetCanonical(strConnection, strKeepAlive) |
| } |
| |
| if len(ctx.Response.Header.Server()) == 0 { |
| ctx.Response.Header.SetServerBytes(serverName) |
| } |
| |
| if bw == nil { |
| bw = acquireWriter(ctx) |
| } |
| if err = writeResponse(ctx, bw); err != nil { |
| break |
| } |
| |
| if br == nil || connectionClose { |
| err = bw.Flush() |
| releaseWriter(s, bw) |
| bw = nil |
| if err != nil { |
| break |
| } |
| if connectionClose { |
| break |
| } |
| } |
| |
| if hijackHandler != nil { |
| var hjr io.Reader |
| hjr = c |
| if br != nil { |
| hjr = br |
| br = nil |
| |
| // br may point to ctx.fbr, so do not return ctx into pool. |
| ctx = s.acquireCtx(c) |
| } |
| if bw != nil { |
| err = bw.Flush() |
| releaseWriter(s, bw) |
| bw = nil |
| if err != nil { |
| break |
| } |
| } |
| c.SetReadDeadline(zeroTime) |
| c.SetWriteDeadline(zeroTime) |
| go hijackConnHandler(hjr, c, s, hijackHandler) |
| hijackHandler = nil |
| err = errHijacked |
| break |
| } |
| |
| currentTime = CoarseTimeNow() |
| } |
| |
| if br != nil { |
| releaseReader(s, br) |
| } |
| if bw != nil { |
| releaseWriter(s, bw) |
| } |
| s.releaseCtx(ctx) |
| return err |
| } |
| |
| func (s *Server) updateReadDeadline(c net.Conn, ctx *RequestCtx, lastDeadlineTime time.Time) time.Time { |
| readTimeout := s.ReadTimeout |
| currentTime := ctx.time |
| if s.MaxKeepaliveDuration > 0 { |
| connTimeout := s.MaxKeepaliveDuration - currentTime.Sub(ctx.connTime) |
| if connTimeout <= 0 { |
| return zeroTime |
| } |
| if connTimeout < readTimeout { |
| readTimeout = connTimeout |
| } |
| } |
| |
| // Optimization: update read deadline only if more than 25% |
| // of the last read deadline exceeded. |
| // See https://github.com/golang/go/issues/15133 for details. |
| if currentTime.Sub(lastDeadlineTime) > (readTimeout >> 2) { |
| if err := c.SetReadDeadline(currentTime.Add(readTimeout)); err != nil { |
| panic(fmt.Sprintf("BUG: error in SetReadDeadline(%s): %s", readTimeout, err)) |
| } |
| lastDeadlineTime = currentTime |
| } |
| return lastDeadlineTime |
| } |
| |
| func (s *Server) updateWriteDeadline(c net.Conn, ctx *RequestCtx, lastDeadlineTime time.Time) time.Time { |
| writeTimeout := s.WriteTimeout |
| if s.MaxKeepaliveDuration > 0 { |
| connTimeout := s.MaxKeepaliveDuration - time.Since(ctx.connTime) |
| if connTimeout <= 0 { |
| // MaxKeepAliveDuration exceeded, but let's try sending response anyway |
| // in 100ms with 'Connection: close' header. |
| ctx.SetConnectionClose() |
| connTimeout = 100 * time.Millisecond |
| } |
| if connTimeout < writeTimeout { |
| writeTimeout = connTimeout |
| } |
| } |
| |
| // Optimization: update write deadline only if more than 25% |
| // of the last write deadline exceeded. |
| // See https://github.com/golang/go/issues/15133 for details. |
| currentTime := CoarseTimeNow() |
| if currentTime.Sub(lastDeadlineTime) > (writeTimeout >> 2) { |
| if err := c.SetWriteDeadline(currentTime.Add(writeTimeout)); err != nil { |
| panic(fmt.Sprintf("BUG: error in SetWriteDeadline(%s): %s", writeTimeout, err)) |
| } |
| lastDeadlineTime = currentTime |
| } |
| return lastDeadlineTime |
| } |
| |
| func hijackConnHandler(r io.Reader, c net.Conn, s *Server, h HijackHandler) { |
| hjc := s.acquireHijackConn(r, c) |
| h(hjc) |
| |
| if br, ok := r.(*bufio.Reader); ok { |
| releaseReader(s, br) |
| } |
| c.Close() |
| s.releaseHijackConn(hjc) |
| } |
| |
| func (s *Server) acquireHijackConn(r io.Reader, c net.Conn) *hijackConn { |
| v := s.hijackConnPool.Get() |
| if v == nil { |
| hjc := &hijackConn{ |
| Conn: c, |
| r: r, |
| } |
| return hjc |
| } |
| hjc := v.(*hijackConn) |
| hjc.Conn = c |
| hjc.r = r |
| return hjc |
| } |
| |
| func (s *Server) releaseHijackConn(hjc *hijackConn) { |
| hjc.Conn = nil |
| hjc.r = nil |
| s.hijackConnPool.Put(hjc) |
| } |
| |
| type hijackConn struct { |
| net.Conn |
| r io.Reader |
| } |
| |
| func (c hijackConn) Read(p []byte) (int, error) { |
| return c.r.Read(p) |
| } |
| |
| func (c hijackConn) Close() error { |
| // hijacked conn is closed in hijackConnHandler. |
| return nil |
| } |
| |
| // LastTimeoutErrorResponse returns the last timeout response set |
| // via TimeoutError* call. |
| // |
| // This function is intended for custom server implementations. |
| func (ctx *RequestCtx) LastTimeoutErrorResponse() *Response { |
| return ctx.timeoutResponse |
| } |
| |
| func writeResponse(ctx *RequestCtx, w *bufio.Writer) error { |
| if ctx.timeoutResponse != nil { |
| panic("BUG: cannot write timed out response") |
| } |
| err := ctx.Response.Write(w) |
| ctx.Response.Reset() |
| return err |
| } |
| |
| const ( |
| defaultReadBufferSize = 4096 |
| defaultWriteBufferSize = 4096 |
| ) |
| |
| func acquireByteReader(ctxP **RequestCtx) (*bufio.Reader, error) { |
| ctx := *ctxP |
| s := ctx.s |
| c := ctx.c |
| t := ctx.time |
| s.releaseCtx(ctx) |
| |
| // Make GC happy, so it could garbage collect ctx |
| // while we waiting for the next request. |
| ctx = nil |
| *ctxP = nil |
| |
| v := s.bytePool.Get() |
| if v == nil { |
| v = make([]byte, 1) |
| } |
| b := v.([]byte) |
| n, err := c.Read(b) |
| ch := b[0] |
| s.bytePool.Put(v) |
| ctx = s.acquireCtx(c) |
| ctx.time = t |
| *ctxP = ctx |
| if err != nil { |
| // Treat all errors as EOF on unsuccessful read |
| // of the first request byte. |
| return nil, io.EOF |
| } |
| if n != 1 { |
| panic("BUG: Reader must return at least one byte") |
| } |
| |
| ctx.fbr.c = c |
| ctx.fbr.ch = ch |
| ctx.fbr.byteRead = false |
| r := acquireReader(ctx) |
| r.Reset(&ctx.fbr) |
| return r, nil |
| } |
| |
| func acquireReader(ctx *RequestCtx) *bufio.Reader { |
| v := ctx.s.readerPool.Get() |
| if v == nil { |
| n := ctx.s.ReadBufferSize |
| if n <= 0 { |
| n = defaultReadBufferSize |
| } |
| return bufio.NewReaderSize(ctx.c, n) |
| } |
| r := v.(*bufio.Reader) |
| r.Reset(ctx.c) |
| return r |
| } |
| |
| func releaseReader(s *Server, r *bufio.Reader) { |
| s.readerPool.Put(r) |
| } |
| |
| func acquireWriter(ctx *RequestCtx) *bufio.Writer { |
| v := ctx.s.writerPool.Get() |
| if v == nil { |
| n := ctx.s.WriteBufferSize |
| if n <= 0 { |
| n = defaultWriteBufferSize |
| } |
| return bufio.NewWriterSize(ctx.c, n) |
| } |
| w := v.(*bufio.Writer) |
| w.Reset(ctx.c) |
| return w |
| } |
| |
| func releaseWriter(s *Server, w *bufio.Writer) { |
| s.writerPool.Put(w) |
| } |
| |
| func (s *Server) acquireCtx(c net.Conn) *RequestCtx { |
| v := s.ctxPool.Get() |
| var ctx *RequestCtx |
| if v == nil { |
| ctx = &RequestCtx{ |
| s: s, |
| } |
| keepBodyBuffer := !s.ReduceMemoryUsage |
| ctx.Request.keepBodyBuffer = keepBodyBuffer |
| ctx.Response.keepBodyBuffer = keepBodyBuffer |
| } else { |
| ctx = v.(*RequestCtx) |
| } |
| ctx.c = c |
| return ctx |
| } |
| |
| // Init2 prepares ctx for passing to RequestHandler. |
| // |
| // conn is used only for determining local and remote addresses. |
| // |
| // This function is intended for custom Server implementations. |
| // See https://github.com/valyala/httpteleport for details. |
| func (ctx *RequestCtx) Init2(conn net.Conn, logger Logger, reduceMemoryUsage bool) { |
| ctx.c = conn |
| ctx.logger.logger = logger |
| ctx.connID = nextConnID() |
| ctx.s = fakeServer |
| ctx.connRequestNum = 0 |
| ctx.connTime = CoarseTimeNow() |
| ctx.time = ctx.connTime |
| |
| keepBodyBuffer := !reduceMemoryUsage |
| ctx.Request.keepBodyBuffer = keepBodyBuffer |
| ctx.Response.keepBodyBuffer = keepBodyBuffer |
| } |
| |
| // Init prepares ctx for passing to RequestHandler. |
| // |
| // remoteAddr and logger are optional. They are used by RequestCtx.Logger(). |
| // |
| // This function is intended for custom Server implementations. |
| func (ctx *RequestCtx) Init(req *Request, remoteAddr net.Addr, logger Logger) { |
| if remoteAddr == nil { |
| remoteAddr = zeroTCPAddr |
| } |
| c := &fakeAddrer{ |
| laddr: zeroTCPAddr, |
| raddr: remoteAddr, |
| } |
| if logger == nil { |
| logger = defaultLogger |
| } |
| ctx.Init2(c, logger, true) |
| req.CopyTo(&ctx.Request) |
| } |
| |
| var fakeServer = &Server{ |
| // Initialize concurrencyCh for TimeoutHandler |
| concurrencyCh: make(chan struct{}, DefaultConcurrency), |
| } |
| |
| type fakeAddrer struct { |
| net.Conn |
| laddr net.Addr |
| raddr net.Addr |
| } |
| |
| func (fa *fakeAddrer) RemoteAddr() net.Addr { |
| return fa.raddr |
| } |
| |
| func (fa *fakeAddrer) LocalAddr() net.Addr { |
| return fa.laddr |
| } |
| |
| func (fa *fakeAddrer) Read(p []byte) (int, error) { |
| panic("BUG: unexpected Read call") |
| } |
| |
| func (fa *fakeAddrer) Write(p []byte) (int, error) { |
| panic("BUG: unexpected Write call") |
| } |
| |
| func (fa *fakeAddrer) Close() error { |
| panic("BUG: unexpected Close call") |
| } |
| |
| func (s *Server) releaseCtx(ctx *RequestCtx) { |
| if ctx.timeoutResponse != nil { |
| panic("BUG: cannot release timed out RequestCtx") |
| } |
| ctx.c = nil |
| ctx.fbr.c = nil |
| s.ctxPool.Put(ctx) |
| } |
| |
| func (s *Server) getServerName() []byte { |
| v := s.serverName.Load() |
| var serverName []byte |
| if v == nil { |
| serverName = []byte(s.Name) |
| if len(serverName) == 0 { |
| serverName = defaultServerName |
| } |
| s.serverName.Store(serverName) |
| } else { |
| serverName = v.([]byte) |
| } |
| return serverName |
| } |
| |
| func (s *Server) writeFastError(w io.Writer, statusCode int, msg string) { |
| w.Write(statusLine(statusCode)) |
| fmt.Fprintf(w, "Connection: close\r\n"+ |
| "Server: %s\r\n"+ |
| "Date: %s\r\n"+ |
| "Content-Type: text/plain\r\n"+ |
| "Content-Length: %d\r\n"+ |
| "\r\n"+ |
| "%s", |
| s.getServerName(), serverDate.Load(), len(msg), msg) |
| } |
| |
| func writeErrorResponse(bw *bufio.Writer, ctx *RequestCtx, err error) *bufio.Writer { |
| if _, ok := err.(*ErrSmallBuffer); ok { |
| ctx.Error("Too big request header", StatusRequestHeaderFieldsTooLarge) |
| } else { |
| ctx.Error("Error when parsing request", StatusBadRequest) |
| } |
| ctx.SetConnectionClose() |
| if bw == nil { |
| bw = acquireWriter(ctx) |
| } |
| writeResponse(ctx, bw) |
| bw.Flush() |
| return bw |
| } |