예제 #1
0
func (session *Session) SendForTraversal(url string, data string) (string, error) {
	var (
		resp *http.Response // http response
		buf  bytes.Buffer   // contains http response body
		err  error
	)
	resp, err = session.request(url, data)
	if err != nil {
		return "", err
	}
	log.Println(resp)
	defer func() {
		if resp.Body != nil {
			resp.Body.Close()
		}
	}()
	_, err = buf.ReadFrom(resp.Body)
	if err != nil {
		return "", err
	}
	session.StatusCode = resp.StatusCode
	location, err := resp.Location()
	if err != nil {
		return "", err
	}
	session.Location = location.String()
	return buf.String(), nil
}
예제 #2
0
// save saves the body of a response corresponding to a request.
func (c *CachedRoundTrip) save(req *http.Request, resp *http.Response) error {
	if resp.StatusCode == http.StatusMovedPermanently || resp.StatusCode == http.StatusTemporaryRedirect {
		u, err := resp.Location()
		if err != nil {
			return err
		}

		err = c.Cache.Put(req.URL, c.newEntry([]byte("REDIRECT:"+u.String()), resp))
		if err != nil {
			return err
		}
		return nil
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	resp.Body.Close()
	err = c.Cache.Put(req.URL, c.newEntry(body, resp))
	if err != nil {
		return err
	}
	resp.Body = ioutil.NopCloser(bytes.NewReader(body))
	return nil
}
예제 #3
0
파일: requests.go 프로젝트: kula/etcdctl
// handleResp handles the responses from the etcd server
// If status code is OK, read the http body and return it as byte array
// If status code is TemporaryRedirect, update leader.
// If status code is InternalServerError, sleep for 200ms.
func (c *Client) handleResp(resp *http.Response) (bool, []byte) {
	defer resp.Body.Close()

	code := resp.StatusCode

	if code == http.StatusTemporaryRedirect {
		u, err := resp.Location()

		if err != nil {
			logger.Warning(err)
		} else {
			c.cluster.updateLeaderFromURL(u)
		}

		return false, nil

	} else if code == http.StatusInternalServerError {
		time.Sleep(time.Millisecond * 200)

	} else if validHttpStatusCode[code] {
		b, err := ioutil.ReadAll(resp.Body)

		if err != nil {
			return false, nil
		}

		return true, b
	}

	logger.Warning("bad status code ", resp.StatusCode)
	return false, nil
}
예제 #4
0
// assertions
func (s *FormsAuthSuite) assertRedirectToLogin(res *http.Response, c *check.C) {
	c.Assert(res.StatusCode, check.Equals, http.StatusSeeOther)
	location, err := res.Location()
	c.Assert(err, check.IsNil)
	expectedURL := fmt.Sprintf("%s?returnURL=%s", s.loginURL, url.QueryEscape(s.protectedURL))
	c.Assert(location.RequestURI(), check.Equals, expectedURL)
}
예제 #5
0
// UpgradeToken returns the assertion to upgrade the token on IAM to get the
// 'purchased' scopes
func (a *AssetsService) UpgradeToken() error {
	var (
		req      *http.Request
		res      *http.Response
		location *url.URL
		err      error
	)

	req, err = a.client.NewRequest("GET", "assets", "/v1.0/asset/access", nil)
	if err != nil {
		return err
	}

	req.Header.Add("No-Redirect", "true")
	res, err = a.client.httpClient.Do(req)
	if err != nil {
		return err
	}

	location, err = res.Location()
	if err != nil {
		return err
	}

	req, err = a.client.NewRequest("GET", "iam", fmt.Sprintf("/v1.0/oauth/token/upgrade?%s", location.RawQuery), nil)
	if err != nil {
		return err
	}

	_, err = returnErrorHTTPSimple(a.client, req, err, 204)
	return err
}
예제 #6
0
파일: ssdp.go 프로젝트: hlandau/portmap
func (c *client) handleResponse(res *http.Response) {
	if res.StatusCode != 200 {
		return
	}

	st := res.Header.Get("ST")
	if st == "" {
		return
	}

	loc, err := res.Location()
	if err != nil {
		return
	}

	usn := res.Header.Get("USN")
	if usn == "" {
		usn = loc.String()
	}

	ev := Event{
		Location: loc,
		ST:       st,
		USN:      usn,
	}

	select {
	// events not being waited for are simply dropped
	case c.eventChan <- ev:
	default:
	}
}
예제 #7
0
파일: types.go 프로젝트: tommie/acme-go
func newAuthorization(authz *protocol.Authorization, resp *http.Response) (*Authorization, error) {
	st := authz.Status
	if st == "" {
		// Missing status value means "pending". ACME spec Sec. 5.3.
		st = protocol.StatusPending
	}

	id, err := newIdentifier(authz.Identifier)
	if err != nil {
		return nil, err
	}

	uri, err := resp.Location()
	if err == http.ErrNoLocation {
		// Fall back to request URI.
		// TODO: Check that the request wasn't for a new authorization.
		uri = resp.Request.URL
	} else if err != nil {
		return nil, err
	}

	ra, _ := retryAfter(resp.Header, 0)

	return &Authorization{
		Authorization: *authz,
		Status:        st,
		Identifier:    id,
		URI:           uri.String(),
		RetryAfter:    ra,
	}, nil
}
예제 #8
0
// followRedirects is a custom HTTP redirects handler which appends
// cookie header to the request.
//
// r - The original response.
//
// Returns response from the new location.
func followRedirects(r *http.Response) (*http.Response, error) {
	if location, err := r.Location(); err == nil && location != nil {
		c := &http.Client{}
		req, _ := http.NewRequest("GET", location.String(), nil)
		req.Header.Set("X-WebRocket-Cookie", Cookie)
		return c.Do(req)
	}
	return r, nil
}
예제 #9
0
// ChangeToHttp On redirect change the response location from https to http.
// It makes the client come back to us over http.
func ChangeToHttp(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
	if IsResponseRedirected(resp) {
		location, _ := resp.Location()
		if location != nil {
			if location.Scheme == "https" {
				location.Scheme = "http"
				resp.Header.Set("Location", location.String())
				println("ChangeToHttp response handler: ", ctx.Req.Host, "->", resp.Header.Get("Location"))
			}
		}
	}
	return resp
}
예제 #10
0
파일: http.go 프로젝트: dhilton/hub
func (t *verboseTransport) dumpResponse(resp *http.Response) {
	info := fmt.Sprintf("< HTTP %d", resp.StatusCode)
	location, err := resp.Location()
	if err == nil {
		info = fmt.Sprintf("%s\n< Location: %s", info, location.String())
	}
	t.verbosePrintln(info)
	t.dumpHeaders(resp.Header, "<")
	body := t.dumpBody(resp.Body)
	if body != nil {
		// reset body since it's been read
		resp.Body = body
	}
}
예제 #11
0
func (request *Request) pollForAsynchronousResponse(acceptedResponse *http.Response) (*http.Response, error) {
	var resp *http.Response = acceptedResponse

	for {
		if resp.StatusCode != http.StatusAccepted {
			return resp, nil
		}

		if retryAfter := resp.Header.Get("Retry-After"); retryAfter != "" {
			retryTime, err := strconv.Atoi(strings.TrimSpace(retryAfter))
			if err != nil {
				return nil, err
			}

			request.client.logger.Printf("[INFO] Polling pausing for %d seconds as per Retry-After header", retryTime)
			time.Sleep(time.Duration(retryTime) * time.Second)
		}

		pollLocation, err := resp.Location()
		if err != nil {
			return nil, err
		}

		request.client.logger.Printf("[INFO] Polling %q for operation completion", pollLocation.String())
		req, err := retryablehttp.NewRequest("GET", pollLocation.String(), bytes.NewReader([]byte{}))
		if err != nil {
			return nil, err
		}

		err = request.client.tokenRequester.addAuthorizationToRequest(req)
		if err != nil {
			return nil, err
		}

		resp, err := request.client.httpClient.Do(req)
		if err != nil {
			return nil, err
		}
		if resp.StatusCode == http.StatusAccepted {
			continue
		}

		return resp, err
	}
}
예제 #12
0
// returnErrorByHTTPStatusCode returns the http error code or nil if it returns the
// desired error
func returnErrorByHTTPStatusCode(res *http.Response, desiredStatusCode int) (string, error) {
	var (
		location       *url.URL
		locationString string
	)
	location, _ = res.Location()
	if location == nil {
		locationString = ""
	} else {
		locationString = location.String()
	}

	if res.StatusCode == desiredStatusCode {
		return locationString, nil
	}
	if http.StatusText(res.StatusCode) == "" {
		return "", fmt.Errorf("HTTP Error %d", res.StatusCode)
	}
	return locationString, fmt.Errorf("%d %s", res.StatusCode, http.StatusText(res.StatusCode))
}
// DowloadReleaseAsset downloads a release asset.
//
// DowloadReleaseAsset returns an io.ReadCloser that reads the contents of the
// specified release asset. It is the caller's responsibility to close the ReadCloser.
//
// GitHub API docs : http://developer.github.com/v3/repos/releases/#get-a-single-release-asset
func (s *RepositoriesService) DownloadReleaseAsset(owner, repo string, id int) (io.ReadCloser, error) {
	u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id)

	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Accept", defaultMediaType)

	var resp *http.Response
	if s.client.client.Transport == nil {
		resp, err = http.DefaultTransport.RoundTrip(req)
	} else {
		resp, err = s.client.client.Transport.RoundTrip(req)
	}
	if err != nil {
		return nil, err
	}

	// GitHub API streamed the asset directly
	if resp.StatusCode == http.StatusOK {
		return resp.Body, nil
	}

	if resp.StatusCode != http.StatusFound {
		return nil, fmt.Errorf("Expected status code 200 or 302, got %d", resp.StatusCode)
	}

	// GitHub API redirected to pre-signed S3 URL
	downloadURL, err := resp.Location()
	if err != nil {
		return nil, err
	}

	resp, err = http.Get(downloadURL.String())
	if err != nil {
		return nil, err
	}

	return resp.Body, nil
}
예제 #14
0
파일: client.go 프로젝트: yepengxj/heketi
// Wait for the job to finish, waiting waitTime on every loop
func (c *Client) waitForResponseWithTimer(r *http.Response,
	waitTime time.Duration) (*http.Response, error) {

	// Get temp resource
	location, err := r.Location()
	if err != nil {
		return nil, err
	}

	for {
		// Create request
		req, err := http.NewRequest("GET", location.String(), nil)
		if err != nil {
			return nil, err
		}

		// Set token
		err = c.setToken(req)
		if err != nil {
			return nil, err
		}

		// Wait for response
		r, err = c.do(req)
		if err != nil {
			return nil, err
		}

		// Check if the request is pending
		if r.Header.Get("X-Pending") == "true" {
			if r.StatusCode != http.StatusOK {
				return nil, utils.GetErrorFromResponse(r)
			}
			time.Sleep(waitTime)
		} else {
			return r, nil
		}
	}

}
예제 #15
0
func (ar *actionResolver) next(resp *http.Response) (*http.Request, error) {
	if resp.StatusCode != http.StatusTemporaryRedirect {
		return nil, nil
	}

	ar.redirectCount += 1
	if ar.redirectCount >= redirectMax {
		return nil, errors.New("too many redirects")
	}

	loc, err := resp.Location()
	if err != nil {
		return nil, err
	}

	req, err := ar.action.HTTPRequest()
	if err != nil {
		return nil, err
	}

	req.URL = loc
	return req, nil
}
예제 #16
0
func TestLogin(t *testing.T) {
	testCases := map[string]struct {
		CSRF       csrf.CSRF
		Auth       *testAuth
		Path       string
		PostValues url.Values

		ExpectStatusCode int
		ExpectRedirect   string
		ExpectContains   []string
		ExpectThen       string
	}{
		"display form": {
			CSRF: &csrf.FakeCSRF{Token: "test"},
			Auth: &testAuth{},
			Path: "/login",

			ExpectStatusCode: 200,
			ExpectContains: []string{
				`action="/login"`,
				`name="csrf" value="test"`,
			},
		},
		"display form with errors": {
			CSRF: &csrf.FakeCSRF{Token: "test"},
			Auth: &testAuth{},
			Path: "?then=foo&reason=failed&username=user",

			ExpectStatusCode: 200,
			ExpectContains: []string{
				`action="/"`,
				`name="then" value="foo"`,
				`An unknown error has occurred`,
				`danger`,
			},
		},
		"redirect when POST fails CSRF": {
			CSRF:           &csrf.FakeCSRF{Token: "test"},
			Auth:           &testAuth{},
			Path:           "/login",
			PostValues:     url.Values{"csrf": []string{"wrong"}},
			ExpectRedirect: "/login?reason=token+expired",
		},
		"redirect with 'then' when POST fails CSRF": {
			CSRF:           &csrf.FakeCSRF{Token: "test"},
			Auth:           &testAuth{},
			Path:           "/login?then=test",
			PostValues:     url.Values{"csrf": []string{"wrong"}},
			ExpectRedirect: "/login?reason=token+expired&then=test",
		},
		"redirect when no username": {
			CSRF: &csrf.FakeCSRF{Token: "test"},
			Auth: &testAuth{},
			Path: "/login",
			PostValues: url.Values{
				"csrf": []string{"test"},
			},
			ExpectRedirect: "/login?reason=user+required",
		},
		"redirect when not authenticated": {
			CSRF: &csrf.FakeCSRF{Token: "test"},
			Auth: &testAuth{Success: false},
			Path: "/login",
			PostValues: url.Values{
				"csrf":     []string{"test"},
				"username": []string{"user"},
			},
			ExpectRedirect: "/login?reason=access+denied",
		},
		"redirect on auth error": {
			CSRF: &csrf.FakeCSRF{Token: "test"},
			Auth: &testAuth{Err: errors.New("failed")},
			Path: "/login",
			PostValues: url.Values{
				"csrf":     []string{"test"},
				"username": []string{"user"},
			},
			ExpectRedirect: "/login?reason=unknown+error",
		},
		"redirect preserving then param": {
			CSRF: &csrf.FakeCSRF{Token: "test"},
			Auth: &testAuth{Err: errors.New("failed")},
			Path: "/login",
			PostValues: url.Values{
				"csrf":     []string{"test"},
				"username": []string{"user"},
				"then":     []string{"anotherurl"},
			},
			ExpectRedirect: "/login?reason=unknown+error&then=anotherurl",
		},
		"login successful": {
			CSRF: &csrf.FakeCSRF{Token: "test"},
			Auth: &testAuth{Success: true, User: &user.DefaultInfo{Name: "user"}},
			Path: "/login?then=done",
			PostValues: url.Values{
				"csrf":     []string{"test"},
				"username": []string{"user"},
			},
			ExpectThen: "done",
		},
	}

	for k, testCase := range testCases {
		server := httptest.NewServer(NewLogin(testCase.CSRF, testCase.Auth, DefaultLoginFormRenderer))

		var resp *http.Response
		if testCase.PostValues != nil {
			r, err := postForm(server.URL+testCase.Path, testCase.PostValues)
			if err != nil {
				t.Errorf("%s: unexpected error: %v", k, err)
				continue
			}
			resp = r
		} else {
			r, err := getURL(server.URL + testCase.Path)
			if err != nil {
				t.Errorf("%s: unexpected error: %v", k, err)
				continue
			}
			resp = r
		}
		defer resp.Body.Close()

		if testCase.ExpectStatusCode != 0 && testCase.ExpectStatusCode != resp.StatusCode {
			t.Errorf("%s: unexpected response: %#v", k, resp)
			continue
		}

		if testCase.ExpectRedirect != "" {
			uri, err := resp.Location()
			if err != nil {
				t.Errorf("%s: unexpected error: %v", k, err)
				continue
			}
			if uri.String() != server.URL+testCase.ExpectRedirect {
				t.Errorf("%s: unexpected redirect: %s", k, uri.String())
			}
		}

		if testCase.ExpectThen != "" && (!testCase.Auth.Called || testCase.Auth.Then != testCase.ExpectThen) {
			t.Errorf("%s: did not find expected 'then' value: %#v", k, testCase.Auth)
		}

		if len(testCase.ExpectContains) > 0 {
			data, _ := ioutil.ReadAll(resp.Body)
			body := string(data)
			for i := range testCase.ExpectContains {
				if !strings.Contains(body, testCase.ExpectContains[i]) {
					t.Errorf("%s: did not find expected value %s: %s", k, testCase.ExpectContains[i], body)
					continue
				}
			}
		}
	}
}
예제 #17
0
// SendRequest sends a HTTP request and returns a Response as defined by etcd
func (c *Client) SendRequest(rr *RawRequest) (*RawResponse, error) {
	var req *http.Request
	var resp *http.Response
	var httpPath string
	var err error
	var respBody []byte

	var numReqs = 1

	checkRetry := c.CheckRetry
	if checkRetry == nil {
		checkRetry = DefaultCheckRetry
	}

	cancelled := make(chan bool, 1)
	reqLock := new(sync.Mutex)

	if rr.Cancel != nil {
		cancelRoutine := make(chan bool)
		defer close(cancelRoutine)

		go func() {
			select {
			case <-rr.Cancel:
				cancelled <- true
				logger.Debug("send.request is cancelled")
			case <-cancelRoutine:
				return
			}

			// Repeat canceling request until this thread is stopped
			// because we have no idea about whether it succeeds.
			for {
				reqLock.Lock()
				c.httpClient.Transport.(*http.Transport).CancelRequest(req)
				reqLock.Unlock()

				select {
				case <-time.After(100 * time.Millisecond):
				case <-cancelRoutine:
					return
				}
			}
		}()
	}

	// If we connect to a follower and consistency is required, retry until
	// we connect to a leader
	sleep := 25 * time.Millisecond
	maxSleep := time.Second

	for attempt := 0; ; attempt++ {
		if attempt > 0 {
			select {
			case <-cancelled:
				return nil, ErrRequestCancelled
			case <-time.After(sleep):
				sleep = sleep * 2
				if sleep > maxSleep {
					sleep = maxSleep
				}
			}
		}

		logger.Debug("Connecting to etcd: attempt ", attempt+1, " for ", rr.RelativePath)

		// get httpPath if not set
		if httpPath == "" {
			httpPath = c.getHttpPath(rr.RelativePath)
		}

		// Return a cURL command if curlChan is set
		if c.cURLch != nil {
			command := fmt.Sprintf("curl -X %s %s", rr.Method, httpPath)
			for key, value := range rr.Values {
				command += fmt.Sprintf(" -d %s=%s", key, value[0])
			}
			if c.credentials != nil {
				command += fmt.Sprintf(" -u %s", c.credentials.username)
			}
			c.sendCURL(command)
		}

		logger.Debug("send.request.to ", httpPath, " | method ", rr.Method)

		req, err := func() (*http.Request, error) {
			reqLock.Lock()
			defer reqLock.Unlock()

			if rr.Values == nil {
				if req, err = http.NewRequest(rr.Method, httpPath, nil); err != nil {
					return nil, err
				}
			} else {
				body := strings.NewReader(rr.Values.Encode())
				if req, err = http.NewRequest(rr.Method, httpPath, body); err != nil {
					return nil, err
				}

				req.Header.Set("Content-Type",
					"application/x-www-form-urlencoded; param=value")
			}
			return req, nil
		}()

		if err != nil {
			return nil, err
		}

		if c.credentials != nil {
			req.SetBasicAuth(c.credentials.username, c.credentials.password)
		}

		resp, err = c.httpClient.Do(req)
		// clear previous httpPath
		httpPath = ""
		defer func() {
			if resp != nil {
				resp.Body.Close()
			}
		}()

		// If the request was cancelled, return ErrRequestCancelled directly
		select {
		case <-cancelled:
			return nil, ErrRequestCancelled
		default:
		}

		numReqs++

		// network error, change a machine!
		if err != nil {
			logger.Debug("network error: ", err.Error())
			lastResp := http.Response{}
			if checkErr := checkRetry(c.cluster, numReqs, lastResp, err); checkErr != nil {
				return nil, checkErr
			}

			c.cluster.failure()
			continue
		}

		// if there is no error, it should receive response
		logger.Debug("recv.response.from ", httpPath)

		if validHttpStatusCode[resp.StatusCode] {
			// try to read byte code and break the loop
			respBody, err = ioutil.ReadAll(resp.Body)
			if err == nil {
				logger.Debug("recv.success ", httpPath)
				break
			}
			// ReadAll error may be caused due to cancel request
			select {
			case <-cancelled:
				return nil, ErrRequestCancelled
			default:
			}

			if err == io.ErrUnexpectedEOF {
				// underlying connection was closed prematurely, probably by timeout
				// TODO: empty body or unexpectedEOF can cause http.Transport to get hosed;
				// this allows the client to detect that and take evasive action. Need
				// to revisit once code.google.com/p/go/issues/detail?id=8648 gets fixed.
				respBody = []byte{}
				break
			}
		}

		if resp.StatusCode == http.StatusTemporaryRedirect {
			u, err := resp.Location()

			if err != nil {
				logger.Warning(err)
			} else {
				// set httpPath for following redirection
				httpPath = u.String()
			}
			resp.Body.Close()
			continue
		}

		if checkErr := checkRetry(c.cluster, numReqs, *resp,
			errors.New("Unexpected HTTP status code")); checkErr != nil {
			return nil, checkErr
		}
		resp.Body.Close()
	}

	r := &RawResponse{
		StatusCode: resp.StatusCode,
		Body:       respBody,
		Header:     resp.Header,
	}

	return r, nil
}
예제 #18
0
func TestImplicit(t *testing.T) {
	testCases := map[string]struct {
		CSRF       csrf.CSRF
		Implicit   *testImplicit
		Path       string
		PostValues url.Values

		ExpectStatusCode int
		ExpectRedirect   string
		ExpectContains   []string
		ExpectThen       string
	}{
		"display confirm form": {
			CSRF:     &csrf.FakeCSRF{Token: "test", Err: nil},
			Implicit: &testImplicit{Success: true, User: &user.DefaultInfo{Name: "user"}},
			Path:     "/login",
			ExpectContains: []string{
				`action="/login"`,
				`You are now logged in as`,
			},
		},
		"successful POST redirects": {
			CSRF:       &csrf.FakeCSRF{Token: "test", Err: nil},
			Implicit:   &testImplicit{Success: true, User: &user.DefaultInfo{Name: "user"}},
			Path:       "/login?then=%2Ffoo",
			PostValues: url.Values{"csrf": []string{"test"}},
			ExpectThen: "/foo",
		},
		"redirect when POST fails CSRF": {
			CSRF:           &csrf.FakeCSRF{Token: "test", Err: nil},
			Implicit:       &testImplicit{Success: true, User: &user.DefaultInfo{Name: "user"}},
			Path:           "/login",
			PostValues:     url.Values{"csrf": []string{"wrong"}},
			ExpectRedirect: "/login?reason=token+expired",
		},
		"redirect when not authenticated": {
			CSRF:           &csrf.FakeCSRF{Token: "test", Err: nil},
			Implicit:       &testImplicit{Success: false},
			Path:           "/login",
			PostValues:     url.Values{"csrf": []string{"test"}},
			ExpectRedirect: "/login?reason=access+denied",
		},
		"redirect on auth failure": {
			CSRF:           &csrf.FakeCSRF{Token: "test", Err: nil},
			Implicit:       &testImplicit{Err: errors.New("failed")},
			Path:           "/login",
			PostValues:     url.Values{"csrf": []string{"test"}},
			ExpectRedirect: "/login?reason=access+denied",
		},
		"expect GET error": {
			CSRF:           &csrf.FakeCSRF{Token: "test", Err: nil},
			Implicit:       &testImplicit{Err: errors.New("failed")},
			ExpectContains: []string{`"message">An unknown error has occurred. Contact your administrator`},
		},
	}

	for k, testCase := range testCases {
		server := httptest.NewServer(NewConfirm(testCase.CSRF, testCase.Implicit, DefaultConfirmFormRenderer))

		var resp *http.Response
		if testCase.PostValues != nil {
			r, err := postForm(server.URL+testCase.Path, testCase.PostValues)
			if err != nil {
				t.Errorf("%s: unexpected error: %v", k, err)
				continue
			}
			resp = r
		} else {
			r, err := getURL(server.URL + testCase.Path)
			if err != nil {
				t.Errorf("%s: unexpected error: %v", k, err)
				continue
			}
			resp = r
		}
		defer resp.Body.Close()

		if testCase.ExpectStatusCode != 0 && testCase.ExpectStatusCode != resp.StatusCode {
			t.Errorf("%s: unexpected response: %#v", k, resp)
			continue
		}

		if testCase.ExpectRedirect != "" {
			uri, err := resp.Location()
			if err != nil {
				t.Errorf("%s: unexpected error: %v", testCase.ExpectRedirect, err)
				continue
			}
			if uri.String() != server.URL+testCase.ExpectRedirect {
				t.Errorf("%s: unexpected redirect: %s", testCase.ExpectRedirect, uri.String())
			}
		}

		if testCase.ExpectThen != "" && (!testCase.Implicit.Called || testCase.Implicit.Then != testCase.ExpectThen) {
			t.Errorf("%s: did not find expected 'then' value: %#v", k, testCase.Implicit)
		}

		if len(testCase.ExpectContains) > 0 {
			data, _ := ioutil.ReadAll(resp.Body)
			body := string(data)
			for i := range testCase.ExpectContains {
				if !strings.Contains(body, testCase.ExpectContains[i]) {
					t.Errorf("%s: did not find expected value %s: %s", k, testCase.ExpectContains[i], body)
					continue
				}
			}
		}
	}
}
예제 #19
0
파일: requests.go 프로젝트: rayleyva/fleet
// SendRequest sends a HTTP request and returns a Response as defined by etcd
func (c *Client) SendRequest(rr *RawRequest) (*RawResponse, error) {

	var req *http.Request
	var resp *http.Response
	var httpPath string
	var err error
	var respBody []byte

	reqs := make([]http.Request, 0)
	resps := make([]http.Response, 0)

	checkRetry := c.CheckRetry
	if checkRetry == nil {
		checkRetry = DefaultCheckRetry
	}

	cancelled := make(chan bool, 1)
	reqLock := new(sync.Mutex)

	if rr.Cancel != nil {
		cancelRoutine := make(chan bool)
		defer close(cancelRoutine)

		go func() {
			select {
			case <-rr.Cancel:
				cancelled <- true
				logger.Debug("send.request is cancelled")
			case <-cancelRoutine:
				return
			}

			// Repeat canceling request until this thread is stopped
			// because we have no idea about whether it succeeds.
			for {
				reqLock.Lock()
				c.httpClient.Transport.(*http.Transport).CancelRequest(req)
				reqLock.Unlock()

				select {
				case <-time.After(100 * time.Millisecond):
				case <-cancelRoutine:
					return
				}
			}
		}()
	}

	// if we connect to a follower, we will retry until we find a leader
	for attempt := 0; ; attempt++ {
		select {
		case <-cancelled:
			return nil, ErrRequestCancelled
		default:
		}

		logger.Debug("begin attempt", attempt, "for", rr.RelativePath)

		if rr.Method == "GET" && c.config.Consistency == WEAK_CONSISTENCY {
			// If it's a GET and consistency level is set to WEAK,
			// then use a random machine.
			httpPath = c.getHttpPath(true, rr.RelativePath)
		} else {
			// Else use the leader.
			httpPath = c.getHttpPath(false, rr.RelativePath)
		}

		// Return a cURL command if curlChan is set
		if c.cURLch != nil {
			command := fmt.Sprintf("curl -X %s %s", rr.Method, httpPath)
			for key, value := range rr.Values {
				command += fmt.Sprintf(" -d %s=%s", key, value[0])
			}
			c.sendCURL(command)
		}

		logger.Debug("send.request.to ", httpPath, " | method ", rr.Method)

		reqLock.Lock()
		if rr.Values == nil {
			req, err = http.NewRequest(rr.Method, httpPath, nil)
		} else {
			req, err = http.NewRequest(rr.Method, httpPath,
				strings.NewReader(rr.Values.Encode()))

			req.Header.Set("Content-Type",
				"application/x-www-form-urlencoded; param=value")
		}
		reqLock.Unlock()

		if err != nil {
			return nil, err
		}

		reqs = append(reqs, *req)

		respchan := make(chan *http.Response, 1)
		errchan := make(chan error, 1)
		responded := make(chan bool)

		go func() {
			resp, err := c.httpClient.Do(req)
			close(responded)
			if resp != nil {
				respchan <- resp
			} else if err != nil {
				errchan <- err
			} else {
				errchan <- errors.New("http client responded with nil response and nil error")
			}
		}()

		if rr.Cancel == nil {
			go func() {
				select {
				case <-time.After(c.config.ResponseTimeout):
					reqLock.Lock()
					c.httpClient.Transport.(*http.Transport).CancelRequest(req)
					reqLock.Unlock()
					errchan <- errors.New("Timed out waiting for response")
				case <-responded:
					return
				}
			}()
		}

		// If the request was cancelled, return ErrRequestCancelled directly
		select {
		case resp = <-respchan:
		case <-cancelled:
			return nil, ErrRequestCancelled
		case err = <-errchan:
			logger.Debug("network error:", err.Error())
			resps = append(resps, http.Response{})
			if checkErr := checkRetry(c.cluster, reqs, resps, err); checkErr != nil {
				return nil, checkErr
			}

			c.cluster.switchLeader(attempt % len(c.cluster.Machines))
			continue
		}

		// if there is no error, it should receive response
		resps = append(resps, *resp)
		defer resp.Body.Close()
		logger.Debug("recv.response.from", httpPath)

		if validHttpStatusCode[resp.StatusCode] {
			// try to read byte code and break the loop
			respBody, err = ioutil.ReadAll(resp.Body)
			if err == nil {
				logger.Debug("recv.success.", httpPath)
				break
			}
		}

		// if resp is TemporaryRedirect, set the new leader and retry
		if resp.StatusCode == http.StatusTemporaryRedirect {
			u, err := resp.Location()

			if err != nil {
				logger.Warning(err)
			} else {
				// Update cluster leader based on redirect location
				// because it should point to the leader address
				c.cluster.updateLeaderFromURL(u)
				logger.Debug("recv.response.relocate", u.String())
			}
			resp.Body.Close()
			continue
		}

		if checkErr := checkRetry(c.cluster, reqs, resps,
			errors.New("Unexpected HTTP status code")); checkErr != nil {
			return nil, checkErr
		}
		resp.Body.Close()
	}

	r := &RawResponse{
		StatusCode: resp.StatusCode,
		Body:       respBody,
		Header:     resp.Header,
	}

	return r, nil
}
예제 #20
0
func (c *Client) SendRequest(rr *RawRequest) (*RawResponse, error) {

	var req *http.Request
	var resp *http.Response
	var httpPath string
	var err error
	var respBody []byte

	var numReqs = 1

	checkRetry := c.CheckRetry
	if checkRetry == nil {
		checkRetry = DefaultCheckRetry
	}

	cancelled := make(chan bool, 1)
	reqLock := new(sync.Mutex)

	if rr.cancel != nil {
		cancelRoutine := make(chan bool)
		defer close(cancelRoutine)

		go func() {
			select {
			case <-rr.cancel:
				cancelled <- true
				logger.Debug("send.request is cancelled")
			case <-cancelRoutine:
				return
			}

			// Repeat canceling request until this thread is stopped
			// because we have no idea about whether it succeeds.
			for {
				reqLock.Lock()
				c.httpClient.Transport.(*http.Transport).CancelRequest(req)
				reqLock.Unlock()

				select {
				case <-time.After(100 * time.Millisecond):
				case <-cancelRoutine:
					return
				}
			}
		}()
	}

	// If we connect to a follower and consistency is required, retry until
	// we connect to a leader
	sleep := 25 * time.Millisecond
	maxSleep := time.Second

	for attempt := 0; ; attempt++ {
		if attempt > 0 {
			select {
			case <-cancelled:
				return nil, ErrRequestCancelled
			case <-time.After(sleep):
				sleep = sleep * 2
				if sleep > maxSleep {
					sleep = maxSleep
				}
			}
		}

		logger.Debug("Connecting to eureka: attempt %d for %s", attempt+1, rr.relativePath)

		httpPath = c.getHttpPath(false, rr.relativePath)

		//		logger.Debug("send.request.to %s | method %s ", httpPath, rr.method)

		req, err := func() (*http.Request, error) {
			reqLock.Lock()
			defer reqLock.Unlock()

			if req, err = http.NewRequest(rr.method, httpPath, bytes.NewReader(rr.body)); err != nil {
				return nil, err
			}

			req.Header.Set("Content-Type", "application/json")
			return req, nil
		}()

		if err != nil {
			return nil, err
		}

		resp, err = c.httpClient.Do(req)
		defer func() {
			if resp != nil {
				resp.Body.Close()
			}
		}()

		// If the request was cancelled, return ErrRequestCancelled directly
		select {
		case <-cancelled:
			return nil, ErrRequestCancelled
		default:
		}

		numReqs++

		// network error, change a machine!
		if err != nil {
			logger.Error("network error: %v", err.Error())
			lastResp := http.Response{}
			if checkErr := checkRetry(c.Cluster, numReqs, lastResp, err); checkErr != nil {
				return nil, checkErr
			}

			c.Cluster.switchLeader(attempt % len(c.Cluster.Machines))
			continue
		}

		// if there is no error, it should receive response
		logger.Debug("recv.response.from " + httpPath)

		if validHttpStatusCode[resp.StatusCode] {
			// try to read byte code and break the loop
			respBody, err = ioutil.ReadAll(resp.Body)
			if err == nil {
				logger.Debug("recv.success " + httpPath + " " + resp.Status)
				break
			}
			// ReadAll error may be caused due to cancel request
			select {
			case <-cancelled:
				return nil, ErrRequestCancelled
			default:
			}

			if err == io.ErrUnexpectedEOF {
				// underlying connection was closed prematurely, probably by timeout
				// TODO: empty body or unexpectedEOF can cause http.Transport to get hosed;
				// this allows the client to detect that and take evasive action. Need
				// to revisit once code.google.com/p/go/issues/detail?id=8648 gets fixed.
				respBody = []byte{}
				break
			}
		}

		// if resp is TemporaryRedirect, set the new leader and retry
		if resp.StatusCode == http.StatusTemporaryRedirect {
			u, err := resp.Location()

			if err != nil {
				logger.Warning("%v", err)
			} else {
				// Update cluster leader based on redirect location
				// because it should point to the leader address
				c.Cluster.updateLeaderFromURL(u)
				logger.Debug("recv.response.relocate " + u.String())
			}
			resp.Body.Close()
			continue
		}

		if checkErr := checkRetry(c.Cluster, numReqs, *resp,
			errors.New("Unexpected HTTP status code")); checkErr != nil {
			return nil, checkErr
		}
		resp.Body.Close()
	}

	r := &RawResponse{
		StatusCode: resp.StatusCode,
		Body:       respBody,
		Header:     resp.Header,
	}

	return r, nil
}
예제 #21
0
func TestGrant(t *testing.T) {
	testCases := map[string]struct {
		CSRF           csrf.CSRF
		Auth           *testAuth
		ClientRegistry *test.ClientRegistry
		AuthRegistry   *test.ClientAuthorizationRegistry

		Path       string
		PostValues url.Values

		ExpectStatusCode        int
		ExpectCreatedAuthScopes []string
		ExpectUpdatedAuthScopes []string
		ExpectRedirect          string
		ExpectContains          []string
		ExpectThen              string
	}{
		"display form": {
			CSRF:           &csrf.FakeCSRF{Token: "test"},
			Auth:           goodAuth("username"),
			ClientRegistry: goodClientRegistry("myclient", []string{"myredirect"}),
			AuthRegistry:   emptyAuthRegistry(),
			Path:           "/grant?client_id=myclient&scopes=myscope1%20myscope2&redirect_uri=/myredirect&then=/authorize",

			ExpectStatusCode: 200,
			ExpectContains: []string{
				`action="/grant"`,
				`name="csrf" value="test"`,
				`name="client_id" value="myclient"`,
				`name="scopes" value="myscope1 myscope2"`,
				`name="redirect_uri" value="/myredirect"`,
				`name="then" value="/authorize"`,
			},
		},

		"Unauthenticated with redirect": {
			CSRF: &csrf.FakeCSRF{Token: "test"},
			Auth: badAuth(nil),
			Path: "/grant?then=/authorize",

			ExpectStatusCode: 302,
			ExpectRedirect:   "/authorize",
		},

		"Unauthenticated without redirect": {
			CSRF: &csrf.FakeCSRF{Token: "test"},
			Auth: badAuth(nil),
			Path: "/grant",

			ExpectStatusCode: 200,
			ExpectContains:   []string{"reauthenticate"},
		},

		"Auth error with redirect": {
			CSRF: &csrf.FakeCSRF{Token: "test"},
			Auth: badAuth(errors.New("Auth error")),
			Path: "/grant?then=/authorize",

			ExpectStatusCode: 302,
			ExpectRedirect:   "/authorize",
		},

		"Auth error without redirect": {
			CSRF: &csrf.FakeCSRF{Token: "test"},
			Auth: badAuth(errors.New("Auth error")),
			Path: "/grant",

			ExpectStatusCode: 200,
			ExpectContains:   []string{"reauthenticate"},
		},

		"error when POST fails CSRF": {
			CSRF:           &csrf.FakeCSRF{Token: "test"},
			Auth:           goodAuth("username"),
			ClientRegistry: goodClientRegistry("myclient", []string{"myredirect"}),
			AuthRegistry:   emptyAuthRegistry(),
			Path:           "/grant",
			PostValues: url.Values{
				"client_id":    {"myclient"},
				"scopes":       {"myscope1 myscope2"},
				"redirect_uri": {"/myredirect"},
				"then":         {"/authorize"},
				"csrf":         {"wrong"},
			},

			ExpectStatusCode: 200,
			ExpectContains:   []string{"CSRF"},
		},

		"error displaying form with invalid client": {
			CSRF:           &csrf.FakeCSRF{Token: "test"},
			Auth:           goodAuth("username"),
			ClientRegistry: badClientRegistry(nil),
			AuthRegistry:   emptyAuthRegistry(),
			Path:           "/grant",

			ExpectStatusCode: 200,
			ExpectContains:   []string{"find client"},
		},

		"error submitting form with invalid client": {
			CSRF:           &csrf.FakeCSRF{Token: "test"},
			Auth:           goodAuth("username"),
			ClientRegistry: badClientRegistry(nil),
			AuthRegistry:   emptyAuthRegistry(),
			Path:           "/grant",
			PostValues: url.Values{
				"approve":      {"true"},
				"client_id":    {"myclient"},
				"scopes":       {"myscope1 myscope2"},
				"redirect_uri": {"/myredirect"},
				"then":         {"/authorize"},
				"csrf":         {"test"},
			},

			ExpectStatusCode: 200,
			ExpectContains:   []string{"find client"},
		},

		"successful create grant with redirect": {
			CSRF:           &csrf.FakeCSRF{Token: "test"},
			Auth:           goodAuth("username"),
			ClientRegistry: goodClientRegistry("myclient", []string{"myredirect"}),
			AuthRegistry:   emptyAuthRegistry(),
			Path:           "/grant",
			PostValues: url.Values{
				"approve":      {"true"},
				"client_id":    {"myclient"},
				"scopes":       {"myscope1 myscope2"},
				"redirect_uri": {"/myredirect"},
				"then":         {"/authorize"},
				"csrf":         {"test"},
			},

			ExpectStatusCode:        302,
			ExpectCreatedAuthScopes: []string{"myscope1", "myscope2"},
			ExpectRedirect:          "/authorize",
		},

		"successful create grant without redirect": {
			CSRF:           &csrf.FakeCSRF{Token: "test"},
			Auth:           goodAuth("username"),
			ClientRegistry: goodClientRegistry("myclient", []string{"myredirect"}),
			AuthRegistry:   emptyAuthRegistry(),
			Path:           "/grant",
			PostValues: url.Values{
				"approve":      {"true"},
				"client_id":    {"myclient"},
				"scopes":       {"myscope1 myscope2"},
				"redirect_uri": {"/myredirect"},
				"csrf":         {"test"},
			},

			ExpectStatusCode:        200,
			ExpectCreatedAuthScopes: []string{"myscope1", "myscope2"},
			ExpectContains: []string{
				"granted",
				"no redirect",
			},
		},

		"successful update grant with identical scopes": {
			CSRF:           &csrf.FakeCSRF{Token: "test"},
			Auth:           goodAuth("username"),
			ClientRegistry: goodClientRegistry("myclient", []string{"myredirect"}),
			AuthRegistry:   existingAuthRegistry([]string{"myscope2", "myscope1"}),
			Path:           "/grant",
			PostValues: url.Values{
				"approve":      {"true"},
				"client_id":    {"myclient"},
				"scopes":       {"myscope1 myscope2"},
				"redirect_uri": {"/myredirect"},
				"then":         {"/authorize"},
				"csrf":         {"test"},
			},

			ExpectStatusCode:        302,
			ExpectUpdatedAuthScopes: []string{"myscope1", "myscope2"},
			ExpectRedirect:          "/authorize",
		},

		"successful update grant with additional scopes": {
			CSRF:           &csrf.FakeCSRF{Token: "test"},
			Auth:           goodAuth("username"),
			ClientRegistry: goodClientRegistry("myclient", []string{"myredirect"}),
			AuthRegistry:   existingAuthRegistry([]string{"existingscope2", "existingscope1"}),
			Path:           "/grant",
			PostValues: url.Values{
				"approve":      {"true"},
				"client_id":    {"myclient"},
				"scopes":       {"newscope1 existingscope1"},
				"redirect_uri": {"/myredirect"},
				"then":         {"/authorize"},
				"csrf":         {"test"},
			},

			ExpectStatusCode:        302,
			ExpectUpdatedAuthScopes: []string{"existingscope1", "existingscope2", "newscope1"},
			ExpectRedirect:          "/authorize",
		},

		"successful reject grant": {
			CSRF:           &csrf.FakeCSRF{Token: "test"},
			Auth:           goodAuth("username"),
			ClientRegistry: goodClientRegistry("myclient", []string{"myredirect"}),
			AuthRegistry:   existingAuthRegistry([]string{"existingscope2", "existingscope1"}),
			Path:           "/grant",
			PostValues: url.Values{
				"deny":         {"true"},
				"client_id":    {"myclient"},
				"scopes":       {"newscope1 existingscope1"},
				"redirect_uri": {"/myredirect"},
				"then":         {"/authorize"},
				"csrf":         {"test"},
			},

			ExpectStatusCode: 302,
			ExpectRedirect:   "/authorize?error=access_denied",
		},
	}

	for k, testCase := range testCases {
		server := httptest.NewServer(NewGrant(testCase.CSRF, testCase.Auth, DefaultFormRenderer, testCase.ClientRegistry, testCase.AuthRegistry))

		var resp *http.Response
		if testCase.PostValues != nil {
			r, err := postForm(server.URL+testCase.Path, testCase.PostValues)
			if err != nil {
				t.Errorf("%s: unexpected error: %v", k, err)
				continue
			}
			resp = r
		} else {
			r, err := getURL(server.URL + testCase.Path)
			if err != nil {
				t.Errorf("%s: unexpected error: %v", k, err)
				continue
			}
			resp = r
		}
		defer resp.Body.Close()

		if testCase.ExpectStatusCode != 0 && testCase.ExpectStatusCode != resp.StatusCode {
			t.Errorf("%s: unexpected response: %#v", k, resp)
			continue
		}

		if len(testCase.ExpectCreatedAuthScopes) > 0 {
			auth := testCase.AuthRegistry.CreatedAuthorization
			if auth == nil {
				t.Errorf("%s: expected created auth, got nil", k)
				continue
			}
			if !reflect.DeepEqual(testCase.ExpectCreatedAuthScopes, auth.Scopes) {
				t.Errorf("%s: expected created scopes %v, got %v", k, testCase.ExpectCreatedAuthScopes, auth.Scopes)
			}
		}

		if len(testCase.ExpectUpdatedAuthScopes) > 0 {
			auth := testCase.AuthRegistry.UpdatedAuthorization
			if auth == nil {
				t.Errorf("%s: expected updated auth, got nil", k)
				continue
			}
			if !reflect.DeepEqual(testCase.ExpectUpdatedAuthScopes, auth.Scopes) {
				t.Errorf("%s: expected updated scopes %v, got %v", k, testCase.ExpectUpdatedAuthScopes, auth.Scopes)
			}
		}

		if testCase.ExpectRedirect != "" {
			uri, err := resp.Location()
			if err != nil {
				t.Errorf("%s: unexpected error: %v", k, err)
				continue
			}
			if uri.String() != server.URL+testCase.ExpectRedirect {
				t.Errorf("%s: unexpected redirect: %s", k, uri.String())
			}
		}

		if len(testCase.ExpectContains) > 0 {
			data, _ := ioutil.ReadAll(resp.Body)
			body := string(data)
			for i := range testCase.ExpectContains {
				if !strings.Contains(body, testCase.ExpectContains[i]) {
					t.Errorf("%s: did not find expected value %s: %s", k, testCase.ExpectContains[i], body)
					continue
				}
			}
		}
	}
}
예제 #22
0
파일: requests.go 프로젝트: WH-Wang/libgo
// SendRequest sends a HTTP request and returns a Response as defined by etcd
func (c *Client) SendRequest(rr *RawRequest, json string) (*RawResponse, error) {

	var req *http.Request
	var resp *http.Response
	var httpPath string
	var err error
	var respBody []byte

	var numReqs = 1
	log.Info("------------entry------")
	checkRetry := c.CheckRetry
	if checkRetry == nil {
		checkRetry = DefaultCheckRetry
	}

	cancelled := make(chan bool, 1)
	reqLock := new(sync.Mutex)

	if rr.Cancel != nil {
		cancelRoutine := make(chan bool)
		defer close(cancelRoutine)

		go func() {
			select {
			case <-rr.Cancel:
				cancelled <- true
				fmt.Println("send.request is cancelled")
			case <-cancelRoutine:
				return
			}

			// Repeat canceling request until this thread is stopped
			// because we have no idea about whether it succeeds.
			for {
				reqLock.Lock()
				c.httpClient.Transport.(*http.Transport).CancelRequest(req)
				reqLock.Unlock()

				select {
				case <-time.After(100 * time.Millisecond):
				case <-cancelRoutine:
					return
				}
			}
		}()
	}

	log.Info("Connecting to geard deamon: attempt")

	httpPath = rr.Url

	// Return a cURL command if curlChan is set
	c.OpenCURL()
	if c.cURLch != nil {
		command := fmt.Sprintf("curl")
		if rr.Method != "" {
			command += fmt.Sprintf(" -X %s", rr.Method)
		}
		if rr.Url != "" {
			command += fmt.Sprintf(" %s", rr.Url)
		}

		if json != "" {
			command += fmt.Sprintf(" -d '%s' ", json)
		}
		log.Info(command)
		c.sendCURL(command)
	}

	reqLock.Lock()

	if json == "" {
		if req, err = http.NewRequest(rr.Method, httpPath, nil); err != nil {
			return nil, err
		}
	} else {
		body := strings.NewReader(json)
		if req, err = http.NewRequest(rr.Method, httpPath, body); err != nil {
			return nil, err
		}

		req.Header.Set("Content-Type", rr.ContentType)
	}
	reqLock.Unlock()
	resp, err = c.httpClient.Do(req)
	defer func() {
		if resp != nil {
			resp.Body.Close()
		}
	}()

	// If the request was cancelled, return ErrRequestCancelled directly
	select {
	case <-cancelled:
		return nil, ErrRequestCancelled
	default:
	}

	numReqs++

	// network error, change a machine!
	if err != nil {
		log.Error("network error:", err.Error())
		lastResp := http.Response{}
		if checkErr := checkRetry(numReqs, lastResp, err); checkErr != nil {
			return nil, checkErr
		}

		//c.cluster.switchLeader(attempt % len(c.cluster.Machines))
		//continue
	}

	// if there is no error, it should receive response
	log.Error("recv.response.from", httpPath)
	log.Info(resp)
	if validHttpStatusCode[resp.StatusCode] {
		// try to read byte code and break the loop
		log.Info("--------if entry----------")
		log.Info(resp.StatusCode)
		respBody, err = ioutil.ReadAll(resp.Body)
		if err == nil {
			log.Error("recv.success.", httpPath)
			//break
		}
		// ReadAll error may be caused due to cancel request
		select {
		case <-cancelled:
			return nil, ErrRequestCancelled
		default:
		}
	}

	// if resp is TemporaryRedirect, set the new leader and retry
	if resp.StatusCode == http.StatusTemporaryRedirect {
		u, err := resp.Location()

		if err != nil {
			log.Error(err)
		} else {
			// Update cluster leader based on redirect location
			// because it should point to the leader address
			//c.cluster.updateLeaderFromURL(u)
			log.Error("recv.response.relocate", u.String())
		}
		resp.Body.Close()
		//continue
	}

	if checkErr := checkRetry(numReqs, *resp,
		errors.New("Unexpected HTTP status code")); checkErr != nil {
		return nil, checkErr
	}
	resp.Body.Close()
	//}

	r := &RawResponse{
		StatusCode: resp.StatusCode,
		Body:       respBody,
		Header:     resp.Header,
	}

	return r, nil
}