@@ -11,7 +11,7 @@ import (
1111 "fmt"
1212 "net/http"
1313 "os"
14- "path"
14+ "path/filepath "
1515 "regexp"
1616 "strconv"
1717 "strings"
@@ -90,11 +90,10 @@ func httpBase(ctx *context.Context) *serviceHandler {
9090
9191 isWiki := false
9292 unitType := unit .TypeCode
93- var wikiRepoName string
93+
9494 if strings .HasSuffix (reponame , ".wiki" ) {
9595 isWiki = true
9696 unitType = unit .TypeWiki
97- wikiRepoName = reponame
9897 reponame = reponame [:len (reponame )- 5 ]
9998 }
10099
@@ -107,16 +106,16 @@ func httpBase(ctx *context.Context) *serviceHandler {
107106 repoExist := true
108107 repo , err := repo_model .GetRepositoryByName (ctx , owner .ID , reponame )
109108 if err != nil {
110- if repo_model .IsErrRepoNotExist (err ) {
111- if redirectRepoID , err := repo_model .LookupRedirect (ctx , owner .ID , reponame ); err == nil {
112- context .RedirectToRepo (ctx .Base , redirectRepoID )
113- return nil
114- }
115- repoExist = false
116- } else {
109+ if ! repo_model .IsErrRepoNotExist (err ) {
117110 ctx .ServerError ("GetRepositoryByName" , err )
118111 return nil
119112 }
113+
114+ if redirectRepoID , err := repo_model .LookupRedirect (ctx , owner .ID , reponame ); err == nil {
115+ context .RedirectToRepo (ctx .Base , redirectRepoID )
116+ return nil
117+ }
118+ repoExist = false
120119 }
121120
122121 // Don't allow pushing if the repo is archived
@@ -292,22 +291,9 @@ func httpBase(ctx *context.Context) *serviceHandler {
292291
293292 environ = append (environ , repo_module .EnvRepoID + fmt .Sprintf ("=%d" , repo .ID ))
294293
295- w := ctx .Resp
296- r := ctx .Req
297- cfg := & serviceConfig {
298- UploadPack : true ,
299- ReceivePack : true ,
300- Env : environ ,
301- }
302-
303- r .URL .Path = strings .ToLower (r .URL .Path ) // blue: In case some repo name has upper case name
294+ ctx .Req .URL .Path = strings .ToLower (ctx .Req .URL .Path ) // blue: In case some repo name has upper case name
304295
305- dir := repo_model .RepoPath (username , reponame )
306- if isWiki {
307- dir = repo_model .RepoPath (username , wikiRepoName )
308- }
309-
310- return & serviceHandler {cfg , w , r , dir , cfg .Env }
296+ return & serviceHandler {repo , isWiki , environ }
311297}
312298
313299var (
@@ -352,32 +338,31 @@ func dummyInfoRefs(ctx *context.Context) {
352338 _ , _ = ctx .Write (infoRefsCache )
353339}
354340
355- type serviceConfig struct {
356- UploadPack bool
357- ReceivePack bool
358- Env []string
359- }
360-
361341type serviceHandler struct {
362- cfg * serviceConfig
363- w http.ResponseWriter
364- r * http.Request
365- dir string
342+ repo * repo_model.Repository
343+ isWiki bool
366344 environ []string
367345}
368346
369- func (h * serviceHandler ) setHeaderNoCache () {
370- h .w .Header ().Set ("Expires" , "Fri, 01 Jan 1980 00:00:00 GMT" )
371- h .w .Header ().Set ("Pragma" , "no-cache" )
372- h .w .Header ().Set ("Cache-Control" , "no-cache, max-age=0, must-revalidate" )
347+ func (h * serviceHandler ) getRepoDir () string {
348+ if h .isWiki {
349+ return h .repo .WikiPath ()
350+ }
351+ return h .repo .RepoPath ()
352+ }
353+
354+ func setHeaderNoCache (ctx * context.Context ) {
355+ ctx .Resp .Header ().Set ("Expires" , "Fri, 01 Jan 1980 00:00:00 GMT" )
356+ ctx .Resp .Header ().Set ("Pragma" , "no-cache" )
357+ ctx .Resp .Header ().Set ("Cache-Control" , "no-cache, max-age=0, must-revalidate" )
373358}
374359
375- func ( h * serviceHandler ) setHeaderCacheForever ( ) {
360+ func setHeaderCacheForever ( ctx * context. Context ) {
376361 now := time .Now ().Unix ()
377362 expires := now + 31536000
378- h . w .Header ().Set ("Date" , fmt .Sprintf ("%d" , now ))
379- h . w .Header ().Set ("Expires" , fmt .Sprintf ("%d" , expires ))
380- h . w .Header ().Set ("Cache-Control" , "public, max-age=31536000" )
363+ ctx . Resp .Header ().Set ("Date" , fmt .Sprintf ("%d" , now ))
364+ ctx . Resp .Header ().Set ("Expires" , fmt .Sprintf ("%d" , expires ))
365+ ctx . Resp .Header ().Set ("Cache-Control" , "public, max-age=31536000" )
381366}
382367
383368func containsParentDirectorySeparator (v string ) bool {
@@ -394,95 +379,95 @@ func containsParentDirectorySeparator(v string) bool {
394379
395380func isSlashRune (r rune ) bool { return r == '/' || r == '\\' }
396381
397- func (h * serviceHandler ) sendFile (contentType , file string ) {
382+ func (h * serviceHandler ) sendFile (ctx * context. Context , contentType , file string ) {
398383 if containsParentDirectorySeparator (file ) {
399384 log .Error ("request file path contains invalid path: %v" , file )
400- h . w .WriteHeader (http .StatusBadRequest )
385+ ctx . Resp .WriteHeader (http .StatusBadRequest )
401386 return
402387 }
403- reqFile := path .Join (h .dir , file )
388+ reqFile := filepath .Join (h .getRepoDir () , file )
404389
405390 fi , err := os .Stat (reqFile )
406391 if os .IsNotExist (err ) {
407- h . w .WriteHeader (http .StatusNotFound )
392+ ctx . Resp .WriteHeader (http .StatusNotFound )
408393 return
409394 }
410395
411- h . w .Header ().Set ("Content-Type" , contentType )
412- h . w .Header ().Set ("Content-Length" , fmt .Sprintf ("%d" , fi .Size ()))
413- h . w .Header ().Set ("Last-Modified" , fi .ModTime ().Format (http .TimeFormat ))
414- http .ServeFile (h . w , h . r , reqFile )
396+ ctx . Resp .Header ().Set ("Content-Type" , contentType )
397+ ctx . Resp .Header ().Set ("Content-Length" , fmt .Sprintf ("%d" , fi .Size ()))
398+ ctx . Resp .Header ().Set ("Last-Modified" , fi .ModTime ().Format (http .TimeFormat ))
399+ http .ServeFile (ctx . Resp , ctx . Req , reqFile )
415400}
416401
417402// one or more key=value pairs separated by colons
418403var safeGitProtocolHeader = regexp .MustCompile (`^[0-9a-zA-Z]+=[0-9a-zA-Z]+(:[0-9a-zA-Z]+=[0-9a-zA-Z]+)*$` )
419404
420- func prepareGitCmdWithAllowedService (service string , h * serviceHandler ) (* git.Command , error ) {
421- if service == "receive-pack" && h . cfg . ReceivePack {
422- return git .NewCommand (h . r . Context () , "receive-pack" ), nil
405+ func prepareGitCmdWithAllowedService (ctx * context. Context , service string ) (* git.Command , error ) {
406+ if service == "receive-pack" {
407+ return git .NewCommand (ctx , "receive-pack" ), nil
423408 }
424- if service == "upload-pack" && h . cfg . UploadPack {
425- return git .NewCommand (h . r . Context () , "upload-pack" ), nil
409+ if service == "upload-pack" {
410+ return git .NewCommand (ctx , "upload-pack" ), nil
426411 }
427412
428413 return nil , fmt .Errorf ("service %q is not allowed" , service )
429414}
430415
431- func serviceRPC (h * serviceHandler , service string ) {
416+ func serviceRPC (ctx * context. Context , h * serviceHandler , service string ) {
432417 defer func () {
433- if err := h . r .Body .Close (); err != nil {
418+ if err := ctx . Req .Body .Close (); err != nil {
434419 log .Error ("serviceRPC: Close: %v" , err )
435420 }
436421 }()
437422
438423 expectedContentType := fmt .Sprintf ("application/x-git-%s-request" , service )
439- if h . r .Header .Get ("Content-Type" ) != expectedContentType {
440- log .Error ("Content-Type (%q) doesn't match expected: %q" , h . r .Header .Get ("Content-Type" ), expectedContentType )
441- h . w .WriteHeader (http .StatusUnauthorized )
424+ if ctx . Req .Header .Get ("Content-Type" ) != expectedContentType {
425+ log .Error ("Content-Type (%q) doesn't match expected: %q" , ctx . Req .Header .Get ("Content-Type" ), expectedContentType )
426+ ctx . Resp .WriteHeader (http .StatusUnauthorized )
442427 return
443428 }
444429
445- cmd , err := prepareGitCmdWithAllowedService (service , h )
430+ cmd , err := prepareGitCmdWithAllowedService (ctx , service )
446431 if err != nil {
447432 log .Error ("Failed to prepareGitCmdWithService: %v" , err )
448- h . w .WriteHeader (http .StatusUnauthorized )
433+ ctx . Resp .WriteHeader (http .StatusUnauthorized )
449434 return
450435 }
451436
452- h . w .Header ().Set ("Content-Type" , fmt .Sprintf ("application/x-git-%s-result" , service ))
437+ ctx . Resp .Header ().Set ("Content-Type" , fmt .Sprintf ("application/x-git-%s-result" , service ))
453438
454- reqBody := h . r .Body
439+ reqBody := ctx . Req .Body
455440
456441 // Handle GZIP.
457- if h . r .Header .Get ("Content-Encoding" ) == "gzip" {
442+ if ctx . Req .Header .Get ("Content-Encoding" ) == "gzip" {
458443 reqBody , err = gzip .NewReader (reqBody )
459444 if err != nil {
460445 log .Error ("Fail to create gzip reader: %v" , err )
461- h . w .WriteHeader (http .StatusInternalServerError )
446+ ctx . Resp .WriteHeader (http .StatusInternalServerError )
462447 return
463448 }
464449 }
465450
466451 // set this for allow pre-receive and post-receive execute
467452 h .environ = append (h .environ , "SSH_ORIGINAL_COMMAND=" + service )
468453
469- if protocol := h . r .Header .Get ("Git-Protocol" ); protocol != "" && safeGitProtocolHeader .MatchString (protocol ) {
454+ if protocol := ctx . Req .Header .Get ("Git-Protocol" ); protocol != "" && safeGitProtocolHeader .MatchString (protocol ) {
470455 h .environ = append (h .environ , "GIT_PROTOCOL=" + protocol )
471456 }
472457
473458 var stderr bytes.Buffer
474- cmd .AddArguments ("--stateless-rpc" ).AddDynamicArguments (h .dir )
475- cmd .SetDescription (fmt .Sprintf ("%s %s %s [repo_path: %s]" , git .GitExecutable , service , "--stateless-rpc" , h .dir ))
459+ cmd .AddArguments ("--stateless-rpc" ).AddDynamicArguments (h .getRepoDir () )
460+ cmd .SetDescription (fmt .Sprintf ("%s %s %s [repo_path: %s]" , git .GitExecutable , service , "--stateless-rpc" , h .getRepoDir () ))
476461 if err := cmd .Run (& git.RunOpts {
477- Dir : h .dir ,
462+ Dir : h .getRepoDir () ,
478463 Env : append (os .Environ (), h .environ ... ),
479- Stdout : h . w ,
464+ Stdout : ctx . Resp ,
480465 Stdin : reqBody ,
481466 Stderr : & stderr ,
482467 UseContextTimeout : true ,
483468 }); err != nil {
484469 if err .Error () != "signal: killed" {
485- log .Error ("Fail to serve RPC(%s) in %s: %v - %s" , service , h .dir , err , stderr .String ())
470+ log .Error ("Fail to serve RPC(%s) in %s: %v - %s" , service , h .getRepoDir () , err , stderr .String ())
486471 }
487472 return
488473 }
@@ -492,20 +477,20 @@ func serviceRPC(h *serviceHandler, service string) {
492477func ServiceUploadPack (ctx * context.Context ) {
493478 h := httpBase (ctx )
494479 if h != nil {
495- serviceRPC (h , "upload-pack" )
480+ serviceRPC (ctx , h , "upload-pack" )
496481 }
497482}
498483
499484// ServiceReceivePack implements Git Smart HTTP protocol
500485func ServiceReceivePack (ctx * context.Context ) {
501486 h := httpBase (ctx )
502487 if h != nil {
503- serviceRPC (h , "receive-pack" )
488+ serviceRPC (ctx , h , "receive-pack" )
504489 }
505490}
506491
507- func getServiceType (r * http. Request ) string {
508- serviceType := r .FormValue ("service" )
492+ func getServiceType (ctx * context. Context ) string {
493+ serviceType := ctx . Req .FormValue ("service" )
509494 if ! strings .HasPrefix (serviceType , "git-" ) {
510495 return ""
511496 }
@@ -534,28 +519,28 @@ func GetInfoRefs(ctx *context.Context) {
534519 if h == nil {
535520 return
536521 }
537- h . setHeaderNoCache ()
538- service := getServiceType (h . r )
539- cmd , err := prepareGitCmdWithAllowedService (service , h )
522+ setHeaderNoCache (ctx )
523+ service := getServiceType (ctx )
524+ cmd , err := prepareGitCmdWithAllowedService (ctx , service )
540525 if err == nil {
541- if protocol := h . r .Header .Get ("Git-Protocol" ); protocol != "" && safeGitProtocolHeader .MatchString (protocol ) {
526+ if protocol := ctx . Req .Header .Get ("Git-Protocol" ); protocol != "" && safeGitProtocolHeader .MatchString (protocol ) {
542527 h .environ = append (h .environ , "GIT_PROTOCOL=" + protocol )
543528 }
544529 h .environ = append (os .Environ (), h .environ ... )
545530
546- refs , _ , err := cmd .AddArguments ("--stateless-rpc" , "--advertise-refs" , "." ).RunStdBytes (& git.RunOpts {Env : h .environ , Dir : h .dir })
531+ refs , _ , err := cmd .AddArguments ("--stateless-rpc" , "--advertise-refs" , "." ).RunStdBytes (& git.RunOpts {Env : h .environ , Dir : h .getRepoDir () })
547532 if err != nil {
548533 log .Error (fmt .Sprintf ("%v - %s" , err , string (refs )))
549534 }
550535
551- h . w .Header ().Set ("Content-Type" , fmt .Sprintf ("application/x-git-%s-advertisement" , service ))
552- h . w .WriteHeader (http .StatusOK )
553- _ , _ = h . w .Write (packetWrite ("# service=git-" + service + "\n " ))
554- _ , _ = h . w .Write ([]byte ("0000" ))
555- _ , _ = h . w .Write (refs )
536+ ctx . Resp .Header ().Set ("Content-Type" , fmt .Sprintf ("application/x-git-%s-advertisement" , service ))
537+ ctx . Resp .WriteHeader (http .StatusOK )
538+ _ , _ = ctx . Resp .Write (packetWrite ("# service=git-" + service + "\n " ))
539+ _ , _ = ctx . Resp .Write ([]byte ("0000" ))
540+ _ , _ = ctx . Resp .Write (refs )
556541 } else {
557- updateServerInfo (ctx , h .dir )
558- h .sendFile ("text/plain; charset=utf-8" , "info/refs" )
542+ updateServerInfo (ctx , h .getRepoDir () )
543+ h .sendFile (ctx , "text/plain; charset=utf-8" , "info/refs" )
559544 }
560545}
561546
@@ -564,12 +549,12 @@ func GetTextFile(p string) func(*context.Context) {
564549 return func (ctx * context.Context ) {
565550 h := httpBase (ctx )
566551 if h != nil {
567- h . setHeaderNoCache ()
552+ setHeaderNoCache (ctx )
568553 file := ctx .Params ("file" )
569554 if file != "" {
570- h .sendFile ("text/plain" , "objects/info/" + file )
555+ h .sendFile (ctx , "text/plain" , "objects/info/" + file )
571556 } else {
572- h .sendFile ("text/plain" , p )
557+ h .sendFile (ctx , "text/plain" , p )
573558 }
574559 }
575560 }
@@ -579,17 +564,17 @@ func GetTextFile(p string) func(*context.Context) {
579564func GetInfoPacks (ctx * context.Context ) {
580565 h := httpBase (ctx )
581566 if h != nil {
582- h . setHeaderCacheForever ()
583- h .sendFile ("text/plain; charset=utf-8" , "objects/info/packs" )
567+ setHeaderCacheForever (ctx )
568+ h .sendFile (ctx , "text/plain; charset=utf-8" , "objects/info/packs" )
584569 }
585570}
586571
587572// GetLooseObject implements Git dumb HTTP
588573func GetLooseObject (ctx * context.Context ) {
589574 h := httpBase (ctx )
590575 if h != nil {
591- h . setHeaderCacheForever ()
592- h .sendFile ("application/x-git-loose-object" , fmt .Sprintf ("objects/%s/%s" ,
576+ setHeaderCacheForever (ctx )
577+ h .sendFile (ctx , "application/x-git-loose-object" , fmt .Sprintf ("objects/%s/%s" ,
593578 ctx .Params ("head" ), ctx .Params ("hash" )))
594579 }
595580}
@@ -598,16 +583,16 @@ func GetLooseObject(ctx *context.Context) {
598583func GetPackFile (ctx * context.Context ) {
599584 h := httpBase (ctx )
600585 if h != nil {
601- h . setHeaderCacheForever ()
602- h .sendFile ("application/x-git-packed-objects" , "objects/pack/pack-" + ctx .Params ("file" )+ ".pack" )
586+ setHeaderCacheForever (ctx )
587+ h .sendFile (ctx , "application/x-git-packed-objects" , "objects/pack/pack-" + ctx .Params ("file" )+ ".pack" )
603588 }
604589}
605590
606591// GetIdxFile implements Git dumb HTTP
607592func GetIdxFile (ctx * context.Context ) {
608593 h := httpBase (ctx )
609594 if h != nil {
610- h . setHeaderCacheForever ()
611- h .sendFile ("application/x-git-packed-objects-toc" , "objects/pack/pack-" + ctx .Params ("file" )+ ".idx" )
595+ setHeaderCacheForever (ctx )
596+ h .sendFile (ctx , "application/x-git-packed-objects-toc" , "objects/pack/pack-" + ctx .Params ("file" )+ ".idx" )
612597 }
613598}
0 commit comments