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