grack 0.0.2 → 0.1.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +7 -0
  3. data/.yardopts +1 -0
  4. data/LICENSE +22 -0
  5. data/NEWS.md +19 -0
  6. data/README.md +211 -0
  7. data/Rakefile +222 -0
  8. data/lib/git_adapter.rb +1 -0
  9. data/lib/grack.rb +1 -0
  10. data/lib/grack/app.rb +482 -0
  11. data/lib/grack/compatible_git_adapter.rb +99 -0
  12. data/lib/grack/file_streamer.rb +41 -0
  13. data/lib/grack/git_adapter.rb +142 -0
  14. data/lib/grack/io_streamer.rb +45 -0
  15. data/tests/app_test.rb +534 -0
  16. data/tests/compatible_git_adapter_test.rb +137 -0
  17. data/tests/example/_git/COMMIT_EDITMSG +1 -0
  18. data/tests/example/_git/HEAD +1 -0
  19. data/tests/example/_git/config +6 -0
  20. data/tests/example/_git/description +1 -0
  21. data/tests/example/_git/hooks/applypatch-msg.sample +15 -0
  22. data/tests/example/_git/hooks/commit-msg.sample +24 -0
  23. data/tests/example/_git/hooks/post-commit.sample +8 -0
  24. data/tests/example/_git/hooks/post-receive.sample +15 -0
  25. data/tests/example/_git/hooks/post-update.sample +8 -0
  26. data/tests/example/_git/hooks/pre-applypatch.sample +14 -0
  27. data/tests/example/_git/hooks/pre-commit.sample +50 -0
  28. data/tests/example/_git/hooks/pre-rebase.sample +169 -0
  29. data/tests/example/_git/hooks/prepare-commit-msg.sample +36 -0
  30. data/tests/example/_git/hooks/update.sample +128 -0
  31. data/tests/example/_git/index +0 -0
  32. data/tests/example/_git/info/exclude +6 -0
  33. data/tests/example/_git/info/refs +1 -0
  34. data/tests/example/_git/logs/HEAD +1 -0
  35. data/tests/example/_git/logs/refs/heads/master +1 -0
  36. data/tests/example/_git/objects/31/d73eb4914a8ddb6cb0e4adf250777161118f90 +0 -0
  37. data/tests/example/_git/objects/cb/067e06bdf6e34d4abebf6cf2de85d65a52c65e +0 -0
  38. data/tests/example/_git/objects/ce/013625030ba8dba906f756967f9e9ca394464a +0 -0
  39. data/tests/example/_git/objects/info/packs +2 -0
  40. data/tests/example/_git/objects/pack/pack-62c9f443d8405cd6da92dcbb4f849cc01a339c06.idx +0 -0
  41. data/tests/example/_git/objects/pack/pack-62c9f443d8405cd6da92dcbb4f849cc01a339c06.pack +0 -0
  42. data/tests/example/_git/refs/heads/master +1 -0
  43. data/tests/file_streamer_test.rb +37 -0
  44. data/tests/git_adapter_test.rb +104 -0
  45. data/tests/io_streamer_test.rb +36 -0
  46. data/tests/test_helper.rb +36 -0
  47. metadata +292 -19
  48. data/lib/git_http.rb +0 -304
@@ -1,304 +0,0 @@
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