dot_net_services 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +24 -0
- data/README +52 -0
- data/lib/dot_net_services/authentication.rb +168 -0
- data/lib/dot_net_services/error.rb +4 -0
- data/lib/dot_net_services/message_buffer.rb +269 -0
- data/lib/dot_net_services/session.rb +282 -0
- data/lib/dot_net_services.rb +143 -0
- data/lib/net/http/create_mb.rb +14 -0
- data/lib/net/http/retrieve.rb +14 -0
- data/lib/net/http/subscribe.rb +14 -0
- data/lib/net/http/unsubscribe.rb +14 -0
- data/spec/integration/TestService/Service/AnonymousResourceService.cs +9 -0
- data/spec/integration/TestService/Service/App.config +32 -0
- data/spec/integration/TestService/Service/PlainTextService.cs +37 -0
- data/spec/integration/TestService/Service/Program.cs +49 -0
- data/spec/integration/TestService/Service/Properties/AssemblyInfo.cs +33 -0
- data/spec/integration/TestService/Service/ResourceContract.cs +17 -0
- data/spec/integration/TestService/Service/ResourceService.cs +58 -0
- data/spec/integration/TestService/Service/Service.csproj +71 -0
- data/spec/integration/TestService/TestService.sln +33 -0
- data/spec/integration/end_to_end_spec.rb +84 -0
- data/spec/integration/vmb_spec.rb +30 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/unit/dot_net_services/authentication_spec.rb +289 -0
- data/spec/unit/dot_net_services/message_buffer_spec.rb +161 -0
- data/spec/unit/dot_net_services/session_spec.rb +247 -0
- metadata +87 -0
@@ -0,0 +1,282 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'cgi'
|
3
|
+
require 'net/http'
|
4
|
+
|
5
|
+
module DotNetServices
|
6
|
+
|
7
|
+
# The Session class is used for communicating with endpoints of .NET Services bus.
|
8
|
+
#
|
9
|
+
# == Usage examples
|
10
|
+
#
|
11
|
+
# A Session instance represents a .NET Services bus endpoint. Typically, you create it by calling Session#open
|
12
|
+
# and then exchange HTTP messages using methods like #get, #post, etc. It looks like this:
|
13
|
+
#
|
14
|
+
# Send a GET with no parameters:
|
15
|
+
#
|
16
|
+
# session = Session.open('/MySolution/MyService', :username => 'MySolution', :password => 'my_password')
|
17
|
+
# get_response = session.get
|
18
|
+
# # ... process response, which is a Net::HTTP::Response instance
|
19
|
+
#
|
20
|
+
# Send a GET with ?age=21&sex=Male query string:
|
21
|
+
#
|
22
|
+
# get_with_query_response = session.get :age => 21, :sex => "Male"
|
23
|
+
#
|
24
|
+
# Send a POST with name=Joe&email=joe@the_plumber.com (URL-encoded form) as a body:
|
25
|
+
#
|
26
|
+
# post_response = session.post :name => 'Joe', :email => 'joe@the_plumber.com'
|
27
|
+
#
|
28
|
+
# So far, Session API is similar to Net::HTTP::Session. Which is the whole point of the REST style
|
29
|
+
# communication - it is all just HTTP! However, there is one small twist. HTTP::Session represents a server
|
30
|
+
# (host and port). DotNetServices::Session represents a service endpoint. Which is a <b>URL</b>, not a server. In
|
31
|
+
# fact, all .NET Services endpoints have the same server, servicebus.windows.net. So an endpoint address is really
|
32
|
+
# just a path on that server.
|
33
|
+
#
|
34
|
+
# And sometimes you need to send requests to a path under the endpoint. For example, to update the name of customer
|
35
|
+
# #12345, you would do a POST to /MySolution/Customer/12345 with &name=Dave body. Creating a new Session for that is
|
36
|
+
# both counterintuitive and expensive (see the Details section below). For this reason, DotNetServices::Session has
|
37
|
+
# methods like post_to_url:
|
38
|
+
#
|
39
|
+
# result = session.post_to_url(12345, :name => 'Dave')
|
40
|
+
#
|
41
|
+
# == Details
|
42
|
+
#
|
43
|
+
# When a session is created, it's given the URL of an endpoint (that looks like /SolutionName/path/to/service), and
|
44
|
+
# username / password. These are <b>solution's</b> username and password, needed to prove to the bus that
|
45
|
+
# your consumer is a part of this solution. If the solution allows anonymous access, username / password are not
|
46
|
+
# necessary.
|
47
|
+
#
|
48
|
+
# When a session is opened (or is asked to send a first request to the endpoint), the session authenticates itself
|
49
|
+
# to the .NET Services using Identity Service [http://accesscontrol.windows.net], and obtains a security token from it.
|
50
|
+
#
|
51
|
+
# Security token is a Base64-encoded string. Unless an endpoint allows unauthenticated access, any request that comes
|
52
|
+
# to the endpoint must have this security token attached to it (as X-MS-Identity-Token HTTP header).
|
53
|
+
# DotNetServices::Session takes care of this responsibility, as well as renewing stale tokens.
|
54
|
+
#
|
55
|
+
# Session also has methods get_from_relay and post_to_relay, that are used for communicating to the .NET Services
|
56
|
+
# bus management endpoint. These methods (createmb, retrieve, query) are used by MessageBuffer, an application normally
|
57
|
+
# shouldn't need to use them directly.
|
58
|
+
#
|
59
|
+
# == Guidelines
|
60
|
+
#
|
61
|
+
# Obtaining a security token is an expensive operation, so it is <b>not</b> recommended to create a new instance of
|
62
|
+
# a Session for every outgoing request to the endpoint. It's better to use one Session instance per endpoint.
|
63
|
+
# Session takes care of expiring security tokens autoimatically, so Session instances don't go stale even after long
|
64
|
+
# periods of inactivity.
|
65
|
+
class Session
|
66
|
+
|
67
|
+
attr_reader :authenticator
|
68
|
+
|
69
|
+
class << self
|
70
|
+
# Open a .Net Services session at the +endpoint+.
|
71
|
+
#
|
72
|
+
# If +auth_data+ is provided, authenticates the session with
|
73
|
+
# the identity service using it. In this version, +auth_data+ should be a hash containing +:username+ and
|
74
|
+
# +:password+ keys. Other authentication mechanisms (InformationCard, X.509 certificates etc) that the Identity
|
75
|
+
# service provides are not exposed via a REST interface, therefore are not available to this library.
|
76
|
+
#
|
77
|
+
# If #open is called with a block, it passes the session instance as an argument to the block and returns the
|
78
|
+
# result of the block. If there is no block, session instance is returned instead.
|
79
|
+
def open(endpoint, auth_data = nil, &block)
|
80
|
+
Session.new(endpoint, auth_data).open(&block)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Initialize a new .NET Session at the +endpoint+.
|
85
|
+
#
|
86
|
+
# If +auth_data+ is provided, remember it. Acquiring
|
87
|
+
# authentication token is postponed until it's needed.
|
88
|
+
def initialize(endpoint, auth_data=nil)
|
89
|
+
@endpoint_uri = setup_endpoint(endpoint)
|
90
|
+
@authenticator = Authentication.setup(auth_data)
|
91
|
+
end
|
92
|
+
|
93
|
+
# See DotNetServices::Session.open.
|
94
|
+
def open
|
95
|
+
authenticate
|
96
|
+
block_given? ? yield(self) : self
|
97
|
+
end
|
98
|
+
|
99
|
+
# Acquire a security token from the Identity Service.
|
100
|
+
#
|
101
|
+
# This method will do nothing if:
|
102
|
+
# * this session already holds a security token and the token has not expired yet, or
|
103
|
+
# * the session was created without auth_data (anonymous session)
|
104
|
+
def authenticate
|
105
|
+
@authenticator.authenticate
|
106
|
+
end
|
107
|
+
|
108
|
+
# Send a GET request to the .NET Services endpoint.
|
109
|
+
#
|
110
|
+
# If query options are provided, they should be a hash, and they are converted to a query string and attached to
|
111
|
+
# the GET request target URL.
|
112
|
+
# Thus, session.get(:foo => 'bar') sends a GET to #{endpoint}?foo=bar
|
113
|
+
def get(query_options=nil)
|
114
|
+
get_from_url(nil, query_options)
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
def get_from_url(url, query_options=nil)
|
119
|
+
uri = setup_query_string(url, query_options)
|
120
|
+
get_request = Net::HTTP::Get.new(uri.request_uri)
|
121
|
+
enhance_and_execute(get_request)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Sends a GET to the service management endpoint.
|
125
|
+
#
|
126
|
+
# This is used by MessageBuffer for VMB management. Applications shouldn't need to use this method.
|
127
|
+
def get_from_relay(query_options=nil)
|
128
|
+
uri = setup_query_string(nil, query_options)
|
129
|
+
get_request = Net::HTTP::Get.new(uri.request_uri)
|
130
|
+
route_to_relay(get_request)
|
131
|
+
enhance_and_execute(get_request)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Send a POST request to the .NET Services endpoint.
|
135
|
+
#
|
136
|
+
# If +data+ is a Hash it is converted to a URL-encoded form, otherwise it is placed in the post body without any
|
137
|
+
# conversion.
|
138
|
+
#
|
139
|
+
# +content_type+ by default is set to +application/x-www-form-urlencoded+, which is appropriate when +data+ is a
|
140
|
+
# Hash. In any other case, you should specify +content_type+ explicitly.
|
141
|
+
def post(data=nil, content_type='application/x-www-form-urlencoded')
|
142
|
+
post_to_url("", data, content_type)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Sends a POST request to a path below .NET Services endpoint.
|
146
|
+
#
|
147
|
+
# If an application neeeds to send a POST to a service that uses URL to receive data, it should use this method
|
148
|
+
# instead of creating a new session for every new request. For example, if a service endpoint is called
|
149
|
+
# /Solution/Maps, and it's URL template is /Solution/Maps/*country*/*province*/*city*, a way to create a map of
|
150
|
+
# Calgary would be:
|
151
|
+
#
|
152
|
+
# session.post_to_url('/Canada/Calgary', map_data, content_type=...)
|
153
|
+
def post_to_url(url, data=nil, content_type='application/x-www-form-urlencoded')
|
154
|
+
post_request = Net::HTTP::Post.new(@endpoint_uri.path + url.to_s)
|
155
|
+
if data
|
156
|
+
post_request.content_type = content_type
|
157
|
+
if content_type == 'application/x-www-form-urlencoded'
|
158
|
+
post_request.form_data = data
|
159
|
+
else
|
160
|
+
post_request.body = data
|
161
|
+
end
|
162
|
+
end
|
163
|
+
enhance_and_execute(post_request)
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
# Sends a POST to the service management endpoint.
|
168
|
+
#
|
169
|
+
# This is used by MessageBuffer for VMB management. Applications should use MessageBuffer#keep_alive.
|
170
|
+
def post_to_relay(data=nil, content_type='application/x-www-form-urlencoded')
|
171
|
+
post_request = Net::HTTP::Post.new(@endpoint_uri.path)
|
172
|
+
|
173
|
+
if data
|
174
|
+
post_request.content_type = content_type
|
175
|
+
if content_type == 'application/x-www-form-urlencoded'
|
176
|
+
post_request.form_data = data
|
177
|
+
else
|
178
|
+
post_request.body = data
|
179
|
+
end
|
180
|
+
end
|
181
|
+
route_to_relay(post_request)
|
182
|
+
enhance_and_execute(post_request)
|
183
|
+
end
|
184
|
+
|
185
|
+
# Create a new message buffer (sends an X-CREATEMB to the service management endpoint).
|
186
|
+
#
|
187
|
+
# This is used by MessageBuffer for VMB management. Applications should use MessageBuffer#open.
|
188
|
+
def createmb
|
189
|
+
request = Net::HTTP::CreateMB.new(@endpoint_uri.path)
|
190
|
+
route_to_relay(request)
|
191
|
+
enhance_and_execute(request)
|
192
|
+
end
|
193
|
+
|
194
|
+
# Subscribe a message buffer to a virtual endpoint (sends an X-SUBSCRIBE to the VMB management endpoint).
|
195
|
+
#
|
196
|
+
# This is used by MessageBuffer for VMB management. Applications should use MessageBuffer#open.
|
197
|
+
def subscribe(request_options)
|
198
|
+
uri = setup_query_string(nil, request_options)
|
199
|
+
request = Net::HTTP::Subscribe.new(uri.request_uri)
|
200
|
+
route_to_relay(request)
|
201
|
+
enhance_and_execute(request)
|
202
|
+
end
|
203
|
+
|
204
|
+
# Unsubscribe a message buffer from a virtual endpoint (sends an X-UNSUBSCRIBE to the VMB management endpoint).
|
205
|
+
#
|
206
|
+
# This is used by MessageBuffer for VMB management. Applications should use MessageBuffer#open.
|
207
|
+
def unsubscribe(request_options)
|
208
|
+
uri = setup_query_string(nil, request_options)
|
209
|
+
request = Net::HTTP::Unsubscribe.new(uri.request_uri)
|
210
|
+
route_to_relay(request)
|
211
|
+
enhance_and_execute(request)
|
212
|
+
end
|
213
|
+
|
214
|
+
# Retrieve the next message from the message buffer (sends an X-RETRIEVE to the service management endpoint).
|
215
|
+
#
|
216
|
+
# This is used by MessageBuffer for VMB management. Applications should use MessageBuffer#poll.
|
217
|
+
def retrieve(query_options={})
|
218
|
+
query_options = stringify_and_downcase_keys(query_options)
|
219
|
+
query_options['timeout'] ||= 15
|
220
|
+
uri = setup_query_string(nil, query_options)
|
221
|
+
request = Net::HTTP::Retrieve.new(uri.request_uri)
|
222
|
+
route_to_relay(request)
|
223
|
+
enhance_and_execute(request)
|
224
|
+
end
|
225
|
+
|
226
|
+
|
227
|
+
# Delete an endpoint (sends a DELETE to the service management endpoint).
|
228
|
+
#
|
229
|
+
# This is used by MessageBuffer for VMB management. Applications should use MessageBuffer#delete.
|
230
|
+
def delete
|
231
|
+
request = Net::HTTP::Delete.new(@endpoint_uri.path)
|
232
|
+
route_to_relay(request)
|
233
|
+
enhance_and_execute(request)
|
234
|
+
end
|
235
|
+
|
236
|
+
# The URL of a .NET services endpoint this Session is connected to.
|
237
|
+
def endpoint
|
238
|
+
@endpoint_uri.to_s
|
239
|
+
end
|
240
|
+
|
241
|
+
private
|
242
|
+
|
243
|
+
def enhance_and_execute(request)
|
244
|
+
@authenticator.enhance(request)
|
245
|
+
DotNetServices.proxy.start(@endpoint_uri.host, @endpoint_uri.port) { |http| http.request(request) }
|
246
|
+
end
|
247
|
+
|
248
|
+
def route_to_relay(request)
|
249
|
+
request['X-Process-At'] = 'http://schemas.microsoft.com/ws/2007/08/connect/roles/relay'
|
250
|
+
end
|
251
|
+
|
252
|
+
def setup_endpoint(endpoint)
|
253
|
+
if endpoint !~ /^http(s?):\/\//
|
254
|
+
endpoint = '/' + endpoint unless endpoint[0] == ?/
|
255
|
+
endpoint = DotNetServices.root_url + endpoint
|
256
|
+
end
|
257
|
+
endpoint += '/' unless endpoint[-1] == ?/
|
258
|
+
URI.parse(endpoint)
|
259
|
+
end
|
260
|
+
|
261
|
+
def setup_query_string(url, query_options)
|
262
|
+
if url && !url.empty?
|
263
|
+
uri = @endpoint_uri + url.to_s
|
264
|
+
else
|
265
|
+
uri = @endpoint_uri.dup
|
266
|
+
end
|
267
|
+
|
268
|
+
if query_options
|
269
|
+
query = query_options.map { |k, v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}" }.join('&')
|
270
|
+
uri.query = query
|
271
|
+
end
|
272
|
+
uri
|
273
|
+
end
|
274
|
+
|
275
|
+
def stringify_and_downcase_keys(options)
|
276
|
+
new_options = {}
|
277
|
+
options.each { |key, value| new_options[key.to_s.downcase] = value unless value.nil? }
|
278
|
+
new_options
|
279
|
+
end
|
280
|
+
|
281
|
+
end
|
282
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'dot_net_services/session'
|
2
|
+
require 'dot_net_services/authentication'
|
3
|
+
require 'dot_net_services/error'
|
4
|
+
require 'dot_net_services/message_buffer'
|
5
|
+
require 'net/http/create_mb'
|
6
|
+
require 'net/http/retrieve'
|
7
|
+
require 'net/http/subscribe'
|
8
|
+
|
9
|
+
# DotNetServices is the top-level namespace of <b>.NET Services for Ruby</b>, Ruby interoperability layer for
|
10
|
+
# .NET Services plain HTTP (REST) API.
|
11
|
+
#
|
12
|
+
# == .NET Services
|
13
|
+
#
|
14
|
+
# Since you are reading this, you probably already know that .NET Services is a Microsoft product, described as the
|
15
|
+
# "Internet Services Bus", a messaging bus in the cloud. Like any other messaging bus, it helps services and consumers
|
16
|
+
# to communicate with each other. Further information on .NET Services can be found on MSDN:
|
17
|
+
# http://go.microsoft.com/fwlink/?LinkID=129428.
|
18
|
+
#
|
19
|
+
# While .NET Services primarily target .NET platform, non-Microsoft software is also catered for,
|
20
|
+
# by providing open protocols for most of the bus functionality, in both SOAP/WS-* and plain HTTP style. The latter
|
21
|
+
# is structured in a way that makes it easy to implement REST interactions over the bus, including both
|
22
|
+
# synchronous and asynchronous semantics. .NET Services bus doesn't force an application to use REST, any
|
23
|
+
# other kind of plain HTTP protocol can be routed through it, too.
|
24
|
+
#
|
25
|
+
#
|
26
|
+
# == Functionality
|
27
|
+
#
|
28
|
+
# This library is a simple Ruby wrapper for .NET Services plain HTPP (REST) APIs. Current version allows Ruby
|
29
|
+
# developers to build consumers of synchronous RESTful services (implemented as .NET WCF services, using
|
30
|
+
# webHttpRelayBinding, provided by the .NET Services SDK). You can also exchange messages between Ruby processes via
|
31
|
+
# Volatile Message Buffers (VMBs), which are .NET Services mechanism for asynchronous communications.
|
32
|
+
#
|
33
|
+
# .NET Services for Ruby looks like Net::HTTP API from the standard library, but deals with authentication,
|
34
|
+
# VMB management and message retrieval.
|
35
|
+
#
|
36
|
+
# Since this is a 0.1.0 version of an interoperability layer for a PDC version of a bus, a few things are missing.
|
37
|
+
#
|
38
|
+
# Most notably, with this version you still can not:
|
39
|
+
# * communicate with WCF services bound by anything other than webHttpRelayBinding.
|
40
|
+
# * communicate with the bus via WS-* protocols.
|
41
|
+
# * communicate with a WCF-based service via a VMB. At least, not until .NET Services SDK adds a WCF binding for
|
42
|
+
# REST via VMB. Once it's available, we should be able to work with it.
|
43
|
+
# * use VMB pub-sub and multicast functionality
|
44
|
+
# * use Workflow Services
|
45
|
+
# * use Identity Service as a dfederated identity provider for end-users
|
46
|
+
#
|
47
|
+
# == Usage examples
|
48
|
+
#
|
49
|
+
# Say, you are a cellphone dealer. You have a Point of Sale web application, written in Rails. It sells cellphones.
|
50
|
+
# The phones you sell are operated by a phone company, which offers a .NET Services solution (group of services in
|
51
|
+
# the .NET Services world), called Dealership. It includes two services, Customer and Provisioning.
|
52
|
+
#
|
53
|
+
# Customer service is a RESTful service endpoint, implemented by WCF service with a webHttpRelayBinding. It's listening
|
54
|
+
# on /Customer URL, and provides the usual CRUD functionality for managing customers.
|
55
|
+
#
|
56
|
+
# Provisioning service endpoint is a VMB. Coincidentally, it's also written in Ruby! :) When a customer buys a new phone,
|
57
|
+
# provisioning application should activate the phohe on the network, so that the customer can actually make calls with it.
|
58
|
+
# Activation can take several minutes, even hours, and we don't want the point of sale terminal to hang until it's over.
|
59
|
+
# Hence the use of a VMB as an asynchronous communication device.
|
60
|
+
#
|
61
|
+
# To create a customer, point of sale app needs to post a URL-encoded form to a webHttpRelayBinding endpoint. That's
|
62
|
+
# where you need to use a DotNetServices::Session class:
|
63
|
+
#
|
64
|
+
# require 'dotnetservices'
|
65
|
+
# session = DotNetServices::Session.open("/Dealership/Customer", :username => 'Dealership', :password => '_password')
|
66
|
+
# session.post :first_name => 'John', :last_name => 'Doe'
|
67
|
+
#
|
68
|
+
# Posting a message to a Provisioning endpoint would actually look the same:
|
69
|
+
#
|
70
|
+
# session = DotNetServices::Session.open("/Dealership/Provisioning", :username => 'Dealership', :password => '_password')
|
71
|
+
# session.post :phone_number => '555 888-8888'
|
72
|
+
#
|
73
|
+
# DotNetServices::MessageBuffer is needed by the Provisioning *service* (and not the client), to register the VMB and
|
74
|
+
# retrieve messages from it. Usage typically looks like this:
|
75
|
+
#
|
76
|
+
# buffer = DotNetServices::MessageBuffer.new("/Dealership/Provisioning",
|
77
|
+
# :username => 'Dealership', :password => '_password')
|
78
|
+
# buffer.open_and_poll do |message|
|
79
|
+
# ... process incoming messages ...
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# This code will check that the VMB exists, create one if necessary, and continuously poll it for new messages. Whenever
|
83
|
+
# a new message is sent to VMB, MessageBuffer#open_and_poll retrieves it and invokesthe block, passing it a
|
84
|
+
# Net::HTTP::Request instance that looks exactly like what the sender originally sent to the VMB. Nice and easy.
|
85
|
+
#
|
86
|
+
# To understand and use .NET Services for Ruby, you should probably also read the documentation for the two classes
|
87
|
+
# mentioned above: DotNetServices::Session and DotNetServices::MessageBuffer.
|
88
|
+
#
|
89
|
+
# Happy bussing!
|
90
|
+
module DotNetServices
|
91
|
+
|
92
|
+
class << self
|
93
|
+
|
94
|
+
# The name of the host providing DotNetServices relay services.
|
95
|
+
def relay_host
|
96
|
+
"servicebus.windows.net"
|
97
|
+
end
|
98
|
+
|
99
|
+
# The name of the host providing DotNetServices identity services.
|
100
|
+
def identity_host
|
101
|
+
'accesscontrol.windows.net'
|
102
|
+
end
|
103
|
+
|
104
|
+
# The root URL used for exposing services.
|
105
|
+
def root_url
|
106
|
+
"http://#{relay_host}/services"
|
107
|
+
end
|
108
|
+
|
109
|
+
# Host and port of the bus endpoint.
|
110
|
+
def host_port
|
111
|
+
return @host_port if @host_port
|
112
|
+
uri = URI.parse(root_url)
|
113
|
+
@host_port = [uri.host, uri.port]
|
114
|
+
@host_port
|
115
|
+
end
|
116
|
+
|
117
|
+
# Use an HTTP proxy to connect to .NET Services
|
118
|
+
#
|
119
|
+
# If you call this method, all subsequent communications with .NET Services will use an HTTP proxy.
|
120
|
+
# You must given hostname and password #use proxy
|
121
|
+
# DotNetServices.use_proxy()
|
122
|
+
def use_proxy(proxy_host, proxy_port, proxy_user = nil, proxy_password = nil)
|
123
|
+
@proxy = Net::HTTP::Proxy(proxy_host, proxy_port, proxy_user, proxy_password)
|
124
|
+
end
|
125
|
+
|
126
|
+
# An HTTP proxy to connect through.
|
127
|
+
#
|
128
|
+
# Defaults to Net::HTTP (direct connection without proxy). To set a proxy, see #use_proxy.
|
129
|
+
def proxy
|
130
|
+
@proxy || Net::HTTP
|
131
|
+
end
|
132
|
+
|
133
|
+
# TODO implement!
|
134
|
+
def logger
|
135
|
+
if RAILS_ENV
|
136
|
+
nil
|
137
|
+
else
|
138
|
+
@logger ||= nil
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,14 @@
|
|
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
|
@@ -0,0 +1,14 @@
|
|
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
|
@@ -0,0 +1,14 @@
|
|
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
|
@@ -0,0 +1,14 @@
|
|
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
|
@@ -0,0 +1,32 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
2
|
+
<configuration>
|
3
|
+
<system.serviceModel>
|
4
|
+
|
5
|
+
<bindings>
|
6
|
+
<!-- Application Binding -->
|
7
|
+
<webHttpRelayBinding>
|
8
|
+
<binding name="default" />
|
9
|
+
</webHttpRelayBinding>
|
10
|
+
</bindings>
|
11
|
+
|
12
|
+
<services>
|
13
|
+
<!-- Application Service -->
|
14
|
+
<service name="DotNetServicesRuby.ResourceService"
|
15
|
+
behaviorConfiguration="default">
|
16
|
+
<endpoint contract="DotNetServicesRuby.ResourceContract"
|
17
|
+
binding="webHttpRelayBinding"
|
18
|
+
bindingConfiguration="default"
|
19
|
+
address="" />
|
20
|
+
</service>
|
21
|
+
</services>
|
22
|
+
|
23
|
+
<behaviors>
|
24
|
+
<serviceBehaviors>
|
25
|
+
<behavior name="default">
|
26
|
+
<serviceDebug httpHelpPageEnabled="false" httpsHelpPageEnabled="false" />
|
27
|
+
</behavior>
|
28
|
+
</serviceBehaviors>
|
29
|
+
</behaviors>
|
30
|
+
|
31
|
+
</system.serviceModel>
|
32
|
+
</configuration>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
namespace DotNetServicesRuby
|
2
|
+
{
|
3
|
+
using System;
|
4
|
+
using System.Runtime.Serialization;
|
5
|
+
using System.ServiceModel;
|
6
|
+
using System.ServiceModel.Web;
|
7
|
+
using System.ServiceModel.Channels;
|
8
|
+
|
9
|
+
[ServiceContract]
|
10
|
+
[ServiceBehavior]
|
11
|
+
class PlainTextService
|
12
|
+
{
|
13
|
+
[OperationContract(Action="*", ReplyAction="*")]
|
14
|
+
[WebInvoke(BodyStyle=WebMessageBodyStyle.Bare)]
|
15
|
+
//public string Process(string foo)
|
16
|
+
//{
|
17
|
+
// return "hi mom";
|
18
|
+
//}
|
19
|
+
|
20
|
+
public Message Process(Message message)
|
21
|
+
{
|
22
|
+
var httpRequest = (HttpRequestMessageProperty)(message.Properties["httpRequest"]);
|
23
|
+
|
24
|
+
var query = httpRequest.QueryString;
|
25
|
+
var verb = httpRequest.Method;
|
26
|
+
var body = "";
|
27
|
+
|
28
|
+
Message response = Message.CreateMessage(MessageVersion.None, "GETRESPONSE", "hi, mom");
|
29
|
+
|
30
|
+
HttpResponseMessageProperty responseProperty = new HttpResponseMessageProperty();
|
31
|
+
responseProperty.Headers.Add("Content-Type", "text/plain");
|
32
|
+
response.Properties.Add(HttpResponseMessageProperty.Name, responseProperty);
|
33
|
+
return response;
|
34
|
+
}
|
35
|
+
|
36
|
+
}
|
37
|
+
}
|
@@ -0,0 +1,49 @@
|
|
1
|
+
namespace DotNetServicesRuby
|
2
|
+
{
|
3
|
+
using System;
|
4
|
+
using System.ServiceModel;
|
5
|
+
using System.ServiceModel.Web;
|
6
|
+
using System.ServiceModel.Description;
|
7
|
+
using Microsoft.ServiceBus;
|
8
|
+
|
9
|
+
class Program
|
10
|
+
{
|
11
|
+
static void Main(string[] args)
|
12
|
+
{
|
13
|
+
|
14
|
+
System.Console.WriteLine("Enter solution name: ");
|
15
|
+
var solutionName = System.Console.ReadLine();
|
16
|
+
System.Console.WriteLine("Enter password for " + solutionName + " solution:");
|
17
|
+
var password = System.Console.ReadLine();
|
18
|
+
|
19
|
+
var behavior = new TransportClientEndpointBehavior();
|
20
|
+
behavior.CredentialType = TransportClientCredentialType.UserNamePassword;
|
21
|
+
behavior.Credentials.UserName.UserName = solutionName;
|
22
|
+
behavior.Credentials.UserName.Password = password;
|
23
|
+
|
24
|
+
var address = new Uri(String.Format("http://{0}/services/{1}/TestService/", ServiceBusEnvironment.DefaultRelayHostName, solutionName));
|
25
|
+
var host = new ServiceHost(typeof(ResourceService), address);
|
26
|
+
host.Description.Endpoints[0].Behaviors.Add(behavior);
|
27
|
+
host.Open();
|
28
|
+
|
29
|
+
//var anonymousAddress = new Uri(String.Format("http://{0}/services/{1}/AnonymousTestService/", ServiceBusEnvironment.DefaultRelayHostName, solutionName));
|
30
|
+
//var anonymousHost = new ServiceHost(typeof(AnonymousResourceService), anonymousAddress);
|
31
|
+
//anonymousHost.Description.Endpoints[0].Behaviors.Add(behavior);
|
32
|
+
//anonymousHost.Open();
|
33
|
+
|
34
|
+
//var plainTextAddress = new Uri(String.Format("http://{0}/services/{1}/PlainTextTestService/", ServiceBusEnvironment.DefaultRelayHostName, solutionName));
|
35
|
+
//var plainTextHost = new WebServiceHost(typeof(PlainTextService), plainTextAddress);
|
36
|
+
//plainTextHost.Description.Endpoints[0].Behaviors.Add(behavior);
|
37
|
+
//plainTextHost.Open();
|
38
|
+
|
39
|
+
Console.WriteLine("Service address: " + address);
|
40
|
+
Console.WriteLine("Press [Enter] to exit");
|
41
|
+
Console.ReadLine();
|
42
|
+
|
43
|
+
host.Close();
|
44
|
+
//anonymousHost.Close();
|
45
|
+
//plainTextHost.Close();
|
46
|
+
}
|
47
|
+
|
48
|
+
}
|
49
|
+
}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
using System.Reflection;
|
2
|
+
using System.Runtime.CompilerServices;
|
3
|
+
using System.Runtime.InteropServices;
|
4
|
+
|
5
|
+
// General Information about an assembly is controlled through the following
|
6
|
+
// set of attributes. Change these attribute values to modify the information
|
7
|
+
// associated with an assembly.
|
8
|
+
[assembly: AssemblyTitle("Service")]
|
9
|
+
[assembly: AssemblyDescription("")]
|
10
|
+
[assembly: AssemblyConfiguration("")]
|
11
|
+
[assembly: AssemblyCompany("")]
|
12
|
+
[assembly: AssemblyProduct("Service")]
|
13
|
+
[assembly: AssemblyCopyright("Copyright © 2006")]
|
14
|
+
[assembly: AssemblyTrademark("")]
|
15
|
+
[assembly: AssemblyCulture("")]
|
16
|
+
|
17
|
+
// Setting ComVisible to false makes the types in this assembly not visible
|
18
|
+
// to COM components. If you need to access a type in this assembly from
|
19
|
+
// COM, set the ComVisible attribute to true on that type.
|
20
|
+
[assembly: ComVisible(false)]
|
21
|
+
|
22
|
+
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
23
|
+
[assembly: Guid("23fce5dd-8bf8-403c-b748-d24504e7c5c1")]
|
24
|
+
|
25
|
+
// Version information for an assembly consists of the following four values:
|
26
|
+
//
|
27
|
+
// Major Version
|
28
|
+
// Minor Version
|
29
|
+
// Build Number
|
30
|
+
// Revision
|
31
|
+
//
|
32
|
+
[assembly: AssemblyVersion("1.0.0.0")]
|
33
|
+
[assembly: AssemblyFileVersion("1.0.0.0")]
|
@@ -0,0 +1,17 @@
|
|
1
|
+
namespace DotNetServicesRuby
|
2
|
+
{
|
3
|
+
using System;
|
4
|
+
using System.Runtime.Serialization;
|
5
|
+
using System.ServiceModel;
|
6
|
+
using System.ServiceModel.Web;
|
7
|
+
using System.ServiceModel.Channels;
|
8
|
+
|
9
|
+
[ServiceContract(Name = "ResourceContract", Namespace = "http://samples.microsoft.com/ServiceModel/Relay/")]
|
10
|
+
public interface ResourceContract
|
11
|
+
{
|
12
|
+
[OperationContract(Action = "*", ReplyAction = "*")]
|
13
|
+
Message Process(Message message);
|
14
|
+
}
|
15
|
+
|
16
|
+
public interface ResourceChannel : ResourceContract, IClientChannel { }
|
17
|
+
}
|