2015-03-19 19:30:55 +00:00
|
|
|
package lfs
|
2015-02-13 23:51:01 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/tls"
|
2015-03-26 17:10:17 +00:00
|
|
|
"errors"
|
2015-03-05 19:49:15 +00:00
|
|
|
"fmt"
|
2015-03-05 18:39:37 +00:00
|
|
|
"github.com/rubyist/tracerx"
|
2015-03-05 19:49:15 +00:00
|
|
|
"io"
|
2015-04-10 18:43:58 +00:00
|
|
|
"net"
|
2015-02-13 23:51:01 +00:00
|
|
|
"net/http"
|
|
|
|
"os"
|
2015-03-05 19:49:15 +00:00
|
|
|
"strings"
|
2015-04-10 18:43:58 +00:00
|
|
|
"time"
|
2015-02-13 23:51:01 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func DoHTTP(c *Configuration, req *http.Request) (*http.Response, error) {
|
2015-03-05 19:49:15 +00:00
|
|
|
traceHttpRequest(c, req)
|
2015-03-26 16:56:25 +00:00
|
|
|
res, err := c.HttpClient().Do(req)
|
2015-03-26 18:46:33 +00:00
|
|
|
if res == nil {
|
|
|
|
res = &http.Response{StatusCode: 0, Header: make(http.Header), Request: req}
|
|
|
|
}
|
2015-03-22 18:13:52 +00:00
|
|
|
traceHttpResponse(c, res)
|
2015-03-05 18:39:37 +00:00
|
|
|
return res, err
|
2015-02-13 23:51:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Configuration) HttpClient() *http.Client {
|
|
|
|
if c.httpClient == nil {
|
2015-04-10 18:43:58 +00:00
|
|
|
tr := &http.Transport{
|
|
|
|
Proxy: http.ProxyFromEnvironment,
|
|
|
|
Dial: (&net.Dialer{
|
2015-04-28 23:35:18 +00:00
|
|
|
Timeout: 5 * time.Second,
|
2015-04-10 18:43:58 +00:00
|
|
|
KeepAlive: 30 * time.Second,
|
|
|
|
}).Dial,
|
2015-04-28 23:35:18 +00:00
|
|
|
TLSHandshakeTimeout: 5 * time.Second,
|
2015-04-10 18:43:58 +00:00
|
|
|
}
|
2015-03-05 18:49:21 +00:00
|
|
|
sslVerify, _ := c.GitConfig("http.sslverify")
|
|
|
|
if sslVerify == "false" || len(os.Getenv("GIT_SSL_NO_VERIFY")) > 0 {
|
|
|
|
tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
2015-02-13 23:51:01 +00:00
|
|
|
}
|
2015-03-26 17:10:17 +00:00
|
|
|
c.httpClient = &http.Client{
|
|
|
|
Transport: tr,
|
|
|
|
CheckRedirect: checkRedirect,
|
|
|
|
}
|
2015-02-13 23:51:01 +00:00
|
|
|
}
|
2015-03-26 16:56:25 +00:00
|
|
|
return c.httpClient
|
2015-02-13 23:51:01 +00:00
|
|
|
}
|
2015-03-05 19:49:15 +00:00
|
|
|
|
2015-03-26 17:10:17 +00:00
|
|
|
func checkRedirect(req *http.Request, via []*http.Request) error {
|
|
|
|
if len(via) >= 3 {
|
|
|
|
return errors.New("stopped after 3 redirects")
|
|
|
|
}
|
|
|
|
|
|
|
|
oldest := via[0]
|
|
|
|
for key, _ := range oldest.Header {
|
2015-03-26 17:19:59 +00:00
|
|
|
if key == "Authorization" {
|
|
|
|
if req.URL.Scheme != oldest.URL.Scheme || req.URL.Host != oldest.URL.Host {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
2015-03-26 17:10:17 +00:00
|
|
|
req.Header.Set(key, oldest.Header.Get(key))
|
|
|
|
}
|
|
|
|
|
2015-03-26 18:46:33 +00:00
|
|
|
tracerx.Printf("api: redirect %s %s to %s", oldest.Method, oldest.URL, req.URL)
|
|
|
|
|
2015-03-26 17:10:17 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-03-05 19:49:15 +00:00
|
|
|
var tracedTypes = []string{"json", "text", "xml", "html"}
|
|
|
|
|
|
|
|
func traceHttpRequest(c *Configuration, req *http.Request) {
|
|
|
|
tracerx.Printf("HTTP: %s %s", req.Method, req.URL.String())
|
|
|
|
|
|
|
|
if c.isTracingHttp == false {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-03-22 20:13:37 +00:00
|
|
|
if req.Body != nil {
|
|
|
|
req.Body = newCountedRequest(req)
|
|
|
|
}
|
|
|
|
|
2015-03-05 21:04:07 +00:00
|
|
|
fmt.Fprintf(os.Stderr, "> %s %s %s\n", req.Method, req.URL.RequestURI(), req.Proto)
|
2015-03-05 19:49:15 +00:00
|
|
|
for key, _ := range req.Header {
|
2015-03-05 21:04:07 +00:00
|
|
|
fmt.Fprintf(os.Stderr, "> %s: %s\n", key, req.Header.Get(key))
|
2015-03-05 19:49:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-22 18:13:52 +00:00
|
|
|
func traceHttpResponse(c *Configuration, res *http.Response) {
|
2015-03-26 18:46:33 +00:00
|
|
|
if res == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-03-05 19:49:15 +00:00
|
|
|
tracerx.Printf("HTTP: %d", res.StatusCode)
|
|
|
|
|
|
|
|
if c.isTracingHttp == false {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-03-05 21:18:02 +00:00
|
|
|
fmt.Fprintf(os.Stderr, "\n")
|
2015-03-05 21:04:07 +00:00
|
|
|
fmt.Fprintf(os.Stderr, "< %s %s\n", res.Proto, res.Status)
|
2015-03-05 19:49:15 +00:00
|
|
|
for key, _ := range res.Header {
|
2015-03-05 21:04:07 +00:00
|
|
|
fmt.Fprintf(os.Stderr, "< %s: %s\n", key, res.Header.Get(key))
|
2015-03-05 19:49:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
traceBody := false
|
|
|
|
ctype := strings.ToLower(strings.SplitN(res.Header.Get("Content-Type"), ";", 2)[0])
|
|
|
|
for _, tracedType := range tracedTypes {
|
|
|
|
if strings.Contains(ctype, tracedType) {
|
|
|
|
traceBody = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-22 18:30:54 +00:00
|
|
|
res.Body = newCountedResponse(res)
|
2015-03-05 19:49:15 +00:00
|
|
|
if traceBody {
|
2015-03-05 21:04:07 +00:00
|
|
|
res.Body = newTracedBody(res.Body)
|
2015-03-05 19:49:15 +00:00
|
|
|
}
|
2015-03-05 21:19:53 +00:00
|
|
|
|
|
|
|
fmt.Fprintf(os.Stderr, "\n")
|
2015-03-05 19:49:15 +00:00
|
|
|
}
|
|
|
|
|
2015-03-22 18:30:54 +00:00
|
|
|
const (
|
|
|
|
countingUpload = iota
|
|
|
|
countingDownload
|
|
|
|
)
|
|
|
|
|
2015-03-05 21:18:02 +00:00
|
|
|
type countingBody struct {
|
2015-03-22 18:30:54 +00:00
|
|
|
Direction int
|
|
|
|
Size int64
|
2015-03-22 18:12:04 +00:00
|
|
|
io.ReadCloser
|
2015-03-05 21:18:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r *countingBody) Read(p []byte) (int, error) {
|
2015-03-22 18:12:04 +00:00
|
|
|
n, err := r.ReadCloser.Read(p)
|
2015-03-05 21:18:02 +00:00
|
|
|
r.Size += int64(n)
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *countingBody) Close() error {
|
2015-03-22 18:30:54 +00:00
|
|
|
if r.Direction == countingUpload {
|
|
|
|
fmt.Fprintf(os.Stderr, "* uploaded %d bytes\n", r.Size)
|
|
|
|
} else {
|
|
|
|
fmt.Fprintf(os.Stderr, "* downloaded %d bytes\n", r.Size)
|
|
|
|
}
|
2015-03-22 18:12:04 +00:00
|
|
|
return r.ReadCloser.Close()
|
2015-03-05 21:18:02 +00:00
|
|
|
}
|
|
|
|
|
2015-03-22 18:30:54 +00:00
|
|
|
func newCountedResponse(res *http.Response) *countingBody {
|
|
|
|
return &countingBody{countingDownload, 0, res.Body}
|
|
|
|
}
|
|
|
|
|
|
|
|
func newCountedRequest(req *http.Request) *countingBody {
|
|
|
|
return &countingBody{countingUpload, 0, req.Body}
|
2015-03-05 21:18:02 +00:00
|
|
|
}
|
|
|
|
|
2015-03-05 19:49:15 +00:00
|
|
|
type tracedBody struct {
|
2015-03-22 18:12:04 +00:00
|
|
|
io.ReadCloser
|
2015-03-05 19:49:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r *tracedBody) Read(p []byte) (int, error) {
|
2015-03-22 18:12:04 +00:00
|
|
|
n, err := r.ReadCloser.Read(p)
|
2015-03-05 21:04:07 +00:00
|
|
|
fmt.Fprintf(os.Stderr, "%s\n", string(p[0:n]))
|
2015-03-05 19:49:15 +00:00
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *tracedBody) Close() error {
|
2015-03-22 18:12:04 +00:00
|
|
|
return r.ReadCloser.Close()
|
2015-03-05 19:49:15 +00:00
|
|
|
}
|
|
|
|
|
2015-03-05 21:04:07 +00:00
|
|
|
func newTracedBody(body io.ReadCloser) *tracedBody {
|
|
|
|
return &tracedBody{body}
|
2015-03-05 19:49:15 +00:00
|
|
|
}
|