Esempio n. 1
0
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
}
Esempio n. 2
0
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
}
Esempio n. 3
0
// 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
}
Esempio n. 4
0
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
}
Esempio n. 5
0
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
}
Esempio n. 6
0
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
}
Esempio n. 7
0
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
	}
}