// Create a new client for making http requests against a Jazz server with the provided credentials // The client will execute the requests authenticating somewhat transparently when needed func NewClient(userID string, password string) (*Client, error) { jClient := &Client{} jClient.userID = userID jClient.password = password options := cookiejar.Options{ PublicSuffixList: publicsuffix.List, } jar, err := cookiejar.New(&options) if err != nil { return nil, err } client := http.Client{Jar: jar} tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } client.Transport = tr client.CheckRedirect = nil jClient.httpClient = &client // Provide a no-op logger as the default jClient.Log = log.New(ioutil.Discard, "", log.LstdFlags) return jClient, nil }
func addRedirectFunctionality(client *http.Client, ro *RequestOptions) { client.CheckRedirect = func(req *http.Request, via []*http.Request) error { if ro.RedirectLimit == 0 { ro.RedirectLimit = RedirectLimit } if len(via) >= ro.RedirectLimit { return ErrRedirectLimitExceeded } if ro.SensitiveHTTPHeaders == nil { ro.SensitiveHTTPHeaders = SensitiveHTTPHeaders } for k, vv := range via[0].Header { // Is this a sensitive header? if _, found := ro.SensitiveHTTPHeaders[k]; found && !ro.RedirectLocationTrusted { continue } for _, v := range vv { req.Header.Add(k, v) } } return nil } }
/*-------------------------------------------------------------------------------*/ func TestCallbackError(t *testing.T) { port := 8202 //create controller for all mocks ctrl := gomock.NewController(t) //check mocks at end defer ctrl.Finish() //key values loser := "you are a loser" state := "jabba da hut/:)" //make sure we are decoding correctly by adding strange chars pageMapper := NewSimplePageMapper(three, "notused", "notused") cookieMapper := NewMockCookieMapper(ctrl) serveMux, authConn := createDispatcherWithMocks(ctrl, pageMapper, cookieMapper, nil) go func() { http.ListenAndServe(fmt.Sprintf(":%d", port), serveMux) }() //don't care about the cookie name cookieMapper.EXPECT().CookieName().Return("my_chef").AnyTimes() //just to get the constants authConn.EXPECT().ErrorValueName().Return("error") authConn.EXPECT().CodeValueName().Return("code") authConn.EXPECT().ClientTokenValueName().Return("dontbotherimnotgoingtousethisanyway") // this is what happens when google refuses v := url.Values{ //no code! "state": []string{state}, "error": []string{loser}, } returnURLHost := fmt.Sprintf("localhost:%d", port) returnURL, err := url.Parse(fmt.Sprintf("http://%s%s?%s", returnURLHost, returl, v.Encode())) if err != nil { t.Fatalf("Can't understand url: %s", err) } client := new(http.Client) client.CheckRedirect = func(req *http.Request, via []*http.Request) error { checkRedirValues(t, "error from goog", via, map[string][]string{ "path": []string{req.URL.Path, three}, "host": []string{req.URL.Host, returnURLHost}, "state": []string{req.URL.Query().Get("state"), ""}, "error": []string{req.URL.Query().Get("error"), loser}, "service": []string{req.URL.Query().Get("service"), "google"}, "via url[0]": []string{via[0].URL.String(), returnURL.String()}, }) return stopProcessing } resp := createReqAndDo(t, client, returnURL.String(), nil) for k, v := range resp.Header { if k == "Set-Cookie" { t.Errorf("Should not have set cookie on error: %s\n", v[0]) } } }
/*-------------------------------------------------------------------------------*/ func TestLogout(t *testing.T) { port := 8203 //create controller for all mocks ctrl := gomock.NewController(t) //check mocks at end defer ctrl.Finish() pageMapper := NewSimplePageMapper("notused", "notused", two) sm := NewMockSessionManager(ctrl) cookieMapper := NewSimpleCookieMapper(appName) serveMux, _ := createDispatcherWithMocks(ctrl, pageMapper, cookieMapper, sm) sm.EXPECT().Destroy(gomock.Any()).Return(nil) go func() { http.ListenAndServe(fmt.Sprintf(":%d", port), serveMux) }() logoutURLHost := fmt.Sprintf("localhost:%d", port) logoutURL, err := url.Parse(fmt.Sprintf("http://%s%s", logoutURLHost, "/fart/google/logout")) if err != nil { t.Fatalf("Can't understand url: %s", err) } client := new(http.Client) client.CheckRedirect = func(req *http.Request, via []*http.Request) error { checkRedirValues(t, "error from goog", via, map[string][]string{ "path": []string{req.URL.Path, two}, "host": []string{req.URL.Host, logoutURLHost}, "via url[0]": []string{via[0].URL.String(), logoutURL.String()}, }) return stopProcessing } resp := createReqAndDo(t, client, logoutURL.String(), &http.Cookie{ Name: cookieMapper.CookieName(), Value: "forty-series-tires", }) for k, v := range resp.Header { if k == "Set-Cookie" { p := strings.Split(v[0], ";") for _, piece := range p { if strings.Index(piece, cookieMapper.CookieName()) != -1 { if strings.TrimSpace(piece) != cookieMapper.CookieName()+"=" { t.Errorf("Cookie not destroyed properly! '%s'", piece) } } if strings.Index(piece, "Max-Age") != -1 { if strings.TrimSpace(piece) != "Max-Age=0" { t.Errorf("Cookie not destroyed properly! '%s'", piece) } } } } } }
// newRawClient creates an http package Client taking into account both the parameters and package // variables. func newRawClient(noredirect bool) *http.Client { tr := http.Transport{ResponseHeaderTimeout: ResponseHeaderTimeout, Proxy: http.ProxyFromEnvironment} tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: NoCertCheck} c := http.Client{Transport: &tr} if noredirect { c.CheckRedirect = func(*http.Request, []*http.Request) error { return fmt.Errorf(noRedirectError) } } return &c }
// buildRequest is where most of the magic happens for request processing func buildRequest(httpMethod, url string, ro *RequestOptions, httpClient *http.Client) (*http.Response, error) { if ro == nil { ro = &RequestOptions{} } // Create our own HTTP client if httpClient == nil { httpClient = BuildHTTPClient(*ro) } defaultRedirectLimit := 30 httpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error { if len(via) > defaultRedirectLimit { return fmt.Errorf("%d consecutive requests(redirects)", len(via)) } if len(via) == 0 { // No redirects return nil } // mutate the subsequent redirect requests with the first Header for key, val := range via[0].Header { req.Header[key] = val } return nil } // Build our URL var ( err error ) if len(ro.Params) != 0 { if url, err = buildURLParams(url, ro.Params); err != nil { return nil, err } } // Build the request req, err := buildHTTPRequest(httpMethod, url, ro) if err != nil { return nil, err } // Do we need to add any HTTP headers or Basic Auth? addHTTPHeaders(ro, req) addCookies(ro, req) return httpClient.Do(req) }
func httpDl(uri, fileName string) error { fmt.Printf("httpDl: %s\n", uri) options := cookiejar.Options{ PublicSuffixList: publicsuffix.List, } jar, err := cookiejar.New(&options) if err != nil { return err } client := http.Client{Jar: jar} client.CheckRedirect = func(req *http.Request, via []*http.Request) error { dumpReq(req) if len(via) >= 10 { return fmt.Errorf("too many redirects") } if len(via) == 0 { return nil } for attr, val := range via[0].Header { if _, ok := req.Header[attr]; !ok { req.Header[attr] = val } } return nil } req, err := http.NewRequest("GET", uri, nil) // Note: this is crucial. Dropbox will return some html if User-Agent is not defined req.Header.Add("User-Agent", "curl/7.43.0") dumpReq(req) if err != nil { return err } resp, err := client.Do(req) dumpResp(resp) if err != nil { return err } d, err := ioutil.ReadAll(resp.Body) resp.Body.Close() if err != nil { return err } if resp.StatusCode != 200 { return fmt.Errorf("httpDl() failed because StatusCode = %d", resp.StatusCode) } return ioutil.WriteFile(fileName, d, 0644) }
func randomPage(command *bot.Cmd) (string, error) { var redirectNotAllowed = errors.New("redirect") redirectedURL := "" client := http.Client{} client.CheckRedirect = func(req *http.Request, via []*http.Request) error { redirectedURL = req.URL.String() return redirectNotAllowed } _, err := client.Get(randomURL) if urlError, ok := err.(*url.Error); ok && urlError.Err == redirectNotAllowed { return redirectedURL, nil } return "", err }
func Test_RedirectHandleRedirects(t *testing.T) { to := "http://www.yfu.de" testStore.Add(&redirect{From: "foo", To: to}) client := new(http.Client) var redirectURL *url.URL client.CheckRedirect = func(req *http.Request, via []*http.Request) error { redirectURL = req.URL return errors.New("") // we don't want to carry out the redirect, just get the target URL } client.Get(server.URL + "/foo") if redirectURL == nil { t.Fatalf("no redirect observed!") } if redirectURL.String() != to { t.Errorf("expected %q, got %q", to, redirectURL.String()) } }
func init() { _, regex, proxy, _ = parseRule(loadRule("proxy.txt")) pool.New = func() interface{} { client := new(http.Client) client.Transport = &http.Transport{ Proxy: func(req *http.Request) (*url.URL, error) { for i, rule := range regex { if rule.MatchString(req.URL.Host) { return proxy[i], nil } } return nil, nil }, } client.CheckRedirect = func(_ *http.Request, _ []*http.Request) error { return disableRedirect } return client } }
// proxyRequest does not use the local storage and directly proxies the // request to the upstream server. func (ph *Handler) proxyRequest(ctx context.Context, w http.ResponseWriter, r *http.Request, vh *types.VirtualHost) { //!TODO: use the upstream for the vhost - if the vhost is not a "simple" one // or has authentication or is a FS, this will not work client := http.Client{} client.CheckRedirect = func(req *http.Request, via []*http.Request) error { return ErrNoRedirects } newURL := vh.UpstreamAddress.ResolveReference(r.URL) req, err := http.NewRequest("GET", newURL.String(), nil) if err != nil { log.Printf("[%p] Got error\n %s\n while making request ", r, err) return } for headerName, headerValue := range r.Header { req.Header.Set(headerName, strings.Join(headerValue, ",")) } resp, err := client.Do(req) if err != nil && err != ErrNoRedirects { if urlError, ok := err.(*url.Error); !(ok && urlError.Err == ErrNoRedirects) { log.Printf("[%p] Got error\n %s\n while proxying %s to %s", r, err, r.URL.String(), newURL.String()) return } } defer resp.Body.Close() respHeaders := w.Header() for headerName, headerValue := range resp.Header { respHeaders.Set(headerName, strings.Join(headerValue, ",")) } ph.finishRequest(resp.StatusCode, w, r, resp.Body) }
func TestResponseURL(t *testing.T) { c := new(http.Client) req := NewRequest(c) url := "http://httpbin.org/get" resp, _ := req.Get(url) u, _ := resp.URL() assert.Equal(t, u.String(), url) url = "http://httpbin.org/redirect/3" resp, _ = req.Get(url) u, _ = resp.URL() assert.Equal(t, u.String(), "http://httpbin.org/get") url = "http://httpbin.org/redirect/3" c.CheckRedirect = func(req *http.Request, via []*http.Request) error { return errors.New("redirect") } resp, _ = req.Get(url) u, _ = resp.URL() assert.Equal(t, u.String(), "http://httpbin.org/relative-redirect/2") }
func main() { client := http.Client{} client.CheckRedirect = func(req *http.Request, via []*http.Request) error { fmt.Fprintf(os.Stderr, "Redirect: %v\n", req.URL) return nil } var url string if len(os.Args) < 2 { url = "http://golang.org" } else { url = os.Args[2] } page, err := client.Get(url) if err != nil { fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error()) return } io.Copy(os.Stdout, page.Body) page.Body.Close() }
/*-------------------------------------------------------------------------------*/ func TestGoogleLogin(t *testing.T) { port := 8201 //create controller for all mocks ctrl := gomock.NewController(t) //check mocks at end defer ctrl.Finish() //the key values st := "/frob bob" loginurl := "/fart/google/login" code := "barfly" one := "/1.html" sid := "id of session, sid vicious?" //authconn is a wrapper around the google auth connector with all mock methods, except AuthURL //pm is a mock for testing that we get a call to LoginLandingPage pm := NewMockPageMapper(ctrl) sm := NewMockSessionManager(ctrl) cm := NewSimpleCookieMapper(appName) serveMux, authconn := createDispatcherWithMocks(ctrl, pm, cm, sm) session := NewMockSession(ctrl) session.EXPECT().SessionId().Return(sid).AnyTimes() //when we succeed at logging in, it filters down to the session sm.EXPECT().Generate(gomock.Any(), gomock.Any(), gomock.Any(), st, code).Return(session, nil) //consumed by the google object under test deploy := NewMockDeploymentEnvironment(ctrl) deploy.EXPECT().RedirectHost(gomock.Any()).Return(fmt.Sprintf("http://localhost:%d", port)) detail := NewMockOauthClientDetail(ctrl) detail.EXPECT().ClientId(gomock.Any()).Return(id) detail.EXPECT().ClientSecret(gomock.Any()).Return(seekret) //we are testing the AuthURL method, and NOT testing ExchangeForToken() as it requires a //real network and a real client id and seekret google := NewGoogleOauth2(SCOPE, PROMPT, detail, deploy) //these are just accessing the constants, so don't care how many times //authconn.EXPECT().Name().Return("google").AnyTimes() authconn.EXPECT().StateValueName().Return("state").AnyTimes() authconn.EXPECT().ErrorValueName().Return("error").AnyTimes() authconn.EXPECT().CodeValueName().Return("code").AnyTimes() authconn.EXPECT().ClientTokenValueName().Return("notused").AnyTimes() //phase1 is not used by google because of oauth2 authconn.EXPECT().Phase1(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() //this is actually under test, the google.AuthURL method authconn.EXPECT().UserInteractionURL(gomock.Any(), st, returl).Return(google.UserInteractionURL(nil, st, returl)) //this is mocked out because it has the side effect of a network call... we can use the mocks //to return an error which we do in the second case gomock.InOrder( authconn.EXPECT().Phase2("", code).Return(nil, nil), authconn.EXPECT().Phase2("", code).Return(nil, badTransport), ) //testing that page mapper's login method gets called during the login process to generate the //final web page to land on pm.EXPECT().LoginLandingPage(authconn, st, code).Return(one) pm.EXPECT().ErrorPage(authconn, gomock.Any()).Return(three) go func() { http.ListenAndServe(fmt.Sprintf(":%d", port), serveMux) }() //we need to compute a return url (stage 2) because we expect to see at redir of stage 1 returnURLBase := fmt.Sprintf("http://localhost:%d%s", port, returl) //we need to compute a login url, with a state value to make sure it is propagated all the //way through to LoginLandingPage() v := url.Values{ "state": []string{st}, } loginURL, err := url.Parse(fmt.Sprintf("http://localhost:%d%s?%s", port, loginurl, v.Encode())) if err != nil { t.Fatalf("Can't understand url: %s", err) } //setup client to not really do redirects so we can look at what's going on client := new(http.Client) client.CheckRedirect = func(req *http.Request, via []*http.Request) error { checkRedirValues(t, "phase 1 of login", via, map[string][]string{ "path": []string{req.URL.Path, GOOGLE_AUTH_URL_PATH}, "host": []string{req.URL.Host, GOOGLE_AUTH_URL_HOST[len("https://"):]}, "scheme": []string{req.URL.Scheme, "https"}, "state": []string{req.URL.Query().Get("state"), st}, "client_id": []string{req.URL.Query().Get("client_id"), id}, "via url[0]": []string{via[0].URL.String(), loginURL.String()}, }) if !strings.HasPrefix(req.URL.Query().Get("redirect_uri"), returnURLBase) { t.Errorf("Serious problems understanding the callback uri: %s", req.URL.Query().Get("redirect_uri")) } return stopProcessing } createReqAndDo(t, client, loginURL.String(), nil) // next stage is to test that if we get the callabck we land on the right page // in the right state... compute a URL like google would send us v = url.Values{ "code": []string{code}, "state": []string{st}, "error": []string{}, } returnURL, err := url.Parse(fmt.Sprintf("%s?%s", returnURLBase, v.Encode())) if err != nil { t.Fatalf("Can't understand url: %s", err) } //now check the value we redirect back to on successful login... this simulates what google //would send back to us after successful handshake... again, we don't allow the redir //to be processed client.CheckRedirect = func(req *http.Request, via []*http.Request) error { checkRedirValues(t, "phase 2 of login", via, map[string][]string{ "path": []string{req.URL.Path, one}, "host": []string{req.URL.Host, fmt.Sprintf("localhost:%d", port)}, "scheme": []string{req.URL.Scheme, "http"}, "state": []string{req.URL.Query().Get("state"), ""}, //sanity "via url[0]": []string{via[0].URL.String(), returnURL.String()}, }) return stopProcessing } //make sure cookie manager sent us something resp := createReqAndDo(t, client, returnURL.String(), nil) found := false for k, v := range resp.Header { if k == "Set-Cookie" { found = true p := strings.Split(v[0], ";") if strings.Index(p[0], cm.CookieName()) == -1 { t.Errorf("Found a cookie but expected name '%s' but couldn't find it in header: %s", cm.CookieName(), p[0]) } if strings.Index(p[0], sid) == -1 { t.Errorf("Found a cookie but expected value '%s' but couldn't find it in header: %s", sid, p[0]) } } } if !found { t.Errorf("Didn't find cookie '%s'", cm.CookieName()) } //this tests that if the transport connection to the provider fails, we get the error page client.CheckRedirect = func(req *http.Request, via []*http.Request) error { checkRedirValues(t, "phase 2 (bad network)", via, map[string][]string{ "path": []string{req.URL.Path, three}, "host": []string{req.URL.Host, fmt.Sprintf("localhost:%d", port)}, "via url[0]": []string{via[0].URL.String(), returnURL.String()}, //error parameter is checked in a diff test }) return stopProcessing } createReqAndDo(t, client, returnURL.String(), nil) }
// Wraps a client using WrapCheckRedirect to prevent net-to-Unix redirects. func RestrictRedirects(c *http.Client) { c.CheckRedirect = WrapCheckRedirect(c.CheckRedirect) }
//InitWithCustomHTTPClient initializes the underlying client that communicates with Stormpath with a custom http.Client func InitWithCustomHTTPClient(credentials Credentials, cache Cache, httpClient *http.Client) { httpClient.CheckRedirect = checkRedirect client = &Client{credentials, httpClient, cache} initLog() }
func setHTTPClient(c *http.Client) { c.Jar = mustInitCookieJar() c.CheckRedirect = checkRedirect client = c }