ghoul_grack 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in ghoul_grack.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2009 Scott Chacon <schacon@gmail.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ 'Software'), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "ghoul_grack/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "ghoul_grack"
7
+ s.version = GhoulGrack::VERSION
8
+ s.authors = ["George Drummond"]
9
+ s.email = ["george@accountsapp.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{GRack for use in Ghoul. Original code by Scott Chacon <schacon@gmail.com>}
12
+ s.description = %q{GRack for use in Ghoul. Original code by Scott Chacon <schacon@gmail.com>}
13
+
14
+ s.rubyforge_project = "ghoul_grack"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ # s.add_development_dependency "rspec"
23
+ # s.add_runtime_dependency "rest-client"
24
+ end
@@ -0,0 +1,307 @@
1
+ require "ghoul_grack/version"
2
+ require 'zlib'
3
+ require 'rack/request'
4
+ require 'rack/response'
5
+ require 'rack/utils'
6
+ require 'time'
7
+
8
+ module GhoulGrack
9
+ class GitHttp
10
+ class App
11
+
12
+ SERVICES = [
13
+ ["POST", 'service_rpc', "(.*?)/git-upload-pack$", 'upload-pack'],
14
+ ["POST", 'service_rpc', "(.*?)/git-receive-pack$", 'receive-pack'],
15
+
16
+ ["GET", 'get_info_refs', "(.*?)/info/refs$"],
17
+ ["GET", 'get_text_file', "(.*?)/HEAD$"],
18
+ ["GET", 'get_text_file', "(.*?)/objects/info/alternates$"],
19
+ ["GET", 'get_text_file', "(.*?)/objects/info/http-alternates$"],
20
+ ["GET", 'get_info_packs', "(.*?)/objects/info/packs$"],
21
+ ["GET", 'get_text_file', "(.*?)/objects/info/[^/]*$"],
22
+ ["GET", 'get_loose_object', "(.*?)/objects/[0-9a-f]{2}/[0-9a-f]{38}$"],
23
+ ["GET", 'get_pack_file', "(.*?)/objects/pack/pack-[0-9a-f]{40}\\.pack$"],
24
+ ["GET", 'get_idx_file', "(.*?)/objects/pack/pack-[0-9a-f]{40}\\.idx$"],
25
+ ]
26
+
27
+ def initialize(config = false)
28
+ set_config(config)
29
+ end
30
+
31
+ def set_config(config)
32
+ @config = config || {}
33
+ end
34
+
35
+ def set_config_setting(key, value)
36
+ @config[key] = value
37
+ end
38
+
39
+ def call(env)
40
+ @env = env
41
+ @req = Rack::Request.new(env)
42
+
43
+ cmd, path, @reqfile, @rpc = match_routing
44
+
45
+ return render_method_not_allowed if cmd == 'not_allowed'
46
+ return render_not_found if !cmd
47
+
48
+ @dir = get_git_dir(path)
49
+ return render_not_found if !@dir
50
+
51
+ Dir.chdir(@dir) do
52
+ self.method(cmd).call()
53
+ end
54
+ end
55
+
56
+ # ---------------------------------
57
+ # actual command handling functions
58
+ # ---------------------------------
59
+
60
+ def service_rpc
61
+ return render_no_access if !has_access(@rpc, true)
62
+ input = read_body
63
+
64
+ @res = Rack::Response.new
65
+ @res.status = 200
66
+ @res["Content-Type"] = "application/x-git-%s-result" % @rpc
67
+ @res.finish do
68
+ command = git_command("#{@rpc} --stateless-rpc #{@dir}")
69
+ IO.popen(command, File::RDWR) do |pipe|
70
+ pipe.write(input)
71
+ while !pipe.eof?
72
+ block = pipe.read(8192) # 8M at a time
73
+ @res.write block # steam it to the client
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ def get_info_refs
80
+ service_name = get_service_type
81
+
82
+ if has_access(service_name)
83
+ cmd = git_command("#{service_name} --stateless-rpc --advertise-refs .")
84
+ refs = `#{cmd}`
85
+
86
+ @res = Rack::Response.new
87
+ @res.status = 200
88
+ @res["Content-Type"] = "application/x-git-%s-advertisement" % service_name
89
+ hdr_nocache
90
+ @res.write(pkt_write("# service=git-#{service_name}\n"))
91
+ @res.write(pkt_flush)
92
+ @res.write(refs)
93
+ @res.finish
94
+ else
95
+ dumb_info_refs
96
+ end
97
+ end
98
+
99
+ def dumb_info_refs
100
+ update_server_info
101
+ send_file(@reqfile, "text/plain; charset=utf-8") do
102
+ hdr_nocache
103
+ end
104
+ end
105
+
106
+ def get_info_packs
107
+ # objects/info/packs
108
+ send_file(@reqfile, "text/plain; charset=utf-8") do
109
+ hdr_nocache
110
+ end
111
+ end
112
+
113
+ def get_loose_object
114
+ send_file(@reqfile, "application/x-git-loose-object") do
115
+ hdr_cache_forever
116
+ end
117
+ end
118
+
119
+ def get_pack_file
120
+ send_file(@reqfile, "application/x-git-packed-objects") do
121
+ hdr_cache_forever
122
+ end
123
+ end
124
+
125
+ def get_idx_file
126
+ send_file(@reqfile, "application/x-git-packed-objects-toc") do
127
+ hdr_cache_forever
128
+ end
129
+ end
130
+
131
+ def get_text_file
132
+ send_file(@reqfile, "text/plain") do
133
+ hdr_nocache
134
+ end
135
+ end
136
+
137
+ # ------------------------
138
+ # logic helping functions
139
+ # ------------------------
140
+
141
+ F = ::File
142
+
143
+ # some of this borrowed from the Rack::File implementation
144
+ def send_file(reqfile, content_type)
145
+ reqfile = File.join(@dir, reqfile)
146
+ return render_not_found if !F.exists?(reqfile)
147
+
148
+ @res = Rack::Response.new
149
+ @res.status = 200
150
+ @res["Content-Type"] = content_type
151
+ @res["Last-Modified"] = F.mtime(reqfile).httpdate
152
+
153
+ yield
154
+
155
+ if size = F.size?(reqfile)
156
+ @res["Content-Length"] = size.to_s
157
+ @res.finish do
158
+ F.open(reqfile, "rb") do |file|
159
+ while part = file.read(8192)
160
+ @res.write part
161
+ end
162
+ end
163
+ end
164
+ else
165
+ body = [F.read(reqfile)]
166
+ size = Rack::Utils.bytesize(body.first)
167
+ @res["Content-Length"] = size
168
+ @res.write body
169
+ @res.finish
170
+ end
171
+ end
172
+
173
+ def get_git_dir(path)
174
+ root = @config[:project_root] || `pwd`
175
+ path = File.join(root, path)
176
+ if File.exists?(path) # TODO: check is a valid git directory
177
+ return path
178
+ end
179
+ false
180
+ end
181
+
182
+ def get_service_type
183
+ service_type = @req.params['service']
184
+ return false if !service_type
185
+ return false if service_type[0, 4] != 'git-'
186
+ service_type.gsub('git-', '')
187
+ end
188
+
189
+ def match_routing
190
+ cmd = nil
191
+ path = nil
192
+ SERVICES.each do |method, handler, match, rpc|
193
+ if m = Regexp.new(match).match(@req.path_info)
194
+ return ['not_allowed'] if method != @req.request_method
195
+ cmd = handler
196
+ path = m[1]
197
+ file = @req.path_info.sub(path + '/', '')
198
+ return [cmd, path, file, rpc]
199
+ end
200
+ end
201
+ return nil
202
+ end
203
+
204
+ def has_access(rpc, check_content_type = false)
205
+ if check_content_type
206
+ return false if @req.content_type != "application/x-git-%s-request" % rpc
207
+ end
208
+ return false if !['upload-pack', 'receive-pack'].include? rpc
209
+ if rpc == 'receive-pack'
210
+ return @config[:receive_pack] if @config.include? :receive_pack
211
+ end
212
+ if rpc == 'upload-pack'
213
+ return @config[:upload_pack] if @config.include? :upload_pack
214
+ end
215
+ return get_config_setting(rpc)
216
+ end
217
+
218
+ def get_config_setting(service_name)
219
+ service_name = service_name.gsub('-', '')
220
+ setting = get_git_config("http.#{service_name}")
221
+ if service_name == 'uploadpack'
222
+ return setting != 'false'
223
+ else
224
+ return setting == 'true'
225
+ end
226
+ end
227
+
228
+ def get_git_config(config_name)
229
+ cmd = git_command("config #{config_name}")
230
+ `#{cmd}`.chomp
231
+ end
232
+
233
+ def read_body
234
+ if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/
235
+ input = Zlib::GzipReader.new(@req.body).read
236
+ else
237
+ input = @req.body.read
238
+ end
239
+ end
240
+
241
+ def update_server_info
242
+ cmd = git_command("update-server-info")
243
+ `#{cmd}`
244
+ end
245
+
246
+ def git_command(command)
247
+ git_bin = @config[:git_path] || 'git'
248
+ command = "#{git_bin} #{command}"
249
+ command
250
+ end
251
+
252
+ # --------------------------------------
253
+ # HTTP error response handling functions
254
+ # --------------------------------------
255
+
256
+ PLAIN_TYPE = {"Content-Type" => "text/plain"}
257
+
258
+ def render_method_not_allowed
259
+ if @env['SERVER_PROTOCOL'] == "HTTP/1.1"
260
+ [405, PLAIN_TYPE, ["Method Not Allowed"]]
261
+ else
262
+ [400, PLAIN_TYPE, ["Bad Request"]]
263
+ end
264
+ end
265
+
266
+ def render_not_found
267
+ [404, PLAIN_TYPE, ["Not Found"]]
268
+ end
269
+
270
+ def render_no_access
271
+ [403, PLAIN_TYPE, ["Forbidden"]]
272
+ end
273
+
274
+
275
+ # ------------------------------
276
+ # packet-line handling functions
277
+ # ------------------------------
278
+
279
+ def pkt_flush
280
+ '0000'
281
+ end
282
+
283
+ def pkt_write(str)
284
+ (str.size + 4).to_s(base=16).rjust(4, '0') + str
285
+ end
286
+
287
+
288
+ # ------------------------
289
+ # header writing functions
290
+ # ------------------------
291
+
292
+ def hdr_nocache
293
+ @res["Expires"] = "Fri, 01 Jan 1980 00:00:00 GMT"
294
+ @res["Pragma"] = "no-cache"
295
+ @res["Cache-Control"] = "no-cache, max-age=0, must-revalidate"
296
+ end
297
+
298
+ def hdr_cache_forever
299
+ now = Time.now().to_i
300
+ @res["Date"] = now.to_s
301
+ @res["Expires"] = (now + 31536000).to_s;
302
+ @res["Cache-Control"] = "public, max-age=31536000";
303
+ end
304
+
305
+ end
306
+ end
307
+ end
@@ -0,0 +1,3 @@
1
+ module GhoulGrack
2
+ VERSION = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,52 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ghoul_grack
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - George Drummond
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-10-23 00:00:00.000000000Z
13
+ dependencies: []
14
+ description: GRack for use in Ghoul. Original code by Scott Chacon <schacon@gmail.com>
15
+ email:
16
+ - george@accountsapp.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - Gemfile
23
+ - LICENSE
24
+ - Rakefile
25
+ - ghoul_grack.gemspec
26
+ - lib/ghoul_grack.rb
27
+ - lib/ghoul_grack/version.rb
28
+ homepage: ''
29
+ licenses: []
30
+ post_install_message:
31
+ rdoc_options: []
32
+ require_paths:
33
+ - lib
34
+ required_ruby_version: !ruby/object:Gem::Requirement
35
+ none: false
36
+ requirements:
37
+ - - ! '>='
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ required_rubygems_version: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ requirements: []
47
+ rubyforge_project: ghoul_grack
48
+ rubygems_version: 1.8.11
49
+ signing_key:
50
+ specification_version: 3
51
+ summary: GRack for use in Ghoul. Original code by Scott Chacon <schacon@gmail.com>
52
+ test_files: []