ghoul_grack 0.0.1

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.
@@ -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: []