dot_net_services 0.0.1
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 +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
|
+
}
|