@@ -11,7 +11,7 @@ import (
11
11
"fmt"
12
12
"net/http"
13
13
"os"
14
- "path"
14
+ "path/filepath "
15
15
"regexp"
16
16
"strconv"
17
17
"strings"
@@ -90,11 +90,10 @@ func httpBase(ctx *context.Context) *serviceHandler {
90
90
91
91
isWiki := false
92
92
unitType := unit .TypeCode
93
- var wikiRepoName string
93
+
94
94
if strings .HasSuffix (reponame , ".wiki" ) {
95
95
isWiki = true
96
96
unitType = unit .TypeWiki
97
- wikiRepoName = reponame
98
97
reponame = reponame [:len (reponame )- 5 ]
99
98
}
100
99
@@ -107,16 +106,16 @@ func httpBase(ctx *context.Context) *serviceHandler {
107
106
repoExist := true
108
107
repo , err := repo_model .GetRepositoryByName (ctx , owner .ID , reponame )
109
108
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 ) {
117
110
ctx .ServerError ("GetRepositoryByName" , err )
118
111
return nil
119
112
}
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
120
119
}
121
120
122
121
// Don't allow pushing if the repo is archived
@@ -292,22 +291,9 @@ func httpBase(ctx *context.Context) *serviceHandler {
292
291
293
292
environ = append (environ , repo_module .EnvRepoID + fmt .Sprintf ("=%d" , repo .ID ))
294
293
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
304
295
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 }
311
297
}
312
298
313
299
var (
@@ -352,32 +338,31 @@ func dummyInfoRefs(ctx *context.Context) {
352
338
_ , _ = ctx .Write (infoRefsCache )
353
339
}
354
340
355
- type serviceConfig struct {
356
- UploadPack bool
357
- ReceivePack bool
358
- Env []string
359
- }
360
-
361
341
type serviceHandler struct {
362
- cfg * serviceConfig
363
- w http.ResponseWriter
364
- r * http.Request
365
- dir string
342
+ repo * repo_model.Repository
343
+ isWiki bool
366
344
environ []string
367
345
}
368
346
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" )
373
358
}
374
359
375
- func ( h * serviceHandler ) setHeaderCacheForever ( ) {
360
+ func setHeaderCacheForever ( ctx * context. Context ) {
376
361
now := time .Now ().Unix ()
377
362
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" )
381
366
}
382
367
383
368
func containsParentDirectorySeparator (v string ) bool {
@@ -394,95 +379,95 @@ func containsParentDirectorySeparator(v string) bool {
394
379
395
380
func isSlashRune (r rune ) bool { return r == '/' || r == '\\' }
396
381
397
- func (h * serviceHandler ) sendFile (contentType , file string ) {
382
+ func (h * serviceHandler ) sendFile (ctx * context. Context , contentType , file string ) {
398
383
if containsParentDirectorySeparator (file ) {
399
384
log .Error ("request file path contains invalid path: %v" , file )
400
- h . w .WriteHeader (http .StatusBadRequest )
385
+ ctx . Resp .WriteHeader (http .StatusBadRequest )
401
386
return
402
387
}
403
- reqFile := path .Join (h .dir , file )
388
+ reqFile := filepath .Join (h .getRepoDir () , file )
404
389
405
390
fi , err := os .Stat (reqFile )
406
391
if os .IsNotExist (err ) {
407
- h . w .WriteHeader (http .StatusNotFound )
392
+ ctx . Resp .WriteHeader (http .StatusNotFound )
408
393
return
409
394
}
410
395
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 )
415
400
}
416
401
417
402
// one or more key=value pairs separated by colons
418
403
var safeGitProtocolHeader = regexp .MustCompile (`^[0-9a-zA-Z]+=[0-9a-zA-Z]+(:[0-9a-zA-Z]+=[0-9a-zA-Z]+)*$` )
419
404
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
423
408
}
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
426
411
}
427
412
428
413
return nil , fmt .Errorf ("service %q is not allowed" , service )
429
414
}
430
415
431
- func serviceRPC (h * serviceHandler , service string ) {
416
+ func serviceRPC (ctx * context. Context , h * serviceHandler , service string ) {
432
417
defer func () {
433
- if err := h . r .Body .Close (); err != nil {
418
+ if err := ctx . Req .Body .Close (); err != nil {
434
419
log .Error ("serviceRPC: Close: %v" , err )
435
420
}
436
421
}()
437
422
438
423
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 )
442
427
return
443
428
}
444
429
445
- cmd , err := prepareGitCmdWithAllowedService (service , h )
430
+ cmd , err := prepareGitCmdWithAllowedService (ctx , service )
446
431
if err != nil {
447
432
log .Error ("Failed to prepareGitCmdWithService: %v" , err )
448
- h . w .WriteHeader (http .StatusUnauthorized )
433
+ ctx . Resp .WriteHeader (http .StatusUnauthorized )
449
434
return
450
435
}
451
436
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 ))
453
438
454
- reqBody := h . r .Body
439
+ reqBody := ctx . Req .Body
455
440
456
441
// Handle GZIP.
457
- if h . r .Header .Get ("Content-Encoding" ) == "gzip" {
442
+ if ctx . Req .Header .Get ("Content-Encoding" ) == "gzip" {
458
443
reqBody , err = gzip .NewReader (reqBody )
459
444
if err != nil {
460
445
log .Error ("Fail to create gzip reader: %v" , err )
461
- h . w .WriteHeader (http .StatusInternalServerError )
446
+ ctx . Resp .WriteHeader (http .StatusInternalServerError )
462
447
return
463
448
}
464
449
}
465
450
466
451
// set this for allow pre-receive and post-receive execute
467
452
h .environ = append (h .environ , "SSH_ORIGINAL_COMMAND=" + service )
468
453
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 ) {
470
455
h .environ = append (h .environ , "GIT_PROTOCOL=" + protocol )
471
456
}
472
457
473
458
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 () ))
476
461
if err := cmd .Run (& git.RunOpts {
477
- Dir : h .dir ,
462
+ Dir : h .getRepoDir () ,
478
463
Env : append (os .Environ (), h .environ ... ),
479
- Stdout : h . w ,
464
+ Stdout : ctx . Resp ,
480
465
Stdin : reqBody ,
481
466
Stderr : & stderr ,
482
467
UseContextTimeout : true ,
483
468
}); err != nil {
484
469
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 ())
486
471
}
487
472
return
488
473
}
@@ -492,20 +477,20 @@ func serviceRPC(h *serviceHandler, service string) {
492
477
func ServiceUploadPack (ctx * context.Context ) {
493
478
h := httpBase (ctx )
494
479
if h != nil {
495
- serviceRPC (h , "upload-pack" )
480
+ serviceRPC (ctx , h , "upload-pack" )
496
481
}
497
482
}
498
483
499
484
// ServiceReceivePack implements Git Smart HTTP protocol
500
485
func ServiceReceivePack (ctx * context.Context ) {
501
486
h := httpBase (ctx )
502
487
if h != nil {
503
- serviceRPC (h , "receive-pack" )
488
+ serviceRPC (ctx , h , "receive-pack" )
504
489
}
505
490
}
506
491
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" )
509
494
if ! strings .HasPrefix (serviceType , "git-" ) {
510
495
return ""
511
496
}
@@ -534,28 +519,28 @@ func GetInfoRefs(ctx *context.Context) {
534
519
if h == nil {
535
520
return
536
521
}
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 )
540
525
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 ) {
542
527
h .environ = append (h .environ , "GIT_PROTOCOL=" + protocol )
543
528
}
544
529
h .environ = append (os .Environ (), h .environ ... )
545
530
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 () })
547
532
if err != nil {
548
533
log .Error (fmt .Sprintf ("%v - %s" , err , string (refs )))
549
534
}
550
535
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 )
556
541
} 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" )
559
544
}
560
545
}
561
546
@@ -564,12 +549,12 @@ func GetTextFile(p string) func(*context.Context) {
564
549
return func (ctx * context.Context ) {
565
550
h := httpBase (ctx )
566
551
if h != nil {
567
- h . setHeaderNoCache ()
552
+ setHeaderNoCache (ctx )
568
553
file := ctx .Params ("file" )
569
554
if file != "" {
570
- h .sendFile ("text/plain" , "objects/info/" + file )
555
+ h .sendFile (ctx , "text/plain" , "objects/info/" + file )
571
556
} else {
572
- h .sendFile ("text/plain" , p )
557
+ h .sendFile (ctx , "text/plain" , p )
573
558
}
574
559
}
575
560
}
@@ -579,17 +564,17 @@ func GetTextFile(p string) func(*context.Context) {
579
564
func GetInfoPacks (ctx * context.Context ) {
580
565
h := httpBase (ctx )
581
566
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" )
584
569
}
585
570
}
586
571
587
572
// GetLooseObject implements Git dumb HTTP
588
573
func GetLooseObject (ctx * context.Context ) {
589
574
h := httpBase (ctx )
590
575
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" ,
593
578
ctx .Params ("head" ), ctx .Params ("hash" )))
594
579
}
595
580
}
@@ -598,16 +583,16 @@ func GetLooseObject(ctx *context.Context) {
598
583
func GetPackFile (ctx * context.Context ) {
599
584
h := httpBase (ctx )
600
585
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" )
603
588
}
604
589
}
605
590
606
591
// GetIdxFile implements Git dumb HTTP
607
592
func GetIdxFile (ctx * context.Context ) {
608
593
h := httpBase (ctx )
609
594
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" )
612
597
}
613
598
}
0 commit comments