Class: DotNetServices::Session

Inherits:
Object
  • Object
show all
Defined in:
lib/dot_net_services/session.rb

Overview

The Session class is used for communicating with endpoints of .NET Services bus.

Usage examples

A Session instance represents a .NET Services bus endpoint. Typically, you create it by calling Session#open and then exchange HTTP messages using methods like #get, #post, etc. It looks like this:

Send a GET with no parameters:

session = Session.open('/MySolution/MyService', :username => 'MySolution', :password => 'my_password')
get_response = session.get
# ... process response, which is a Net::HTTP::Response instance

Send a GET with ?age=21&sex=Male query string:

get_with_query_response = session.get :age => 21, :sex => "Male"

Send a POST with name=Joe&email=joe@the_plumber.com (URL-encoded form) as a body:

post_response = session.post :name => 'Joe', :email => 'joe@the_plumber.com'

So far, Session API is similar to Net::HTTP::Session. Which is the whole point of the REST style communication - it is all just HTTP! However, there is one small twist. HTTP::Session represents a server (host and port). DotNetServices::Session represents a service endpoint. Which is a URL, not a server. In fact, all .NET Services endpoints have the same server, servicebus.windows.net. So an endpoint address is really just a path on that server.

And sometimes you need to send requests to a path under the endpoint. For example, to update the name of customer #12345, you would do a POST to /MySolution/Customer/12345 with &name=Dave body. Creating a new Session for that is both counterintuitive and expensive (see the Details section below). For this reason, DotNetServices::Session has methods like post_to_url:

result = session.post_to_url(12345, :name => 'Dave')

Details

When a session is created, it’s given the URL of an endpoint (that looks like /SolutionName/path/to/service), and username / password. These are solution’s username and password, needed to prove to the bus that your consumer is a part of this solution. If the solution allows anonymous access, username / password are not necessary.

When a session is opened (or is asked to send a first request to the endpoint), the session authenticates itself to the .NET Services using Identity Service [accesscontrol.windows.net], and obtains a security token from it.

Security token is a Base64-encoded string. Unless an endpoint allows unauthenticated access, any request that comes to the endpoint must have this security token attached to it (as X-MS-Identity-Token HTTP header). DotNetServices::Session takes care of this responsibility, as well as renewing stale tokens.

Session also has methods get_from_relay and post_to_relay, that are used for communicating to the .NET Services bus management endpoint. These methods (createmb, retrieve, query) are used by MessageBuffer, an application normally shouldn’t need to use them directly.

Guidelines

Obtaining a security token is an expensive operation, so it is not recommended to create a new instance of a Session for every outgoing request to the endpoint. It’s better to use one Session instance per endpoint. Session takes care of expiring security tokens autoimatically, so Session instances don’t go stale even after long periods of inactivity.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(endpoint, auth_data = nil) ⇒ Session

Initialize a new .NET Session at the endpoint.

If auth_data is provided, remember it. Acquiring authentication token is postponed until it’s needed.



89
90
91
92
# File 'lib/dot_net_services/session.rb', line 89

def initialize(endpoint, auth_data=nil)
  @endpoint_uri = setup_endpoint(endpoint)
  @authenticator = Authentication.setup(auth_data)
end

Instance Attribute Details

#authenticatorObject (readonly)

Returns the value of attribute authenticator.



68
69
70
# File 'lib/dot_net_services/session.rb', line 68

def authenticator
  @authenticator
end

Class Method Details

.open(endpoint, auth_data = nil, &block) ⇒ Object

Open a .Net Services session at the endpoint.

If auth_data is provided, authenticates the session with the identity service using it. In this version, auth_data should be a hash containing :username and :password keys. Other authentication mechanisms (InformationCard, X.509 certificates etc) that the Identity service provides are not exposed via a REST interface, therefore are not available to this library.

If #open is called with a block, it passes the session instance as an argument to the block and returns the result of the block. If there is no block, session instance is returned instead.



80
81
82
# File 'lib/dot_net_services/session.rb', line 80

def open(endpoint, auth_data = nil, &block)
  Session.new(endpoint, auth_data).open(&block)
end

Instance Method Details

#authenticateObject

Acquire a security token from the Identity Service.

This method will do nothing if:

  • this session already holds a security token and the token has not expired yet, or

  • the session was created without auth_data (anonymous session)



119
120
121
# File 'lib/dot_net_services/session.rb', line 119

def authenticate
  @authenticator.authenticate
end

#creatembObject

Create a new message buffer (sends an X-CREATEMB to the service management endpoint).

This is used by MessageBuffer for VMB management. Applications should use MessageBuffer#open.



203
204
205
206
207
# File 'lib/dot_net_services/session.rb', line 203

def createmb
  request = Net::HTTP::CreateMB.new(@endpoint_uri.path)
  route_to_relay(request)
  enhance_and_execute(request)
end

#deleteObject

Delete an endpoint (sends a DELETE to the service management endpoint).

This is used by MessageBuffer for VMB management. Applications should use MessageBuffer#delete.



245
246
247
248
249
# File 'lib/dot_net_services/session.rb', line 245

def delete
  request = Net::HTTP::Delete.new(@endpoint_uri.path) 
  route_to_relay(request)
  enhance_and_execute(request)
end

#endpointObject

The URL of a .NET services endpoint this Session is connected to.



252
253
254
# File 'lib/dot_net_services/session.rb', line 252

def endpoint
  @endpoint_uri.to_s
end

#get(query_options = nil) ⇒ Object

Send a GET request to the .NET Services endpoint.

If query options are provided, they should be a hash, and they are converted to a query string and attached to the GET request target URL. Thus, session.get(:foo => ‘bar’) sends a GET to ##endpoint?foo=bar



128
129
130
# File 'lib/dot_net_services/session.rb', line 128

def get(query_options=nil)
  get_from_url(nil, query_options)
end

#get_from_relay(query_options = nil) ⇒ Object

Sends a GET to the service management endpoint.

This is used by MessageBuffer for VMB management. Applications shouldn’t need to use this method.



142
143
144
145
146
147
# File 'lib/dot_net_services/session.rb', line 142

def get_from_relay(query_options=nil)
  uri = setup_query_string(nil, query_options)
  get_request = Net::HTTP::Get.new(uri.request_uri)
  route_to_relay(get_request)
  enhance_and_execute(get_request)
end

#get_from_url(url, query_options = nil) ⇒ Object



133
134
135
136
137
# File 'lib/dot_net_services/session.rb', line 133

def get_from_url(url, query_options=nil)
  uri = setup_query_string(url, query_options)
  get_request = Net::HTTP::Get.new(uri.request_uri)
  enhance_and_execute(get_request)
end

#openObject

See DotNetServices::Session.open.



109
110
111
112
# File 'lib/dot_net_services/session.rb', line 109

def open
  authenticate
  block_given? ? yield(self) : self
end

#post(data = nil, content_type = 'application/x-www-form-urlencoded') ⇒ Object

Send a POST request to the .NET Services endpoint.

If data is a Hash it is converted to a URL-encoded form, otherwise it is placed in the post body without any conversion.

content_type by default is set to application/x-www-form-urlencoded, which is appropriate when data is a Hash. In any other case, you should specify content_type explicitly.



156
157
158
# File 'lib/dot_net_services/session.rb', line 156

def post(data=nil, content_type='application/x-www-form-urlencoded')
  post_to_url("", data, content_type)
end

#post_to_relay(data = nil, content_type = 'application/x-www-form-urlencoded') ⇒ Object

Sends a POST to the service management endpoint.

This is used by MessageBuffer for VMB management. Applications should use MessageBuffer#keep_alive.



185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/dot_net_services/session.rb', line 185

def post_to_relay(data=nil, content_type='application/x-www-form-urlencoded')
  post_request = Net::HTTP::Post.new(@endpoint_uri.path)

  if data
    post_request.content_type = content_type
    if content_type == 'application/x-www-form-urlencoded'
      post_request.form_data = data
    else
      post_request.body = data
    end
  end
  route_to_relay(post_request)
  enhance_and_execute(post_request)
end

#post_to_url(url, data = nil, content_type = 'application/x-www-form-urlencoded') ⇒ Object

Sends a POST request to a path below .NET Services endpoint.

If an application neeeds to send a POST to a service that uses URL to receive data, it should use this method instead of creating a new session for every new request. For example, if a service endpoint is called /Solution/Maps, and it’s URL template is /Solution/Maps/country/province/city, a way to create a map of Calgary would be:

session.post_to_url('/Canada/Calgary', map_data, content_type=...)


168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/dot_net_services/session.rb', line 168

def post_to_url(url, data=nil, content_type='application/x-www-form-urlencoded')
  post_request = Net::HTTP::Post.new(@endpoint_uri.path + url.to_s)
  if data
    post_request.content_type = content_type
    if content_type == 'application/x-www-form-urlencoded'
      post_request.form_data = data
    else
      post_request.body = data
    end
  end
  enhance_and_execute(post_request)
end

#retrieve(query_options = {}) ⇒ Object

Retrieve the next message from the message buffer (sends an X-RETRIEVE to the service management endpoint).

This is used by MessageBuffer for VMB management. Applications should use MessageBuffer#poll.



232
233
234
235
236
237
238
239
# File 'lib/dot_net_services/session.rb', line 232

def retrieve(query_options={})
  query_options = stringify_and_downcase_keys(query_options)
  query_options['timeout'] ||= 15
  uri = setup_query_string(nil, query_options)
  request = Net::HTTP::Retrieve.new(uri.request_uri)
  route_to_relay(request)
  enhance_and_execute(request)
end

#subscribe(request_options) ⇒ Object

Subscribe a message buffer to a virtual endpoint (sends an X-SUBSCRIBE to the VMB management endpoint).

This is used by MessageBuffer for VMB management. Applications should use MessageBuffer#open.



212
213
214
215
216
217
# File 'lib/dot_net_services/session.rb', line 212

def subscribe(request_options)
  uri = setup_query_string(nil, request_options)
  request = Net::HTTP::Subscribe.new(uri.request_uri)
  route_to_relay(request)
  enhance_and_execute(request)
end

#unsubscribe(request_options) ⇒ Object

Unsubscribe a message buffer from a virtual endpoint (sends an X-UNSUBSCRIBE to the VMB management endpoint).

This is used by MessageBuffer for VMB management. Applications should use MessageBuffer#open.



222
223
224
225
226
227
# File 'lib/dot_net_services/session.rb', line 222

def unsubscribe(request_options)
  uri = setup_query_string(nil, request_options)
  request = Net::HTTP::Unsubscribe.new(uri.request_uri)
  route_to_relay(request)
  enhance_and_execute(request)
end