forked from nvdnkpr/git-http-backend
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
125 lines (105 loc) · 3.38 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
var Duplex = require('stream').Duplex;
var inherits = require('inherits');
var url = require('url');
var qs = require('querystring');
var Service = require('./lib/service.js');
var regex = {
'git-receive-pack': RegExp('([0-9a-fA-F]+) ([0-9a-fA-F]+)'
+ ' (refs\/[^ \u0000]+)( |00|\u0000)'
+ '|^(0000)$'
),
'git-upload-pack': /^\S+ ([0-9a-fA-F]+)/,
'done': /([0-9a-fA-F]+done|0000)\s*$/,
'have': /[0-9a-fA-F]+have\s[0-9a-fA-F]/
};
var fields = {
'git-receive-pack': [ 'last', 'head', 'refname' ],
'git-upload-pack': [ 'head', 'have' ]
};
module.exports = Backend;
inherits(Backend, Duplex);
function Backend (uri, cb) {
if (!(this instanceof Backend)) return new Backend(uri, cb);
var self = this;
Duplex.call(this);
if (cb) {
this.on('service', function (s) { cb(null, s) });
this.on('error', cb);
}
try { uri = decodeURIComponent(uri) }
catch (err) { return error(msg) }
var u = url.parse(uri);
if (/\.\/|\.\./.test(u.pathname)) return error('invalid git path');
this.parsed = false;
var parts = u.pathname.split('/');
if (/\/info\/refs$/.test(u.pathname)) {
var params = qs.parse(u.query);
this.service = params.service;
this.info = true;
}
else {
this.service = parts[parts.length-1];
}
if (this.service === 'git-upload-pack') {}
else if (this.service === 'git-receive-pack') {}
else return error('unsupported git service');
if (this.info) {
var service = new Service({ cmd: this.service, info: true }, self);
process.nextTick(function () {
self.emit('service', service);
});
}
function error (msg) {
var err = typeof msg === 'string' ? new Error(msg) : msg;
process.nextTick(function () { self.emit('error', err) });
}
}
Backend.prototype._read = function (n) {
if (this._stream && this._stream.next) {
this._ready = false;
this._stream.next();
}
else this._ready = n;
};
Backend.prototype._emitService = function() {
this.emit('service', new Service(this.serviceInfo, this));
};
Backend.prototype._write = function (buf, enc, next) {
if (this._stream) {
this._next = next;
this._stream.push(buf);
return;
}
else if (this.info) {
this._buffer = buf;
this._next = next;
return;
}
if (this._prev) buf = Buffer.concat([ this._prev, buf ]);
var m, s = buf.toString('utf8');
if (!this.serviceInfo && (m = regex[this.service].exec(s))) {
this._prev = null;
this._buffer = buf;
this._next = next;
var keys = fields[this.service];
var row = { cmd: this.service };
for (var i = 0; i < keys.length; i++) {
row[keys[i]] = m[i+1];
}
this.serviceInfo = row;
if ( this.service === 'git-receive-pack' ) return this._emitService();
}
else if (!this.serviceInfo && buf.length >= 512) {
return this.emit('error', new Error('unrecognized input'));
}
if (this.serviceInfo && regex.have.test(s)) {
this.serviceInfo.have = true;
return this._emitService(); // emit early because request can no longer be clone
}
if ( this.serviceInfo && regex.done.test(s)) {
this._emitService();
} else {
this._prev = (!this.serviceInfo ? buf : undefined);
next();
}
};