lib/shuck/server.rb in shuck-0.0.8 vs lib/shuck/server.rb in shuck-0.0.9

- old
+ new

@@ -3,21 +3,26 @@ require 'shuck/xml_adapter' module Shuck class Request CREATE_BUCKET = "CREATE_BUCKET" + LIST_BUCKETS = "LIST_BUCKETS" + LS_BUCKET = "LS_BUCKET" STORE = "STORE" COPY = "COPY" GET = "GET" + GET_ACL = "GET_ACL" + SET_ACL = "SET_ACL" MOVE = "MOVE" DELETE = "DELETE" - attr_accessor :bucket,:object,:type,:src_bucket,:src_object,:method,:webrick_request,:path + attr_accessor :bucket,:object,:type,:src_bucket,:src_object,:method,:webrick_request,:path,:is_path_style def inspect puts "-----Inspect Shuck Request" puts "Type: #{@type}" + puts "Is Path Style: #{@is_path_style}" puts "Request Method: #{@method}" puts "Bucket: #{@bucket}" puts "Object: #{@object}" puts "Src Bucket: #{@src_bucket}" puts "Src Object: #{@src_object}" @@ -28,98 +33,81 @@ class Servlet < WEBrick::HTTPServlet::AbstractServlet def initialize(server,store,hostname) super(server) @store = store @hostname = hostname + @root_hostnames = [hostname,'localhost','s3.amazonaws.com','s3.localhost'] end def do_GET(request, response) - path = request.path - path_len = path.size - host = request["HOST"] - path_style_request = true - bucket = nil - if host != @hostname - bucket = host.split(".")[0] - path_style_request = false - end + s_req = normalize_request(request) - if path == "/" and path_style_request + case s_req.type + when 'LIST_BUCKETS' response.status = 200 - response['Content-Type'] = 'text/xml' + response['Content-Type'] = 'application/xml' buckets = @store.buckets response.body = XmlAdapter.buckets(buckets) - else - if path_style_request - elems = path[1,path_len].split("/") - bucket = elems[0] - else - elems = path.split("/") - end - - if elems.size == 0 - # List buckets - buckets = @store.buckets + when 'LS_BUCKET' + bucket_obj = @store.get_bucket(s_req.bucket) + if bucket_obj response.status = 200 - response.body = XmlAdapter.buckets(buckets) + response.body = XmlAdapter.bucket(bucket_obj) response['Content-Type'] = "application/xml" - elsif elems.size == 1 - bucket_obj = @store.get_bucket(bucket) - if bucket_obj - response.status = 200 - response.body = XmlAdapter.bucket(bucket_obj) - response['Content-Type'] = "application/xml" - else - response.status = 404 - response.body = XmlAdapter.error_no_such_bucket(bucket) - response['Content-Type'] = "application/xml" - end else - object = elems[1,elems.size].join('/') - real_obj = @store.get_object(bucket,object, request) + response.status = 404 + response.body = XmlAdapter.error_no_such_bucket(s_req.bucket) + response['Content-Type'] = "application/xml" + end + when 'GET_ACL' + response.status = 200 + response.body = XmlAdapter.acl() + response['Content-Type'] = 'application/xml' + when 'GET' + real_obj = @store.get_object(s_req.bucket,s_req.object,request) + if !real_obj + response.status = 404 + response.body = "" + return + end - if real_obj - response.status = 200 - response['Content-Type'] = real_obj.content_type - content_length = File::Stat.new(real_obj.io.path).size - response['Etag'] = real_obj.md5 - response['Accept-Ranges'] = "bytes" + response.status = 200 + response['Content-Type'] = real_obj.content_type + content_length = File::Stat.new(real_obj.io.path).size + response['Etag'] = real_obj.md5 + response['Accept-Ranges'] = "bytes" - # Added Range Query support - if range = request.header["range"].first - response.status = 206 - if range =~ /bytes=(\d*)-(\d*)/ - start = $1.to_i - finish = $2.to_i - finish_str = "" - if finish == 0 - finish = content_length - 1 - finish_str = "#{finish}" - else - finish_str = finish.to_s - end - - bytes_to_read = finish - start + 1 - response['Content-Range'] = "bytes #{start}-#{finish_str}/#{content_length}" - real_obj.io.pos = start - response.body = real_obj.io.read(bytes_to_read) - return - end + # Added Range Query support + if range = request.header["range"].first + response.status = 206 + if range =~ /bytes=(\d*)-(\d*)/ + start = $1.to_i + finish = $2.to_i + finish_str = "" + if finish == 0 + finish = content_length - 1 + finish_str = "#{finish}" + else + finish_str = finish.to_s end - response['Content-Length'] = File::Stat.new(real_obj.io.path).size - response.body = real_obj.io - else - response.status = 404 - response.body = "" + + bytes_to_read = finish - start + 1 + response['Content-Range'] = "bytes #{start}-#{finish_str}/#{content_length}" + real_obj.io.pos = start + response.body = real_obj.io.read(bytes_to_read) + return end end + response['Content-Length'] = File::Stat.new(real_obj.io.path).size + response.body = real_obj.io end end def do_PUT(request,response) s_req = normalize_request(request) + case s_req.type when Request::COPY @store.copy_object(s_req.src_bucket,s_req.src_object,s_req.bucket,s_req.object) when Request::STORE real_obj = @store.store_object(s_req.bucket,s_req.object,s_req.webrick_request) @@ -140,63 +128,111 @@ def do_DELETE(request,response) p request end private - # This method takes a webrick request and generates a normalized Shuck request - def normalize_request(webrick_r) - path = webrick_r.path + + def normalize_get(webrick_req,s_req) + path = webrick_req.path path_len = path.size - path_style_request = true - host = webrick_r["Host"] - bucket = nil - object = nil - path_style_request = true + query = webrick_req.query + if path == "/" and s_req.is_path_style + s_req.type = Request::LIST_BUCKETS + else + if s_req.is_path_style + elems = path[1,path_len].split("/") + s_req.bucket = elems[0] + else + elems = path.split("/") + end - if host != @hostname - bucket = host.split(".")[0] - path_style_request = false + if elems.size == 0 + # List buckets + s_req.type = Request::LIST_BUCKETS + elsif elems.size == 1 + s_req.type = Request::LS_BUCKET + else + if query["acl"] == "" + s_req.type = Request::GET_ACL + else + s_req.type = Request::GET + end + object = elems[1,elems.size].join('/') + s_req.object = object + end end + end - req = Request.new - req.path = webrick_r.path - + def normalize_put(webrick_req,s_req) + path = webrick_req.path + path_len = path.size if path == "/" - if bucket - req.type = Request::CREATE_BUCKET + if s_req.bucket + s_req.type = Request::CREATE_BUCKET end else - if path_style_request + if s_req.is_path_style elems = path[1,path_len].split("/") - bucket = elems[0] + s_req.bucket = elems[0] if elems.size == 1 - req.type = Request::CREATE_BUCKET + s_req.type = Request::CREATE_BUCKET else - req.type = Request::STORE - object = elems[1,elems.size].join('/') + if webrick_req.request_line =~ /\?acl/ + s_req.type = Request::SET_ACL + else + s_req.type = Request::STORE + end + s_req.object = elems[1,elems.size].join('/') end else - req.type = Request::STORE - object = webrick_r.path + if webrick_req.request_line =~ /\?acl/ + s_req.type = Request::SET_ACL + else + s_req.type = Request::STORE + end + s_req.object = webrick_req.path end end - copy_source = webrick_r.header["x-amz-copy-source"] + copy_source = webrick_req.header["x-amz-copy-source"] if copy_source and copy_source.size == 1 src_elems = copy_source.first.split("/") root_offset = src_elems[0] == "" ? 1 : 0 - req.src_bucket = src_elems[root_offset] - req.src_object = src_elems[1 + root_offset,src_elems.size].join("/") - req.type = Request::COPY + s_req.src_bucket = src_elems[root_offset] + s_req.src_object = src_elems[1 + root_offset,src_elems.size].join("/") + s_req.type = Request::COPY end - req.bucket = bucket - req.object = object - req.webrick_request = webrick_r - return req + s_req.webrick_request = webrick_req end + # This method takes a webrick request and generates a normalized Shuck request + def normalize_request(webrick_req) + host_header= webrick_req["Host"] + host = host_header.split(':')[0] + + s_req = Request.new + s_req.path = webrick_req.path + s_req.is_path_style = true + + if !@root_hostnames.include?(host) + s_req.bucket = host.split(".")[0] + s_req.is_path_style = false + end + + case webrick_req.request_method + when 'PUT' + normalize_put(webrick_req,s_req) + when 'GET' + normalize_get(webrick_req,s_req) + else + raise "Unknown Request" + end + + return s_req + end + def dump_request(request) puts "----------Dump Request-------------" puts request.request_method puts request.path request.each do |k,v| @@ -206,10 +242,10 @@ end end class Server - def initialize(port,store,hostname = "localhost") + def initialize(port,store,hostname) @port = port @store = store @hostname = hostname end