func (u *Upstream) ping() (up bool, ok bool) { if u.PingPath != "" { // the url must be syntactically valid for this to work but the host will be ignored because we // are overriding the connection always request, err := http.NewRequest("GET", "http://localhost"+u.PingPath, nil) request.Header.Set("Connection", "Keep-Alive") // not sure if this should be here for a ping if err != nil { falcore.Error("Bad Ping request: %v", err) return false, true } res, err := u.Transport.transport.RoundTrip(request) if err != nil { falcore.Error("[%s] Failed Ping to %v:%v: %v", u.Name, u.Transport.host, u.Transport.port, err) return false, true } else { res.Body.Close() } if res.StatusCode == 200 { return true, true } falcore.Error("[%s] Failed Ping to %v:%v: %v", u.Name, u.Transport.host, u.Transport.port, res.Status) // bad status return false, true } return false, false }
func (f *FileFilter) FilterRequest(req *falcore.Request) (res *http.Response) { // Clean asset path asset_path := filepath.Clean(filepath.FromSlash(req.HttpRequest.URL.Path)) // Resolve PathPrefix if strings.HasPrefix(asset_path, f.PathPrefix) { asset_path = asset_path[len(f.PathPrefix):] } else { // The requested path doesn't fall into the scope of paths that are supposed to be handled by this filter return } // Resolve FSBase if f.BasePath != "" { asset_path = filepath.Join(f.BasePath, asset_path) } else { falcore.Error("file_filter requires a BasePath") return falcore.StringResponse(req.HttpRequest, 500, nil, "Server Error\n") } // Open File if file, err := os.Open(asset_path); err == nil { // If it's a directory, try opening the directory index if stat, err := file.Stat(); f.DirectoryIndex != "" && err == nil && stat.Mode()&os.ModeDir > 0 { file.Close() asset_path = filepath.Join(asset_path, f.DirectoryIndex) if file, err = os.Open(asset_path); err != nil { return } } // Make sure it's an actual file if stat, err := file.Stat(); err == nil && stat.Mode()&os.ModeType == 0 { res = &http.Response{ Request: req.HttpRequest, StatusCode: 200, Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1, Body: file, Header: make(http.Header), ContentLength: stat.Size(), } if ct := mime.TypeByExtension(filepath.Ext(asset_path)); ct != "" { res.Header.Set("Content-Type", ct) } } else { file.Close() } } else { falcore.Finest("Can't open %v: %v", asset_path, err) } return }
// fixme: probably should use net.SplitHostPort func SplitHostPort(hostPort string, defaultPort int) (string, int) { parts := strings.Split(hostPort, ":") upstreamHost := parts[0] upstreamPort := defaultPort if len(parts) > 1 { var err error upstreamPort, err = strconv.Atoi(parts[1]) if err != nil { upstreamPort = defaultPort falcore.Error("UpstreamPool Error converting port to int for", upstreamHost, ":", err) } } return upstreamHost, upstreamPort }
func (f *FileFilter) FilterRequest(req *falcore.Request) (res *http.Response) { // Clean asset path asset_path := filepath.Clean(filepath.FromSlash(req.HttpRequest.URL.Path)) // Resolve PathPrefix if strings.HasPrefix(asset_path, f.PathPrefix) { asset_path = asset_path[len(f.PathPrefix):] } else { falcore.Debug("%v doesn't match prefix %v", asset_path, f.PathPrefix) res = falcore.StringResponse(req.HttpRequest, 404, nil, "Not found.") return } // Resolve FSBase if f.BasePath != "" { asset_path = filepath.Join(f.BasePath, asset_path) } else { falcore.Error("file_filter requires a BasePath") return falcore.StringResponse(req.HttpRequest, 500, nil, "Server Error\n") } // Open File if file, err := os.Open(asset_path); err == nil { // Make sure it's an actual file if stat, err := file.Stat(); err == nil && stat.Mode()&os.ModeType == 0 { res = &http.Response{ Request: req.HttpRequest, StatusCode: 200, Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1, Body: file, Header: make(http.Header), ContentLength: stat.Size(), } if ct := mime.TypeByExtension(filepath.Ext(asset_path)); ct != "" { res.Header.Set("Content-Type", ct) } } else { file.Close() } } else { falcore.Finest("Can't open %v: %v", asset_path, err) } return }
func (t *UpstreamTransport) dial(n, a string) (c net.Conn, err error) { var addr *net.TCPAddr addr, err = t.lookupIp() falcore.Fine("Dialing connection to %v", addr) var ctcp *net.TCPConn ctcp, err = net.DialTCP("tcp4", nil, addr) if err != nil { falcore.Error("Dial Failed: %v", err) return } // FIXME: Go1 has a race that causes problems with timeouts // Recommend disabling until Go1.1 if t.timeout > 0 { c = &timeoutConnWrapper{conn: ctcp, timeout: t.timeout} } else { c = ctcp } return }
func (u *Upstream) FilterRequest(request *falcore.Request) (res *http.Response) { var err error req := request.HttpRequest if u.Name != "" { request.CurrentStage.Name = fmt.Sprintf("%s[%s]", request.CurrentStage.Name, u.Name) } // Throttle // Wait for an opening, then increment in flight counter u.throttleC.L.Lock() u.throttleQueue += 1 for u.throttleMax > 0 && u.throttleInFlight >= u.throttleMax { u.throttleC.Wait() } u.throttleQueue -= 1 u.throttleInFlight += 1 u.throttleC.L.Unlock() // Decrement and signal when done defer func() { u.throttleC.L.Lock() u.throttleInFlight -= 1 u.throttleC.Signal() u.throttleC.L.Unlock() }() // Force the upstream to use http if u.ForceHttp || req.URL.Scheme == "" { req.URL.Scheme = "http" req.URL.Host = req.Host } before := time.Now() req.Header.Set("Connection", "Keep-Alive") var upstrRes *http.Response upstrRes, err = u.Transport.transport.RoundTrip(req) diff := falcore.TimeDiff(before, time.Now()) if err == nil { // Copy response over to new record. Remove connection noise. Add some sanity. res = falcore.StringResponse(req, upstrRes.StatusCode, nil, "") if upstrRes.ContentLength > 0 { res.ContentLength = upstrRes.ContentLength res.Body = upstrRes.Body } else if res.ContentLength == -1 { res.Body = upstrRes.Body res.ContentLength = -1 res.TransferEncoding = []string{"chunked"} } else { // Any bytes? var testBuf [1]byte n, _ := io.ReadFull(upstrRes.Body, testBuf[:]) if n == 1 { // Yes there are. Chunked it is. res.TransferEncoding = []string{"chunked"} res.ContentLength = -1 rc := &passThruReadCloser{ io.MultiReader(bytes.NewBuffer(testBuf[:]), upstrRes.Body), upstrRes.Body, } res.Body = rc } else { // There was an error reading the body upstrRes.Body.Close() res.ContentLength = 0 res.Body = nil } } // Copy over headers with a few exceptions res.Header = make(http.Header) for hn, hv := range upstrRes.Header { switch hn { case "Content-Length": case "Connection": case "Transfer-Encoding": default: res.Header[hn] = hv } } } else { if nerr, ok := err.(net.Error); ok && nerr.Timeout() { falcore.Error("%s [%s] Upstream Timeout error: %v", request.ID, u.Name, err) res = falcore.StringResponse(req, 504, nil, "Gateway Timeout\n") request.CurrentStage.Status = 2 // Fail } else { falcore.Error("%s [%s] Upstream error: %v", request.ID, u.Name, err) res = falcore.StringResponse(req, 502, nil, "Bad Gateway\n") request.CurrentStage.Status = 2 // Fail } } falcore.Debug("%s %s [%s] [%s] %s s=%d Time=%.4f", request.ID, u.Name, req.Method, u.Transport.host, req.URL, res.StatusCode, diff) return }
func (c *CompressionFilter) FilterResponse(request *falcore.Request, res *http.Response) { req := request.HttpRequest if accept := req.Header.Get("Accept-Encoding"); accept != "" { // Is content an acceptable type for encoding? var compress = false var content_type = res.Header.Get("Content-Type") for _, t := range c.types { if content_type == t { compress = true break } } // Is the content already compressed if res.Header.Get("Content-Encoding") != "" { compress = false } if !compress { request.CurrentStage.Status = 1 // Skip return } // Figure out which encoding to use options := strings.Split(accept, ",") var mode string for _, opt := range options { if m := strings.TrimSpace(opt); m == "gzip" || m == "deflate" { mode = m break } } var compressor io.WriteCloser pReader, pWriter := io.Pipe() switch mode { case "gzip": compressor = gzip.NewWriter(pWriter) case "deflate": comp, err := flate.NewWriter(pWriter, -1) if err != nil { falcore.Error("Compression Error: %v", err) request.CurrentStage.Status = 1 // Skip return } compressor = comp default: request.CurrentStage.Status = 1 // Skip return } // Perform compression var rdr = res.Body go func() { _, err := io.Copy(compressor, rdr) compressor.Close() pWriter.Close() rdr.Close() if err != nil { falcore.Error("Error compressing body: %v", err) } }() res.ContentLength = -1 res.Body = pReader res.Header.Set("Content-Encoding", mode) } else { request.CurrentStage.Status = 1 // Skip } }