package grequests import ( "bytes" "context" "encoding/json" "encoding/xml" "errors" "io" "net/http" "net/http/cookiejar" "net/http/httptest" "net/url" "os" "testing" "time" ) type BasicGetResponse struct { Args struct{} `json:"args"` Headers struct { Accept string `json:"Accept"` AcceptEncoding string `json:"Accept-Encoding"` AcceptLanguage string `json:"Accept-Language"` Dnt string `json:"Dst"` Host string `json:"Host"` UserAgent string `json:"User-Agent"` } `json:"headers"` Origin string `json:"origin"` URL string `json:"url"` } type BasicGetResponseNewHeader struct { Args struct{} `json:"args"` Headers struct { Accept string `json:"Accept"` AcceptEncoding string `json:"Accept-Encoding"` AcceptLanguage string `json:"Accept-Language"` Dnt string `json:"Dst"` Host string `json:"Host"` UserAgent string `json:"User-Agent"` XWonderfulHeader string `json:"X-Wonderful-Header"` } `json:"headers"` Origin string `json:"origin"` URL string `json:"url"` } type BasicGetResponseBasicAuth struct { Args struct{} `json:"args"` Headers struct { Accept string `json:"Accept"` AcceptEncoding string `json:"Accept-Encoding"` AcceptLanguage string `json:"Accept-Language"` Dnt string `json:"Dst"` Host string `json:"Host"` UserAgent string `json:"User-Agent"` Authorization string `json:"Authorization"` } `json:"headers"` Origin string `json:"origin"` URL string `json:"url"` } type BasicGetResponseArgs struct { Args struct { Goodbye string `json:"Goodbye"` Hello string `json:"Hello"` } `json:"args"` Headers struct { Accept string `json:"Accept"` AcceptEncoding string `json:"Accept-Encoding"` AcceptLanguage string `json:"Accept-Language"` Dnt string `json:"Dst"` Host string `json:"Host"` UserAgent string `json:"User-Agent"` Authorization string `json:"Authorization"` } `json:"headers"` Origin string `json:"origin"` URL string `json:"url"` } type GetXMLSample struct { XMLName xml.Name `xml:"slideshow"` Title string `xml:"title,attr"` Date string `xml:"date,attr"` Author string `xml:"author,attr"` Slide []struct { Type string `xml:"type,attr"` Title string `xml:"title"` } `xml:"slide"` } type TestJSONCookies struct { Cookies struct { AnotherCookie string `json:"AnotherCookie"` TestCookie string `json:"TestCookie"` } `json:"cookies"` } type MassiveJSONBlob struct { Type string `json:"type"` Features []struct { Type string `json:"type"` Properties struct { MAPBLKLOT string `json:"MAPBLKLOT"` BLKLOT string `json:"BLKLOT"` BLOCKNUM string `json:"BLOCK_NUM"` LOTNUM string `json:"LOT_NUM"` FROMST string `json:"FROM_ST"` TOST string `json:"TO_ST"` STREET string `json:"STREET"` STTYPE interface{} `json:"ST_TYPE"` ODDEVEN string `json:"ODD_EVEN"` } `json:"properties"` Geometry struct { Type string `json:"type"` Coordinates []struct { Num0 []float64 `json:"0,omitempty"` Num1 []float64 `json:"1,omitempty"` Num2 []float64 `json:"2,omitempty"` Num3 []float64 `json:"3,omitempty"` Num4 []float64 `json:"4,omitempty"` Num5 []float64 `json:"5,omitempty"` Num6 []float64 `json:"6,omitempty"` Num7 []float64 `json:"7,omitempty"` Num8 []float64 `json:"8,omitempty"` Num9 []float64 `json:"9,omitempty"` Num10 []float64 `json:"10,omitempty"` } `json:"-"` } `json:"geometry"` } `json:"features"` } type GithubSelfJSON struct { ID int `json:"id"` Name string `json:"name"` FullName string `json:"full_name"` Owner struct { Login string `json:"login"` ID int `json:"id"` AvatarURL string `json:"avatar_url"` GravatarID string `json:"gravatar_id"` URL string `json:"url"` HTMLURL string `json:"html_url"` FollowersURL string `json:"followers_url"` FollowingURL string `json:"following_url"` GistsURL string `json:"gists_url"` StarredURL string `json:"starred_url"` SubscriptionsURL string `json:"subscriptions_url"` OrganizationsURL string `json:"organizations_url"` ReposURL string `json:"repos_url"` EventsURL string `json:"events_url"` ReceivedEventsURL string `json:"received_events_url"` Type string `json:"type"` SiteAdmin bool `json:"site_admin"` } `json:"owner"` Private bool `json:"private"` HTMLURL string `json:"html_url"` Description string `json:"description"` Fork bool `json:"fork"` URL string `json:"url"` ForksURL string `json:"forks_url"` KeysURL string `json:"keys_url"` CollaboratorsURL string `json:"collaborators_url"` TeamsURL string `json:"teams_url"` HooksURL string `json:"hooks_url"` IssueEventsURL string `json:"issue_events_url"` EventsURL string `json:"events_url"` AssigneesURL string `json:"assignees_url"` BranchesURL string `json:"branches_url"` TagsURL string `json:"tags_url"` BlobsURL string `json:"blobs_url"` GitTagsURL string `json:"git_tags_url"` GitRefsURL string `json:"git_refs_url"` TreesURL string `json:"trees_url"` StatusesURL string `json:"statuses_url"` LanguagesURL string `json:"languages_url"` StargazersURL string `json:"stargazers_url"` ContributorsURL string `json:"contributors_url"` SubscribersURL string `json:"subscribers_url"` SubscriptionURL string `json:"subscription_url"` CommitsURL string `json:"commits_url"` GitCommitsURL string `json:"git_commits_url"` CommentsURL string `json:"comments_url"` IssueCommentURL string `json:"issue_comment_url"` ContentsURL string `json:"contents_url"` CompareURL string `json:"compare_url"` MergesURL string `json:"merges_url"` ArchiveURL string `json:"archive_url"` DownloadsURL string `json:"downloads_url"` IssuesURL string `json:"issues_url"` PullsURL string `json:"pulls_url"` MilestonesURL string `json:"milestones_url"` NotificationsURL string `json:"notifications_url"` LabelsURL string `json:"labels_url"` ReleasesURL string `json:"releases_url"` DeploymentsURL string `json:"deployments_url"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` PushedAt time.Time `json:"pushed_at"` GitURL string `json:"git_url"` SSHURL string `json:"ssh_url"` CloneURL string `json:"clone_url"` SvnURL string `json:"svn_url"` Homepage string `json:"homepage"` Size int `json:"size"` StargazersCount int `json:"stargazers_count"` WatchersCount int `json:"watchers_count"` Language string `json:"language"` HasIssues bool `json:"has_issues"` HasDownloads bool `json:"has_downloads"` HasWiki bool `json:"has_wiki"` HasPages bool `json:"has_pages"` ForksCount int `json:"forks_count"` MirrorURL interface{} `json:"mirror_url"` OpenIssuesCount int `json:"open_issues_count"` Forks int `json:"forks"` OpenIssues int `json:"open_issues"` Watchers int `json:"watchers"` DefaultBranch string `json:"default_branch"` NetworkCount int `json:"network_count"` SubscribersCount int `json:"subscribers_count"` } func TestGetNoOptions(t *testing.T) { resp, _ := Get("http://httpbin.org/get", nil) verifyOkResponse(resp, t) } func TestGetNoOptionsCustomClient(t *testing.T) { resp, _ := Get("http://httpbin.org/get", &RequestOptions{HTTPClient: http.DefaultClient}) verifyOkResponse(resp, t) } func TestGetCustomTLSHandshakeTimeout(t *testing.T) { ro := &RequestOptions{TLSHandshakeTimeout: 10 * time.Millisecond} if _, err := Get("https://httpbin.org", ro); err == nil { t.Error("unexpected: successful TLS Handshake") } } func TestGetCustomDialTimeout(t *testing.T) { ro := &RequestOptions{DialTimeout: time.Nanosecond} if _, err := Get("http://httpbin.org", ro); err == nil { t.Error("unexpected: successful connection") } } func TestGetProxy(t *testing.T) { ch := make(chan string, 1) ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ch <- "real server" })) defer ts.Close() proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ch <- "proxy for " + r.URL.String() })) defer proxy.Close() pu, err := url.Parse(proxy.URL) if err != nil { t.Fatal(err) } resp, err := Head(ts.URL, &RequestOptions{Proxies: map[string]*url.URL{pu.Scheme: pu}}) defer http.DefaultTransport.(*http.Transport).CloseIdleConnections() if err != nil { t.Error("Unable to make request: ", err) } if resp.Ok != true { t.Error("Response is not OK for some reason: ", resp.StatusCode) } got := <-ch want := "proxy for " + ts.URL + "/" if got != want { t.Errorf("want %q, got %q", want, got) } } func TestGetSyncInvalidProxyScheme(t *testing.T) { resp, err := Get("http://httpbin.org/get", &RequestOptions{Proxies: map[string]*url.URL{"gopher": nil}}) if err != nil { t.Error("Request failed: ", err) } verifyOkResponse(resp, t) } func TestGetSyncNoOptions(t *testing.T) { resp, err := Get("http://httpbin.org/get", nil) if err != nil { t.Error("Request failed: ", err) } verifyOkResponse(resp, t) } func TestGetNoOptionsGzip(t *testing.T) { resp, _ := Get("https://httpbin.org/gzip", nil) verifyOkResponse(resp, t) } func TestGetWithCookies(t *testing.T) { resp, err := Get("http://httpbin.org/cookies", &RequestOptions{ Cookies: []*http.Cookie{ { Name: "TestCookie", Value: "Random Value", HttpOnly: true, Secure: false, }, { Name: "AnotherCookie", Value: "Some Value", HttpOnly: true, Secure: false, }, }, }) if err != nil { t.Error("Unable to make request", err) } if resp.Ok != true { t.Error("Request did not return OK") } myJSONStruct := &TestJSONCookies{} if err := resp.JSON(myJSONStruct); err != nil { t.Error("Cannot serialize cookie JSON: ", err) } if myJSONStruct.Cookies.TestCookie != "Random Value" { t.Errorf("Cookie value not set properly: %#v", myJSONStruct) } if myJSONStruct.Cookies.AnotherCookie != "Some Value" { t.Errorf("Cookie value not set properly: %#v", myJSONStruct) } } func TestGetWithCookiesCustomCookieJar(t *testing.T) { cookieJar, _ := cookiejar.New(nil) resp, err := Get("http://httpbin.org/cookies", &RequestOptions{ CookieJar: cookieJar, Cookies: []*http.Cookie{ { Name: "TestCookie", Value: "Random Value", HttpOnly: true, Secure: false, }, { Name: "AnotherCookie", Value: "Some Value", HttpOnly: true, Secure: false, }, }, }) if err != nil { t.Error("Unable to make request", err) } if resp.Ok != true { t.Error("Request did not return OK") } myJSONStruct := &TestJSONCookies{} if err := resp.JSON(myJSONStruct); err != nil { t.Error("Cannot serialize cookie JSON: ", err) } if myJSONStruct.Cookies.TestCookie != "Random Value" { t.Errorf("Cookie value not set properly: %#v", myJSONStruct) } if myJSONStruct.Cookies.AnotherCookie != "Some Value" { t.Errorf("Cookie value not set properly: %#v", myJSONStruct) } } func TestGetSession(t *testing.T) { session := NewSession(nil) resp, err := session.Get("http://httpbin.org/cookies/set", &RequestOptions{Params: map[string]string{"one": "two"}}) if err != nil { t.Fatal("Cannot set cookie: ", err) } if resp.Ok != true { t.Error("Request did not return OK") } resp, err = session.Get("http://httpbin.org/cookies/set", &RequestOptions{Params: map[string]string{"two": "three"}}) if err != nil { t.Fatal("Cannot set cookie: ", err) } if resp.Ok != true { t.Error("Request did not return OK") } resp, err = session.Get("http://httpbin.org/cookies/set", &RequestOptions{Params: map[string]string{"three": "four"}}) if err != nil { t.Fatal("Cannot set cookie: ", err) } if resp.Ok != true { t.Error("Request did not return OK") } cookieURL, err := url.Parse("http://httpbin.org") if err != nil { t.Error("We (for some reason) cannot parse the cookie URL") } if len(session.HTTPClient.Jar.Cookies(cookieURL)) != 3 { t.Error("Invalid number of cookies provided: ", session.HTTPClient.Jar.Cookies(cookieURL)) } for _, cookie := range session.HTTPClient.Jar.Cookies(cookieURL) { switch cookie.Name { case "one": if cookie.Value != "two" { t.Error("Cookie value is not valid", cookie) } case "two": if cookie.Value != "three" { t.Error("Cookie value is not valid", cookie) } case "three": if cookie.Value != "four" { t.Error("Cookie value is not valid", cookie) } default: t.Error("We should not have any other cookies: ", cookie) } } session.CloseIdleConnections() } //func TestGetNoOptionsDeflate(t *testing.T) { // verifyOkResponse(<-GetAsync("http://httpbin.org/deflate", nil), t) //} func xmlASCIIDecoder(charset string, input io.Reader) (io.Reader, error) { return input, nil } func TestGetInvalidURL(t *testing.T) { resp, err := Get("%../dir/", &RequestOptions{Params: map[string]string{"1": "2"}}) if err == nil { t.Error("Some how the request was valid to make request", err) } resp.ClearInternalBuffer() // This will panic without our nil checks } func TestGetInvalidURLNoParams(t *testing.T) { _, err := Get("%../dir/", nil) if err == nil { t.Error("Some how the request was valid to make request", err) } } func TestGetInvalidURLSession(t *testing.T) { session := NewSession(nil) if _, err := session.Get("%../dir/", nil); err == nil { t.Error("Some how the request was valid to make request ", err) } } func TestGetXMLSerialize(t *testing.T) { resp, err := Get("http://httpbin.org/xml", nil) if err != nil { t.Error("Unable to make request", err) } if resp.Ok != true { t.Error("Request did not return OK") } userXML := &GetXMLSample{} if err := resp.XML(userXML, xmlASCIIDecoder); err != nil { t.Error("Unable to consume the response as XML: ", err) } if userXML.Title != "Sample Slide Show" { t.Errorf("Invalid XML serialization %#v", userXML) } if err := resp.XML(int(123), nil); err == nil { t.Error("Still able to consume XML from used response") } } func TestGetCustomUserAgent(t *testing.T) { ro := &RequestOptions{UserAgent: "LeviBot 0.1"} resp, _ := Get("http://httpbin.org/get", ro) jsonResp := verifyOkResponse(resp, t) if jsonResp.Headers.UserAgent != "LeviBot 0.1" { t.Error("User agent header not properly set") } } func TestGetBasicAuth(t *testing.T) { ro := &RequestOptions{Auth: []string{"Levi", "Bot"}} resp, err := Get("http://httpbin.org/get", ro) // Not the usual JSON so copy and paste from below if err != nil { t.Error("Unable to make request", err) } if resp.Ok != true { t.Error("Request did not return OK") } myJSONStruct := &BasicGetResponseBasicAuth{} err = resp.JSON(myJSONStruct) if err != nil { t.Error("Unable to coerce to JSON", err) } if myJSONStruct.Headers.Authorization != "Basic TGV2aTpCb3Q=" { t.Error("Unable to set HTTP basic auth", myJSONStruct.Headers) } } func TestGetCustomHeader(t *testing.T) { ro := &RequestOptions{UserAgent: "LeviBot 0.1", Headers: map[string]string{"X-Wonderful-Header": "1"}} resp, err := Get("http://httpbin.org/get", ro) // Not the usual JSON so copy and paste from below if err != nil { t.Error("Unable to make request", err) } if resp.Ok != true { t.Error("Request did not return OK") } myJSONStruct := &BasicGetResponseNewHeader{} err = resp.JSON(myJSONStruct) if err != nil { t.Error("Unable to coerce to JSON", err) } if myJSONStruct.Headers.XWonderfulHeader != "1" { t.Error("Unable to set custom HTTP header", myJSONStruct.Headers) } } func TestGetInvalidSSLCertNoVerify(t *testing.T) { ro := &RequestOptions{InsecureSkipVerify: true} for _, badSSL := range []string{ "https://self-signed.badssl.com/", "https://expired.badssl.com/", "https://wrong.host.badssl.com/", } { resp, err := Get(badSSL, ro) if err != nil { t.Error("Unable to make request", err) } if resp.Ok != true { t.Error("Request did not return OK") } } } func TestGetInvalidSSLCertNoVerifyNoOptions(t *testing.T) { for _, badSSL := range []string{ "https://self-signed.badssl.com/", "https://expired.badssl.com/", "https://wrong.host.badssl.com/", } { resp, err := Get(badSSL, nil) if err == nil { t.Error("Unable to make request", err) } if resp.Ok == true { t.Error("Request did not return OK") } } } func TestGetInvalidSSLCertNoCompression(t *testing.T) { ro := &RequestOptions{UserAgent: "LeviBot 0.1", DisableCompression: true} resp, err := Get("https://self-signed.badssl.com/", ro) if err == nil { t.Error("SSL verification worked when it shouldn't of", err) } if resp.Ok == true { t.Error("Request did return OK") } } func TestGetInvalidSSLCertWithCompression(t *testing.T) { ro := &RequestOptions{UserAgent: "LeviBot 0.1", DisableCompression: false} resp, err := Get("https://self-signed.badssl.com/", ro) if err == nil { t.Error("SSL verification worked when it shouldn't of", err) } if resp.Ok == true { t.Error("Request did return OK") } } func TestErrorResponseNOOP(t *testing.T) { ro := &RequestOptions{UserAgent: "LeviBot 0.1", DisableCompression: false} resp, err := Get("https://self-signed.badssl.com/", ro) if err == nil { t.Error("SSL verification worked when it shouldn't of", err) } if resp.Ok == true { t.Error("Request did return OK") } myJSONStruct := &BasicGetResponseArgs{} if err := resp.JSON(myJSONStruct); err == nil { t.Error("Somehow Able to convert to JSON", err) } if resp.Bytes() != nil { t.Error("Somehow byte buffer is working now (bytes)", resp.Bytes()) } if resp.String() != "" { t.Error("Somehow byte buffer is working now (bytes)", resp.String()) } resp.ClearInternalBuffer() if resp.Bytes() != nil { t.Error("Somehow byte buffer is working now (bytes)", resp.Bytes()) } if resp.String() != "" { t.Error("Somehow byte buffer is working now (bytes)", resp.String()) } userXML := &GetXMLSample{} if err := resp.XML(userXML, xmlASCIIDecoder); err == nil { t.Errorf("Somehow to consume the response as XML: %#v", userXML) } fileName := "randomFile" if err := resp.DownloadToFile(fileName); err == nil { t.Error("Somehow able to download to file: ", err) } var buf [1]byte if written, err := resp.Read(buf[:]); written != -1 && err == nil { t.Error("Somehow we were able to read from our error response") } } func TestGetInvalidSSLCertNoCompressionNoVerify(t *testing.T) { ro := &RequestOptions{UserAgent: "LeviBot 0.1", InsecureSkipVerify: true, DisableCompression: true} resp, err := Get("https://self-signed.badssl.com/", ro) if err != nil { t.Error("SSL verification worked when it shouldn't of", err) } if resp.Ok != true { t.Error("Request did return OK") } } func TestGetInvalidSSLCertWithCompressionNoVerify(t *testing.T) { ro := &RequestOptions{UserAgent: "LeviBot 0.1", InsecureSkipVerify: true, DisableCompression: false} resp, err := Get("https://self-signed.badssl.com/", ro) if err != nil { t.Error("SSL verification worked when it shouldn't of", err) } if resp.Ok != true { t.Error("Request did return OK") } } func TestGetInvalidSSLCert(t *testing.T) { ro := &RequestOptions{UserAgent: "LeviBot 0.1"} resp, err := Get("https://self-signed.badssl.com/", ro) if err == nil { t.Error("SSL verification worked when it shouldn't of", err) } if resp.Ok == true { t.Error("Request did return OK") } } func TestGetBasicArgs(t *testing.T) { ro := &RequestOptions{ Params: map[string]string{"Hello": "World"}, } resp, _ := Get("http://httpbin.org/get?Goodbye=World", ro) verifyOkArgsResponse(resp, t) } func TestGetBasicArgsQueryStruct(t *testing.T) { ro := &RequestOptions{ QueryStruct: struct { Hello string `url:"Hello"` }{ "World", }, } resp, _ := Get("http://httpbin.org/get?Goodbye=World", ro) verifyOkArgsResponse(resp, t) } func TestGetBasicArgsQueryStructErr(t *testing.T) { ro := &RequestOptions{ QueryStruct: 5, } resp, err := Get("http://httpbin.org/get?Goodbye=World", ro) if err == nil { t.Error("URL Parsing should have failed") } if resp.Ok == true { t.Error("Request did return OK") } } func TestGetBasicArgsQueryStructUrlQueryErr(t *testing.T) { ro := &RequestOptions{ QueryStruct: 5, } resp, err := Get("http://httpbin.org/get?Goodbye=World%zz", ro) if err == nil { t.Error("URL Parsing should have failed") } if resp.Ok == true { t.Error("Request did return OK") } } func TestGetBasicArgsQueryStructUrlErr(t *testing.T) { ro := &RequestOptions{ QueryStruct: 5, } resp, err := Get("%", ro) if err == nil { t.Error("URL Parsing should have failed") } if resp.Ok == true { t.Error("Request did return OK") } } func TestGetBasicArgsErr(t *testing.T) { ro := &RequestOptions{ Params: map[string]string{"Hello": "World"}, } resp, err := Get("http://httpbin.org/get?Goodbye=%zzz", ro) if err == nil { t.Error("URL Parsing should have failed") } if resp.Ok == true { t.Error("Request did return OK") } } func TestGetBasicArgsParams(t *testing.T) { ro := &RequestOptions{ Params: map[string]string{"Hello": "World", "Goodbye": "World"}, } resp, _ := Get("http://httpbin.org/get", ro) verifyOkArgsResponse(resp, t) } func TestGetBasicArgsParamsOverwrite(t *testing.T) { ro := &RequestOptions{ Params: map[string]string{"Hello": "World", "Goodbye": "World"}, } resp, _ := Get("http://httpbin.org/get?Hello=Nothing", ro) verifyOkArgsResponse(resp, t) } func TestGetFileDownload(t *testing.T) { resp, err := Get("http://httpbin.org/get", nil) fileName := "randomFile" if err := resp.DownloadToFile(fileName); err != nil { t.Error("Unable to download to file: ", err) } if err := resp.DownloadToFile("."); err == nil { t.Error("Able to create file '.'") } fd, err := os.Open(fileName) defer fd.Close() defer os.Remove(fileName) if err != nil { t.Error("Unable to open file to verify content ", err) } jsonDecoder := json.NewDecoder(fd) myJSONStruct := &BasicGetResponse{} if err := jsonDecoder.Decode(myJSONStruct); err != nil { t.Error("Unable to cocerce file to JSON ", err) } if myJSONStruct.URL != "http://httpbin.org/get" { t.Error("For some reason the URL isn't the same", myJSONStruct.URL) } if myJSONStruct.Headers.Host != "httpbin.org" { t.Error("The host header is invalid") } if resp.Bytes() != nil { t.Error("JSON decoding did not fully consume the response stream (Bytes)", resp.Bytes()) } if resp.String() != "" { t.Error("JSON decoding did not fully consume the response stream (String)", resp.String()) } if resp.StatusCode != 200 { t.Error("Response returned a non-200 code") } } func TestJsonConsumedResponse(t *testing.T) { resp, err := Get("http://httpbin.org/get", nil) if err != nil { t.Error("Unable to make request", err) } if resp.Ok != true { t.Error("Request did not return OK") } if resp.Bytes() == nil { t.Error("Unable to coerce value to bytes", resp.Bytes()) } resp.ClearInternalBuffer() if err := resp.JSON(struct{}{}); err == nil { t.Error("Struct should not be able to hold JSON: ") } } func TestDownloadConsumedResponse(t *testing.T) { resp, err := Get("http://httpbin.org/get", nil) if err != nil { t.Error("Unable to make request", err) } if resp.Ok != true { t.Error("Request did not return OK") } if resp.Bytes() == nil { t.Error("Unable to coerce value to bytes") } resp.ClearInternalBuffer() if err := resp.DownloadToFile("randomFile"); err == nil { t.Error("Still able to download file: ", err) } defer os.Remove("randomFile") } func TestGetBytes(t *testing.T) { resp, err := Get("http://httpbin.org/get", nil) if err != nil { t.Error("Unable to make request", err) } if resp.Ok != true { t.Error("Request did not return OK") } if resp.Bytes() == nil { t.Error("JSON decoding did not fully consume the response stream") } if bytes.Compare(resp.Bytes(), resp.Bytes()) != 0 { t.Error("Body bytes have not been cached", resp.Bytes()) } } func TestGetBytesNoBuffer(t *testing.T) { resp, err := Get("http://httpbin.org/get", nil) if err != nil { t.Error("Unable to make request", err) } if resp.Ok != true { t.Error("Request did not return OK") } if resp.Bytes() == nil { t.Error("Cannot coerce HTTP response to bytes") } if bytes.Compare(resp.Bytes(), resp.Bytes()) != 0 { t.Error("Body bytes have not been cached", resp.Bytes()) } if err := resp.DownloadToFile("randomFile"); err != nil { t.Error("Unable to download file: ", err) } defer os.Remove("randomFile") resp.ClearInternalBuffer() if resp.Bytes() != nil { t.Error("Internal Buffer not cleaned up") } } func TestGetString(t *testing.T) { resp, err := Get("http://httpbin.org/get", nil) if err != nil { t.Error("Unable to make request", err) } if resp.Ok != true { t.Error("Request did not return OK") } if resp.String() == "" { t.Error("Response Stream not returned as string", resp.String()) } if resp.String() != resp.String() { t.Error("Body string have not been cached", resp.String()) } if err := resp.DownloadToFile("randomFile"); err != nil { t.Error("Unable to download file: ", err) } defer os.Remove("randomFile") resp.ClearInternalBuffer() if resp.String() != "" { t.Error("Internal Buffer not cleaned up") } } func TestGetRedirectHeaderCopy(t *testing.T) { srv := httptest.NewServer(http.DefaultServeMux) http.HandleFunc("/foo", func(w http.ResponseWriter, req *http.Request) { if req.Header.Get("X-Custom") == "" { http.Error(w, "no custom header found", http.StatusBadRequest) return } }) resp, err := Get(srv.URL+"/foo", &RequestOptions{Headers: map[string]string{"X-Custom": "1"}}) if err != nil { t.Error("Redirect request failed", err) } if resp.Ok != true { t.Error("Request did not return OK") } srv.Close() } func TestGetRedirectSecretHeaderNoCopy(t *testing.T) { srv := httptest.NewServer(http.DefaultServeMux) http.HandleFunc("/sec", func(w http.ResponseWriter, req *http.Request) { if req.Header.Get("X-Custom") == "" { http.Error(w, "no custom header found", http.StatusBadRequest) return } }) resp, err := Get(srv.URL+"/sec", &RequestOptions{ Headers: map[string]string{"X-Custom": "1"}, SensitiveHTTPHeaders: map[string]struct{}{"X-Custom": {}}, }) if err != nil { t.Error("Redirect request failed", err) } if resp.Ok != true { t.Error("Request did not return OK") } srv.Close() } func TestMassiveJSONFile(t *testing.T) { if testing.Short() { t.Skip("Skipping massive JSON file download because short was called") } resp, err := Get("https://raw.githubusercontent.com/levigross/sf-city-lots-json/master/citylots.json", nil) if err != nil { t.Error("Request to massive JSON blob failed", err) } myjson := &MassiveJSONBlob{} if err := resp.JSON(myjson); err != nil { t.Error("Unable to serialize massive JSON blob", err) } if myjson.Type != "FeatureCollection" { t.Error("JSON did not properly serialize") } } func TestGitHubSelfJSON(t *testing.T) { resp, err := Get("https://api.github.com/repos/levigross/grequests", nil) if err != nil { t.Error("Request to reddit JSON blob failed", err) } myjson := &GithubSelfJSON{} if err := resp.JSON(myjson); err != nil { t.Error("Unable to serialize reddit JSON blob", err) } } func TestUnlimitedRedirects(t *testing.T) { srv := httptest.NewServer(http.DefaultServeMux) http.HandleFunc("/bar", func(w http.ResponseWriter, req *http.Request) { http.Redirect(w, req, "/bar", http.StatusMovedPermanently) }) resp, err := Get(srv.URL+"/bar", &RequestOptions{Headers: map[string]string{"X-Custom": "1"}}) if err == nil { t.Error("Redirect limitation failed", err) } if resp.Ok == true { t.Error("Request did not returned") } srv.Close() } func TestAuthStripOnRedirect(t *testing.T) { t.SkipNow() srv := httptest.NewServer(http.DefaultServeMux) http.HandleFunc("/test/", func(w http.ResponseWriter, req *http.Request) { if req.Header.Get("Authorization") != "" { http.Error(w, "Found Auth: "+req.Header.Get("Authorization"), http.StatusInternalServerError) return } if req.Header.Get("WWW-Authenticate") != "" { http.Error(w, "Found Auth: "+req.Header.Get("WWW-Authenticate"), http.StatusInternalServerError) return } if req.Header.Get("Proxy-Authorization") != "" { http.Error(w, "Found Auth: "+req.Header.Get("Proxy-Authorization"), http.StatusInternalServerError) return } io.WriteString(w, "OK") }) resp, err := Get(srv.URL+"/test", &RequestOptions{ Auth: []string{"one ", "two"}, Headers: map[string]string{"WWW-Authenticate": "foo", "Proxy-Authorization": "bar"}, }) if err != nil { t.Error("Request had creds inside", err) } if resp.Ok != true { t.Error("Request had creds inside", resp.StatusCode, resp.String()) } srv.Close() } func TestNoRedirect(t *testing.T) { srv := httptest.NewServer(http.DefaultServeMux) http.HandleFunc("/3tester/", func(w http.ResponseWriter, req *http.Request) { http.Redirect(w, req, "/", http.StatusMovedPermanently) }) client := &http.Client{ CheckRedirect: func(req *http.Request, via []*http.Request) error { return errors.New("cancel redirection") }, } _, err := Get(srv.URL+"/3tester/", &RequestOptions{ HTTPClient: client, }) if err == nil { t.Error("Request passed when it was supposed to fail on redirect", err) } srv.Close() } func verifyOkArgsResponse(resp *Response, t *testing.T) *BasicGetResponseArgs { if resp.Error != nil { t.Error("Unable to make request", resp.Error) } if resp.Ok != true { t.Error("Request did not return OK") } myJSONStruct := &BasicGetResponseArgs{} if err := resp.JSON(myJSONStruct); err != nil { t.Error("Unable to coerce to JSON", err) } if myJSONStruct.Args.Goodbye != "World" && myJSONStruct.Args.Hello != "World" { t.Error("Args not properly set", myJSONStruct.Args) } if myJSONStruct.URL != "http://httpbin.org/get?Goodbye=World&Hello=World" { t.Error("Url is not properly constructed", myJSONStruct.URL) } if resp.Bytes() != nil { t.Error("JSON decoding did not fully consume the response stream (Bytes)", resp.Bytes()) } if resp.String() != "" { t.Error("JSON decoding did not fully consume the response stream (String)", resp.String()) } if resp.StatusCode != 200 { t.Error("Response returned a non-200 code") } return myJSONStruct } func TestGetCustomRequestTimeout(t *testing.T) { ro := &RequestOptions{RequestTimeout: 2 * time.Nanosecond} if _, err := Get("http://httpbin.org", ro); err == nil { t.Error("unexpected: successful connection") } } func TestGetCustomRequestTimeoutContext(t *testing.T) { derContext := context.Background() ctx, cancel := context.WithTimeout(derContext, time.Microsecond) ro := &RequestOptions{Context: ctx} cancel() if _, err := Get("http://httpbin.org", ro); err == nil { t.Error("unexpected: successful connection") } } // verifyResponse will verify the following conditions // 1. The request didn't return an error // 2. The response returned an OK (a status code within the 200 range) // 3. The output can be coerced to JSON (this may change later) // It should only be run when testing GET request to http://httpbin.org/get expecting JSON func verifyOkResponse(resp *Response, t *testing.T) *BasicGetResponse { if resp.Error != nil { t.Error("Unable to make request", resp.Error) } if resp.Ok != true { t.Error("Request did not return OK") } myJSONStruct := &BasicGetResponse{} if err := resp.JSON(myJSONStruct); err != nil { t.Error("Unable to coerce to JSON", err) } if myJSONStruct.Headers.Host != "httpbin.org" { t.Error("The host header is invalid") } if resp.Bytes() != nil { t.Errorf("JSON decoding did not fully consume the response stream (Bytes) %#v", resp.Bytes()) } if resp.String() != "" { t.Error("JSON decoding did not fully consume the response stream (String)", resp.String()) } if resp.StatusCode != 200 { t.Error("Response returned a non-200 code") } return myJSONStruct }