gitlab-grack 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ coverage/
2
+ pkg/
data/.gitmodules ADDED
@@ -0,0 +1,3 @@
1
+ [submodule "tests/example"]
2
+ path = tests/example
3
+ url = git://github.com/schacon/simplegit.git
data/README.md ADDED
@@ -0,0 +1,91 @@
1
+ Grack - Ruby/Rack Git Smart-HTTP Server Handler
2
+ ===============================================
3
+
4
+ This project aims to replace the builtin git-http-backend CGI handler
5
+ distributed with C Git with a Rack application. This reason for doing this
6
+ is to allow far more webservers to be able to handle Git smart http requests.
7
+
8
+ The default git-http-backend only runs as a CGI script, and specifically is
9
+ only targeted for Apache 2.x usage (it requires PATH_INFO to be set and
10
+ specifically formatted). So, instead of trying to get it to work with
11
+ other CGI capable webservers (Lighttpd, etc), we can get it running on nearly
12
+ every major and minor webserver out there by making it Rack capable. Rack
13
+ applications can run with the following handlers:
14
+
15
+ * CGI
16
+ * FCGI
17
+ * Mongrel (and EventedMongrel and SwiftipliedMongrel)
18
+ * WEBrick
19
+ * SCGI
20
+ * LiteSpeed
21
+ * Thin
22
+
23
+ These web servers include Rack handlers in their distributions:
24
+
25
+ * Ebb
26
+ * Fuzed
27
+ * Phusion Passenger (which is mod_rack for Apache and for nginx)
28
+ * Unicorn
29
+
30
+ With [Warbler](http://caldersphere.rubyforge.org/warbler/classes/Warbler.html),
31
+ and JRuby, we can also generate a WAR file that can be deployed in any Java
32
+ web application server (Tomcat, Glassfish, Websphere, JBoss, etc).
33
+
34
+ Since the git-http-backend is really just a simple wrapper for the upload-pack
35
+ and receive-pack processes with the '--stateless-rpc' option, it does not
36
+ actually re-implement very much.
37
+
38
+ Dependencies
39
+ ========================
40
+ * Ruby - http://www.ruby-lang.org
41
+ * Rack - http://rack.rubyforge.org
42
+ * A Rack-compatible web server
43
+ * Git >= 1.7 (currently the 'pu' branch)
44
+ * Mocha (only for running the tests)
45
+
46
+ Quick Start
47
+ ========================
48
+ $ gem install rack
49
+ $ (edit config.ru to set git project path)
50
+ $ rackup --host 127.0.0.1 -p 8080 config.ru
51
+ $ git clone http://127.0.0.1:8080/schacon/grit.git
52
+
53
+ Contributing
54
+ ========================
55
+ If you would like to contribute to the Grack project, I prefer to get
56
+ pull-requests via GitHub. You should include tests for whatever functionality
57
+ you add. Just fork this project, push your changes to your fork and click
58
+ the 'pull request' button. To run the tests, you first need to install the
59
+ 'mocha' mocking library and initialize the submodule.
60
+
61
+ $ sudo gem install mocha
62
+ $ git submodule init
63
+ $ git submodule update
64
+
65
+ Then you should be able to run the tests with a 'rake' command. You can also
66
+ run coverage tests with 'rake rcov' if you have rcov installed.
67
+
68
+ License
69
+ ========================
70
+ (The MIT License)
71
+
72
+ Copyright (c) 2009 Scott Chacon <schacon@gmail.com>
73
+
74
+ Permission is hereby granted, free of charge, to any person obtaining
75
+ a copy of this software and associated documentation files (the
76
+ 'Software'), to deal in the Software without restriction, including
77
+ without limitation the rights to use, copy, modify, merge, publish,
78
+ distribute, sublicense, and/or sell copies of the Software, and to
79
+ permit persons to whom the Software is furnished to do so, subject to
80
+ the following conditions:
81
+
82
+ The above copyright notice and this permission notice shall be
83
+ included in all copies or substantial portions of the Software.
84
+
85
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
86
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
87
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
88
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
89
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
90
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
91
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ task :default => :test
5
+
6
+ desc "Run the tests."
7
+ task :test do
8
+ Dir.glob("tests/*_test.rb").each do |f|
9
+ system "ruby #{f}"
10
+ end
11
+ end
12
+
13
+ desc "Run test coverage."
14
+ task :rcov do
15
+ system "rcov tests/*_test.rb -i lib/git_http.rb -x rack -x Library -x tests"
16
+ system "open coverage/index.html"
17
+ end
18
+
19
+ namespace :grack do
20
+ desc "Start Grack"
21
+ task :start do
22
+ system "rackup config.ru -p 8080"
23
+ end
24
+ end
25
+
26
+ desc "Start everything."
27
+ multitask :start => [ 'grack:start' ]
@@ -0,0 +1,9 @@
1
+ #! /usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/lib')
3
+ require 'lib/git_http'
4
+ config = {
5
+ :project_root => "/opt",
6
+ :upload_pack => true,
7
+ :receive_pack => false,
8
+ }
9
+ Rack::Handler::FastCGI.run(GitHttp::App.new(config))
data/grack.gemspec ADDED
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/grack/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Scott Chacon"]
6
+ gem.email = ["schacon@gmail.com"]
7
+ gem.description = %q{Ruby/Rack Git Smart-HTTP Server Handler}
8
+ gem.summary = %q{Ruby/Rack Git Smart-HTTP Server Handler}
9
+ gem.homepage = "https://github.com/schacon/grack"
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- tests/*`.split("\n")
14
+ gem.name = "gitlab-grack"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Grack::VERSION
17
+
18
+ gem.add_dependency("rack", "~> 1.4.1")
19
+ gem.add_development_dependency("mocha", "~> 0.11")
20
+ end
data/install.txt ADDED
@@ -0,0 +1,60 @@
1
+ Installation
2
+ ========================
3
+
4
+ ** This documentation is not finished yet. I haven't tested all of
5
+ these and it's obviously incomplete - these are currently just notes.
6
+
7
+ FastCGI
8
+ ---------------------------------------
9
+ Here is an example config from lighttpd server:
10
+ ----
11
+ # main fastcgi entry
12
+ $HTTP["url"] =~ "^/myapp/.+$" {
13
+ fastcgi.server = ( "/myapp" =>
14
+ ( "localhost" =>
15
+ ( "bin-path" => "/var/www/localhost/cgi-bin/dispatch.fcgi",
16
+ "docroot" => "/var/www/localhost/htdocs/myapp",
17
+ "host" => "127.0.0.1",
18
+ "port" => 1026,
19
+ "check-local" => "disable"
20
+ )
21
+ )
22
+ )
23
+ } # HTTP[url]
24
+ ----
25
+ You can use the examples/dispatch.fcgi file as your dispatcher.
26
+
27
+ (Example Apache setup?)
28
+
29
+ Installing in a Java application server
30
+ ---------------------------------------
31
+ # install Warbler
32
+ $ sudo gem install warbler
33
+ $ cd gitsmart
34
+ $ (edit config.ru)
35
+ $ warble
36
+ $ cp gitsmart.war /path/to/java/autodeploy/dir
37
+
38
+ Unicorn
39
+ ---------------------------------------
40
+ With Unicorn (http://unicorn.bogomips.org/) you can just run 'unicorn'
41
+ in the directory with the config.ru file.
42
+
43
+ Thin
44
+ ---------------------------------------
45
+ thin.yml
46
+ ---
47
+ pid: /home/deploy/myapp/server/thin.pid
48
+ log: /home/deploy/myapp/logs/thin.log
49
+ timeout: 30
50
+ port: 7654
51
+ max_conns: 1024
52
+ chdir: /home/deploy/myapp/site_files
53
+ rackup: /home/deploy/myapp/server/config.ru
54
+ max_persistent_conns: 512
55
+ environment: production
56
+ address: 127.0.0.1
57
+ servers: 1
58
+ daemonize: true
59
+
60
+
data/lib/grack.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "grack/bundle"
2
+
3
+ module Grack
4
+
5
+ end
data/lib/grack/auth.rb ADDED
@@ -0,0 +1,37 @@
1
+ require 'rack/auth/basic'
2
+ require 'rack/auth/abstract/handler'
3
+ require 'rack/auth/abstract/request'
4
+
5
+ module Grack
6
+ class Auth < Rack::Auth::Basic
7
+ def call(env)
8
+ @env = env
9
+ @request = Rack::Request.new(env)
10
+ @auth = Request.new(env)
11
+
12
+ if not @auth.provided?
13
+ unauthorized
14
+ elsif not @auth.basic?
15
+ bad_request
16
+ else
17
+ result = if (access = valid? and access == true)
18
+ @env['REMOTE_USER'] = @auth.username
19
+ @app.call(env)
20
+ else
21
+ if access == '404'
22
+ render_not_found
23
+ elsif access == '403'
24
+ render_no_access
25
+ else
26
+ unauthorized
27
+ end
28
+ end
29
+ result
30
+ end
31
+ end# method call
32
+
33
+ def valid?
34
+ false
35
+ end
36
+ end# class Auth
37
+ end# module Grack
@@ -0,0 +1,20 @@
1
+ require 'rack/builder'
2
+ require 'grack/auth'
3
+ require 'grack/server'
4
+
5
+ module Grack
6
+ module Bundle
7
+ extend self
8
+
9
+ def new(config)
10
+ Rack::Builder.new do
11
+ use Grack::Auth do |username, password|
12
+ false
13
+ end
14
+
15
+ run Grack::Server.new(config)
16
+ end
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,304 @@
1
+ require 'zlib'
2
+ require 'rack/request'
3
+ require 'rack/response'
4
+ require 'rack/utils'
5
+ require 'time'
6
+
7
+ module Grack
8
+ class Server
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
@@ -0,0 +1,3 @@
1
+ module Grack
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,237 @@
1
+ require 'rack'
2
+ require 'rack/test'
3
+ require 'test/unit'
4
+ require 'mocha'
5
+ require 'digest/sha1'
6
+
7
+ require_relative '../lib/grack/server.rb'
8
+ require 'pp'
9
+
10
+ class GitHttpTest < Test::Unit::TestCase
11
+ include Rack::Test::Methods
12
+
13
+ def example
14
+ File.expand_path(File.dirname(__FILE__))
15
+ end
16
+
17
+ def app
18
+ config = {
19
+ :project_root => example,
20
+ :upload_pack => true,
21
+ :receive_pack => true,
22
+ }
23
+ Grack::Server.new(config)
24
+ end
25
+
26
+ def test_upload_pack_advertisement
27
+ get "/example/info/refs?service=git-upload-pack"
28
+ assert_equal 200, r.status
29
+ assert_equal "application/x-git-upload-pack-advertisement", r.headers["Content-Type"]
30
+ assert_equal "001e# service=git-upload-pack", r.body.split("\n").first
31
+ assert_match 'multi_ack_detailed', r.body
32
+ end
33
+
34
+ def test_no_access_wrong_content_type_up
35
+ post "/example/git-upload-pack"
36
+ assert_equal 403, r.status
37
+ end
38
+
39
+ def test_no_access_wrong_content_type_rp
40
+ post "/example/git-receive-pack"
41
+ assert_equal 403, r.status
42
+ end
43
+
44
+ def test_no_access_wrong_method_rcp
45
+ get "/example/git-upload-pack"
46
+ assert_equal 400, r.status
47
+ end
48
+
49
+ def test_no_access_wrong_command_rcp
50
+ post "/example/git-upload-packfile"
51
+ assert_equal 404, r.status
52
+ end
53
+
54
+ def test_no_access_wrong_path_rcp
55
+ post "/example-wrong/git-upload-pack"
56
+ assert_equal 404, r.status
57
+ end
58
+
59
+ def test_upload_pack_rpc
60
+ IO.stubs(:popen).returns(MockProcess.new)
61
+ post "/example/git-upload-pack", {}, {"CONTENT_TYPE" => "application/x-git-upload-pack-request"}
62
+ assert_equal 200, r.status
63
+ assert_equal "application/x-git-upload-pack-result", r.headers["Content-Type"]
64
+ end
65
+
66
+ def test_receive_pack_advertisement
67
+ get "/example/info/refs?service=git-receive-pack"
68
+ assert_equal 200, r.status
69
+ assert_equal "application/x-git-receive-pack-advertisement", r.headers["Content-Type"]
70
+ assert_equal "001f# service=git-receive-pack", r.body.split("\n").first
71
+ assert_match 'report-status', r.body
72
+ assert_match 'delete-refs', r.body
73
+ assert_match 'ofs-delta', r.body
74
+ end
75
+
76
+ def test_recieve_pack_rpc
77
+ IO.stubs(:popen).yields(MockProcess.new)
78
+ post "/example/git-receive-pack", {}, {"CONTENT_TYPE" => "application/x-git-receive-pack-request"}
79
+ assert_equal 200, r.status
80
+ assert_equal "application/x-git-receive-pack-result", r.headers["Content-Type"]
81
+ end
82
+
83
+ def test_info_refs_dumb
84
+ get "/example/.git/info/refs"
85
+ assert_equal 200, r.status
86
+ end
87
+
88
+ def test_info_packs
89
+ get "/example/.git/objects/info/packs"
90
+ assert_equal 200, r.status
91
+ assert_match /P pack-(.*?).pack/, r.body
92
+ end
93
+
94
+ def test_loose_objects
95
+ path, content = write_test_objects
96
+ get "/example/.git/objects/#{path}"
97
+ assert_equal 200, r.status
98
+ assert_equal content, r.body
99
+ remove_test_objects
100
+ end
101
+
102
+ def test_pack_file
103
+ path, content = write_test_objects
104
+ get "/example/.git/objects/pack/pack-#{content}.pack"
105
+ assert_equal 200, r.status
106
+ assert_equal content, r.body
107
+ remove_test_objects
108
+ end
109
+
110
+ def test_index_file
111
+ path, content = write_test_objects
112
+ get "/example/.git/objects/pack/pack-#{content}.idx"
113
+ assert_equal 200, r.status
114
+ assert_equal content, r.body
115
+ remove_test_objects
116
+ end
117
+
118
+ def test_text_file
119
+ get "/example/.git/HEAD"
120
+ assert_equal 200, r.status
121
+ assert_equal 41, r.body.size # submodules have detached head
122
+ end
123
+
124
+ def test_no_size_avail
125
+ File.stubs('size?').returns(false)
126
+ get "/example/.git/HEAD"
127
+ assert_equal 200, r.status
128
+ assert_equal 46, r.body.size # submodules have detached head
129
+ end
130
+
131
+ def test_config_upload_pack_off
132
+ a1 = app
133
+ a1.set_config_setting(:upload_pack, false)
134
+ session = Rack::Test::Session.new(a1)
135
+ session.get "/example/info/refs?service=git-upload-pack"
136
+ assert_equal 404, session.last_response.status
137
+ end
138
+
139
+ def test_config_receive_pack_off
140
+ a1 = app
141
+ a1.set_config_setting(:receive_pack, false)
142
+ session = Rack::Test::Session.new(a1)
143
+ session.get "/example/info/refs?service=git-receive-pack"
144
+ assert_equal 404, session.last_response.status
145
+ end
146
+
147
+ def test_config_bad_service
148
+ get "/example/info/refs?service=git-receive-packfile"
149
+ assert_equal 404, r.status
150
+ end
151
+
152
+ def test_git_config_receive_pack
153
+ app1 = Grack::Server.new({:project_root => example})
154
+ session = Rack::Test::Session.new(app1)
155
+
156
+ app1.stubs(:get_git_config).with('http.receivepack').returns('')
157
+ session.get "/example/info/refs?service=git-receive-pack"
158
+ assert_equal 404, session.last_response.status
159
+
160
+ app1.stubs(:get_git_config).with('http.receivepack').returns('true')
161
+ session.get "/example/info/refs?service=git-receive-pack"
162
+ assert_equal 200, session.last_response.status
163
+
164
+ app1.stubs(:get_git_config).with('http.receivepack').returns('false')
165
+ session.get "/example/info/refs?service=git-receive-pack"
166
+ assert_equal 404, session.last_response.status
167
+ end
168
+
169
+ def test_git_config_upload_pack
170
+ app1 = Grack::Server.new({:project_root => example})
171
+ session = Rack::Test::Session.new(app1)
172
+
173
+ app1.stubs(:get_git_config).with('http.uploadpack').returns('')
174
+ session.get "/example/info/refs?service=git-upload-pack"
175
+ assert_equal 200, session.last_response.status
176
+
177
+ app1.stubs(:get_git_config).with('http.uploadpack').returns('true')
178
+ session.get "/example/info/refs?service=git-upload-pack"
179
+ assert_equal 200, session.last_response.status
180
+
181
+ app1.stubs(:get_git_config).with('http.uploadpack').returns('false')
182
+ session.get "/example/info/refs?service=git-upload-pack"
183
+ assert_equal 404, session.last_response.status
184
+ end
185
+
186
+ private
187
+
188
+ def r
189
+ last_response
190
+ end
191
+
192
+ def write_test_objects
193
+ content = Digest::SHA1.hexdigest('gitrocks')
194
+ base = File.join(File.expand_path(File.dirname(__FILE__)), 'example', '.git', 'objects')
195
+ obj = File.join(base, '20')
196
+ Dir.mkdir(obj) rescue nil
197
+ file = File.join(obj, content[0, 38])
198
+ File.open(file, 'w') { |f| f.write(content) }
199
+ pack = File.join(base, 'pack', "pack-#{content}.pack")
200
+ File.open(pack, 'w') { |f| f.write(content) }
201
+ idx = File.join(base, 'pack', "pack-#{content}.idx")
202
+ File.open(idx, 'w') { |f| f.write(content) }
203
+ ["20/#{content[0,38]}", content]
204
+ end
205
+
206
+ def remove_test_objects
207
+ content = Digest::SHA1.hexdigest('gitrocks')
208
+ base = File.join(File.expand_path(File.dirname(__FILE__)), 'example', '.git', 'objects')
209
+ obj = File.join(base, '20')
210
+ file = File.join(obj, content[0, 38])
211
+ pack = File.join(base, 'pack', "pack-#{content}.pack")
212
+ idx = File.join(base, 'pack', "pack-#{content}.idx")
213
+ File.unlink(file)
214
+ File.unlink(pack)
215
+ File.unlink(idx)
216
+ end
217
+
218
+ end
219
+
220
+ class MockProcess
221
+
222
+ def initialize
223
+ @counter = 0
224
+ end
225
+
226
+ def write(data)
227
+ end
228
+
229
+ def read(data)
230
+ end
231
+
232
+ def eof?
233
+ @counter += 1
234
+ @counter > 1 ? true : false
235
+ end
236
+
237
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gitlab-grack
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Scott Chacon
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rack
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.4.1
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 1.4.1
30
+ - !ruby/object:Gem::Dependency
31
+ name: mocha
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '0.11'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '0.11'
46
+ description: Ruby/Rack Git Smart-HTTP Server Handler
47
+ email:
48
+ - schacon@gmail.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - .gitignore
54
+ - .gitmodules
55
+ - README.md
56
+ - Rakefile
57
+ - examples/dispatch.fcgi
58
+ - grack.gemspec
59
+ - install.txt
60
+ - lib/grack.rb
61
+ - lib/grack/auth.rb
62
+ - lib/grack/bundle.rb
63
+ - lib/grack/server.rb
64
+ - lib/grack/version.rb
65
+ - tests/main_test.rb
66
+ homepage: https://github.com/schacon/grack
67
+ licenses: []
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ! '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubyforge_project:
86
+ rubygems_version: 1.8.24
87
+ signing_key:
88
+ specification_version: 3
89
+ summary: Ruby/Rack Git Smart-HTTP Server Handler
90
+ test_files:
91
+ - tests/main_test.rb