@@ -6,13 +6,35 @@ var url = require('url')
66var  auth  =  require ( 'basic-auth' ) 
77var  chalk  =  require ( 'chalk' ) 
88var  fixturez  =  require ( 'fixturez' ) 
9- var  backend  =  require ( 'git-http-backend' ) 
109var  htpasswd  =  require ( 'htpasswd-js' ) 
1110
1211function  pad  ( str )  { 
1312  return  ( str  +  '    ' ) . slice ( 0 ,  7 ) 
1413} 
1514
15+ 
16+ function  matchInfo  ( req )  { 
17+   var  u  =  url . parse ( req . url ) 
18+   if  ( req . method  ===  'GET'  &&  u . pathname . endsWith ( '/info/refs' ) )  { 
19+     return  true 
20+   }  else  { 
21+     return  false 
22+   } 
23+ } 
24+ 
25+ function  matchService  ( req )  { 
26+   var  u  =  url . parse ( req . url ,  true ) 
27+   if  ( req . method  ===  'GET'  &&  u . pathname . endsWith ( '/info/refs' ) )  { 
28+     return  u . query . service 
29+   } 
30+   if  ( req . method  ===  'POST'  &&  req . headers [ 'content-type' ]  ===  'application/x-git-upload-pack-request' )  { 
31+     return  'git-upload-pack' 
32+   } 
33+   if  ( req . method  ===  'POST'  &&  req . headers [ 'content-type' ]  ===  'application/x-git-receive-pack-request' )  { 
34+     return  'git-receive-pack' 
35+   } 
36+ } 
37+ 
1638function  factory  ( config )  { 
1739  if  ( ! config . root )  throw  new  Error ( 'Missing required "gitHttpServer.root" config option' ) 
1840  if  ( ! config . route )  throw  new  Error ( 'Missing required "gitHttpServer.route" config option' ) 
@@ -23,17 +45,19 @@ function factory (config) {
2345  function  getGitDir  ( req )  { 
2446    var  u  =  url . parse ( req . url ) 
2547    if  ( u . pathname . startsWith ( config . route ) )  { 
26-       if  ( req . method  ===  'GET'  &&  u . pathname . endsWith ( '/info/refs' ) )  { 
48+       const  info  =  matchInfo ( req ) 
49+       if  ( info )  { 
2750        let  gitdir  =  u . pathname . replace ( config . route ,  '' ) . replace ( / \/ i n f o \/ r e f s $ / ,  '' ) . replace ( / ^ \/ / ,  '' ) 
2851        let  fixtureName  =  path . posix . basename ( gitdir ) 
2952        return  f . find ( fixtureName ) 
3053      } 
31-       if  ( req . method  ===  'POST'  &&  req . headers [ 'content-type' ]  ===  'application/x-git-upload-pack-request' )  { 
54+       const  service  =  matchService ( req ) 
55+       if  ( service  ===  'git-upload-pack' )  { 
3256        let  gitdir  =  u . pathname . replace ( config . route ,  '' ) . replace ( / \/ g i t - u p l o a d - p a c k $ / ,  '' ) . replace ( / ^ \/ / ,  '' ) 
3357        let  fixtureName  =  path . posix . basename ( gitdir ) 
3458        return  f . find ( fixtureName ) 
3559      } 
36-       if  ( req . method  ===  'POST'    &&   req . headers [ 'content-type' ]   ===   'application/x- git-receive-pack-request ')  { 
60+       if  ( service  ===  'git-receive-pack' )  { 
3761        let  gitdir  =  u . pathname . replace ( config . route ,  '' ) . replace ( / \/ g i t - r e c e i v e - p a c k $ / ,  '' ) . replace ( / ^ \/ / ,  '' ) 
3862        let  fixtureName  =  path . posix . basename ( gitdir ) 
3963        return  f . copy ( fixtureName ) 
@@ -43,78 +67,91 @@ function factory (config) {
4367  } 
4468
4569  return  async  function  middleware  ( req ,  res ,  next )  { 
46-     // handle pre-flight OPTIONS 
47-     if  ( req . method  ===  'OPTIONS' )  { 
48-       res . statusCode  =  204 
49-       res . end ( '' ) 
50-       console . log ( chalk . green ( '[git-http-server] 204 '  +  pad ( req . method )  +  ' '  +  req . url ) ) 
51-       return 
52-     } 
53-     if  ( ! next )  next  =  ( )  =>  void ( 0 ) 
54-     try  { 
55-       var  gitdir  =  getGitDir ( req ) 
56-     }  catch  ( err )  { 
57-       res . statusCode  =  404 
58-       res . end ( err . message  +  '\n' ) 
59-       console . log ( chalk . red ( '[git-http-server] 404 '  +  pad ( req . method )  +  ' '  +  req . url ) ) 
60-       return 
61-     } 
62-     if  ( gitdir  ==  null )  return  next ( ) 
63- 
64-     // Check for a .htaccess file 
65-     let  data  =  null 
6670    try  { 
67-       data  =  fs . readFileSync ( path . join ( gitdir ,  '.htpasswd' ) ,  'utf8' ) 
68-     }  catch  ( err )  { 
69-       // no .htaccess file, proceed without authentication 
70-     } 
71-     if  ( data )  { 
72-       // The previous line would have failed if there wasn't an .htaccess file, so 
73-       // we must treat this as protected. 
74-       let  cred  =  auth . parse ( req . headers [ 'authorization' ] ) 
75-       if  ( cred  ===  undefined )  { 
76-         res . statusCode  =  401 
77-         // The default reason phrase used in Node is "Unauthorized", but 
78-         // we will use "Authorization Required" to match what Github uses. 
79-         res . statusMessage  =  'Authorization Required' 
80-         res . setHeader ( 'WWW-Authenticate' ,  'Basic' ) 
81-         res . end ( 'Unauthorized'  +  '\n' ) 
82-         console . log ( chalk . green ( '[git-http-server] 401 '  +  pad ( req . method )  +  ' '  +  req . url ) ) 
71+       // handle pre-flight OPTIONS 
72+       if  ( req . method  ===  'OPTIONS' )  { 
73+         res . statusCode  =  204 
74+         res . end ( '' ) 
75+         console . log ( chalk . green ( '[git-http-server] 204 '  +  pad ( req . method )  +  ' '  +  req . url ) ) 
8376        return 
8477      } 
85-       let  valid  =  await  htpasswd . authenticate ( { 
86-         username : cred . name , 
87-         password : cred . pass , 
88-         data
89-       } ) 
90-       if  ( ! valid )  { 
91-         res . statusCode  =  401 
92-         // The default reason phrase used in Node is "Unauthorized", but 
93-         // we will use "Authorization Required" to match what Github uses. 
94-         res . statusMessage  =  'Authorization Required' 
95-         res . setHeader ( 'WWW-Authenticate' ,  'Basic' ) 
96-         res . end ( 'Bad credentials'  +  '\n' ) 
97-         console . log ( chalk . green ( '[git-http-server] 401 '  +  pad ( req . method )  +  ' '  +  req . url ) ) 
78+       if  ( ! next )  next  =  ( )  =>  void ( 0 ) 
79+       try  { 
80+         var  gitdir  =  getGitDir ( req ) 
81+       }  catch  ( err )  { 
82+         res . statusCode  =  404 
83+         res . end ( err . message  +  '\n' ) 
84+         console . log ( chalk . red ( '[git-http-server] 404 '  +  pad ( req . method )  +  ' '  +  req . url ) ) 
9885        return 
9986      } 
100-     } 
87+        if   ( gitdir   ==   null )   return   next ( ) 
10188
102-     req . pipe ( backend ( req . url ,  function  ( err ,  service )  { 
103-       if  ( err )  { 
104-         res . statusCode  =  500 
105-         res . end ( err  +  '\n' ) 
106-         console . log ( chalk . red ( '[git-http-server] 500 '  +  pad ( req . method )  +  ' '  +  req . url ) ) 
107-         return 
89+       // Check for a .htaccess file 
90+       let  data  =  null 
91+       try  { 
92+         data  =  fs . readFileSync ( path . join ( gitdir ,  '.htpasswd' ) ,  'utf8' ) 
93+       }  catch  ( err )  { 
94+         // no .htaccess file, proceed without authentication 
95+       } 
96+       if  ( data )  { 
97+         // The previous line would have failed if there wasn't an .htaccess file, so 
98+         // we must treat this as protected. 
99+         let  cred  =  auth . parse ( req . headers [ 'authorization' ] ) 
100+         if  ( cred  ===  undefined )  { 
101+           res . statusCode  =  401 
102+           // The default reason phrase used in Node is "Unauthorized", but 
103+           // we will use "Authorization Required" to match what Github uses. 
104+           res . statusMessage  =  'Authorization Required' 
105+           res . setHeader ( 'WWW-Authenticate' ,  'Basic' ) 
106+           res . end ( 'Unauthorized'  +  '\n' ) 
107+           console . log ( chalk . green ( '[git-http-server] 401 '  +  pad ( req . method )  +  ' '  +  req . url ) ) 
108+           return 
109+         } 
110+         let  valid  =  await  htpasswd . authenticate ( { 
111+           username : cred . name , 
112+           password : cred . pass , 
113+           data
114+         } ) 
115+         if  ( ! valid )  { 
116+           res . statusCode  =  401 
117+           // The default reason phrase used in Node is "Unauthorized", but 
118+           // we will use "Authorization Required" to match what Github uses. 
119+           res . statusMessage  =  'Authorization Required' 
120+           res . setHeader ( 'WWW-Authenticate' ,  'Basic' ) 
121+           res . end ( 'Bad credentials'  +  '\n' ) 
122+           console . log ( chalk . green ( '[git-http-server] 401 '  +  pad ( req . method )  +  ' '  +  req . url ) ) 
123+           return 
124+         } 
108125      } 
109126
127+       const  info  =  matchInfo ( req ) 
128+       const  service  =  matchService ( req ) 
110129      const  env  =  req . headers [ 'git-protocol' ]  ? {  GIT_PROTOCOL : req . headers [ 'git-protocol' ]  }  : { } 
111130
112-       res . setHeader ( 'content-type' ,  service . type ) 
131+       const  args  =  [ '--stateless-rpc'  ] ; 
132+       if  ( info )  args . push ( '--advertise-refs' ) 
133+       args . push ( gitdir ) 
134+ 
135+       if  ( info )  { 
136+         res . setHeader ( 'content-type' ,  `application/x-${ service }  -advertisement` ) 
137+         function  pack  ( s )  { 
138+             var  n  =  ( 4  +  s . length ) . toString ( 16 ) ; 
139+             return  Array ( 4  -  n . length  +  1 ) . join ( '0' )  +  n  +  s ; 
140+         } 
141+         res . write ( pack ( '# service='  +  service  +  '\n' )  +  '0000' ) ; 
142+       }  else  { 
143+         res . setHeader ( 'content-type' ,  `application/x-${ service }  -result` ) 
144+       } 
145+ 
146+       const  ps  =  spawn ( service ,  args ,  {  env } ) 
147+       req . pipe ( ps . stdin ) 
148+       ps . stdout . pipe ( res ) 
113149      console . log ( chalk . green ( '[git-http-server] 200 '  +  pad ( req . method )  +  ' '  +  req . url ) ) 
114-       // console.log('[git-http-server] ' + service.cmd + ' ' + service.args.concat(gitdir).join(' ')) 
115-       var  ps  =  spawn ( service . cmd ,  service . args . concat ( gitdir ) ,  {  env } ) 
116-       ps . stdout . pipe ( service . createStream ( ) ) . pipe ( ps . stdin ) 
117-     } ) ) . pipe ( res ) 
150+     }  catch  ( err )  { 
151+       res . statusCode  =  500 
152+       res . end ( err  +  '\n' ) 
153+       console . log ( chalk . red ( '[git-http-server] 500 '  +  pad ( req . method )  +  ' '  +  req . url ) ) 
154+     } 
118155  } 
119156} 
120157
0 commit comments