Class: Grack::Server
- Inherits:
-
Object
- Object
- Grack::Server
- Defined in:
- lib/grack/server.rb
Constant Summary collapse
- SERVICES =
[ ["POST", 'service_rpc', "(.*?)/git-upload-pack$", 'upload-pack'], ["POST", 'service_rpc', "(.*?)/git-receive-pack$", 'receive-pack'], ["GET", 'get_info_refs', "(.*?)/info/refs$"], ["GET", 'get_text_file', "(.*?)/HEAD$"], ["GET", 'get_text_file', "(.*?)/objects/info/alternates$"], ["GET", 'get_text_file', "(.*?)/objects/info/http-alternates$"], ["GET", 'get_info_packs', "(.*?)/objects/info/packs$"], ["GET", 'get_text_file', "(.*?)/objects/info/[^/]*$"], ["GET", 'get_loose_object', "(.*?)/objects/[0-9a-f]{2}/[0-9a-f]{38}$"], ["GET", 'get_pack_file', "(.*?)/objects/pack/pack-[0-9a-f]{40}\\.pack$"], ["GET", 'get_idx_file', "(.*?)/objects/pack/pack-[0-9a-f]{40}\\.idx$"], ]
- CRLF =
Uses chunked (streaming) transfer, otherwise response blocks to calculate Content-Length header en.wikipedia.org/wiki/Chunked_transfer_encoding
"\r\n"
- F =
logic helping functions
::File
- PLAIN_TYPE =
HTTP error response handling functions
{"Content-Type" => "text/plain"}
Instance Method Summary collapse
- #call(env) ⇒ Object
- #dumb_info_refs ⇒ Object
- #encode_chunk(chunk) ⇒ Object
- #get_config_setting(service_name) ⇒ Object
- #get_git_config(config_name) ⇒ Object
- #get_git_dir(path) ⇒ Object
- #get_idx_file ⇒ Object
- #get_info_packs ⇒ Object
- #get_info_refs ⇒ Object
- #get_loose_object ⇒ Object
- #get_pack_file ⇒ Object
- #get_service_type ⇒ Object
- #get_text_file ⇒ Object
- #git_command(command) ⇒ Object
- #has_access(rpc, check_content_type = false) ⇒ Object
- #hdr_cache_forever ⇒ Object
-
#hdr_nocache ⇒ Object
———————— header writing functions ————————.
-
#initialize(config = false) ⇒ Server
constructor
A new instance of Server.
- #match_routing ⇒ Object
-
#pkt_flush ⇒ Object
—————————— packet-line handling functions ——————————.
- #pkt_write(str) ⇒ Object
- #read_body ⇒ Object
- #render_method_not_allowed ⇒ Object
- #render_no_access ⇒ Object
- #render_not_found ⇒ Object
-
#send_file(reqfile, content_type) ⇒ Object
some of this borrowed from the Rack::File implementation.
- #service_rpc ⇒ Object
- #set_config(config) ⇒ Object
- #set_config_setting(key, value) ⇒ Object
- #terminating_chunk ⇒ Object
- #update_server_info ⇒ Object
Constructor Details
#initialize(config = false) ⇒ Server
Returns a new instance of Server.
25 26 27 |
# File 'lib/grack/server.rb', line 25 def initialize(config = false) set_config(config) end |
Instance Method Details
#call(env) ⇒ Object
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/grack/server.rb', line 37 def call(env) @env = env @req = Rack::Request.new(env) cmd, path, @reqfile, @rpc = match_routing return render_method_not_allowed if cmd == 'not_allowed' return render_not_found if !cmd @dir = get_git_dir(path) return render_not_found if !@dir Dir.chdir(@dir) do self.method(cmd).call() end end |
#dumb_info_refs ⇒ Object
117 118 119 120 121 122 |
# File 'lib/grack/server.rb', line 117 def dumb_info_refs update_server_info send_file(@reqfile, "text/plain; charset=utf-8") do hdr_nocache end end |
#encode_chunk(chunk) ⇒ Object
88 89 90 91 |
# File 'lib/grack/server.rb', line 88 def encode_chunk(chunk) size_in_hex = chunk.size.to_s(16) [ size_in_hex, CRLF, chunk, CRLF ].join end |
#get_config_setting(service_name) ⇒ Object
236 237 238 239 240 241 242 243 244 |
# File 'lib/grack/server.rb', line 236 def get_config_setting(service_name) service_name = service_name.gsub('-', '') setting = get_git_config("http.#{service_name}") if service_name == 'uploadpack' return setting != 'false' else return setting == 'true' end end |
#get_git_config(config_name) ⇒ Object
246 247 248 249 |
# File 'lib/grack/server.rb', line 246 def get_git_config(config_name) cmd = git_command("config #{config_name}") `#{cmd}`.chomp end |
#get_git_dir(path) ⇒ Object
191 192 193 194 195 196 197 198 |
# File 'lib/grack/server.rb', line 191 def get_git_dir(path) root = @config[:project_root] || `pwd` path = File.join(root, path) if File.exists?(path) # TODO: check is a valid git directory return path end false end |
#get_idx_file ⇒ Object
143 144 145 146 147 |
# File 'lib/grack/server.rb', line 143 def get_idx_file send_file(@reqfile, "application/x-git-packed-objects-toc") do hdr_cache_forever end end |
#get_info_packs ⇒ Object
124 125 126 127 128 129 |
# File 'lib/grack/server.rb', line 124 def get_info_packs # objects/info/packs send_file(@reqfile, "text/plain; charset=utf-8") do hdr_nocache end end |
#get_info_refs ⇒ Object
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/grack/server.rb', line 97 def get_info_refs service_name = get_service_type if has_access(service_name) cmd = git_command("#{service_name} --stateless-rpc --advertise-refs .") refs = `#{cmd}` @res = Rack::Response.new @res.status = 200 @res["Content-Type"] = "application/x-git-%s-advertisement" % service_name hdr_nocache @res.write(pkt_write("# service=git-#{service_name}\n")) @res.write(pkt_flush) @res.write(refs) @res.finish else dumb_info_refs end end |
#get_loose_object ⇒ Object
131 132 133 134 135 |
# File 'lib/grack/server.rb', line 131 def get_loose_object send_file(@reqfile, "application/x-git-loose-object") do hdr_cache_forever end end |
#get_pack_file ⇒ Object
137 138 139 140 141 |
# File 'lib/grack/server.rb', line 137 def get_pack_file send_file(@reqfile, "application/x-git-packed-objects") do hdr_cache_forever end end |
#get_service_type ⇒ Object
200 201 202 203 204 205 |
# File 'lib/grack/server.rb', line 200 def get_service_type service_type = @req.params['service'] return false if !service_type return false if service_type[0, 4] != 'git-' service_type.gsub('git-', '') end |
#get_text_file ⇒ Object
149 150 151 152 153 |
# File 'lib/grack/server.rb', line 149 def get_text_file send_file(@reqfile, "text/plain") do hdr_nocache end end |
#git_command(command) ⇒ Object
264 265 266 267 268 |
# File 'lib/grack/server.rb', line 264 def git_command(command) git_bin = @config[:git_path] || 'git' command = "#{git_bin} #{command}" command end |
#has_access(rpc, check_content_type = false) ⇒ Object
222 223 224 225 226 227 228 229 230 231 232 233 234 |
# File 'lib/grack/server.rb', line 222 def has_access(rpc, check_content_type = false) if check_content_type return false if @req.content_type != "application/x-git-%s-request" % rpc end return false if !['upload-pack', 'receive-pack'].include? rpc if rpc == 'receive-pack' return @config[:receive_pack] if @config.include? :receive_pack end if rpc == 'upload-pack' return @config[:upload_pack] if @config.include? :upload_pack end return get_config_setting(rpc) end |
#hdr_cache_forever ⇒ Object
316 317 318 319 320 321 |
# File 'lib/grack/server.rb', line 316 def hdr_cache_forever now = Time.now().to_i @res["Date"] = now.to_s @res["Expires"] = (now + 31536000).to_s; @res["Cache-Control"] = "public, max-age=31536000"; end |
#hdr_nocache ⇒ Object
header writing functions
310 311 312 313 314 |
# File 'lib/grack/server.rb', line 310 def hdr_nocache @res["Expires"] = "Fri, 01 Jan 1980 00:00:00 GMT" @res["Pragma"] = "no-cache" @res["Cache-Control"] = "no-cache, max-age=0, must-revalidate" end |
#match_routing ⇒ Object
207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/grack/server.rb', line 207 def match_routing cmd = nil path = nil SERVICES.each do |method, handler, match, rpc| if m = Regexp.new(match).match(@req.path_info) return ['not_allowed'] if method != @req.request_method cmd = handler path = m[1] file = @req.path_info.sub(path + '/', '') return [cmd, path, file, rpc] end end return nil end |
#pkt_flush ⇒ Object
packet-line handling functions
297 298 299 |
# File 'lib/grack/server.rb', line 297 def pkt_flush '0000' end |
#pkt_write(str) ⇒ Object
301 302 303 |
# File 'lib/grack/server.rb', line 301 def pkt_write(str) (str.size + 4).to_s(base=16).rjust(4, '0') + str end |
#read_body ⇒ Object
251 252 253 254 255 256 257 |
# File 'lib/grack/server.rb', line 251 def read_body if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/ input = Zlib::GzipReader.new(@req.body).read else input = @req.body.read end end |
#render_method_not_allowed ⇒ Object
276 277 278 279 280 281 282 |
# File 'lib/grack/server.rb', line 276 def render_method_not_allowed if @env['SERVER_PROTOCOL'] == "HTTP/1.1" [405, PLAIN_TYPE, ["Method Not Allowed"]] else [400, PLAIN_TYPE, ["Bad Request"]] end end |
#render_no_access ⇒ Object
288 289 290 |
# File 'lib/grack/server.rb', line 288 def render_no_access [403, PLAIN_TYPE, ["Forbidden"]] end |
#render_not_found ⇒ Object
284 285 286 |
# File 'lib/grack/server.rb', line 284 def render_not_found [404, PLAIN_TYPE, ["Not Found"]] end |
#send_file(reqfile, content_type) ⇒ Object
some of this borrowed from the Rack::File implementation
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/grack/server.rb', line 162 def send_file(reqfile, content_type) reqfile = File.join(@dir, reqfile) return render_not_found if !F.exists?(reqfile) @res = Rack::Response.new @res.status = 200 @res["Content-Type"] = content_type @res["Last-Modified"] = F.mtime(reqfile).httpdate yield if size = F.size?(reqfile) @res["Content-Length"] = size.to_s @res.finish do F.open(reqfile, "rb") do |file| while part = file.read(8192) @res.write part end end end else body = [F.read(reqfile)] size = Rack::Utils.bytesize(body.first) @res["Content-Length"] = size @res.write body @res.finish end end |
#service_rpc ⇒ Object
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/grack/server.rb', line 64 def service_rpc return render_no_access if !has_access(@rpc, true) input = read_body @res = Rack::Response.new @res.status = 200 @res["Content-Type"] = "application/x-git-%s-result" % @rpc @res["Transfer-Encoding"] = "chunked" @res["Cache-Control"] = "no-cache" @res.finish do command = git_command("#{@rpc} --stateless-rpc #{@dir}") IO.popen(command, File::RDWR) do |pipe| pipe.write(input) pipe.close_write while !pipe.eof? block = pipe.read(8192) # 8KB at a time @res.write encode_chunk(block) # stream it to the client end @res.write terminating_chunk end end end |
#set_config(config) ⇒ Object
29 30 31 |
# File 'lib/grack/server.rb', line 29 def set_config(config) @config = config || {} end |
#set_config_setting(key, value) ⇒ Object
33 34 35 |
# File 'lib/grack/server.rb', line 33 def set_config_setting(key, value) @config[key] = value end |
#terminating_chunk ⇒ Object
93 94 95 |
# File 'lib/grack/server.rb', line 93 def terminating_chunk [ 0, CRLF, CRLF ].join end |
#update_server_info ⇒ Object
259 260 261 262 |
# File 'lib/grack/server.rb', line 259 def update_server_info cmd = git_command("update-server-info") `#{cmd}` end |