Permalink
Browse files

[ref issue 14] tweak to Etag in headers to include doublequotes.

  • Loading branch information...
1 parent 5df7524 commit 898dde95dc329ffa2db1a86ea37ee1d23ed53ea3 @sbussetti sbussetti committed Jun 10, 2012
Showing with 114 additions and 2 deletions.
  1. +2 −2 lib/fakes3/server.rb
  2. +25 −0 test/boto_test.rb
  3. +87 −0 test/botocmd.py
View
@@ -90,7 +90,7 @@ def do_GET(request, response)
stat = File::Stat.new(real_obj.io.path)
response['Last-Modified'] = stat.mtime.to_s
- response['Etag'] = real_obj.md5
+ response['Etag'] = "\"#{real_obj.md5}\""
response['Accept-Ranges'] = "bytes"
response['Last-Ranges'] = "bytes"
@@ -140,7 +140,7 @@ def do_PUT(request,response)
end
real_obj = @store.store_object(bucket_obj,s_req.object,s_req.webrick_request)
- response['Etag'] = real_obj.md5
+ response['Etag'] = "\"#{real_obj.md5}\""
when Request::CREATE_BUCKET
@store.create_bucket(s_req.bucket)
end
View
@@ -0,0 +1,25 @@
+require 'test/test_helper'
+require 'fileutils'
+
+class BotoTest < Test::Unit::TestCase
+ def setup
+ cmdpath = File.expand_path(File.join(File.dirname(__FILE__),'botocmd.py'))
+ @botocmd = "python #{cmdpath} -t localhost -p 10453"
+ end
+
+ def teardown
+ end
+
+ def test_store
+ File.open(__FILE__,'rb') do |input|
+ File.open("/tmp/fakes3_upload",'wb') do |output|
+ output << input.read
+ end
+ end
+ output = `#{@botocmd} put /tmp/fakes3_upload s3://s3cmd_bucket/upload`
+ assert_match(/stored/,output)
+
+ FileUtils.rm("/tmp/fakes3_upload")
+ end
+
+end
View
@@ -0,0 +1,87 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# fakes3cmd.py -- an s3cmd-like script that accepts a custom host and portname
+import re
+import os
+from optparse import OptionParser
+
+try:
+ from boto.s3.connection import S3Connection, OrdinaryCallingFormat
+ from boto.s3.key import Key
+except ImportError:
+ raise Exception('You must install the boto package for python')
+
+
+class FakeS3Cmd(object):
+ COMMANDS = ['mb', 'rb', 'put', ]
+ def __init__(self, host, port):
+ self.host = host
+ self.port = port
+ self.conn = None
+ self._connect()
+
+ def _connect(self):
+ print 'Connecting: %s:%s' % (self.host, self.port)
+ self.conn = S3Connection(is_secure=False,
+ calling_format=OrdinaryCallingFormat(),
+ aws_access_key_id='',
+ aws_secret_access_key='',
+ port=self.port, host=self.host)
+
+
+ @staticmethod
+ def _parse_uri(path):
+ match = re.match(r's3://([^/]+)(?:/(.*))?', path, re.I)
+ ## (bucket, key)
+ return match.groups()
+
+ def mb(self, path, *args):
+ if not self.conn:
+ self._connect()
+
+ bucket, _ = self._parse_uri(path)
+ self.conn.create_bucket(bucket)
+ print 'made bucket: [%s]' % bucket
+
+ def rb(self, path, *args):
+ if not self.conn:
+ self._connect()
+
+ bucket, _ = self._parse_uri(path)
+ self.conn.delete_bucket(bucket)
+ print 'removed bucket: [%s]' % bucket
+
+ def put(self, *args):
+ if not self.conn:
+ self._connect()
+
+ args = list(args)
+ path = args.pop()
+ bucket_name, prefix = self._parse_uri(path)
+ bucket = self.conn.get_bucket(bucket_name)
+ for src_file in args:
+ key = Key(bucket)
+ key.key = os.path.join(prefix, os.path.basename(src_file))
+ key.set_contents_from_filename(src_file)
+ print 'stored: [%s]' % key.key
+
+
+if __name__ == "__main__":
+ # check for options. TODO: This requires a more verbose help message
+ # to explain how the positional arguments work.
+ parser = OptionParser()
+ parser.add_option("-t", "--host", type="string", default='localhost')
+ parser.add_option("-p", "--port", type='int', default=80)
+ o, args = parser.parse_args()
+
+ if len(args) < 2:
+ raise ValueError('you must minimally supply a desired command and s3 uri')
+
+ cmd = args.pop(0)
+
+ if cmd not in FakeS3Cmd.COMMANDS:
+ raise ValueError('%s is not a valid command' % cmd)
+
+ fs3 = FakeS3Cmd(o.host, o.port)
+ handler = getattr(fs3, cmd)
+ handler(*args)

0 comments on commit 898dde9

Please sign in to comment.