Пример #1
0
// This function alters the original request - adds/removes headers, removes hop headers,
// changes the request path.
func rewriteRequest(req *http.Request, cmd *command.Forward, upstream *command.Upstream) *http.Request {
	outReq := new(http.Request)
	*outReq = *req // includes shallow copies of maps, but we handle this below

	outReq.URL.Scheme = upstream.Scheme
	outReq.URL.Host = fmt.Sprintf("%s:%d", upstream.Host, upstream.Port)
	if len(cmd.RewritePath) != 0 {
		outReq.URL.Path = cmd.RewritePath
	}

	outReq.URL.RawQuery = req.URL.RawQuery

	outReq.Proto = "HTTP/1.1"
	outReq.ProtoMajor = 1
	outReq.ProtoMinor = 1
	outReq.Close = false

	glog.Infof("Proxying request to: %v", outReq)

	outReq.Header = make(http.Header)
	netutils.CopyHeaders(outReq.Header, req.Header)

	if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
		// TODO(pquerna): configure this?  Not all backends properly parse the header..
		if TRUST_FORWARD_HEADER {
			if prior, ok := outReq.Header["X-Forwarded-For"]; ok {
				clientIP = strings.Join(prior, ", ") + ", " + clientIP
			}
		}
		outReq.Header.Set("X-Forwarded-For", clientIP)
	}

	if req.TLS != nil {
		outReq.Header.Set("X-Forwarded-Proto", "https")
	} else {
		outReq.Header.Set("X-Forwarded-Proto", "http")
	}

	if req.Host != "" {
		outReq.Header.Set("X-Forwarded-Host", req.Host)
	}

	outReq.Header.Set("X-Forwarded-Server", vulcanHostname)

	if len(cmd.RemoveHeaders) != 0 {
		netutils.RemoveHeaders(cmd.RemoveHeaders, outReq.Header)
	}

	// Add generic instructions headers to the request
	if len(cmd.AddHeaders) != 0 {
		glog.Info("Proxying instructions headers:", cmd.AddHeaders)
		netutils.CopyHeaders(outReq.Header, cmd.AddHeaders)
	}

	// Remove hop-by-hop headers to the backend.  Especially
	// important is "Connection" because we want a persistent
	// connection, regardless of what the client sent to us.
	netutils.RemoveHeaders(hopHeaders, outReq.Header)
	return outReq
}
Пример #2
0
func MakeRequest(url string, opts Opts) (*http.Response, []byte, error) {
	method := "GET"
	if opts.Method != "" {
		opts.Method = opts.Method
	}
	request, _ := http.NewRequest(method, url, strings.NewReader(opts.Body))
	if opts.Headers != nil {
		netutils.CopyHeaders(request.Header, opts.Headers)
	}

	if len(opts.Host) != 0 {
		request.Host = opts.Host
	}

	var tr *http.Transport
	if strings.HasPrefix(url, "https") {
		tr = &http.Transport{
			DisableKeepAlives: true,
			TLSClientConfig:   &tls.Config{InsecureSkipVerify: true},
		}
	} else {
		tr = &http.Transport{
			DisableKeepAlives: true,
		}
	}

	client := &http.Client{Transport: tr}
	response, err := client.Do(request)
	if err == nil {
		bodyBytes, err := ioutil.ReadAll(response.Body)
		return response, bodyBytes, err
	}
	return response, nil, err
}
Пример #3
0
// Round trips the request to the selected location and writes back the response
func (p *Proxy) proxyRequest(w http.ResponseWriter, r *http.Request) error {

	// Create a unique request with sequential ids that will be passed to all interfaces.
	req := request.NewBaseRequest(r, atomic.AddInt64(&p.lastRequestId, 1), nil)
	location, err := p.router.Route(req)
	if err != nil {
		return err
	}

	// Router could not find a matching location, we can do nothing else.
	if location == nil {
		log.Errorf("%s failed to route", req)
		return errors.FromStatus(http.StatusBadGateway)
	}

	response, err := location.RoundTrip(req)
	if response != nil {
		netutils.CopyHeaders(w.Header(), response.Header)
		w.WriteHeader(response.StatusCode)
		io.Copy(w, response.Body)
		response.Body.Close()
		return nil
	} else {
		return err
	}
}
Пример #4
0
func (l *HttpLocation) copyRequest(req *http.Request, body netutils.MultiReader, endpoint endpoint.Endpoint) *http.Request {
	outReq := new(http.Request)
	*outReq = *req // includes shallow copies of maps, but we handle this below

	// Set the body to the enhanced body that can be re-read multiple times and buffered to disk
	outReq.Body = body

	endpointURL := endpoint.GetUrl()
	outReq.URL.Scheme = endpointURL.Scheme
	outReq.URL.Host = endpointURL.Host
	outReq.URL.Opaque = req.RequestURI
	// raw query is already included in RequestURI, so ignore it to avoid dupes
	outReq.URL.RawQuery = ""

	outReq.Proto = "HTTP/1.1"
	outReq.ProtoMajor = 1
	outReq.ProtoMinor = 1

	// Overwrite close flag so we can keep persistent connection for the backend servers
	outReq.Close = false

	outReq.Header = make(http.Header)
	netutils.CopyHeaders(outReq.Header, req.Header)
	return outReq
}
Пример #5
0
func (w *WebhookSideEffect) Exec() error {
	r, err := http.NewRequest(w.w.Method, w.w.URL, w.getBody())
	if err != nil {
		return err
	}
	if len(w.w.Headers) != 0 {
		netutils.CopyHeaders(r.Header, w.w.Headers)
	}
	if len(w.w.Form) != 0 {
		r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	}
	re, err := http.DefaultClient.Do(r)
	if err != nil {
		return err
	}
	if re.Body != nil {
		defer re.Body.Close()
	}
	body, err := ioutil.ReadAll(re.Body)
	if err != nil {
		return err
	}
	log.Infof("%v got response: (%s): %s", w, re.Status, string(body))
	return nil
}
Пример #6
0
func (tb *Recorder) ObserveRequest(req Request) {
	tb.mutex.Lock()
	defer tb.mutex.Unlock()
	if len(tb.Header) != 0 {
		netutils.CopyHeaders(req.GetHttpRequest().Header, tb.Header)
	}
	tb.ProcessedRequests = append(tb.ProcessedRequests, req)
}
Пример #7
0
func (tb *Recorder) ProcessRequest(req Request) (*http.Response, error) {
	tb.mutex.Lock()
	defer tb.mutex.Unlock()

	if len(tb.Header) != 0 {
		netutils.CopyHeaders(req.GetHttpRequest().Header, tb.Header)
	}
	tb.ProcessedRequests = append(tb.ProcessedRequests, req)
	return tb.Response, tb.Error
}
Пример #8
0
// replyError is a helper function that takes error and replies with HTTP compatible error to the client.
func (p *Proxy) replyError(err error, w http.ResponseWriter, req *http.Request) {
	proxyError := convertError(err)
	statusCode, body, contentType := p.options.ErrorFormatter.Format(proxyError)
	w.Header().Set("Content-Type", contentType)
	if proxyError.Headers() != nil {
		netutils.CopyHeaders(w.Header(), proxyError.Headers())
	}
	w.WriteHeader(statusCode)
	w.Write(body)
}
Пример #9
0
func (tb *Recorder) ObserveResponse(req Request, a Attempt) {
	tb.mutex.Lock()
	defer tb.mutex.Unlock()
	if len(tb.Header) != 0 {
		netutils.CopyHeaders(req.GetHttpRequest().Header, tb.Header)
	}
	tb.ProcessedResponses = append(tb.ProcessedResponses, struct {
		R Request
		A Attempt
	}{R: req, A: a})
}
Пример #10
0
func Post(c *gocheck.C, requestUrl string, header http.Header, body url.Values) (*http.Response, []byte) {
	request, _ := http.NewRequest("POST", requestUrl, strings.NewReader(body.Encode()))
	netutils.CopyHeaders(request.Header, header)
	request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	request.Close = true
	response, err := http.DefaultClient.Do(request)
	if err != nil {
		c.Fatalf("Post: %v", err)
	}

	bodyBytes, err := ioutil.ReadAll(response.Body)
	if err != nil {
		c.Fatalf("Post body failed: %v", err)
	}
	return response, bodyBytes
}
Пример #11
0
func (l *HttpLocation) copyRequest(req *http.Request, endpoint Endpoint) *http.Request {
	outReq := new(http.Request)
	*outReq = *req // includes shallow copies of maps, but we handle this below

	outReq.URL.Scheme = endpoint.GetUrl().Scheme
	outReq.URL.Host = endpoint.GetUrl().Host
	outReq.URL.RawQuery = req.URL.RawQuery

	outReq.Proto = "HTTP/1.1"
	outReq.ProtoMajor = 1
	outReq.ProtoMinor = 1

	// Overwrite close flag so we can keep persistent connection for the backend servers
	outReq.Close = false

	outReq.Header = make(http.Header)
	netutils.CopyHeaders(outReq.Header, req.Header)
	return outReq
}
Пример #12
0
func Get(c *gocheck.C, requestUrl string, header http.Header, body string) (*http.Response, []byte) {
	request, _ := http.NewRequest("GET", requestUrl, strings.NewReader(body))
	netutils.CopyHeaders(request.Header, header)
	request.Close = true
	// the HTTP lib treats Host as a special header.  it only respects the value on req.Host, and ignores
	// values in req.Headers
	if header.Get("Host") != "" {
		request.Host = header.Get("Host")
	}
	response, err := http.DefaultClient.Do(request)
	if err != nil {
		c.Fatalf("Get: %v", err)
	}

	bodyBytes, err := ioutil.ReadAll(response.Body)
	if err != nil {
		c.Fatalf("Get body failed: %v", err)
	}
	return response, bodyBytes
}
Пример #13
0
// Round trips the request to the selected location and writes back the response
func (p *Proxy) proxyRequest(w http.ResponseWriter, req *request.BaseRequest) error {
	location, err := p.router.Route(req)
	if err != nil {
		return err
	}
	// Router could not find a matching location, we can do nothing more
	if location == nil {
		log.Errorf("%s failed to route", req)
		return errors.FromStatus(http.StatusBadGateway)
	}
	response, err := location.RoundTrip(req)
	if response != nil {
		netutils.CopyHeaders(w.Header(), response.Header)
		w.WriteHeader(response.StatusCode)
		io.Copy(w, response.Body)
		defer response.Body.Close()
		return nil
	} else {
		return err
	}
}
Пример #14
0
// This function alters the original request - adds/removes headers, removes hop headers, changes the request path.
func (l *HttpLocation) rewriteRequest(req *http.Request, endpoint Endpoint) *http.Request {
	outReq := new(http.Request)
	*outReq = *req // includes shallow copies of maps, but we handle this below

	outReq.URL.Scheme = endpoint.GetUrl().Scheme
	outReq.URL.Host = endpoint.GetUrl().Host
	outReq.URL.RawQuery = req.URL.RawQuery

	outReq.Proto = "HTTP/1.1"
	outReq.ProtoMajor = 1
	outReq.ProtoMinor = 1
	outReq.Close = false

	outReq.Header = make(http.Header)
	netutils.CopyHeaders(outReq.Header, req.Header)

	if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
		if l.options.TrustForwardHeader {
			if prior, ok := outReq.Header[headers.XForwardedFor]; ok {
				clientIP = strings.Join(prior, ", ") + ", " + clientIP
			}
		}
		outReq.Header.Set(headers.XForwardedFor, clientIP)
	}
	if req.TLS != nil {
		outReq.Header.Set(headers.XForwardedProto, "https")
	} else {
		outReq.Header.Set(headers.XForwardedProto, "http")
	}
	if req.Host != "" {
		outReq.Header.Set(headers.XForwardedHost, req.Host)
	}
	outReq.Header.Set(headers.XForwardedServer, l.options.Hostname)

	// Remove hop-by-hop headers to the backend.  Especially important is "Connection" because we want a persistent
	// connection, regardless of what the client sent to us.
	netutils.RemoveHeaders(headers.HopHeaders, outReq.Header)
	return outReq
}
Пример #15
0
// Proxy the request to the given upstream, in case if upstream is down
// or failover code sequence has been recorded as the reply, return the error.
// Failover sequence - is a special response code from the upstream that indicates
// that upstream is shutting down and is not willing to accept new requests.
func (p *ReverseProxy) proxyToUpstream(
	w http.ResponseWriter,
	req *http.Request,
	cmd *command.Forward,
	upstream *command.Upstream) error {

	// Rewrites the request: adds headers, changes urls etc.
	outReq := rewriteRequest(req, cmd, upstream)

	// Forward the reuest and mirror the response
	upstream.Metrics.Requests.Mark(1)
	startts := time.Now()
	res, err := p.httpTransport.RoundTrip(outReq)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	upstream.Metrics.Latency.Update(time.Since(startts))

	// In some cases upstreams may return special error codes that indicate that instead
	// of proxying the response of the upstream to the client we should initiate a failover
	if cmd.Failover != nil && len(cmd.Failover.Codes) != 0 {
		for _, code := range cmd.Failover.Codes {
			if res.StatusCode == code {
				upstream.Metrics.Failovers.Mark(1)
				glog.Errorf("Upstream %s initiated failover with status code %d", upstream, code)
				return fmt.Errorf("Upstream %s initiated failover with status code %d", upstream, code)
			}
		}
	}

	netutils.CopyHeaders(w.Header(), res.Header)
	w.WriteHeader(res.StatusCode)
	upstream.Metrics.Http.MarkResponseCode(res.StatusCode)
	io.Copy(w, res.Body)
	return nil
}