link-middleware 0.0.2

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,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <http://unlicense.org/>
@@ -0,0 +1,76 @@
1
+ # Rack Middleware for LINK/UNLINK
2
+
3
+ Provides a simple implementation of [HTTP Link and Unlink Methods draft-snell-link-method-08](http://tools.ietf.org/html/draft-snell-link-method-08)
4
+ as configurable [Rack](http://rack.github.io/) middle-ware.
5
+
6
+ The code provides an easy way to instrument any existing Rack based application with support for the
7
+ `LINK` and `UNLINK` HTTP Methods.
8
+
9
+ ## Installation
10
+
11
+ Add the gem into your Gemfile:
12
+
13
+ gem 'link-middleware', :git => "git://github.com/ldodds/link-middleware.git"
14
+
15
+ Or:
16
+
17
+ sudo gem install link-middleware
18
+
19
+ ## Basic Usage
20
+
21
+ There are two separate middleware components:
22
+
23
+ * `LinkMiddleware::Filter` -- provides the basic LINK/UNLINK support
24
+ * `LinkMiddleware::Annotator` --- provides support for annotating requests with stored links
25
+
26
+ Here is a simple example see: `examples/simple.rb`
27
+
28
+ require 'sinatra'
29
+ require 'link-middleware'
30
+
31
+ set :store, LinkMiddleware::MemoryStore.new
32
+
33
+ use LinkMiddleware::Filter, :store => settings.store
34
+ use LinkMiddleware::Annotator, :store => settings.store
35
+
36
+ get '/' do
37
+ 'Hello World'
38
+ end
39
+
40
+ The `Filter` middleware will intercept and process `LINK` and `UNLINK` requests.
41
+
42
+ The `Annotator` middleware will automatically annotate responses to `/` with any stored links.
43
+
44
+ The middleware is configured to use a shared in-memory store. To make this persistent across
45
+ HTTP connections the store instance is cached as a Sinatra setting
46
+
47
+ ## Link Store Implementations
48
+
49
+ * In-Memory
50
+ * Redis (TODO)
51
+ * RDF Triplestore (TODO)
52
+
53
+ To configure a Link Store. The following will instantiate a new instance, passing any
54
+ additional options to the constructor of the store class:
55
+
56
+ use LinkMiddleware::Filter, :store_impl => LinkMiddleware::MemoryStore
57
+
58
+ Or, initialize the middleware with an existing object:
59
+
60
+ use LinkMiddleware::Filter, :store => LinkMiddleware::MemoryStore.new
61
+
62
+ Link Store implementations should have the following methods:
63
+
64
+ add(context_uri, header)
65
+ remove(context_uri, header)
66
+ read(context_uri)
67
+
68
+ The header parameter passed to `add` and `remove` will be an instance of `LinkHeader` from the
69
+ [link-header gem](https://github.com/asplake/link_header). This will have an array of links. Similarly
70
+ the response from `read` should be a `LinkHeader` instance.
71
+
72
+
73
+
74
+ ## Licence
75
+
76
+ Placed into the public domain under the unlicence. See `LICENCE.md`
@@ -0,0 +1,18 @@
1
+ require 'rack'
2
+ require 'link_header'
3
+
4
+ require 'link-middleware/annotator'
5
+ require 'link-middleware/filter'
6
+ require 'link-middleware/store'
7
+ require 'link-middleware/redis_store'
8
+
9
+ class LinkHeader::Link
10
+
11
+ def eql?(other)
12
+ return href == other.href && attr_pairs == other.attr_pairs
13
+ end
14
+
15
+ def hash
16
+ return href.hash + attr_pairs.hash
17
+ end
18
+ end
@@ -0,0 +1,33 @@
1
+ module LinkMiddleware
2
+
3
+ class Annotator
4
+
5
+ def initialize(app, options={})
6
+ @app = app
7
+ @options = options
8
+ if options[:store]
9
+ @store = options[:store]
10
+ elsif options[:store_impl]
11
+ @store = options[:store].new(options)
12
+ else
13
+ @store = LinkMiddleware::MemoryStore.new(options)
14
+ end
15
+ end
16
+
17
+ def call(env)
18
+ status, headers, response = @app.call(env)
19
+ request = Rack::Request.new( env )
20
+ stored_links = @store.read( request.url )
21
+ if !stored_links.links.empty?
22
+ link_header = stored_links.to_s
23
+ if headers["Link"]
24
+ link_header = env["Link"] + ";" + link_header
25
+ end
26
+ headers["Link"] = link_header
27
+ end
28
+ [status, headers, response]
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,53 @@
1
+ module LinkMiddleware
2
+
3
+ class Filter
4
+
5
+ def initialize(app, options={})
6
+ @app = app
7
+ @options = options
8
+ if options[:store]
9
+ @store = options[:store]
10
+ elsif options[:store_impl]
11
+ @store = options[:store].new(options)
12
+ else
13
+ @store = LinkMiddleware::MemoryStore.new(options)
14
+ end
15
+ end
16
+
17
+ def call(env)
18
+ request = Rack::Request.new( env )
19
+ if request.request_method == "LINK"
20
+ return link( request.url, env )
21
+ elsif request.request_method == "UNLINK"
22
+ return unlink( request.url, env )
23
+ end
24
+ @app.call(env)
25
+ end
26
+
27
+ def link(url, env)
28
+ links = LinkHeader.parse( env["HTTP_LINK"] )
29
+ success = @store.add(url,links)
30
+ headers = {}
31
+ if success
32
+ headers["Link"] = links.to_s unless links.links.empty?
33
+ return [200, headers, ["OK"]]
34
+ else
35
+ return [500, headers, ["Unable to add links"]]
36
+ end
37
+ end
38
+
39
+ def unlink(url, env)
40
+ links = LinkHeader.parse( env["HTTP_LINK"] )
41
+ success = @store.remove(url,links)
42
+ headers = {}
43
+ if success
44
+ headers["Link"] = links.to_s unless links.links.empty?
45
+ return [200, headers, ["OK"]]
46
+ else
47
+ return [500, headers, ["Unable to add links"]]
48
+ end
49
+ end
50
+
51
+ end
52
+
53
+ end
@@ -0,0 +1,40 @@
1
+ module LinkMiddleware
2
+ class RedisStore
3
+ def initialize(options={})
4
+ if options[:store]
5
+ @redis = options[:store]
6
+ else
7
+ @redis = Redis.new(options)
8
+ end
9
+ end
10
+
11
+ def add(context_uri, header)
12
+ header.links.each do |link|
13
+ @redis.sadd context_uri, link.to_s
14
+ end
15
+ true
16
+ end
17
+
18
+ def remove(context_uri, header)
19
+ process = true
20
+ header.links.each do |link|
21
+ process = process && @redis.sismember(context_uri, link.to_s)
22
+ end
23
+ header.links.each do |link|
24
+ @redis.srem context_uri, link.to_s
25
+ end if process
26
+ process
27
+ end
28
+
29
+ def read(context_uri)
30
+ header = LinkHeader.new
31
+ links = @redis.smembers context_uri
32
+ links.each do |link|
33
+ header << LinkHeader.parse(link).links.first
34
+ end
35
+ header
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,40 @@
1
+ module LinkMiddleware
2
+
3
+ require 'set'
4
+
5
+ #Use for testing only!
6
+ class MemoryStore
7
+
8
+ def initialize(options={})
9
+ @store = {}
10
+ end
11
+
12
+ def add(context_uri, header)
13
+ stored_header = @store[context_uri] || LinkHeader.new
14
+ header.links.each do |link|
15
+ stored_header << link
16
+ end
17
+ @store[context_uri] = stored_header
18
+ return true
19
+ end
20
+
21
+ def remove(context_uri, header)
22
+ stored_header = @store[context_uri] || LinkHeader.new
23
+ return true if stored_header.links.empty?
24
+
25
+ #if there are links to remove, which aren't in the stored set, then return false
26
+ return false if !(header.links - stored_header.links).empty?
27
+
28
+ #otherwise remove them
29
+ final = stored_header.links - header.links
30
+ @store[context_uri] = LinkHeader.new( final )
31
+ return true
32
+ end
33
+
34
+ def read(context_uri)
35
+ @store[context_uri] || LinkHeader.new
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,3 @@
1
+ module LinkMiddleware
2
+ VERSION = "0.0.2"
3
+ end
metadata ADDED
@@ -0,0 +1,150 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: link-middleware
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Leigh Dodds
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-12-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rack
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: link_header
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: redis
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: simplecov-rcov
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: mock_redis
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ description:
111
+ email:
112
+ - leigh@ldodds.com
113
+ executables: []
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - lib/link-middleware.rb
118
+ - lib/link-middleware/annotator.rb
119
+ - lib/link-middleware/redis_store.rb
120
+ - lib/link-middleware/filter.rb
121
+ - lib/link-middleware/store.rb
122
+ - lib/link-middleware/version.rb
123
+ - LICENSE.md
124
+ - README.md
125
+ homepage: http://github.com/ldidds/link-middleware
126
+ licenses: []
127
+ post_install_message:
128
+ rdoc_options: []
129
+ require_paths:
130
+ - lib
131
+ required_ruby_version: !ruby/object:Gem::Requirement
132
+ none: false
133
+ requirements:
134
+ - - ! '>='
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ required_rubygems_version: !ruby/object:Gem::Requirement
138
+ none: false
139
+ requirements:
140
+ - - ! '>='
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ requirements: []
144
+ rubyforge_project:
145
+ rubygems_version: 1.8.23
146
+ signing_key:
147
+ specification_version: 3
148
+ summary: Implementation of LINK/UNLINK using Rack middleware
149
+ test_files: []
150
+ has_rdoc: