fog 0.0.12 → 0.0.13

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.12
1
+ 0.0.13
data/bin/fog CHANGED
@@ -3,7 +3,9 @@ require File.join(File.dirname(__FILE__), '..', 'lib', 'fog')
3
3
  require 'irb'
4
4
  require 'yaml'
5
5
 
6
- if File.exists?(File.expand_path('~/.fog'))
6
+ if ARGV[0] && File.exists?(File.expand_path(ARGV[0]))
7
+ @credentials = YAML.load(File.open(File.expand_path(ARGV[0])).read)
8
+ elsif File.exists?(File.expand_path('~/.fog'))
7
9
  @credentials = YAML.load(File.open(File.expand_path('~/.fog')).read)
8
10
  end
9
11
 
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{fog}
8
- s.version = "0.0.12"
8
+ s.version = "0.0.13"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Wesley Beary"]
12
- s.date = %q{2009-10-01}
12
+ s.date = %q{2009-10-05}
13
13
  s.default_executable = %q{fog}
14
14
  s.description = %q{brings clouds to you}
15
15
  s.email = %q{me@geemus.com}
@@ -42,6 +42,10 @@ module Fog
42
42
  nil
43
43
  end
44
44
 
45
+ def get_url(key, expires)
46
+ connection.get_object_url(bucket.name, key, expires)
47
+ end
48
+
45
49
  def head(key, options = {})
46
50
  data = connection.head_object(bucket.name, key, options)
47
51
  object_data = {
@@ -33,7 +33,7 @@ unless Fog.mocking?
33
33
  :host => "#{target_bucket_name}.#{@host}",
34
34
  :method => 'PUT',
35
35
  :parser => Fog::Parsers::AWS::S3::CopyObject.new,
36
- :path => target_object_name
36
+ :path => CGI.escape(target_object_name)
37
37
  })
38
38
  end
39
39
 
@@ -19,7 +19,7 @@ unless Fog.mocking?
19
19
  :headers => {},
20
20
  :host => "#{bucket_name}.#{@host}",
21
21
  :method => 'DELETE',
22
- :path => object_name
22
+ :path => CGI.escape(object_name)
23
23
  })
24
24
  end
25
25
 
@@ -39,11 +39,26 @@ unless Fog.mocking?
39
39
  :headers => headers,
40
40
  :host => "#{bucket_name}.#{@host}",
41
41
  :method => 'GET',
42
- :path => object_name,
42
+ :path => CGI.escape(object_name),
43
43
  :block => block
44
44
  })
45
45
  end
46
46
 
47
+ def get_object_url(bucket_name, object_name, expires)
48
+ unless bucket_name
49
+ raise ArgumentError.new('bucket_name is required')
50
+ end
51
+ unless object_name
52
+ raise ArgumentError.new('object_name is required')
53
+ end
54
+ url({
55
+ :headers => {},
56
+ :host => "#{bucket_name}.#{@host}",
57
+ :method => 'GET',
58
+ :path => object_name
59
+ }, expires)
60
+ end
61
+
47
62
  end
48
63
  end
49
64
  end
@@ -34,7 +34,7 @@ unless Fog.mocking?
34
34
  :headers => headers,
35
35
  :host => "#{bucket_name}.#{@host}",
36
36
  :method => 'HEAD',
37
- :path => object_name
37
+ :path => CGI.escape(object_name)
38
38
  })
39
39
  end
40
40
 
@@ -33,7 +33,7 @@ unless Fog.mocking?
33
33
  :headers => headers,
34
34
  :host => "#{bucket_name}.#{@host}",
35
35
  :method => 'PUT',
36
- :path => object_name
36
+ :path => CGI.escape(object_name)
37
37
  })
38
38
  end
39
39
 
@@ -55,7 +55,7 @@ module Fog
55
55
  # :aws_secret_access_key in order to create a connection
56
56
  #
57
57
  # ==== Examples
58
- # sdb = S3.new(
58
+ # s3 = S3.new(
59
59
  # :aws_access_key_id => your_aws_access_key_id,
60
60
  # :aws_secret_access_key => your_aws_secret_access_key
61
61
  # )
@@ -100,7 +100,33 @@ module Fog
100
100
 
101
101
  def request(params)
102
102
  params[:headers]['Date'] = Time.now.utc.strftime("%a, %d %b %Y %H:%M:%S +0000")
103
+ params[:headers]['Authorization'] = "AWS #{@aws_access_key_id}:#{signature(params)}"
103
104
 
105
+ response = @connection.request({
106
+ :block => params[:block],
107
+ :body => params[:body],
108
+ :expects => params[:expects],
109
+ :headers => params[:headers],
110
+ :host => params[:host],
111
+ :method => params[:method],
112
+ :parser => params[:parser],
113
+ :path => params[:path],
114
+ :query => params[:query]
115
+ })
116
+
117
+ response
118
+ end
119
+
120
+ def url(params, expires)
121
+ params[:headers]['Date'] = expires.to_i
122
+ query = [params[:query]].compact
123
+ query << "AWSAccessKeyId=#{@aws_access_key_id}"
124
+ query << "Signature=#{CGI.escape(signature(params))}"
125
+ query << "Expires=#{params[:headers]['Date']}"
126
+ "http://#{params[:host]}/#{params[:path]}?#{query.join('&')}"
127
+ end
128
+
129
+ def signature(params)
104
130
  string_to_sign =
105
131
  <<-DATA
106
132
  #{params[:method]}
@@ -121,9 +147,20 @@ DATA
121
147
  end
122
148
  string_to_sign << "#{canonical_amz_headers}"
123
149
 
124
- canonical_resource = "/"
125
150
  subdomain = params[:host].split(".#{@host}").first
126
- unless subdomain == @host
151
+ unless subdomain =~ /^(?:[a-z]|\d(?!\d{0,2}(?:\.\d{1,3}){3}$))(?:[a-z0-9]|\.(?![\.\-])|\-(?![\.])){1,61}[a-z0-9]$/
152
+ puts("[WARN] fog: the specified s3 bucket name(#{subdomain}) is not a valid dns name. See: http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?Introduction.html")
153
+ params[:host] = params[:host].split("#{subdomain}.")[-1]
154
+ if params[:path]
155
+ params[:path] = "#{subdomain}/#{params[:path]}"
156
+ else
157
+ params[:path] = "#{subdomain}"
158
+ end
159
+ subdomain = nil
160
+ end
161
+
162
+ canonical_resource = "/"
163
+ unless subdomain.nil? || subdomain == @host
127
164
  canonical_resource << "#{CGI.escape(subdomain).downcase}/"
128
165
  end
129
166
  canonical_resource << "#{params[:path]}"
@@ -134,21 +171,6 @@ DATA
134
171
 
135
172
  hmac = @hmac.update(string_to_sign)
136
173
  signature = Base64.encode64(hmac.digest).chomp!
137
- params[:headers]['Authorization'] = "AWS #{@aws_access_key_id}:#{signature}"
138
-
139
- response = @connection.request({
140
- :block => params[:block],
141
- :body => params[:body],
142
- :expects => params[:expects],
143
- :headers => params[:headers],
144
- :host => params[:host],
145
- :method => params[:method],
146
- :parser => params[:parser],
147
- :path => params[:path],
148
- :query => params[:query]
149
- })
150
-
151
- response
152
174
  end
153
175
 
154
176
  end
@@ -17,13 +17,20 @@ unless Fog.mocking?
17
17
 
18
18
  def initialize(url)
19
19
  @uri = URI.parse(url)
20
- @connection = TCPSocket.open(@uri.host, @uri.port)
21
- if @uri.scheme == 'https'
22
- @ssl_context = OpenSSL::SSL::SSLContext.new
23
- @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
24
- @connection = OpenSSL::SSL::SSLSocket.new(@connection, @ssl_context)
25
- @connection.sync_close = true
26
- @connection.connect
20
+ end
21
+
22
+ def connection
23
+ if @connection && !@connection.closed?
24
+ @connection
25
+ else
26
+ @connection = TCPSocket.open(@uri.host, @uri.port)
27
+ if @uri.scheme == 'https'
28
+ @ssl_context = OpenSSL::SSL::SSLContext.new
29
+ @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
30
+ @connection = OpenSSL::SSL::SSLSocket.new(@connection, @ssl_context)
31
+ @connection.sync_close = true
32
+ @connection.connect
33
+ end
27
34
  end
28
35
  end
29
36
 
@@ -45,7 +52,7 @@ unless Fog.mocking?
45
52
  request << "#{key}: #{value}\r\n"
46
53
  end
47
54
  request << "\r\n"
48
- @connection.write(request)
55
+ connection.write(request)
49
56
 
50
57
  if params[:body]
51
58
  if params[:body].is_a?(String)
@@ -54,18 +61,18 @@ unless Fog.mocking?
54
61
  body = params[:body]
55
62
  end
56
63
  while chunk = body.read(CHUNK_SIZE)
57
- @connection.write(chunk)
64
+ connection.write(chunk)
58
65
  end
59
66
  end
60
67
 
61
68
  response = Fog::Response.new
62
69
  response.request = params
63
- response.status = @connection.readline[9..11].to_i
70
+ response.status = connection.readline[9..11].to_i
64
71
  if params[:expects] && params[:expects] != response.status
65
72
  error = true
66
73
  end
67
74
  while true
68
- data = @connection.readline.chomp!
75
+ data = connection.readline.chomp!
69
76
  if data == ""
70
77
  break
71
78
  end
@@ -73,35 +80,35 @@ unless Fog.mocking?
73
80
  response.headers[capitalize(header[0])] = header[1]
74
81
  end
75
82
 
76
- if error || params[:parser]
77
- if error
78
- parser = Fog::Errors::Parser.new
79
- elsif params[:parser]
80
- parser = params[:parser]
83
+ unless params[:method] == 'HEAD'
84
+ if error || params[:parser]
85
+ if error
86
+ parser = Fog::Errors::Parser.new
87
+ elsif params[:parser]
88
+ parser = params[:parser]
89
+ end
90
+ body = Nokogiri::XML::SAX::PushParser.new(parser)
91
+ elsif params[:block]
92
+ body = nil
93
+ else
94
+ body = ''
81
95
  end
82
- body = Nokogiri::XML::SAX::PushParser.new(parser)
83
- elsif params[:block]
84
- body = nil
85
- else
86
- body = ''
87
- end
88
96
 
89
- unless params[:method] == 'HEAD'
90
97
  if response.headers['Content-Length']
91
98
  if error || !params[:block]
92
- body << @connection.read(response.headers['Content-Length'].to_i)
99
+ body << connection.read(response.headers['Content-Length'].to_i)
93
100
  else
94
101
  remaining = response.headers['Content-Length'].to_i
95
102
  while remaining > 0
96
- params[:block].call(@connection.read([CHUNK_SIZE, remaining].min))
103
+ params[:block].call(connection.read([CHUNK_SIZE, remaining].min))
97
104
  remaining -= CHUNK_SIZE;
98
105
  end
99
106
  end
100
107
  elsif response.headers['Transfer-Encoding'] == 'chunked'
101
108
  while true
102
109
  # 2 == "/r/n".length
103
- chunk_size = @connection.readline.chomp!.to_i(16) + 2
104
- chunk = @connection.read(chunk_size)[0...-2]
110
+ chunk_size = connection.readline.chomp!.to_i(16) + 2
111
+ chunk = connection.read(chunk_size)[0...-2]
105
112
  if error || !params[:block]
106
113
  body << chunk
107
114
  else
@@ -112,13 +119,13 @@ unless Fog.mocking?
112
119
  end
113
120
  end
114
121
  end
115
- end
116
122
 
117
- if parser
118
- body.finish
119
- response.body = parser.response
120
- else
121
- response.body = body
123
+ if parser
124
+ body.finish
125
+ response.body = parser.response
126
+ else
127
+ response.body = body
128
+ end
122
129
  end
123
130
 
124
131
  if error
@@ -90,6 +90,18 @@ describe 'Fog::AWS::S3::Objects' do
90
90
 
91
91
  end
92
92
 
93
+ describe "#get_url" do
94
+
95
+ it "should return a signed expiring url" do
96
+ file = File.open(File.dirname(__FILE__) + '/../../../lorem.txt', 'r')
97
+ object = @bucket.objects.create(:key => 'fogobjectname', :body => file)
98
+ url = @bucket.objects.get_url('fogobjectname', Time.now + 60 * 10)
99
+ open(url).read.should == File.open(File.dirname(__FILE__) + '/../../../lorem.txt', 'r').read
100
+ object.destroy
101
+ end
102
+
103
+ end
104
+
93
105
  describe "#head" do
94
106
 
95
107
  it "should return a Fog::AWS::S3::Object with metadata" do
@@ -106,5 +106,11 @@ describe 'S3.get_bucket' do
106
106
  }.should raise_error(Fog::Errors::NotFound)
107
107
  end
108
108
 
109
+ it 'should request non-subdomain buckets and raise a NotFound error' do
110
+ lambda {
111
+ s3.get_bucket('A-invalid--name')
112
+ }.should raise_error(Fog::Errors::NotFound)
113
+ end
114
+
109
115
  end
110
116
  end
@@ -35,6 +35,12 @@ describe 'S3.get_object' do
35
35
  data.should == file.read
36
36
  end
37
37
 
38
+ it 'should return a signed expiring url' do
39
+ url = s3.get_object_url('foggetobject', 'fog_get_object', Time.now + 60 * 10)
40
+ file = File.open(File.dirname(__FILE__) + '/../../../lorem.txt', 'r')
41
+ open(url).read.should == file.read
42
+ end
43
+
38
44
  end
39
45
  describe 'failure' do
40
46
 
@@ -1,4 +1,5 @@
1
1
  require 'spec'
2
+ require 'open-uri'
2
3
 
3
4
  current_directory = File.dirname(__FILE__)
4
5
  require "#{current_directory}/../lib/fog"
@@ -6,7 +7,7 @@ require "#{current_directory}/../lib/fog"
6
7
 
7
8
  def credentials
8
9
  @credentials ||= begin
9
- credentials_path = "#{File.dirname(__FILE__)}/credentials.yml"
10
+ credentials_path = File.expand_path('~/.fog')
10
11
  credentials_data = File.open(credentials_path).read
11
12
  YAML.load(credentials_data)
12
13
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fog
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.12
4
+ version: 0.0.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wesley Beary
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-10-01 00:00:00 -07:00
12
+ date: 2009-10-05 00:00:00 -07:00
13
13
  default_executable: fog
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency