awsraw 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in awsraw.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/awsraw.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "awsraw/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "awsraw"
7
+ s.version = Awsraw::VERSION
8
+ s.authors = ["Pete Yandell"]
9
+ s.email = ["pete@notahat.com"]
10
+ s.homepage = "http://github.com/envato/flickraw"
11
+ s.summary = %q{Minimal AWS client}
12
+ s.description = %q{A client for Amazon Web Services in the style of FlickRaw}
13
+
14
+ s.rubyforge_project = "awsraw"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_development_dependency "rake"
23
+ s.add_development_dependency "rspec"
24
+ # s.add_runtime_dependency "rest-client"
25
+ end
@@ -0,0 +1,78 @@
1
+ require 'net/http'
2
+ require 'awsraw/s3/request'
3
+ require 'awsraw/s3/response'
4
+ require 'awsraw/s3/signer'
5
+
6
+ module AWSRaw
7
+ module S3
8
+
9
+ # A client for the AWS S3 rest API.
10
+ #
11
+ # http://docs.amazonwebservices.com/AmazonS3/latest/API/APIRest.html
12
+ class Client
13
+
14
+ def initialize(access_key_id, secret_access_key)
15
+ @access_key_id = access_key_id
16
+ @secret_access_key = secret_access_key
17
+ end
18
+
19
+ def request(params = {})
20
+ request = Request.new(params, signer)
21
+
22
+ http_request = construct_http_request(request)
23
+
24
+ http_response = Net::HTTP.start(request.uri.host, request.uri.port) do |http|
25
+ http.request(http_request)
26
+ end
27
+
28
+ construct_response(http_response)
29
+ end
30
+
31
+ def request!(params = {})
32
+ response = request(params)
33
+ raise "Uh oh! Failure from S3." if response.failure?
34
+ end
35
+
36
+ private
37
+
38
+ def construct_http_request(request)
39
+ klass = http_request_class(request)
40
+ path = request.uri.request_uri
41
+
42
+ klass.new(path).tap do |http_request|
43
+ request.headers.each do |name, value|
44
+ http_request[name] = value
45
+ end
46
+ http_request.body = request.content
47
+ end
48
+ end
49
+
50
+ def http_request_class(request)
51
+ case request.method
52
+ when "GET"
53
+ Net::HTTP::Get
54
+ when "HEAD"
55
+ Net::HTTP::Head
56
+ when "PUT"
57
+ Net::HTTP::Put
58
+ else
59
+ raise "Invalid HTTP method!"
60
+ end
61
+ end
62
+
63
+ def construct_response(http_response)
64
+ Response.new(
65
+ :code => http_response.code,
66
+ :headers => http_response.to_hash,
67
+ :content => http_response.body
68
+ )
69
+ end
70
+
71
+ def signer
72
+ @signer ||= Signer.new(@access_key_id, @secret_access_key)
73
+ end
74
+
75
+ end
76
+
77
+ end
78
+ end
@@ -0,0 +1,58 @@
1
+ require 'digest/md5'
2
+ require 'time'
3
+ require 'uri'
4
+
5
+ module AWSRaw
6
+ module S3
7
+
8
+ # Note that we use path style (rather than virtual hosted style) requests.
9
+ # This is because virtual hosted requests only support lower case bucket
10
+ # names.
11
+ #
12
+ # See http://docs.amazonwebservices.com/AmazonS3/latest/dev/VirtualHosting.html
13
+ class Request
14
+ def initialize(params, signer)
15
+ @method = params[:method]
16
+ @bucket = params[:bucket]
17
+ @key = params[:key]
18
+ @query = params[:query]
19
+ @headers = params[:headers] || {}
20
+ @content = params[:content]
21
+
22
+ raise "Content without Content-Type" if !@content.nil? && @headers["Content-Type"].nil?
23
+
24
+ headers["Content-MD5"] = content_md5 unless content.nil?
25
+ headers["Date"] = Time.now.rfc2822
26
+ headers["Authorization"] = signer.signature(self)
27
+ end
28
+
29
+ attr_reader :method
30
+ attr_reader :bucket
31
+ attr_reader :key
32
+ attr_reader :query
33
+ attr_reader :headers
34
+ attr_reader :content
35
+
36
+ def host
37
+ "s3.amazonaws.com"
38
+ end
39
+
40
+ def path
41
+ @path ||= URI.escape("/#{bucket}#{key}")
42
+ end
43
+
44
+ def uri
45
+ @uri ||= URI::HTTP.build(
46
+ :host => host,
47
+ :path => path,
48
+ :query => query
49
+ )
50
+ end
51
+
52
+ def content_md5
53
+ @content_md5 ||= Base64.encode64(Digest::MD5.digest(content)).strip
54
+ end
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,26 @@
1
+ module AWSRaw
2
+ module S3
3
+
4
+ class Response
5
+ def initialize(params = {})
6
+ @code = params[:code]
7
+ @headers = params[:headers]
8
+ @content = params[:content]
9
+ end
10
+
11
+ attr_accessor :code
12
+ attr_accessor :headers
13
+ attr_accessor :content
14
+
15
+ def success?
16
+ code =~ /^2\d\d$/
17
+ end
18
+
19
+ def failure?
20
+ !success?
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+
@@ -0,0 +1,60 @@
1
+ require 'digest/sha1'
2
+ require 'openssl'
3
+ require 'uri'
4
+ require 'base64'
5
+
6
+ module AWSRaw
7
+ module S3
8
+
9
+ # Generates the Authorization header for a REST request to S3.
10
+ #
11
+ # See http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAuthentication.html
12
+ class Signer
13
+
14
+ def initialize(access_key_id, secret_access_key)
15
+ @access_key_id = access_key_id
16
+ @secret_access_key = secret_access_key
17
+ end
18
+
19
+ def signature(request)
20
+ string_to_sign = string_to_sign(request)
21
+
22
+ digest = OpenSSL::Digest::Digest.new("sha1")
23
+ sha = OpenSSL::HMAC.digest(digest, @secret_access_key, string_to_sign)
24
+ signature = Base64.encode64(sha).strip
25
+
26
+ "AWS #{@access_key_id}:#{signature}"
27
+ end
28
+
29
+ def string_to_sign(request)
30
+ [
31
+ request.method,
32
+ request.headers["Content-MD5"] || "",
33
+ request.headers["Content-Type"] || "",
34
+ request.headers["Date"],
35
+ canonicalized_amz_headers(request.headers),
36
+ canonicalized_resource(request)
37
+ ].flatten.join("\n")
38
+ end
39
+
40
+ private
41
+
42
+ def canonicalized_amz_headers(headers)
43
+ header_names = headers.keys.
44
+ select {|name| name =~ /^x-amz-/i }.
45
+ sort_by {|name| name.downcase }
46
+
47
+ header_names.map do |name|
48
+ "#{name.downcase}:#{headers[name]}"
49
+ end
50
+ end
51
+
52
+ def canonicalized_resource(request)
53
+ # TODO: Should also append the sub-resource.
54
+ request.path
55
+ end
56
+
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,3 @@
1
+ module Awsraw
2
+ VERSION = "0.0.1"
3
+ end
data/lib/awsraw.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "awsraw/version"
2
+
3
+ module Awsraw
4
+ # Your code goes here...
5
+ end
@@ -0,0 +1,28 @@
1
+ require 'awsraw/s3/client'
2
+
3
+ describe AWSRaw::S3::Client do
4
+
5
+ subject { AWSRaw::S3::Client.new("dummy_access_key_id", "dummy_secret_access_key") }
6
+
7
+ context "#request!" do
8
+ it "returns if the response indicates success" do
9
+ response = stub(:failure? => false)
10
+ subject.stub(:request => response)
11
+
12
+ expect {
13
+ subject.request!(:method => "PUT")
14
+ }.should_not raise_error
15
+ end
16
+
17
+ it "raises an error if the response indicates failure" do
18
+ response = stub(:failure? => true)
19
+ subject.stub(:request => response)
20
+
21
+ expect {
22
+ subject.request!(:method => "PUT")
23
+ }.should raise_error("Uh oh! Failure from S3.")
24
+ end
25
+ end
26
+
27
+ end
28
+
@@ -0,0 +1,49 @@
1
+ require 'awsraw/s3/signer'
2
+
3
+ describe AWSRaw::S3::Signer do
4
+ let(:access_key_id) { "0PN5J17HBGZHT7JJ3X82" }
5
+ let(:secret_access_key) { "uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o" }
6
+
7
+ subject { AWSRaw::S3::Signer.new(access_key_id, secret_access_key) }
8
+
9
+ context "examples from Amazon docs" do
10
+ it "signs a get request correctly" do
11
+ request = stub(
12
+ :method => "GET",
13
+ :path => "/johnsmith/photos/puppy.jpg",
14
+ :headers => { "Date" => "Tue, 27 Mar 2007 19:36:42 +0000" }
15
+ )
16
+
17
+ subject.string_to_sign(request).should ==
18
+ "GET\n\n\nTue, 27 Mar 2007 19:36:42 +0000\n/johnsmith/photos/puppy.jpg"
19
+
20
+ subject.signature(request).should == "AWS #{access_key_id}:xXjDGYUmKxnwqr5KXNPGldn5LbA="
21
+ end
22
+
23
+ it "signs an upload correctly" do
24
+ request = stub(
25
+ :method => "PUT",
26
+ :path => "/static.johnsmith.net/db-backup.dat.gz",
27
+ :headers => {
28
+ "User-Agent" => "curl/7.15.5",
29
+ "Date" => "Tue, 27 Mar 2007 21:06:08 +0000",
30
+ "x-amz-acl" => "public-read",
31
+ "Content-Type" => "application/x-download",
32
+ "Content-MD5" => "4gJE4saaMU4BqNR0kLY+lw==",
33
+ "X-Amz-Meta-ReviewedBy" => "joe@johnsmith.net,jane@johnsmith.net",
34
+ "X-Amz-Meta-FileChecksum" => "0x02661779",
35
+ "X-Amz-Meta-ChecksumAlgorithm" => "crc32",
36
+ "Content-Disposition" => "attachment; filename=database.dat",
37
+ "Content-Encoding" => "gzip",
38
+ "Content-Length" => "5913339"
39
+ }
40
+ )
41
+
42
+ subject.string_to_sign(request).should ==
43
+ "PUT\n4gJE4saaMU4BqNR0kLY+lw==\napplication/x-download\nTue, 27 Mar 2007 21:06:08 +0000\nx-amz-acl:public-read\nx-amz-meta-checksumalgorithm:crc32\nx-amz-meta-filechecksum:0x02661779\nx-amz-meta-reviewedby:joe@johnsmith.net,jane@johnsmith.net\n/static.johnsmith.net/db-backup.dat.gz"
44
+
45
+ subject.signature(request).should == "AWS #{access_key_id}:C0FlOtU8Ylb9KDTpZqYkZPX91iI="
46
+ end
47
+
48
+ end
49
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: awsraw
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Pete Yandell
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-02-12 00:00:00 +11:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rake
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ hash: 3
31
+ segments:
32
+ - 0
33
+ version: "0"
34
+ requirement: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: rspec
37
+ type: :development
38
+ prerelease: false
39
+ version_requirements: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ requirement: *id002
49
+ description: A client for Amazon Web Services in the style of FlickRaw
50
+ email:
51
+ - pete@notahat.com
52
+ executables: []
53
+
54
+ extensions: []
55
+
56
+ extra_rdoc_files: []
57
+
58
+ files:
59
+ - .gitignore
60
+ - Gemfile
61
+ - Rakefile
62
+ - awsraw.gemspec
63
+ - lib/awsraw.rb
64
+ - lib/awsraw/s3/client.rb
65
+ - lib/awsraw/s3/request.rb
66
+ - lib/awsraw/s3/response.rb
67
+ - lib/awsraw/s3/signer.rb
68
+ - lib/awsraw/version.rb
69
+ - spec/s3/client_spec.rb
70
+ - spec/s3/signer_spec.rb
71
+ has_rdoc: true
72
+ homepage: http://github.com/envato/flickraw
73
+ licenses: []
74
+
75
+ post_install_message:
76
+ rdoc_options: []
77
+
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ hash: 3
86
+ segments:
87
+ - 0
88
+ version: "0"
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ hash: 3
95
+ segments:
96
+ - 0
97
+ version: "0"
98
+ requirements: []
99
+
100
+ rubyforge_project: awsraw
101
+ rubygems_version: 1.6.2
102
+ signing_key:
103
+ specification_version: 3
104
+ summary: Minimal AWS client
105
+ test_files:
106
+ - spec/s3/client_spec.rb
107
+ - spec/s3/signer_spec.rb