grack 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/git_http.rb +304 -0
  2. metadata +3 -2
@@ -0,0 +1,304 @@
1
+ require 'zlib'
2
+ require 'rack/request'
3
+ require 'rack/response'
4
+ require 'rack/utils'
5
+ require 'time'
6
+
7
+ class GitHttp
8
+ class App
9
+
10
+ SERVICES = [
11
+ ["POST", 'service_rpc', "(.*?)/git-upload-pack$", 'upload-pack'],
12
+ ["POST", 'service_rpc', "(.*?)/git-receive-pack$", 'receive-pack'],
13
+
14
+ ["GET", 'get_info_refs', "(.*?)/info/refs$"],
15
+ ["GET", 'get_text_file', "(.*?)/HEAD$"],
16
+ ["GET", 'get_text_file', "(.*?)/objects/info/alternates$"],
17
+ ["GET", 'get_text_file', "(.*?)/objects/info/http-alternates$"],
18
+ ["GET", 'get_info_packs', "(.*?)/objects/info/packs$"],
19
+ ["GET", 'get_text_file', "(.*?)/objects/info/[^/]*$"],
20
+ ["GET", 'get_loose_object', "(.*?)/objects/[0-9a-f]{2}/[0-9a-f]{38}$"],
21
+ ["GET", 'get_pack_file', "(.*?)/objects/pack/pack-[0-9a-f]{40}\\.pack$"],
22
+ ["GET", 'get_idx_file', "(.*?)/objects/pack/pack-[0-9a-f]{40}\\.idx$"],
23
+ ]
24
+
25
+ def initialize(config = false)
26
+ set_config(config)
27
+ end
28
+
29
+ def set_config(config)
30
+ @config = config || {}
31
+ end
32
+
33
+ def set_config_setting(key, value)
34
+ @config[key] = value
35
+ end
36
+
37
+ def call(env)
38
+ @env = env
39
+ @req = Rack::Request.new(env)
40
+
41
+ cmd, path, @reqfile, @rpc = match_routing
42
+
43
+ return render_method_not_allowed if cmd == 'not_allowed'
44
+ return render_not_found if !cmd
45
+
46
+ @dir = get_git_dir(path)
47
+ return render_not_found if !@dir
48
+
49
+ Dir.chdir(@dir) do
50
+ self.method(cmd).call()
51
+ end
52
+ end
53
+
54
+ # ---------------------------------
55
+ # actual command handling functions
56
+ # ---------------------------------
57
+
58
+ def service_rpc
59
+ return render_no_access if !has_access(@rpc, true)
60
+ input = read_body
61
+
62
+ @res = Rack::Response.new
63
+ @res.status = 200
64
+ @res["Content-Type"] = "application/x-git-%s-result" % @rpc
65
+ @res.finish do
66
+ command = git_command("#{@rpc} --stateless-rpc #{@dir}")
67
+ IO.popen(command, File::RDWR) do |pipe|
68
+ pipe.write(input)
69
+ while !pipe.eof?
70
+ block = pipe.read(8192) # 8M at a time
71
+ @res.write block # steam it to the client
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ def get_info_refs
78
+ service_name = get_service_type
79
+
80
+ if has_access(service_name)
81
+ cmd = git_command("#{service_name} --stateless-rpc --advertise-refs .")
82
+ refs = `#{cmd}`
83
+
84
+ @res = Rack::Response.new
85
+ @res.status = 200
86
+ @res["Content-Type"] = "application/x-git-%s-advertisement" % service_name
87
+ hdr_nocache
88
+ @res.write(pkt_write("# service=git-#{service_name}\n"))
89
+ @res.write(pkt_flush)
90
+ @res.write(refs)
91
+ @res.finish
92
+ else
93
+ dumb_info_refs
94
+ end
95
+ end
96
+
97
+ def dumb_info_refs
98
+ update_server_info
99
+ send_file(@reqfile, "text/plain; charset=utf-8") do
100
+ hdr_nocache
101
+ end
102
+ end
103
+
104
+ def get_info_packs
105
+ # objects/info/packs
106
+ send_file(@reqfile, "text/plain; charset=utf-8") do
107
+ hdr_nocache
108
+ end
109
+ end
110
+
111
+ def get_loose_object
112
+ send_file(@reqfile, "application/x-git-loose-object") do
113
+ hdr_cache_forever
114
+ end
115
+ end
116
+
117
+ def get_pack_file
118
+ send_file(@reqfile, "application/x-git-packed-objects") do
119
+ hdr_cache_forever
120
+ end
121
+ end
122
+
123
+ def get_idx_file
124
+ send_file(@reqfile, "application/x-git-packed-objects-toc") do
125
+ hdr_cache_forever
126
+ end
127
+ end
128
+
129
+ def get_text_file
130
+ send_file(@reqfile, "text/plain") do
131
+ hdr_nocache
132
+ end
133
+ end
134
+
135
+ # ------------------------
136
+ # logic helping functions
137
+ # ------------------------
138
+
139
+ F = ::File
140
+
141
+ # some of this borrowed from the Rack::File implementation
142
+ def send_file(reqfile, content_type)
143
+ reqfile = File.join(@dir, reqfile)
144
+ return render_not_found if !F.exists?(reqfile)
145
+
146
+ @res = Rack::Response.new
147
+ @res.status = 200
148
+ @res["Content-Type"] = content_type
149
+ @res["Last-Modified"] = F.mtime(reqfile).httpdate
150
+
151
+ yield
152
+
153
+ if size = F.size?(reqfile)
154
+ @res["Content-Length"] = size.to_s
155
+ @res.finish do
156
+ F.open(reqfile, "rb") do |file|
157
+ while part = file.read(8192)
158
+ @res.write part
159
+ end
160
+ end
161
+ end
162
+ else
163
+ body = [F.read(reqfile)]
164
+ size = Rack::Utils.bytesize(body.first)
165
+ @res["Content-Length"] = size
166
+ @res.write body
167
+ @res.finish
168
+ end
169
+ end
170
+
171
+ def get_git_dir(path)
172
+ root = @config[:project_root] || `pwd`
173
+ path = File.join(root, path)
174
+ if File.exists?(path) # TODO: check is a valid git directory
175
+ return path
176
+ end
177
+ false
178
+ end
179
+
180
+ def get_service_type
181
+ service_type = @req.params['service']
182
+ return false if !service_type
183
+ return false if service_type[0, 4] != 'git-'
184
+ service_type.gsub('git-', '')
185
+ end
186
+
187
+ def match_routing
188
+ cmd = nil
189
+ path = nil
190
+ SERVICES.each do |method, handler, match, rpc|
191
+ if m = Regexp.new(match).match(@req.path_info)
192
+ return ['not_allowed'] if method != @req.request_method
193
+ cmd = handler
194
+ path = m[1]
195
+ file = @req.path_info.sub(path + '/', '')
196
+ return [cmd, path, file, rpc]
197
+ end
198
+ end
199
+ return nil
200
+ end
201
+
202
+ def has_access(rpc, check_content_type = false)
203
+ if check_content_type
204
+ return false if @req.content_type != "application/x-git-%s-request" % rpc
205
+ end
206
+ return false if !['upload-pack', 'receive-pack'].include? rpc
207
+ if rpc == 'receive-pack'
208
+ return @config[:receive_pack] if @config.include? :receive_pack
209
+ end
210
+ if rpc == 'upload-pack'
211
+ return @config[:upload_pack] if @config.include? :upload_pack
212
+ end
213
+ return get_config_setting(rpc)
214
+ end
215
+
216
+ def get_config_setting(service_name)
217
+ service_name = service_name.gsub('-', '')
218
+ setting = get_git_config("http.#{service_name}")
219
+ if service_name == 'uploadpack'
220
+ return setting != 'false'
221
+ else
222
+ return setting == 'true'
223
+ end
224
+ end
225
+
226
+ def get_git_config(config_name)
227
+ cmd = git_command("config #{config_name}")
228
+ `#{cmd}`.chomp
229
+ end
230
+
231
+ def read_body
232
+ if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/
233
+ input = Zlib::GzipReader.new(@req.body).read
234
+ else
235
+ input = @req.body.read
236
+ end
237
+ end
238
+
239
+ def update_server_info
240
+ cmd = git_command("update-server-info")
241
+ `#{cmd}`
242
+ end
243
+
244
+ def git_command(command)
245
+ git_bin = @config[:git_path] || 'git'
246
+ command = "#{git_bin} #{command}"
247
+ command
248
+ end
249
+
250
+ # --------------------------------------
251
+ # HTTP error response handling functions
252
+ # --------------------------------------
253
+
254
+ PLAIN_TYPE = {"Content-Type" => "text/plain"}
255
+
256
+ def render_method_not_allowed
257
+ if @env['SERVER_PROTOCOL'] == "HTTP/1.1"
258
+ [405, PLAIN_TYPE, ["Method Not Allowed"]]
259
+ else
260
+ [400, PLAIN_TYPE, ["Bad Request"]]
261
+ end
262
+ end
263
+
264
+ def render_not_found
265
+ [404, PLAIN_TYPE, ["Not Found"]]
266
+ end
267
+
268
+ def render_no_access
269
+ [403, PLAIN_TYPE, ["Forbidden"]]
270
+ end
271
+
272
+
273
+ # ------------------------------
274
+ # packet-line handling functions
275
+ # ------------------------------
276
+
277
+ def pkt_flush
278
+ '0000'
279
+ end
280
+
281
+ def pkt_write(str)
282
+ (str.size + 4).to_s(base=16).rjust(4, '0') + str
283
+ end
284
+
285
+
286
+ # ------------------------
287
+ # header writing functions
288
+ # ------------------------
289
+
290
+ def hdr_nocache
291
+ @res["Expires"] = "Fri, 01 Jan 1980 00:00:00 GMT"
292
+ @res["Pragma"] = "no-cache"
293
+ @res["Cache-Control"] = "no-cache, max-age=0, must-revalidate"
294
+ end
295
+
296
+ def hdr_cache_forever
297
+ now = Time.now().to_i
298
+ @res["Date"] = now.to_s
299
+ @res["Expires"] = (now + 31536000).to_s;
300
+ @res["Cache-Control"] = "public, max-age=31536000";
301
+ end
302
+
303
+ end
304
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -19,7 +19,8 @@ email:
19
19
  executables: []
20
20
  extensions: []
21
21
  extra_rdoc_files: []
22
- files: []
22
+ files:
23
+ - lib/git_http.rb
23
24
  homepage: https://github.com/akshayrawat/grack-gem
24
25
  licenses: []
25
26
  post_install_message: