Created
February 26, 2012 23:52
-
-
Save mfcollins3/1919871 to your computer and use it in GitHub Desktop.
Node.js (CoffeeScript) HTTP server that allows a Git client to clone a repository from an HTTP server
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
connect = require "connect" | |
child_process = require "child_process" | |
spawn = child_process.spawn | |
url = require "url" | |
getInfoRefs = (req, res) -> | |
res.setHeader "Expires", "Fri, 01 Jan 1980 00:00:00 GMT" | |
res.setHeader "Pragma", "no-cache" | |
res.setHeader "Cache-Control", "no-cache, max-age=0, must-revalidate" | |
res.setHeader "Content-Type", "application/x-#{req.query.service}-advertisement" | |
packet = "# service=#{req.query.service}\n" | |
length = packet.length + 4 | |
hex = "0123456789abcdef" | |
prefix = hex.charAt (length >> 12) & 0xf | |
prefix = prefix + hex.charAt (length >> 8) & 0xf | |
prefix = prefix + hex.charAt (length >> 4) & 0xf | |
prefix = prefix + hex.charAt length & 0xf | |
console.log "packet = #{prefix}#{packet}" | |
res.write "#{prefix}#{packet}0000" | |
git = spawn "#{req.query.service}", ["--stateless-rpc", "--advertise-refs", "/Users/michael/Projects/Test/root"] | |
git.stdout.on "data", (data) -> | |
res.write data | |
git.on "exit", -> | |
res.end() | |
serviceRpc = (req, res) -> | |
throw new Error "Expected POST with Content-Type application/x-git-upload-pack-request, but received #{req.headers['content-type']} instead" if req.headers["content-type"] isnt "application/x-git-upload-pack-request" | |
postData = "" | |
req.on "data", (data) -> | |
postData = postData + data | |
req.on "end", -> | |
res.setHeader "Expires", "Fri, 01 Jan 1980 00:00:00 GMT" | |
res.setHeader "Pragma", "no-cache" | |
res.setHeader "Cache-Control", "no-cache, max-age=0, must-revalidate" | |
res.setHeader "Content-Type", "application/x-git-upload-pack-result" | |
git = spawn "git-upload-pack", ["--stateless-rpc", "/Users/michael/Projects/Test/root"] | |
git.stdout.on "data", (data) -> | |
res.write data | |
git.on "exit", -> | |
res.end() | |
result = git.stdin.write postData | |
receivePack = (req, res) -> | |
console.log "receivePack #{req.url}" | |
throw new Error "Expected POST with Content-Type application/x-git-receive-pack-request, but received #{req.headers['content-type']} instead" if req.headers["content-type"] isnt "application/x-git-receive-pack-request" | |
buffer = {} | |
req.on "data", (data) -> | |
buffer = data | |
req.on "end", -> | |
res.setHeader "Expires", "Fri, 01 Jan 1980 00:00:00 GMT" | |
res.setHeader "Pragma", "no-cache" | |
res.setHeader "Cache-Control", "no-cache, max-age=0, must-revalidate" | |
res.setHeader "Content-Type", "application/x-git-receive-pack-result" | |
git = spawn "git-receive-pack", ["--stateless-rpc", "/Users/michael/Projects/Test/root"] | |
git.stdout.on "data", (data) -> | |
res.write data | |
git.on "exit", -> | |
res.end() | |
result = git.stdin.write buffer | |
getTextFile = (req, res) -> | |
console.log "getTextFile #{req.url}" | |
throw new Error "Not implemented" | |
getInfoPacks = (req, res) -> | |
console.log "getInfoPacks #{req.url}" | |
throw new Error "Not implemented" | |
getLooseObject = (req, res) -> | |
console.log "getLooseObject #{req.url}" | |
throw new Error "Not implemented" | |
getPackFile = (req, res) -> | |
console.log "getPackFile #{req.url}" | |
throw new Error "Not implemented" | |
getIdxFile = (req, res) -> | |
console.log "getIdxFile #{req.url}" | |
throw new Error "Not implemented" | |
services = [ | |
{ | |
method: "GET" | |
pattern: /\/HEAD$/ | |
handler: getTextFile | |
} | |
{ | |
method: "GET" | |
pattern: /\/info\/refs$/ | |
handler: getInfoRefs | |
} | |
{ | |
method: "GET" | |
pattern: /\/objects\/info\/alternate$/ | |
handler: getTextFile | |
} | |
{ | |
method: "GET" | |
pattern: /\/objects\/info\/http-alternate$/ | |
handler: getTextFile | |
} | |
{ | |
method: "GET" | |
pattern: /\/objects\/info\/packs$/ | |
handler: getInfoPacks | |
} | |
{ | |
method: "GET" | |
pattern: /\/objects\/[0-9a-f]{2}\/[0-9a-f]{38}$/ | |
handler: getLooseObject | |
} | |
{ | |
method: "GET" | |
pattern: /\/objects\/pack\/pack-[0-9a-f]{40}\.pack$/ | |
handler: getPackFile | |
} | |
{ | |
method: "GET" | |
pattern: /\/objects\/pack\/pack-[0-9a-f]{40}\.idx$/ | |
handler: getIdxFile | |
} | |
{ | |
method: "POST" | |
pattern: /\/git-upload-pack$/ | |
handler: serviceRpc | |
} | |
{ | |
method: "POST" | |
pattern: /\/git-receive-pack$/ | |
handler: receivePack | |
} | |
] | |
server = connect.createServer( | |
connect.query(), | |
(req, res, next) -> | |
console.log "#{req.method} #{req.url}" | |
console.log req.headers | |
console.log req.query | |
method = req.method | |
if method is "HEAD" | |
method = "GET" | |
requestUrl = url.parse req.url | |
service = (service for service in services when service.pattern.test(requestUrl.pathname) and service.method is method) | |
throw new Error("Request not supported") if service.length is 0 | |
service[0].handler req, res | |
) | |
server.listen 3000 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment