under-os-http 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c1b0c0d55e665fc548b88e13195fdb1ae78fd3ff
4
+ data.tar.gz: b6f44330bd12f8eefed0599735e3cdeda7d46479
5
+ SHA512:
6
+ metadata.gz: 96e130ed94fa445e267f37705e4a4411c92e90e31f3f2d7a87030d91a7678662c29fbaec08e10933594655c827bfdfabdc14e19971c350e84fbf6a32dfccd1fc
7
+ data.tar.gz: 6777d5795d5cf5bf35ad8b9fab2fdf46d44407f302ea14f5b332c7fd09ae0ad3901a826b788be7274c0dcf2542f02632a58be638792701bb4f457afc25eba7c2
@@ -0,0 +1,26 @@
1
+ # UnderOs Core
2
+
3
+ This is the HTTP stack module for the [UnderOs](http://under-os.com) project
4
+
5
+ ## Copyright & License
6
+
7
+ All code in this library is released under the terms of the MIT license
8
+
9
+ Copyright (C) 2013-2014 Nikolay Nemshilov
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
15
+ of the Software, and to permit persons to whom the Software is furnished to do so,
16
+ subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in all
19
+ copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
22
+ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
23
+ PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
26
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,5 @@
1
+ class Array
2
+ def to_query(key)
3
+ collect { |value| value.to_query("#{key}[]") }.join '&'
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ class Hash
2
+ #
3
+ # Converts a hash into an URL query string
4
+ #
5
+ def to_query(namespace=nil)
6
+ map do |key, value|
7
+ value.to_query(namespace ? "#{namespace}[#{key}]" : key)
8
+ end.sort * '&'
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ class Object
2
+ def to_query(key)
3
+ "#{key.to_s.url_encode}=#{to_s.url_encode}"
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ class String
2
+ def url_encode(encoding=nil)
3
+ stringByAddingPercentEscapesUsingEncoding ENCODINGS[encoding] || NSUTF8StringEncoding
4
+ end
5
+
6
+ def url_decode(encoding=nil)
7
+ stringByReplacingPercentEscapesUsingEncoding ENCODINGS[encoding] || NSUTF8StringEncoding
8
+ end
9
+ end
@@ -0,0 +1 @@
1
+ UnderOs.extend __FILE__
@@ -0,0 +1,31 @@
1
+ class UnderOs::HTTP
2
+ def self.get(url, options={}, &block)
3
+ request url, options.merge(method: :get), &block
4
+ end
5
+
6
+ def self.post(url, options={}, &block)
7
+ request url, options.merge(method: :post), &block
8
+ end
9
+
10
+ def self.put(url, options={}, &block)
11
+ request url, options.merge(method: :put), &block
12
+ end
13
+
14
+ def self.patch(url, options={}, &block)
15
+ request url, options.merge(method: :patch), &block
16
+ end
17
+
18
+ def self.head(url, options={}, &block)
19
+ request url, options.merge(method: :head), &block
20
+ end
21
+
22
+ def self.delete(url, options={}, &block)
23
+ request url, options.merge(method: :delete), &block
24
+ end
25
+
26
+ protected
27
+
28
+ def self.request(*args, &block)
29
+ Request.new(*args, &block).send
30
+ end
31
+ end
@@ -0,0 +1,30 @@
1
+ #
2
+ # A simple cookie jar thing
3
+ #
4
+ class UnderOs::HTTP::Cookies
5
+ def initialize(cookies, url)
6
+ @url = url.is_a?(String) ? NSURL.URLWithString(url) : url
7
+ @hash = cookies
8
+ end
9
+
10
+ def headers
11
+ NSHTTPCookie.requestHeaderFieldsWithCookies(ios_cookies)
12
+ end
13
+
14
+ def to_s
15
+ headers["Cookie"]
16
+ end
17
+
18
+ protected
19
+
20
+ def ios_cookies
21
+ @hash.map do |key, value|
22
+ NSHTTPCookie.cookieWithProperties({
23
+ NSHTTPCookieDomain => @url.host,
24
+ NSHTTPCookiePath => "/",
25
+ NSHTTPCookieName => key.to_s,
26
+ NSHTTPCookieValue => value.to_s
27
+ })
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,44 @@
1
+ #
2
+ # A little wrap to handle iOS HTTP requests receiving
3
+ #
4
+ class UnderOs::HTTP::Request
5
+ class Receiver
6
+ def initialize(request, stream=false)
7
+ @request = request
8
+ @stream = stream
9
+ @data = NSMutableData.dataWithCapacity(0)
10
+ end
11
+
12
+ def connection(connection, didReceiveResponse:response)
13
+ @data.setLength(0)
14
+ @response = UnderOs::HTTP::Response.new(response, @data)
15
+
16
+ emit(:response)
17
+ end
18
+
19
+ def connection(connection, didReceiveData:data)
20
+ @data.setLength(0) if @stream
21
+ @data.appendData(data)
22
+ emit(:data)
23
+ end
24
+
25
+ # def connection(connection, willCacheResponse:cachedResponse)
26
+ # return nil # don't cache
27
+ # end
28
+
29
+ def connection(connection, didFailWithError:error)
30
+ @response = UnderOs::HTTP::Response.new(error, @data)
31
+
32
+ emit(:failure)
33
+ end
34
+
35
+ def connectionDidFinishLoading(connection)
36
+ emit(:success)
37
+ end
38
+
39
+ def emit(event)
40
+ @request.emit(event, response: @response)
41
+ @request.emit(:complete, response: @response) if event == :failure || event == :success
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,106 @@
1
+ #
2
+ # Options:
3
+ #
4
+ # * :method - GET, POST, ...
5
+ # * :cookies - Hash of cookie values
6
+ # * :stream - boolean, in case you want to stream stuff
7
+ # * :params - the POST/PUT/PATCH params hash
8
+ # * :encoding - the POST data encoding
9
+ #
10
+ # Events:
11
+ #
12
+ # * :response - when response header is received
13
+ # * :data - when a chunk of data is received
14
+ # * :success - when the request is successfully finished
15
+ # * :failure - when request has failed for whatever reason
16
+ # * :complete - when it's complete (either way)
17
+ #
18
+ class UnderOs::HTTP::Request
19
+ include UnderOs::Events
20
+
21
+ attr_reader :url, :method, :params
22
+
23
+ def initialize(url, options={}, &block)
24
+ @url = url
25
+ @options = options
26
+
27
+ on :complete, &block if block_given?
28
+ end
29
+
30
+ def on(*args, &block)
31
+ super *args do |event|
32
+ args = block.arity == 0 ? [] : [event.params[:response]]
33
+ block.call *args
34
+ end
35
+ end
36
+
37
+ def send
38
+ @request = build_request
39
+ @receiver = Receiver.new(self, @options[:stream])
40
+ @connection = NSURLConnection.alloc.initWithRequest(@request, delegate:@receiver)
41
+ @connection.start
42
+
43
+ return self
44
+ end
45
+
46
+ def cancel
47
+ @connection.cancel
48
+
49
+ return self
50
+ end
51
+
52
+ def method
53
+ method = @options[:method] || :get
54
+ method = method.to_s.upcase
55
+ method = 'GET' unless %w[GET POST PUT DELETE PATCH HEAD].include?(method)
56
+ method
57
+ end
58
+
59
+ def headers
60
+ @options[:headers] || {}
61
+ end
62
+
63
+ def cookies
64
+ @options[:cookies] || {}
65
+ end
66
+
67
+ def params
68
+ @options[:params] || {}
69
+ end
70
+
71
+ def encoding
72
+ @options[:encoding] || 'utf-8'
73
+ end
74
+
75
+ protected
76
+
77
+ def build_request
78
+ query = params.to_query
79
+ url = build_url(query)
80
+
81
+ NSMutableURLRequest.requestWithURL(url).tap do |request|
82
+ request.setHTTPMethod method
83
+
84
+ headers.merge(UnderOs::HTTP::Cookies.new(cookies, url).headers).each do |key, value|
85
+ request.addValue value, forHTTPHeaderField:key
86
+ end
87
+
88
+ if %w[POST PUT PATCH DELETE].include?(method) && !query.empty?
89
+ request.addValue "application/x-www-form-urlencoded;charset=#{encoding}", forHTTPHeaderField:"Content-Type"
90
+ request.setHTTPBody query.to_data(encoding)
91
+ end
92
+ end
93
+ end
94
+
95
+ def build_url(query)
96
+ url = @url
97
+
98
+ if method == "GET" && !query.empty?
99
+ url += url.include?('?') ? '&' : '?'
100
+ url += query
101
+ end
102
+
103
+ NSURL.URLWithString(url)
104
+ end
105
+ end
106
+
@@ -0,0 +1,28 @@
1
+ class UnderOs::HTTP::Response
2
+ attr_reader :data
3
+
4
+ def initialize(response, data)
5
+ @response = response
6
+ @data = data # raw NSData
7
+ end
8
+
9
+ def headers
10
+ @response.allHeaderFields
11
+ end
12
+
13
+ def body
14
+ @body ||= @data.to_s
15
+ end
16
+
17
+ def json
18
+ JSON.parse(body)
19
+ end
20
+
21
+ def content_length
22
+ headers["Content-Length"].to_i
23
+ end
24
+
25
+ def content_type
26
+ headers["Content-Type"]
27
+ end
28
+ end
@@ -0,0 +1,49 @@
1
+ #
2
+ # Basically keeps track of cookies between the requests
3
+ #
4
+ class Session
5
+ def initialize(default_options={})
6
+ @options = default_options
7
+ @options[:cookies] ||= {}
8
+ end
9
+
10
+ def cookies
11
+ @options[:cookies]
12
+ end
13
+
14
+ def get(url, options={}, &block)
15
+ request(url, {method: 'GET'}.merge(options), &block).send
16
+ end
17
+
18
+ def post(url, options={}, &block)
19
+ request(url, {method: 'POST'}.merge(options), &block).send
20
+ end
21
+
22
+ def put(url, options={}, &block)
23
+ request(url, {method: 'PUT'}.merge(options), &block).send
24
+ end
25
+
26
+ def delete(url, options={}, &block)
27
+ request(url, {method: 'DELETE'}.merge(options), &block).send
28
+ end
29
+
30
+ def request(url, options={}, &block)
31
+ options = options.merge(@options){|k,a,b| a.merge(b) }
32
+
33
+ UnderOs::HTTP::Request.new(url, options, &block).tap do |request|
34
+ request.on :response do |response|
35
+ save_cookies response.headers["Set-Cookie"] || ''
36
+ end
37
+ end
38
+ end
39
+
40
+ protected
41
+
42
+ def save_cookies(cookies)
43
+ cookies.scan(/([a-z_]+?)=([^; ]+)/).each do |key, value|
44
+ unless ['path', 'domain', 'expires'].include?(key)
45
+ @options[:cookies][key] = value
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,19 @@
1
+ describe Hash do
2
+ describe '#to_query' do
3
+ it "converts a simple hash into a query string" do
4
+ {a: 1, b: 2}.to_query.should == 'a=1&b=2'
5
+ end
6
+
7
+ it "converts nested hashes into queries correctly" do
8
+ {a: 1, b: {c: {d: 4}}}.to_query.should == 'a=1&b%5Bc%5D%5Bd%5D=4'
9
+ end
10
+
11
+ it "handles array values correctly" do
12
+ {a: [1,2,3,4]}.to_query.should == 'a%5B%5D=1&a%5B%5D=2&a%5B%5D=3&a%5B%5D=4'
13
+ end
14
+
15
+ it "escapes any URL sensitive characters in the values" do
16
+ {a: '%= '}.to_query.should == 'a=%25=%20'
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ describe String do
2
+ describe '#url_encode' do
3
+ it "escapes all the URL sensitive symbols" do
4
+ "!*'\"();:@&=+$,/?%#[]% ".url_encode.should == "!*'%22();:@&=+$,/?%25%23%5B%5D%25%20"
5
+ end
6
+
7
+ it "doesn't touch normal characters" do
8
+ "Hello World".url_encode.should == "Hello%20World"
9
+ end
10
+ end
11
+
12
+ describe '#url_decode' do
13
+ it "converts an url encoded string back to normality" do
14
+ "!*'%22();:@&=+$,/?%25%23%5B%5D%25%20".url_decode.should == "!*'\"();:@&=+$,/?%#[]% "
15
+ end
16
+
17
+ it "doesn't break normal characters" do
18
+ "Hello%20World".url_decode.should == "Hello World"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ describe UnderOS::HTTP::Cookies do
2
+ before do
3
+ @cookies = UnderOS::HTTP::Cookies.new({a: 1, b: true}, "http://test.com")
4
+ end
5
+
6
+ it "allows to export them in a string" do
7
+ @cookies.to_s.should == "a=1; b=true"
8
+ end
9
+
10
+ it "allows to export them into an HTTP header hash" do
11
+ @cookies.headers.should == {"Cookie"=>"a=1; b=true"}
12
+ end
13
+ end
@@ -0,0 +1,44 @@
1
+ describe UnderOS::HTTP::Request do
2
+ before do
3
+ @url = "http://test.com"
4
+ @request = UnderOS::HTTP::Request.new(@url, {
5
+ headers: {my: 'headers'},
6
+ cookies: {my: 'cookies'},
7
+ params: {my: 'params'},
8
+ method: :put
9
+ })
10
+ end
11
+
12
+ describe "#constructor" do
13
+ it "assigns the url correctly" do
14
+ @request.url.should == @url
15
+ end
16
+
17
+ it "assigns the request headers correctly" do
18
+ @request.headers.should == {my: 'headers'}
19
+ end
20
+
21
+ it "assigns the request cookies correctly" do
22
+ @request.cookies.should == {my: 'cookies'}
23
+ end
24
+
25
+ it "assigns my params correctly" do
26
+ @request.params.should == {my: 'params'}
27
+ end
28
+
29
+ it "assigns the request method correctly" do
30
+ @request.method.should == 'PUT'
31
+ end
32
+ end
33
+
34
+ describe "#on" do
35
+ it "returns the response object instead of an event into a callback" do
36
+ @response = nil
37
+
38
+ @request.on(:smth) { |r| @response = r }
39
+ @request.emit(:smth, response: 'response')
40
+
41
+ @response.should == 'response'
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,20 @@
1
+ describe UnderOs::HTTP do
2
+ extend Facon::SpecHelpers
3
+
4
+ before do
5
+ @url = 'http://test.com'
6
+ @req = mock 'request', send: :request
7
+ end
8
+
9
+ %w[get post put patch head delete].each do |method|
10
+ describe ".#{method}" do
11
+ it "sends a #{method.upcase} request" do
12
+ UnderOs::HTTP::Request.should.receive(:new)
13
+ .with(@url, foo: 'bar', method: method.to_sym)
14
+ .and_return(@req)
15
+
16
+ UnderOs::HTTP.__send__(method, @url, foo: 'bar').should == :request
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.dirname(__FILE__) + "/../under-os-core/lib/under_os"
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = "under-os-http"
6
+ gem.version = UnderOs::VERSION
7
+ gem.homepage = "http://under-os.com"
8
+
9
+ gem.authors = ["Nikolay Nemshilov"]
10
+ gem.email = ['nemshilov@gmail.com']
11
+ gem.description = "The HTTP stack for the UnderOs project"
12
+ gem.summary = "The HTTP stack for the UnderOs project. Summaries FTW"
13
+ gem.license = 'MIT'
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+
19
+ gem.add_dependency 'under-os-core', UnderOs::VERSION
20
+
21
+ gem.add_development_dependency 'rake'
22
+ gem.add_development_dependency 'motion-facon'
23
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: under-os-http
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.4.0
5
+ platform: ruby
6
+ authors:
7
+ - Nikolay Nemshilov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: under-os-core
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 1.4.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 1.4.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: motion-facon
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: The HTTP stack for the UnderOs project
56
+ email:
57
+ - nemshilov@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - README.md
63
+ - lib/core/array.rb
64
+ - lib/core/hash.rb
65
+ - lib/core/object.rb
66
+ - lib/core/string.rb
67
+ - lib/under-os-http.rb
68
+ - lib/under_os/http.rb
69
+ - lib/under_os/http/cookies.rb
70
+ - lib/under_os/http/receiver.rb
71
+ - lib/under_os/http/request.rb
72
+ - lib/under_os/http/response.rb
73
+ - lib/under_os/http/session.rb
74
+ - spec/core/hash_spec.rb
75
+ - spec/core/string_spec.rb
76
+ - spec/under_os/http/cookies_spec.rb
77
+ - spec/under_os/http/request_spec.rb
78
+ - spec/under_os/http_spec.rb
79
+ - under-os-http.gemspec
80
+ homepage: http://under-os.com
81
+ licenses:
82
+ - MIT
83
+ metadata: {}
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project:
100
+ rubygems_version: 2.2.2
101
+ signing_key:
102
+ specification_version: 4
103
+ summary: The HTTP stack for the UnderOs project. Summaries FTW
104
+ test_files:
105
+ - spec/core/hash_spec.rb
106
+ - spec/core/string_spec.rb
107
+ - spec/under_os/http/cookies_spec.rb
108
+ - spec/under_os/http/request_spec.rb
109
+ - spec/under_os/http_spec.rb