Merge pull request #175 from hawser/http-trace

trace http calls in DoHTTP()
This commit is contained in:
risk danger olson 2015-03-05 14:46:48 -07:00
commit 140b755fbf
3 changed files with 132 additions and 37 deletions

@ -38,7 +38,7 @@ func Download(oidPath string) (io.ReadCloser, int64, *WrappedError) {
}
req.Header.Set("Accept", gitMediaType)
res, wErr := doRequest(req, creds)
res, wErr := doHTTPWithCreds(req, creds)
if wErr != nil {
return nil, 0, wErr
@ -190,11 +190,10 @@ func callOptions(filehash string) (int, *WrappedError) {
return 0, Errorf(err, "Unable to build OPTIONS request for %s", oid)
}
res, wErr := doRequest(req, creds)
res, wErr := doHTTPWithCreds(req, creds)
if wErr != nil {
return 0, wErr
}
tracerx.Printf("api_options_status: %d", res.StatusCode)
return res.StatusCode, nil
}
@ -240,8 +239,7 @@ func callPut(filehash, filename string, cb CopyCallback) *WrappedError {
fmt.Printf("Sending %s\n", filename)
tracerx.Printf("api_put: %s %s", oid, filename)
res, wErr := doRequest(req, creds)
tracerx.Printf("api_put_status: %d", res.StatusCode)
_, wErr := doHTTPWithCreds(req, creds)
return wErr
}
@ -296,7 +294,6 @@ func callExternalPut(filehash, filename string, obj *objectResource, cb CopyCall
if err != nil {
return Errorf(err, "Error attempting to PUT %s", filename)
}
tracerx.Printf("external_put_status: %d", res.StatusCode)
saveCredentials(creds, res)
// Run the verify callback
@ -323,7 +320,6 @@ func callExternalPut(filehash, filename string, obj *objectResource, cb CopyCall
if err != nil {
return Errorf(err, "Error attempting to verify %s", filename)
}
tracerx.Printf("verify_status: %d", verifyRes.StatusCode)
saveCredentials(verifyCreds, verifyRes)
return nil
@ -354,11 +350,10 @@ func callPost(filehash, filename string) (*objectResource, int, *WrappedError) {
req.Header.Set("Accept", gitMediaMetaType)
tracerx.Printf("api_post: %s %s", oid, filename)
res, wErr := doRequest(req, creds)
res, wErr := doHTTPWithCreds(req, creds)
if wErr != nil {
return nil, 0, wErr
}
tracerx.Printf("api_post_status: %d", res.StatusCode)
if res.StatusCode == 202 {
obj := &objectResource{}
@ -404,7 +399,9 @@ func validateMediaHeader(contentType string, reader io.Reader) (bool, int, *Wrap
return true, headerSize, nil
}
func doRequest(req *http.Request, creds Creds) (*http.Response, *WrappedError) {
// Wraps DoHTTP(), and saves or removes credentials from the git credential
// store based on the response.
func doHTTPWithCreds(req *http.Request, creds Creds) (*http.Response, *WrappedError) {
res, err := DoHTTP(Config, req)
var wErr *WrappedError

@ -17,15 +17,24 @@ type Configuration struct {
remotes []string
httpClient *http.Client
redirectingHttpClient *http.Client
isTracingHttp bool
}
var (
Config = &Configuration{CurrentRemote: defaultRemote}
Config = NewConfig()
RedirectError = fmt.Errorf("Unexpected redirection")
httpPrefixRe = regexp.MustCompile("\\Ahttps?://")
defaultRemote = "origin"
)
func NewConfig() *Configuration {
c := &Configuration{
CurrentRemote: defaultRemote,
isTracingHttp: len(os.Getenv("GIT_CURL_VERBOSE")) > 0,
}
return c
}
func (c *Configuration) Endpoint() string {
if url, ok := c.GitConfig("hawser.url"); ok {
return url
@ -69,24 +78,18 @@ func (c *Configuration) RemoteEndpoint(remote string) string {
}
func (c *Configuration) Remotes() []string {
if c.remotes == nil {
c.loadGitConfig()
}
c.loadGitConfig()
return c.remotes
}
func (c *Configuration) GitConfig(key string) (string, bool) {
if c.gitConfig == nil {
c.loadGitConfig()
}
c.loadGitConfig()
value, ok := c.gitConfig[strings.ToLower(key)]
return value, ok
}
func (c *Configuration) SetConfig(key, value string) {
if c.gitConfig == nil {
c.loadGitConfig()
}
c.loadGitConfig()
c.gitConfig[key] = value
}
@ -107,6 +110,10 @@ type AltConfig struct {
}
func (c *Configuration) loadGitConfig() {
if c.gitConfig != nil {
return
}
uniqRemotes := make(map[string]bool)
c.gitConfig = make(map[string]string)
@ -148,10 +155,3 @@ func (c *Configuration) loadGitConfig() {
c.remotes = append(c.remotes, remote)
}
}
func configFileExists(filename string) bool {
if _, err := os.Stat(filename); err == nil {
return true
}
return false
}

@ -2,17 +2,36 @@ package hawser
import (
"crypto/tls"
"fmt"
"github.com/rubyist/tracerx"
"io"
"net/http"
"os"
"strings"
)
func DoHTTP(c *Configuration, req *http.Request) (*http.Response, error) {
var res *http.Response
var err error
var counter *countingBody
if req.Body != nil {
counter = newCountingBody(req.Body)
req.Body = counter
}
traceHttpRequest(c, req)
switch req.Method {
case "GET", "HEAD":
return c.RedirectingHttpClient().Do(req)
res, err = c.RedirectingHttpClient().Do(req)
default:
return c.HttpClient().Do(req)
res, err = c.HttpClient().Do(req)
}
traceHttpResponse(c, res, counter)
return res, err
}
func (c *Configuration) HttpClient() *http.Client {
@ -29,18 +48,97 @@ func (c *Configuration) HttpClient() *http.Client {
func (c *Configuration) RedirectingHttpClient() *http.Client {
if c.redirectingHttpClient == nil {
c.redirectingHttpClient = &http.Client{
Transport: httpTransportFor(c),
tr := &http.Transport{}
sslVerify, _ := c.GitConfig("http.sslverify")
if sslVerify == "false" || len(os.Getenv("GIT_SSL_NO_VERIFY")) > 0 {
tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}
c.redirectingHttpClient = &http.Client{Transport: tr}
}
return c.redirectingHttpClient
}
func httpTransportFor(c *Configuration) *http.Transport {
tr := &http.Transport{}
sslVerify, _ := c.GitConfig("http.sslverify")
if len(os.Getenv("GIT_SSL_NO_VERIFY")) > 0 || sslVerify == "false" {
tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
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
}
fmt.Fprintf(os.Stderr, "> %s %s %s\n", req.Method, req.URL.RequestURI(), req.Proto)
for key, _ := range req.Header {
fmt.Fprintf(os.Stderr, "> %s: %s\n", key, req.Header.Get(key))
}
return tr
}
func traceHttpResponse(c *Configuration, res *http.Response, counter *countingBody) {
tracerx.Printf("HTTP: %d", res.StatusCode)
if c.isTracingHttp == false {
return
}
if counter != nil {
fmt.Fprintf(os.Stderr, "* upload sent off: %d bytes\n", counter.Size)
}
fmt.Fprintf(os.Stderr, "\n")
fmt.Fprintf(os.Stderr, "< %s %s\n", res.Proto, res.Status)
for key, _ := range res.Header {
fmt.Fprintf(os.Stderr, "< %s: %s\n", key, res.Header.Get(key))
}
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
}
}
if traceBody {
fmt.Fprintf(os.Stderr, "\n")
res.Body = newTracedBody(res.Body)
}
fmt.Fprintf(os.Stderr, "\n")
}
type countingBody struct {
body io.ReadCloser
Size int64
}
func (r *countingBody) Read(p []byte) (int, error) {
n, err := r.body.Read(p)
r.Size += int64(n)
return n, err
}
func (r *countingBody) Close() error {
return r.body.Close()
}
func newCountingBody(body io.ReadCloser) *countingBody {
return &countingBody{body, 0}
}
type tracedBody struct {
body io.ReadCloser
}
func (r *tracedBody) Read(p []byte) (int, error) {
n, err := r.body.Read(p)
fmt.Fprintf(os.Stderr, "%s\n", string(p[0:n]))
return n, err
}
func (r *tracedBody) Close() error {
return r.body.Close()
}
func newTracedBody(body io.ReadCloser) *tracedBody {
return &tracedBody{body}
}