@@ -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