under-os-http 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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