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.
- data/LICENSE +21 -24
- data/README +26 -16
- data/Rakefile +65 -0
- data/lib/acs/saml_token_provider.rb +54 -0
- data/lib/acs/shared_secret_token_provider.rb +55 -0
- data/lib/acs/simple_api_auth_token_provider.rb +57 -0
- data/lib/acs/simple_web_token_provider.rb +54 -0
- data/lib/acs/token_constants.rb +112 -0
- data/lib/acs/token_info.rb +33 -0
- data/lib/acs/token_provider.rb +74 -0
- data/lib/acs/token_validator.rb +114 -0
- data/lib/common/dot_net_services_environment.rb +61 -0
- data/lib/common/environment.yml +23 -0
- data/lib/common/host_name_config.yml +45 -0
- data/lib/dot_net_services.rb +31 -144
- data/lib/service_bus/http_proxy.rb +34 -0
- data/lib/service_bus/locked_message_info.rb +34 -0
- data/lib/service_bus/message_buffer.rb +313 -0
- data/lib/service_bus/message_buffer_constants.rb +48 -0
- data/lib/service_bus/message_buffer_policy.rb +55 -0
- data/lib/service_bus/requests.rb +95 -0
- data/test/config/test_config.yml +40 -0
- data/test/dot_net_services_environment_test.rb +54 -0
- data/test/message_buffer_test.rb +96 -0
- data/test/token_test.rb +98 -0
- metadata +50 -48
- data/lib/dot_net_services/authentication.rb +0 -168
- data/lib/dot_net_services/error.rb +0 -4
- data/lib/dot_net_services/message_buffer.rb +0 -283
- data/lib/dot_net_services/session.rb +0 -308
- data/lib/net/http/create_mb.rb +0 -14
- data/lib/net/http/retrieve.rb +0 -14
- data/lib/net/http/subscribe.rb +0 -14
- data/lib/net/http/unsubscribe.rb +0 -14
- data/spec/integration/TestService/Service/AnonymousResourceService.cs +0 -9
- data/spec/integration/TestService/Service/App.config +0 -32
- data/spec/integration/TestService/Service/PlainTextService.cs +0 -37
- data/spec/integration/TestService/Service/Program.cs +0 -49
- data/spec/integration/TestService/Service/Properties/AssemblyInfo.cs +0 -33
- data/spec/integration/TestService/Service/ResourceContract.cs +0 -17
- data/spec/integration/TestService/Service/ResourceService.cs +0 -58
- data/spec/integration/TestService/Service/Service.csproj +0 -71
- data/spec/integration/TestService/TestService.sln +0 -33
- data/spec/integration/end_to_end_spec.rb +0 -84
- data/spec/integration/vmb_spec.rb +0 -30
- data/spec/spec_helper.rb +0 -23
- data/spec/unit/dot_net_services/authentication_spec.rb +0 -289
- data/spec/unit/dot_net_services/message_buffer_spec.rb +0 -161
- 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
|
data/lib/net/http/create_mb.rb
DELETED
@@ -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
|
data/lib/net/http/retrieve.rb
DELETED
@@ -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
|
data/lib/net/http/subscribe.rb
DELETED
@@ -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
|
data/lib/net/http/unsubscribe.rb
DELETED
@@ -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
|