func (rp *FastReverseProxy) handler(ctx *fasthttp.RequestCtx) { req := &ctx.Request resp := &ctx.Response host := string(req.Header.Host()) uri := req.URI() if host == "__ping__" && len(uri.Path()) == 1 && uri.Path()[0] == byte('/') { resp.SetBody(okResponse) return } reqData, err := rp.Router.ChooseBackend(host) if err != nil { log.LogError(reqData.String(), string(uri.Path()), err) } dstScheme := "" dstHost := "" u, err := url.Parse(reqData.Backend) if err == nil { dstScheme = u.Scheme dstHost = u.Host } else { log.LogError(reqData.String(), string(uri.Path()), err) } if dstHost == "" { dstHost = reqData.Backend } upgrade := req.Header.Peek("Upgrade") if len(upgrade) > 0 && bytes.Compare(bytes.ToLower(upgrade), websocketUpgrade) == 0 { resp.SkipResponse = true rp.serveWebsocket(dstHost, reqData, ctx) return } var backendDuration time.Duration logEntry := func() *log.LogEntry { proto := "HTTP/1.0" if req.Header.IsHTTP11() { proto = "HTTP/1.1" } return &log.LogEntry{ Now: time.Now(), BackendDuration: backendDuration, TotalDuration: time.Since(reqData.StartTime), BackendKey: reqData.BackendKey, RemoteAddr: ctx.RemoteAddr().String(), Method: string(ctx.Method()), Path: string(uri.Path()), Proto: proto, Referer: string(ctx.Referer()), UserAgent: string(ctx.UserAgent()), RequestIDHeader: rp.RequestIDHeader, RequestID: string(req.Header.Peek(rp.RequestIDHeader)), StatusCode: resp.StatusCode(), ContentLength: int64(resp.Header.ContentLength()), } } isDebug := len(req.Header.Peek("X-Debug-Router")) > 0 req.Header.Del("X-Debug-Router") if dstHost == "" { resp.SetStatusCode(http.StatusBadRequest) resp.SetBody(noRouteResponseContent) rp.debugHeaders(resp, reqData, isDebug) endErr := rp.Router.EndRequest(reqData, false, logEntry) if endErr != nil { log.LogError(reqData.String(), string(uri.Path()), endErr) } return } if rp.RequestIDHeader != "" && len(req.Header.Peek(rp.RequestIDHeader)) == 0 { unparsedID, err := uuid.NewV4() if err == nil { req.Header.Set(rp.RequestIDHeader, unparsedID.String()) } else { log.LogError(reqData.String(), string(uri.Path()), fmt.Errorf("unable to generate request id: %s", err)) } } hostOnly, _, _ := net.SplitHostPort(dstHost) if hostOnly == "" { hostOnly = dstHost } isIP := net.ParseIP(hostOnly) != nil if !isIP { req.Header.SetBytesV("X-Host", uri.Host()) req.Header.SetBytesV("X-Forwarded-Host", uri.Host()) uri.SetHost(hostOnly) } client := rp.getClient(dstHost, dstScheme == "https") t0 := time.Now().UTC() err = client.Do(req, resp) backendDuration = time.Since(t0) markAsDead := false if err != nil { var isTimeout bool if netErr, ok := err.(net.Error); ok { markAsDead = !netErr.Temporary() isTimeout = netErr.Timeout() } if isTimeout { markAsDead = false err = fmt.Errorf("request timed out after %v: %s", time.Since(reqData.StartTime), err) } else { err = fmt.Errorf("error in backend request: %s", err) } if markAsDead { err = fmt.Errorf("%s *DEAD*", err) } resp.SetStatusCode(http.StatusServiceUnavailable) log.LogError(reqData.String(), string(uri.Path()), err) } rp.debugHeaders(resp, reqData, isDebug) endErr := rp.Router.EndRequest(reqData, markAsDead, logEntry) if endErr != nil { log.LogError(reqData.String(), string(uri.Path()), endErr) } }