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

63
http.go
View File

@ -24,7 +24,7 @@ import "C"
import (
"bytes"
"errors"
"io/ioutil"
"fmt"
"net/http"
"reflect"
"runtime"
@ -124,7 +124,7 @@ func (self *ManagedTransport) Action(url string, action SmartService) (SmartSubt
}
req.Header["User-Agent"] = []string{"git/2.0 (git2go)"}
return newManagedHttpStream(req), nil
return newManagedHttpStream(self, req), nil
}
func (self *ManagedTransport) Close() error {
@ -148,15 +148,17 @@ func (self *ManagedTransport) ensureClient() error {
}
type ManagedHttpStream struct {
owner *ManagedTransport
req *http.Request
resp *http.Response
postBuffer bytes.Buffer
sentRequest bool
}
func newManagedHttpStream(req *http.Request) *ManagedHttpStream {
func newManagedHttpStream(owner *ManagedTransport, req *http.Request) *ManagedHttpStream {
return &ManagedHttpStream{
req: req,
owner: owner,
req: req,
}
}
@ -182,10 +184,51 @@ func (self *ManagedHttpStream) Free() {
}
func (self *ManagedHttpStream) sendRequest() error {
self.req.Body = ioutil.NopCloser(&self.postBuffer)
resp, err := http.DefaultClient.Do(self.req)
if err != nil {
return err
var resp *http.Response
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 {
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
@ -198,6 +241,10 @@ func setLibgit2Error(err error) C.int {
defer C.free(unsafe.Pointer(cstr))
C.giterr_set_str(C.GITERR_NET, cstr)
if gitErr, ok := err.(*GitError); ok {
return C.int(gitErr.Code)
}
return -1
}