grack 0.0.2 → 0.1.0.pre1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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