@@ -44,12 +44,12 @@ THE SOFTWARE.
4444package managed
4545
4646import (
47+ "bytes"
4748 "crypto/tls"
4849 "crypto/x509"
4950 "errors"
5051 "fmt"
5152 "io"
52- "io/ioutil"
5353 "net"
5454 "net/http"
5555 "net/url"
@@ -133,6 +133,25 @@ func (t *httpSmartSubtransport) Action(targetUrl string, action git2go.SmartServ
133133 stream .sendRequestBackground ()
134134 }
135135
136+ client .CheckRedirect = func (req * http.Request , via []* http.Request ) error {
137+ if len (via ) >= 3 {
138+ return fmt .Errorf ("too many redirects" )
139+ }
140+
141+ // golang will change POST to GET in case of redirects.
142+ if len (via ) >= 0 && req .Method != via [0 ].Method {
143+ if via [0 ].URL .Scheme == "https" && req .URL .Scheme == "http" {
144+ return fmt .Errorf ("downgrade from https to http is not allowed: from %q to %q" , via [0 ].URL .String (), req .URL .String ())
145+ }
146+ if via [0 ].URL .Host != req .URL .Host {
147+ return fmt .Errorf ("cross hosts redirects are not allowed: from %s to %s" , via [0 ].URL .Host , req .URL .Host )
148+ }
149+
150+ return http .ErrUseLastResponse
151+ }
152+ return nil
153+ }
154+
136155 return stream , nil
137156}
138157
@@ -165,7 +184,10 @@ func createClientRequest(targetUrl string, action git2go.SmartServiceAction, t *
165184 }
166185 }
167186
168- client := & http.Client {Transport : t , Timeout : fullHttpClientTimeOut }
187+ client := & http.Client {
188+ Transport : t ,
189+ Timeout : fullHttpClientTimeOut ,
190+ }
169191
170192 switch action {
171193 case git2go .SmartServiceActionUploadpackLs :
@@ -218,6 +240,7 @@ type httpSmartSubtransportStream struct {
218240 recvReply sync.WaitGroup
219241 httpError error
220242 m sync.RWMutex
243+ targetURL string
221244}
222245
223246func newManagedHttpStream (owner * httpSmartSubtransport , req * http.Request , client * http.Client ) * httpSmartSubtransportStream {
@@ -246,18 +269,21 @@ func (self *httpSmartSubtransportStream) Read(buf []byte) (int, error) {
246269 self .recvReply .Wait ()
247270
248271 self .m .RLock ()
249- defer self .m .RUnlock ()
250- if self .httpError != nil {
272+ err := self .httpError
273+ self .m .RUnlock ()
274+
275+ if err != nil {
251276 return 0 , self .httpError
252277 }
253-
254278 return self .resp .Body .Read (buf )
255279}
256280
257281func (self * httpSmartSubtransportStream ) Write (buf []byte ) (int , error ) {
258282 self .m .RLock ()
259- defer self .m .RUnlock ()
260- if self .httpError != nil {
283+ err := self .httpError
284+ self .m .RUnlock ()
285+
286+ if err != nil {
261287 return 0 , self .httpError
262288 }
263289 return self .writer .Write (buf )
@@ -308,29 +334,53 @@ func (self *httpSmartSubtransportStream) sendRequest() error {
308334 }
309335 }
310336
311- req := & http.Request {
312- Method : self .req .Method ,
313- URL : self .req .URL ,
314- Header : self .req .Header ,
315- }
316- if req .Method == "POST" {
317- req .Body = self .reader
318- req .ContentLength = - 1
319- }
337+ var content []byte
338+ for {
339+ req := & http.Request {
340+ Method : self .req .Method ,
341+ URL : self .req .URL ,
342+ Header : self .req .Header ,
343+ }
344+ if req .Method == "POST" {
345+ if len (content ) == 0 {
346+ // a copy of the request body needs to be saved so
347+ // it can be reused in case of redirects.
348+ if content , err = io .ReadAll (self .reader ); err != nil {
349+ return err
350+ }
351+ }
352+ req .Body = io .NopCloser (bytes .NewReader (content ))
353+ req .ContentLength = - 1
354+ }
320355
321- req .SetBasicAuth (userName , password )
322- resp , err = self .client .Do (req )
323- if err != nil {
324- return err
325- }
356+ req .SetBasicAuth (userName , password )
357+ resp , err = self .client .Do (req )
358+ if err != nil {
359+ return err
360+ }
326361
327- if resp .StatusCode == http .StatusOK {
328- self .resp = resp
329- self .sentRequest = true
330- return nil
362+ // GET requests will be automatically redirected.
363+ // POST require the new destination, and also the body content.
364+ if req .Method == "POST" && resp .StatusCode >= 301 && resp .StatusCode <= 308 {
365+ // The next try will go against the new destination
366+ self .req .URL , err = resp .Location ()
367+ if err != nil {
368+ return err
369+ }
370+
371+ continue
372+ }
373+
374+ if resp .StatusCode == http .StatusOK {
375+ break
376+ }
377+
378+ io .Copy (io .Discard , resp .Body )
379+ defer resp .Body .Close ()
380+ return fmt .Errorf ("Unhandled HTTP error %s" , resp .Status )
331381 }
332382
333- io . Copy ( ioutil . Discard , resp . Body )
334- defer resp . Body . Close ()
335- return fmt . Errorf ( "Unhandled HTTP error %s" , resp . Status )
383+ self . resp = resp
384+ self . sentRequest = true
385+ return nil
336386}
0 commit comments