http: call the credentials callback when facing a 401

The test for now just tests that we can handle returning a `GIT_EUSER` if the
caller decides to abort.
This commit is contained in:
Carlos Martín Nieto 2017-04-17 20:08:37 +02:00 committed by Carlos Martín Nieto
parent 464c24ab07
commit e2eda97316
1 changed files with 55 additions and 8 deletions

57
http.go
View File

@ -24,7 +24,7 @@ import "C"
import ( import (
"bytes" "bytes"
"errors" "errors"
"io/ioutil" "fmt"
"net/http" "net/http"
"reflect" "reflect"
"runtime" "runtime"
@ -124,7 +124,7 @@ func (self *ManagedTransport) Action(url string, action SmartService) (SmartSubt
} }
req.Header["User-Agent"] = []string{"git/2.0 (git2go)"} req.Header["User-Agent"] = []string{"git/2.0 (git2go)"}
return newManagedHttpStream(req), nil return newManagedHttpStream(self, req), nil
} }
func (self *ManagedTransport) Close() error { func (self *ManagedTransport) Close() error {
@ -148,14 +148,16 @@ func (self *ManagedTransport) ensureClient() error {
} }
type ManagedHttpStream struct { type ManagedHttpStream struct {
owner *ManagedTransport
req *http.Request req *http.Request
resp *http.Response resp *http.Response
postBuffer bytes.Buffer postBuffer bytes.Buffer
sentRequest bool sentRequest bool
} }
func newManagedHttpStream(req *http.Request) *ManagedHttpStream { func newManagedHttpStream(owner *ManagedTransport, req *http.Request) *ManagedHttpStream {
return &ManagedHttpStream{ return &ManagedHttpStream{
owner: owner,
req: req, req: req,
} }
} }
@ -182,12 +184,53 @@ func (self *ManagedHttpStream) Free() {
} }
func (self *ManagedHttpStream) sendRequest() error { func (self *ManagedHttpStream) sendRequest() error {
self.req.Body = ioutil.NopCloser(&self.postBuffer) var resp *http.Response
resp, err := http.DefaultClient.Do(self.req) var err error
var userName string
var password string
for {
req := &http.Request{
Method: self.req.Method,
URL: self.req.URL,
Header: self.req.Header,
}
req.SetBasicAuth(userName, password)
resp, err = http.DefaultClient.Do(req)
if err != nil { if err != nil {
return err return err
} }
if resp.StatusCode == http.StatusOK {
break
}
if resp.StatusCode == http.StatusUnauthorized {
resp.Body.Close()
var cred *C.git_cred
ret := C.git_transport_smart_credentials(&cred, self.owner.owner, nil, C.GIT_CREDTYPE_USERPASS_PLAINTEXT)
if ret != 0 {
return MakeGitError(ret)
}
if cred.credtype != C.GIT_CREDTYPE_USERPASS_PLAINTEXT {
C.git_cred_free(cred)
return fmt.Errorf("Unexpected credential type %d", cred.credtype)
}
ptCred := (*C.git_cred_userpass_plaintext)(unsafe.Pointer(cred))
userName = C.GoString(ptCred.username)
password = C.GoString(ptCred.password)
C.git_cred_free(cred)
continue
}
// Any other error we treat as a hard error and punt back to the caller
resp.Body.Close()
return fmt.Errorf("Unhandled HTTP error %s", resp.Status)
}
self.sentRequest = true self.sentRequest = true
self.resp = resp self.resp = resp
return nil return nil
@ -198,6 +241,10 @@ func setLibgit2Error(err error) C.int {
defer C.free(unsafe.Pointer(cstr)) defer C.free(unsafe.Pointer(cstr))
C.giterr_set_str(C.GITERR_NET, cstr) C.giterr_set_str(C.GITERR_NET, cstr)
if gitErr, ok := err.(*GitError); ok {
return C.int(gitErr.Code)
}
return -1 return -1
} }