Skip to content

Instantly share code, notes, and snippets.

@mfcollins3
Created February 26, 2012 23:52
Show Gist options
  • Save mfcollins3/1919871 to your computer and use it in GitHub Desktop.
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
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