Skip to content

Instantly share code, notes, and snippets.

@bvpatel
Forked from rowland/cookie_jar.go
Created December 14, 2020 06:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bvpatel/6030e1116a0fa75382f18ea147576476 to your computer and use it in GitHub Desktop.
Save bvpatel/6030e1116a0fa75382f18ea147576476 to your computer and use it in GitHub Desktop.
Go http client with cookie support
package main
import (
"http"
"os"
"io"
"io/ioutil"
"fmt"
"regexp"
"bytes"
"strconv"
)
func shouldRedirect(statusCode int) bool {
switch statusCode {
case http.StatusMovedPermanently, http.StatusFound, http.StatusSeeOther, http.StatusTemporaryRedirect:
return true
}
return false
}
type HttpClient struct {
client http.Client
jar CookieJar
Log io.Writer
patterns map[*regexp.Regexp]func(matches []string)
}
func (this *HttpClient) invokeCallbacks(url string) {
for re, cb := range this.patterns {
fmt.Printf("Examining %s...\n", re)
matches := re.FindStringSubmatch(url)
if matches != nil {
fmt.Printf("Executing callback for %s...\n", re)
cb(matches)
}
}
}
func urlencode(data map[string]string) *bytes.Buffer {
m := make(map[string][]string, len(data))
for k, v := range data {
m[k] = []string{v}
}
return bytes.NewBuffer([]byte(http.EncodeQuery(m)))
}
func (this *HttpClient) Do(method string, url string, data map[string]string) (resp *http.Response, finalURL string, err os.Error) {
var base *http.URL
var via []*http.Request
for redirects := 0; redirects < 10; redirects++ {
req := new(http.Request)
req.Method = method
if base == nil {
req.URL, err = http.ParseURL(url)
} else {
req.URL, err = base.ParseURL(url)
}
req.Header = http.Header{"Cookie": {this.jar.Encode()}}
if len(via) > 0 {
lastReq := via[len(via) - 1]
if lastReq.URL.Scheme != "https" {
req.Referer = lastReq.URL.String()
}
}
if data != nil {
body := urlencode(data)
fmt.Println(body)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Content-Length", strconv.Itoa(body.Len()))
req.Body = ioutil.NopCloser(body)
req.ContentLength = int64(body.Len())
}
fmt.Fprintf(this.Log, "%s: %s\n", method, req.URL)
resp, err = this.client.Do(req)
if err != nil {
return
}
this.jar.Update(resp.SetCookie)
if shouldRedirect(resp.StatusCode) {
resp.Body.Close()
if url = resp.Header.Get("Location"); url == "" {
err = os.NewError(fmt.Sprintf("%d response missing Location header", resp.StatusCode))
break
}
this.invokeCallbacks(url)
fmt.Fprintf(this.Log, "Redirected to: %s\n", url)
method = "GET"
base = req.URL
data = nil
via = append(via, req)
continue
}
finalURL = url
return
}
err = &http.URLError{method, url, err}
return
}
func (this *HttpClient) Get(url string) (resp *http.Response, finalURL string, err os.Error) {
return this.Do("GET", url, nil)
}
func (this *HttpClient) Match(pattern string, callback func(matches []string)) {
if this.patterns == nil {
this.patterns = make(map[*regexp.Regexp]func(matches []string))
}
re := regexp.MustCompile(ExpandRegex(pattern))
this.patterns[re] = callback
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment