dot_net_services 0.3.0 → 0.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.
Files changed (49) hide show
  1. data/LICENSE +21 -24
  2. data/README +26 -16
  3. data/Rakefile +65 -0
  4. data/lib/acs/saml_token_provider.rb +54 -0
  5. data/lib/acs/shared_secret_token_provider.rb +55 -0
  6. data/lib/acs/simple_api_auth_token_provider.rb +57 -0
  7. data/lib/acs/simple_web_token_provider.rb +54 -0
  8. data/lib/acs/token_constants.rb +112 -0
  9. data/lib/acs/token_info.rb +33 -0
  10. data/lib/acs/token_provider.rb +74 -0
  11. data/lib/acs/token_validator.rb +114 -0
  12. data/lib/common/dot_net_services_environment.rb +61 -0
  13. data/lib/common/environment.yml +23 -0
  14. data/lib/common/host_name_config.yml +45 -0
  15. data/lib/dot_net_services.rb +31 -144
  16. data/lib/service_bus/http_proxy.rb +34 -0
  17. data/lib/service_bus/locked_message_info.rb +34 -0
  18. data/lib/service_bus/message_buffer.rb +313 -0
  19. data/lib/service_bus/message_buffer_constants.rb +48 -0
  20. data/lib/service_bus/message_buffer_policy.rb +55 -0
  21. data/lib/service_bus/requests.rb +95 -0
  22. data/test/config/test_config.yml +40 -0
  23. data/test/dot_net_services_environment_test.rb +54 -0
  24. data/test/message_buffer_test.rb +96 -0
  25. data/test/token_test.rb +98 -0
  26. metadata +50 -48
  27. data/lib/dot_net_services/authentication.rb +0 -168
  28. data/lib/dot_net_services/error.rb +0 -4
  29. data/lib/dot_net_services/message_buffer.rb +0 -283
  30. data/lib/dot_net_services/session.rb +0 -308
  31. data/lib/net/http/create_mb.rb +0 -14
  32. data/lib/net/http/retrieve.rb +0 -14
  33. data/lib/net/http/subscribe.rb +0 -14
  34. data/lib/net/http/unsubscribe.rb +0 -14
  35. data/spec/integration/TestService/Service/AnonymousResourceService.cs +0 -9
  36. data/spec/integration/TestService/Service/App.config +0 -32
  37. data/spec/integration/TestService/Service/PlainTextService.cs +0 -37
  38. data/spec/integration/TestService/Service/Program.cs +0 -49
  39. data/spec/integration/TestService/Service/Properties/AssemblyInfo.cs +0 -33
  40. data/spec/integration/TestService/Service/ResourceContract.cs +0 -17
  41. data/spec/integration/TestService/Service/ResourceService.cs +0 -58
  42. data/spec/integration/TestService/Service/Service.csproj +0 -71
  43. data/spec/integration/TestService/TestService.sln +0 -33
  44. data/spec/integration/end_to_end_spec.rb +0 -84
  45. data/spec/integration/vmb_spec.rb +0 -30
  46. data/spec/spec_helper.rb +0 -23
  47. data/spec/unit/dot_net_services/authentication_spec.rb +0 -289
  48. data/spec/unit/dot_net_services/message_buffer_spec.rb +0 -161
  49. data/spec/unit/dot_net_services/session_spec.rb +0 -247
@@ -1,283 +0,0 @@
1
- require 'date'
2
-
3
- module DotNetServices
4
-
5
- # The MessageBuffer class is used for creating Volatile Message Buffer (VMB) endpoints on the .NET Services bus, and
6
- # retrieving messages from them.
7
- #
8
- # A VMB is a temporary message buffer that can be used for asynchronous communications between senders and receivers
9
- # of messages. A process may request creation of a VMB on any URL within a solution namespace. Any HTTP requests
10
- # (other than GETs) to that URL are stored in the buffer, until some process retrieves them by sending an HTTP
11
- # X-RETRIEVE request to the management endpoint.
12
- #
13
- # VMB can exist by itself, not tied to the lifecycle of a process that originally created it. Further information
14
- # about VMBs can be found in .NET Services portal [http://go.microsoft.com/fwlink/?LinkID=129428].
15
- #
16
- # == Usage examples
17
- #
18
- # MessageBuffer instance represents a VMB endpoint. Typical usage looks as follows:
19
- #
20
- # error_handler = lambda { |error| logger.error(error) }
21
- #
22
- # buffer = DotNetServices::MessageBuffer.open_and_poll(
23
- # "MySolution/MyVMB",
24
- # {:username => 'MySolution', :password => 'my_password'},
25
- # error_handler) do
26
- # |message|
27
- # ... process the message
28
- # end
29
- #
30
- # This invocation does all of the following:
31
- #
32
- # * Creates a MessageBuffer instance associated with a specified endpoint (/MySolution/MyVMB)
33
- # * obains a security token from trhe Identity service (see Session for details on that).
34
- # * Creates a VMB on the .NET Services bus if it doesn't exist yet
35
- # * immediately starts polling it
36
- # * if it retrieves a message, it passes it to the block. The message is an instance of Net::HTTP::Request that
37
- # looks exactly the same as what the sender originally sent to the bus.
38
- # * if an error occurs while polling, the error is passed to the error_handler block. Polling then continues.
39
- #
40
- # == Guidelines
41
- #
42
- # In cases where an application (which in this case means a process) needs to process multiple types of messages,
43
- # Microsoft recommends to create a single VMB per application, route all message types through the same VMB, and
44
- # route messages to appropriate processors within the application itself.
45
- #
46
- # In the current version of the API, we provide no explicit support for clustering message processors. If you use
47
- # MessageBuffer#open_and_poll(), it's recommended that you only run one cipy of a message processor. If clustering
48
- # is required (for high availability reasons), you can use lower-level MessageBuffer methods to do it.
49
- #
50
- # .NET Services VMBs provide pub-sub and multicast functionality which can be used even over plain HTTP. Which is
51
- # amazing. However, we don't support this functionality in the current version of our library.
52
- class MessageBuffer
53
-
54
- attr_reader :session
55
-
56
- class << self
57
-
58
- # Creates a MessageBuffer instance, acquires a security token, registers a VMB if necessary.
59
- # Unlike Session#open, +auth_data+ is mandatory here.
60
- # If given a block, passes the MessageBuffer instance to the block, and closes it at the end of the block
61
- # returns the result of the block (if called with a block), or the buffer instance opened
62
- def open(name, auth_data, &block)
63
- buffer = MessageBuffer.new(name, auth_data).open
64
- if block_given?
65
- begin
66
- return yield(buffer)
67
- ensure
68
- # TODO gracefully close the buffer
69
- end
70
- else
71
- return buffer
72
- end
73
- end
74
-
75
- # Invoke MessageBuffer#open to create a MessageBuffer instance, subscribe the buffer to receive messages
76
- # posted to the +subscription_endpoint+, then poll it until the containing thread or process is terminated.
77
- #
78
- # Whenever a message is retrieved from the buffer, this method passes it to the block. When an error occurs
79
- # (while polling, or raised by the block), it is passed to +error_handler+, which should be a lambda, or an
80
- # object with handle(error) public method. If no +error_handler+ is provided, the error is printed out
81
- # to STDERR and then ignored.
82
- def open_and_poll(name, subscription_endpoint, auth_data, error_handler=nil, &block)
83
- MessageBuffer.new(name, auth_data).open_and_poll(subscription_endpoint, error_handler, &block)
84
- end
85
- end
86
-
87
- # Initializes a new MessageBuffer instance.
88
- # Does not create a VMB on the .NET Services bus (use #open for that)
89
- def initialize(name, auth_data)
90
- @name = name
91
- @session = Session.new(name, auth_data)
92
- end
93
-
94
- # Register the message buffer (by sending X-CREATEMB to the management endpoint).
95
- #
96
- # Returns the buffer. Raises an exception if the creation is not successful.
97
- def register
98
- createmb_response = @session.createmb
99
-
100
- unless createmb_response.is_a?(Net::HTTPCreated)
101
- # the buffer may already be there
102
- unless @session.get_from_relay.is_a?(Net::HTTPSuccess)
103
- raise "Creating VMB failed. Service responded with #{createmb_response.class.name}"
104
- end
105
- end
106
- self
107
- end
108
-
109
- # Initiates a VMB session by:
110
- # * acquiring a security token
111
- # * checks if there is already a VMB at the endpoint
112
- # * creating a VMB if necessary
113
- def open
114
- register unless @session.get_from_relay.is_a?(Net::HTTPSuccess)
115
- self
116
- end
117
-
118
- # Open the buffer (see #open), subscribe the buffer to receive messages
119
- # posted to the +subscription_endpoint+, then poll it until the containing thread or
120
- # process is terminated.
121
- #
122
- # Whenever a message is retrieved from the buffer, this method passes it to the block. When an error occurs
123
- # (while polling, or raised by the block), it is passed to +error_handler+, which should be a lambda, or an
124
- # object with handle(error) public method. If no +error_handler+ is provided, the error is printed out
125
- # to STDERR and then ignored.
126
- def open_and_poll(subscription_endpoint, error_handler=nil, &block)
127
- raise "MessageBuffer#open_and_poll requires a block" unless block_given?
128
-
129
- @subscription_endpoint = subscription_endpoint
130
-
131
- open
132
- subscribe(@subscription_endpoint)
133
-
134
- begin
135
- loop do
136
- begin
137
- message = poll
138
- block.call(message) if message
139
- rescue Object => error
140
- if error_handler
141
- if error_handler.is_a?(Proc)
142
- error_handler.call(error)
143
- else
144
- error_handler.handle(error)
145
- end
146
- else
147
- STDERR.puts
148
- STDERR.puts "Message Buffer Error"
149
- STDERR.puts error.message
150
- STDERR.puts error.backtrace.map { |line| " #{line}"}
151
- STDERR.puts
152
- end
153
- end
154
- end
155
- ensure
156
- @subscription_endpoint = nil
157
- end
158
- end
159
-
160
- # Poll VMB endpoint for new messages; return the message if one was retrieved, or nil if it wasn't.
161
- #
162
- # Raises an exception if the response from the bus contains an error code (which usually means that the VMB is
163
- # not registered, but may mean other things, for example if the bus itself is not accessible for some reason).
164
- #
165
- # +timeout+ parameter regulates how long a polling request will be held by the bus if there are no messages.
166
- #
167
- # An HTTP server that holds HTTP connections because it has nothing to respond with is somewhat uncommon, so a
168
- # more detailed explanation is due.
169
- #
170
- # Normally, any HTTP interaction begins by opening of a TCP socket from the client to the server. The client
171
- # then writes the HTTP request into that socket and waits until the server responds back and closes the socket.
172
- # If the server doesn't respond for a long time (a minute, usually), the client drops the socket
173
- # and declares timeout.
174
- #
175
- # Many times, when you poll a VMB endpoint, it will have no messages. If the bus simply came back immediately with
176
- # "No Content" response, this would cause a VMB subscriber needs to constantly poll the buffer, abusing the bus
177
- # infrastructure.
178
- #
179
- # .NET Services gets around this problem by making the client connection hang for some time, either until there
180
- # is a message, or a certain number of seconds has passed, and there is still no message. That number of seconds is
181
- # what the +timeout+ parameter specifies. Microsoft suggested that 10-20 seconds is reasonable for
182
- # production purposes. Default value is 15 seconds.
183
- def poll(timeout=15)
184
- response = @session.retrieve(:encoding => 'asreply', :timeout => timeout)
185
- case response
186
- when Net::HTTPNoContent
187
- return nil
188
- when Net::HTTPNotFound
189
- register
190
- subscribe(@subscription_endpoint) if @subscription_endpoint
191
- return nil
192
- when Net::HTTPSuccess
193
- return response
194
- else
195
- raise "Retrieving messages failed. Response was #{response.class.name}" unless response.is_a?(Net::HTTPSuccess)
196
- end
197
- end
198
-
199
- # Delete the message buffer. This removes the buffer entirely
200
- # from the bus, not just the local reference to it.
201
- def delete
202
- response = @session.delete
203
-
204
- unless response.is_a?(Net::HTTPNoContent)
205
- raise "Deleting VMB failed. Response was #{response.class.name}"
206
- end
207
- self
208
- end
209
-
210
- # Queries the VMB management endpoint for the VMB expiry time. With every #poll (HTTP X-RETRIEVE to the VMB management
211
- # endpoint) the bus sets the expiry time of this VMB to 30 minutes later. If 30 minutes pass and there are no
212
- # further polls, the bus automatically delets the buffer.
213
- def expires
214
- response = @session.get_from_relay
215
-
216
- unless response.is_a?(Net::HTTPOK)
217
- raise "Querying expiry status of VMB failed. Response was #{response.class}"
218
- end
219
-
220
- expires_header = response["expires"]
221
- raise "Querying expiry status of VMB failed. Response doesn't have expires: header" unless expires_header
222
- DateTime.parse(expires_header)
223
- end
224
-
225
- # Performs an empty POST to the VMB management endpoint, which extends the VMB expiry time without retrieving any
226
- # messages.
227
- def keep_alive
228
- # if we don't pass a linefeed as a body to the VMB management endpoint, it responds with HTTP 411 Length Required
229
- response = @session.post_to_relay "\n"
230
- case response
231
- when Net::HTTPSuccess
232
- self
233
- else
234
- raise "POST to the VMB management endpoint failed. Response was #{response.class}"
235
- end
236
- end
237
-
238
- # Subscribe to an endpoint.
239
- #
240
- # HTTP messages sent to the +endpoint+ will be routed to this message buffer
241
- def subscribe(target_path)
242
- # Changes for .Net Services March 2009 CTP release (M5)
243
- tmpurl = @session.authenticator.username + "." + DotNetServices.root_url + "/" + target_path
244
-
245
- # Replace unwanted slashes
246
- tmpurl = tmpurl.gsub("\/\/\/", "\/")
247
- tmpurl = tmpurl.gsub("\/\/", "\/")
248
- subscription_endpoint_url = "http://" + tmpurl
249
-
250
- subscribe_response = @session.subscribe(:target => subscription_endpoint_url)
251
- case subscribe_response
252
- when Net::HTTPSuccess
253
- return self
254
- when Net::HTTPConflict
255
- unsubscribe(target_path)
256
- resubscribe_response = @session.subscribe(:target => subscription_endpoint_url)
257
- unless resubscribe_response.is_a?(Net::HTTPSuccess)
258
- raise "Second X-SUBSCRIBE to VMB management endpoint failed. Response was #{resubscribe_response.class}"
259
- end
260
- else
261
- raise "X-SUBSCRIBE to VMB management endpoint failed. Response was #{subscribe_response.class}"
262
- end
263
- end
264
-
265
- # Unsubscribe from an endpoint.
266
- def unsubscribe(target_path)
267
- # Changes for .Net Services March 2009 CTP release (M5)
268
- tmpurl = @session.authenticator.username + "." + DotNetServices.root_url + "/" + target_path
269
-
270
- # Replace unwanted slashes
271
- tmpurl = tmpurl.gsub("\/\/\/", "\/")
272
- tmpurl = tmpurl.gsub("\/\/", "\/")
273
- subscription_endpoint_url = "http://" + tmpurl
274
-
275
- unsubscribe_response = @session.unsubscribe(:target => subscription_endpoint_url)
276
- unless unsubscribe_response.is_a?(Net::HTTPSuccess)
277
- raise "X-UNSUBSCRIBE to VMB management endpoint failed. Response was #{subscribe_response.class}"
278
- end
279
- self
280
- end
281
-
282
- end
283
- end
@@ -1,308 +0,0 @@
1
- require 'uri'
2
- require 'cgi'
3
- require 'net/http'
4
- require 'net/http/unsubscribe'
5
-
6
- module DotNetServices
7
-
8
- # The Session class is used for communicating with endpoints of .NET Services bus.
9
- #
10
- # == Usage examples
11
- #
12
- # A Session instance represents a .NET Services bus endpoint. Typically, you create it by calling Session#open
13
- # and then exchange HTTP messages using methods like #get, #post, etc. It looks like this:
14
- #
15
- # Send a GET with no parameters:
16
- #
17
- # session = Session.open('/MySolution/MyService', :username => 'MySolution', :password => 'my_password')
18
- # get_response = session.get
19
- # # ... process response, which is a Net::HTTP::Response instance
20
- #
21
- # Send a GET with ?age=21&sex=Male query string:
22
- #
23
- # get_with_query_response = session.get :age => 21, :sex => "Male"
24
- #
25
- # Send a POST with name=Joe&email=joe@the_plumber.com (URL-encoded form) as a body:
26
- #
27
- # post_response = session.post :name => 'Joe', :email => 'joe@the_plumber.com'
28
- #
29
- # So far, Session API is similar to Net::HTTP::Session. Which is the whole point of the REST style
30
- # communication - it is all just HTTP! However, there is one small twist. HTTP::Session represents a server
31
- # (host and port). DotNetServices::Session represents a service endpoint. Which is a <b>URL</b>, not a server. In
32
- # fact, all .NET Services endpoints have the same server, servicebus.windows.net. So an endpoint address is really
33
- # just a path on that server.
34
- #
35
- # And sometimes you need to send requests to a path under the endpoint. For example, to update the name of customer
36
- # #12345, you would do a POST to /MySolution/Customer/12345 with &name=Dave body. Creating a new Session for that is
37
- # both counterintuitive and expensive (see the Details section below). For this reason, DotNetServices::Session has
38
- # methods like post_to_url:
39
- #
40
- # result = session.post_to_url(12345, :name => 'Dave')
41
- #
42
- # == Details
43
- #
44
- # When a session is created, it's given the URL of an endpoint (that looks like /SolutionName/path/to/service), and
45
- # username / password. These are <b>solution's</b> username and password, needed to prove to the bus that
46
- # your consumer is a part of this solution. If the solution allows anonymous access, username / password are not
47
- # necessary.
48
- #
49
- # When a session is opened (or is asked to send a first request to the endpoint), the session authenticates itself
50
- # to the .NET Services using Identity Service [http://accesscontrol.windows.net], and obtains a security token from it.
51
- #
52
- # Security token is a Base64-encoded string. Unless an endpoint allows unauthenticated access, any request that comes
53
- # to the endpoint must have this security token attached to it (as X-MS-Identity-Token HTTP header).
54
- # DotNetServices::Session takes care of this responsibility, as well as renewing stale tokens.
55
- #
56
- # Session also has methods get_from_relay and post_to_relay, that are used for communicating to the .NET Services
57
- # bus management endpoint. These methods (createmb, retrieve, query) are used by MessageBuffer, an application normally
58
- # shouldn't need to use them directly.
59
- #
60
- # == Guidelines
61
- #
62
- # Obtaining a security token is an expensive operation, so it is <b>not</b> recommended to create a new instance of
63
- # a Session for every outgoing request to the endpoint. It's better to use one Session instance per endpoint.
64
- # Session takes care of expiring security tokens autoimatically, so Session instances don't go stale even after long
65
- # periods of inactivity.
66
- class Session
67
-
68
- attr_reader :authenticator
69
-
70
- class << self
71
- # Open a .Net Services session at the +endpoint+.
72
- #
73
- # If +auth_data+ is provided, authenticates the session with
74
- # the identity service using it. In this version, +auth_data+ should be a hash containing +:username+ and
75
- # +:password+ keys. Other authentication mechanisms (InformationCard, X.509 certificates etc) that the Identity
76
- # service provides are not exposed via a REST interface, therefore are not available to this library.
77
- #
78
- # If #open is called with a block, it passes the session instance as an argument to the block and returns the
79
- # result of the block. If there is no block, session instance is returned instead.
80
- def open(endpoint, auth_data = nil, &block)
81
- Session.new(endpoint, auth_data).open(&block)
82
- end
83
- end
84
-
85
- # Initialize a new .NET Session at the +endpoint+.
86
- #
87
- # If +auth_data+ is provided, remember it. Acquiring
88
- # authentication token is postponed until it's needed.
89
- def initialize(endpoint, auth_data=nil)
90
- @endpoint_uri = setup_endpoint(endpoint)
91
- @authenticator = Authentication.setup(auth_data)
92
- end
93
-
94
- def initialize(endpoint, auth_data=nil)
95
- # Changes for .Net Services March 2009 CTP release (M5)
96
- username = String.new
97
- if auth_data.nil?
98
- elsif !auth_data.is_a? Hash
99
- else
100
- auth_data_copy = auth_data.dup
101
- username = auth_data_copy.delete(:username)
102
- end
103
-
104
- @endpoint_uri = setup_endpoint(endpoint, username)
105
- @authenticator = Authentication.setup(auth_data)
106
- end
107
-
108
- # See DotNetServices::Session.open.
109
- def open
110
- authenticate
111
- block_given? ? yield(self) : self
112
- end
113
-
114
- # Acquire a security token from the Identity Service.
115
- #
116
- # This method will do nothing if:
117
- # * this session already holds a security token and the token has not expired yet, or
118
- # * the session was created without auth_data (anonymous session)
119
- def authenticate
120
- @authenticator.authenticate
121
- end
122
-
123
- # Send a GET request to the .NET Services endpoint.
124
- #
125
- # If query options are provided, they should be a hash, and they are converted to a query string and attached to
126
- # the GET request target URL.
127
- # Thus, session.get(:foo => 'bar') sends a GET to #{endpoint}?foo=bar
128
- def get(query_options=nil)
129
- get_from_url(nil, query_options)
130
- end
131
-
132
-
133
- def get_from_url(url, query_options=nil)
134
- uri = setup_query_string(url, query_options)
135
- get_request = Net::HTTP::Get.new(uri.request_uri)
136
- enhance_and_execute(get_request)
137
- end
138
-
139
- # Sends a GET to the service management endpoint.
140
- #
141
- # This is used by MessageBuffer for VMB management. Applications shouldn't need to use this method.
142
- def get_from_relay(query_options=nil)
143
- uri = setup_query_string(nil, query_options)
144
- get_request = Net::HTTP::Get.new(uri.request_uri)
145
- route_to_relay(get_request)
146
- enhance_and_execute(get_request)
147
- end
148
-
149
- # Send a POST request to the .NET Services endpoint.
150
- #
151
- # If +data+ is a Hash it is converted to a URL-encoded form, otherwise it is placed in the post body without any
152
- # conversion.
153
- #
154
- # +content_type+ by default is set to +application/x-www-form-urlencoded+, which is appropriate when +data+ is a
155
- # Hash. In any other case, you should specify +content_type+ explicitly.
156
- def post(data=nil, content_type='application/x-www-form-urlencoded')
157
- post_to_url("", data, content_type)
158
- end
159
-
160
- # Sends a POST request to a path below .NET Services endpoint.
161
- #
162
- # If an application neeeds to send a POST to a service that uses URL to receive data, it should use this method
163
- # instead of creating a new session for every new request. For example, if a service endpoint is called
164
- # /Solution/Maps, and it's URL template is /Solution/Maps/*country*/*province*/*city*, a way to create a map of
165
- # Calgary would be:
166
- #
167
- # session.post_to_url('/Canada/Calgary', map_data, content_type=...)
168
- def post_to_url(url, data=nil, content_type='application/x-www-form-urlencoded')
169
- post_request = Net::HTTP::Post.new(@endpoint_uri.path + url.to_s)
170
- if data
171
- post_request.content_type = content_type
172
- if content_type == 'application/x-www-form-urlencoded'
173
- post_request.form_data = data
174
- else
175
- post_request.body = data
176
- end
177
- end
178
- enhance_and_execute(post_request)
179
- end
180
-
181
-
182
- # Sends a POST to the service management endpoint.
183
- #
184
- # This is used by MessageBuffer for VMB management. Applications should use MessageBuffer#keep_alive.
185
- def post_to_relay(data=nil, content_type='application/x-www-form-urlencoded')
186
- post_request = Net::HTTP::Post.new(@endpoint_uri.path)
187
-
188
- if data
189
- post_request.content_type = content_type
190
- if content_type == 'application/x-www-form-urlencoded'
191
- post_request.form_data = data
192
- else
193
- post_request.body = data
194
- end
195
- end
196
- route_to_relay(post_request)
197
- enhance_and_execute(post_request)
198
- end
199
-
200
- # Create a new message buffer (sends an X-CREATEMB to the service management endpoint).
201
- #
202
- # This is used by MessageBuffer for VMB management. Applications should use MessageBuffer#open.
203
- def createmb
204
- request = Net::HTTP::CreateMB.new(@endpoint_uri.path)
205
- route_to_relay(request)
206
- enhance_and_execute(request)
207
- end
208
-
209
- # Subscribe a message buffer to a virtual endpoint (sends an X-SUBSCRIBE to the VMB management endpoint).
210
- #
211
- # This is used by MessageBuffer for VMB management. Applications should use MessageBuffer#open.
212
- def subscribe(request_options)
213
- uri = setup_query_string(nil, request_options)
214
- request = Net::HTTP::Subscribe.new(uri.request_uri)
215
- route_to_relay(request)
216
- enhance_and_execute(request)
217
- end
218
-
219
- # Unsubscribe a message buffer from a virtual endpoint (sends an X-UNSUBSCRIBE to the VMB management endpoint).
220
- #
221
- # This is used by MessageBuffer for VMB management. Applications should use MessageBuffer#open.
222
- def unsubscribe(request_options)
223
- uri = setup_query_string(nil, request_options)
224
- request = Net::HTTP::Unsubscribe.new(uri.request_uri)
225
- route_to_relay(request)
226
- enhance_and_execute(request)
227
- end
228
-
229
- # Retrieve the next message from the message buffer (sends an X-RETRIEVE to the service management endpoint).
230
- #
231
- # This is used by MessageBuffer for VMB management. Applications should use MessageBuffer#poll.
232
- def retrieve(query_options={})
233
- query_options = stringify_and_downcase_keys(query_options)
234
- query_options['timeout'] ||= 15
235
- uri = setup_query_string(nil, query_options)
236
- request = Net::HTTP::Retrieve.new(uri.request_uri)
237
- route_to_relay(request)
238
- enhance_and_execute(request)
239
- end
240
-
241
-
242
- # Delete an endpoint (sends a DELETE to the service management endpoint).
243
- #
244
- # This is used by MessageBuffer for VMB management. Applications should use MessageBuffer#delete.
245
- def delete
246
- request = Net::HTTP::Delete.new(@endpoint_uri.path)
247
- route_to_relay(request)
248
- enhance_and_execute(request)
249
- end
250
-
251
- # The URL of a .NET services endpoint this Session is connected to.
252
- def endpoint
253
- @endpoint_uri.to_s
254
- end
255
-
256
- private
257
-
258
- def enhance_and_execute(request)
259
- @authenticator.enhance(request)
260
- DotNetServices.proxy.start(@endpoint_uri.host, @endpoint_uri.port) { |http| http.request(request) }
261
- end
262
-
263
- def route_to_relay(request)
264
- request['X-Process-At'] = 'http://schemas.microsoft.com/netservices/2009/05/servicebus/connect/roles/relay'
265
- end
266
-
267
- def setup_endpoint(endpoint, username)
268
- if endpoint !~ /^http(s?):\/\//
269
- endpoint = '/' + endpoint unless endpoint[0] == ?/
270
-
271
- # Changes for .Net Services March 2009 CTP release (M5)
272
- if !username.empty?
273
- tmpurl = username + "." + DotNetServices.root_url + "/" + endpoint
274
-
275
- # Replace unnecessary slashes
276
- tmpurl = tmpurl.gsub("\/\/\/", "\/")
277
- tmpurl = tmpurl.gsub("\/\/", "\/")
278
- endpoint = "http://" + tmpurl
279
- else
280
- raise "username must be provided to form the service bus endpoint."
281
- end
282
- end
283
- endpoint += '/' unless endpoint[-1] == ?/
284
-
285
- URI.parse(endpoint)
286
- end
287
- def setup_query_string(url, query_options)
288
- if url && !url.empty?
289
- uri = @endpoint_uri + url.to_s
290
- else
291
- uri = @endpoint_uri.dup
292
- end
293
-
294
- if query_options
295
- query = query_options.map { |k, v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}" }.join('&')
296
- uri.query = query
297
- end
298
- uri
299
- end
300
-
301
- def stringify_and_downcase_keys(options)
302
- new_options = {}
303
- options.each { |key, value| new_options[key.to_s.downcase] = value unless value.nil? }
304
- new_options
305
- end
306
-
307
- end
308
- end
@@ -1,14 +0,0 @@
1
- module Net # :nodoc:
2
- class HTTP # :nodoc:
3
- # Custom request type X-CREATEMB for creating message buffers on
4
- # the .NET Service Bus.
5
- #
6
- # This request should not be used directly. Use
7
- # DotNetServices::Session#createmb instead.
8
- class CreateMB < HTTPRequest
9
- METHOD = "X-CREATEMB"
10
- REQUEST_HAS_BODY = false
11
- RESPONSE_HAS_BODY = true
12
- end
13
- end
14
- end
@@ -1,14 +0,0 @@
1
- module Net # :nodoc:
2
- class HTTP # :nodoc:
3
- # Custom X-RETRIEVE for retrieving messages from the .NET Services
4
- # message buffer.
5
- #
6
- # This request should not be used directly. Use
7
- # DotNetServices::Session#retrieve instead.
8
- class Retrieve < HTTPRequest
9
- METHOD = "X-RETRIEVE"
10
- REQUEST_HAS_BODY = false
11
- RESPONSE_HAS_BODY = true
12
- end
13
- end
14
- end
@@ -1,14 +0,0 @@
1
- module Net # :nodoc:
2
- class HTTP # :nodoc:
3
- # Custom request type X-CREATEMB for creating message buffers on
4
- # the .NET Service Bus.
5
- #
6
- # This request should not be used directly. Use
7
- # DotNetServices::Session#createmb instead.
8
- class Subscribe < HTTPRequest
9
- METHOD = "X-SUBSCRIBE"
10
- REQUEST_HAS_BODY = false
11
- RESPONSE_HAS_BODY = true
12
- end
13
- end
14
- end
@@ -1,14 +0,0 @@
1
- module Net # :nodoc:
2
- class HTTP # :nodoc:
3
- # Custom request type X-CREATEMB for creating message buffers on
4
- # the .NET Service Bus.
5
- #
6
- # This request should not be used directly. Use
7
- # DotNetServices::Session#createmb instead.
8
- class Unsubscribe < HTTPRequest
9
- METHOD = "X-UNSUBSCRIBE"
10
- REQUEST_HAS_BODY = false
11
- RESPONSE_HAS_BODY = true
12
- end
13
- end
14
- end
@@ -1,9 +0,0 @@
1
- namespace DotNetServicesRuby
2
- {
3
- using System.ServiceModel;
4
-
5
- [ServiceBehavior(Name = "SyndicationService", Namespace = "http://samples.microsoft.com/ServiceModel/Relay/")]
6
- class AnonymousResourceService : ResourceService
7
- {
8
- }
9
- }