1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
|
//-----------------------------------------------------------------------
// <copyright file="OAuthChannel.cs" company="Andrew Arnott">
// Copyright (c) Andrew Arnott. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace DotNetOAuth {
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Web;
using DotNetOAuth.Messaging;
/// <summary>
/// An OAuth-specific implementation of the <see cref="Channel"/> class.
/// </summary>
internal class OAuthChannel : Channel {
/// <summary>
/// Initializes a new instance of the <see cref="OAuthChannel"/> class.
/// </summary>
internal OAuthChannel()
: base(new OAuthMessageTypeProvider()) {
}
/// <summary>
/// Searches an incoming HTTP request for data that could be used to assemble
/// a protocol request message.
/// </summary>
/// <param name="request">The HTTP request to search.</param>
/// <returns>A dictionary of data in the request. Should never be null, but may be empty.</returns>
protected internal override IProtocolMessage ReadFromRequest(HttpRequestInfo request) {
if (request == null) {
throw new ArgumentNullException("request");
}
// First search the Authorization header. Use it exclusively if it's present.
string authorization = request.Headers[HttpRequestHeader.Authorization];
if (authorization != null) {
string[] authorizationSections = authorization.Split(';'); // TODO: is this the right delimiter?
string oauthPrefix = Protocol.Default.AuthorizationHeaderScheme + " ";
// The Authorization header may have multiple uses, and OAuth may be just one of them.
// Go through each one looking for an OAuth one.
foreach (string auth in authorizationSections) {
string trimmedAuth = auth.Trim();
if (trimmedAuth.StartsWith(oauthPrefix, StringComparison.Ordinal)) {
// We found an Authorization: OAuth header.
// Parse it according to the rules in section 5.4.1 of the V1.0 spec.
var fields = new Dictionary<string, string>();
foreach (string stringPair in trimmedAuth.Substring(oauthPrefix.Length).Split(',')) {
string[] keyValueStringPair = stringPair.Trim().Split('=');
string key = Uri.UnescapeDataString(keyValueStringPair[0]);
string value = Uri.UnescapeDataString(keyValueStringPair[1].Trim('"'));
fields.Add(key, value);
}
return this.Receive(fields);
}
}
}
// We didn't find an OAuth authorization header. Revert to other payload methods.
return base.ReadFromRequest(request);
}
/// <summary>
/// Gets the protocol message that may be in the given HTTP response stream.
/// </summary>
/// <param name="responseStream">The response that is anticipated to contain an OAuth message.</param>
/// <returns>The deserialized message, if one is found. Null otherwise.</returns>
protected internal override IProtocolMessage ReadFromResponse(Stream responseStream) {
if (responseStream == null) {
throw new ArgumentNullException("responseStream");
}
using (StreamReader reader = new StreamReader(responseStream)) {
string response = reader.ReadToEnd();
var fields = HttpUtility.ParseQueryString(response).ToDictionary();
return Receive(fields);
}
}
/// <summary>
/// Sends a direct message to a remote party and waits for the response.
/// </summary>
/// <param name="request">The message to send.</param>
/// <returns>The remote party's response.</returns>
protected internal override IProtocolMessage Request(IDirectedProtocolMessage request) {
if (request == null) {
throw new ArgumentNullException("request");
}
HttpWebRequest httpRequest;
MessageScheme transmissionMethod = MessageScheme.AuthorizationHeaderRequest;
switch (transmissionMethod) {
case MessageScheme.AuthorizationHeaderRequest:
httpRequest = this.InitializeRequestAsAuthHeader(request);
break;
case MessageScheme.PostRequest:
httpRequest = this.InitializeRequestAsPost(request);
break;
case MessageScheme.GetRequest:
httpRequest = this.InitializeRequestAsGet(request);
break;
default:
throw new NotSupportedException();
}
// Submit the request and await the reply.
Dictionary<string, string> responseFields;
try {
using (HttpWebResponse response = (HttpWebResponse)httpRequest.GetResponse()) {
using (StreamReader reader = new StreamReader(response.GetResponseStream())) {
string queryString = reader.ReadToEnd();
responseFields = HttpUtility.ParseQueryString(queryString).ToDictionary();
}
}
} catch (WebException ex) {
throw new ProtocolException(MessagingStrings.ErrorInRequestReplyMessage, ex);
}
Type messageType = this.MessageTypeProvider.GetResponseMessageType(request, responseFields);
var responseSerialize = MessageSerializer.Get(messageType);
var responseMessage = responseSerialize.Deserialize(responseFields);
return responseMessage;
}
/// <summary>
/// Queues a message for sending in the response stream where the fields
/// are sent in the response stream in querystring style.
/// </summary>
/// <param name="response">The message to send as a response.</param>
/// <remarks>
/// This method implements spec V1.0 section 5.3.
/// </remarks>
protected override void SendDirectMessageResponse(IProtocolMessage response) {
MessageSerializer serializer = MessageSerializer.Get(response.GetType());
var fields = serializer.Serialize(response);
string responseBody = MessagingUtilities.CreateQueryString(fields);
Response encodedResponse = new Response {
Body = Encoding.UTF8.GetBytes(responseBody),
OriginalMessage = response,
Status = System.Net.HttpStatusCode.OK,
Headers = new System.Net.WebHeaderCollection(),
};
this.QueueIndirectOrResponseMessage(encodedResponse);
}
/// <summary>
/// Reports an error to the user via the user agent.
/// </summary>
/// <param name="exception">The error information.</param>
protected override void ReportErrorToUser(ProtocolException exception) {
throw new NotImplementedException();
}
/// <summary>
/// Sends an error result directly to the calling remote party according to the
/// rules of the protocol.
/// </summary>
/// <param name="exception">The error information.</param>
protected override void ReportErrorAsDirectResponse(ProtocolException exception) {
throw new NotImplementedException();
}
/// <summary>
/// Prepares to send a request to the Service Provider via the Authorization header.
/// </summary>
/// <param name="requestMessage">The message to be transmitted to the ServiceProvider.</param>
/// <returns>The web request ready to send.</returns>
/// <remarks>
/// This method implements OAuth 1.0 section 5.2, item #1 (described in section 5.4).
/// </remarks>
private HttpWebRequest InitializeRequestAsAuthHeader(IDirectedProtocolMessage requestMessage) {
var serializer = MessageSerializer.Get(requestMessage.GetType());
var fields = serializer.Serialize(requestMessage);
HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(requestMessage.Recipient);
StringBuilder authorization = new StringBuilder();
authorization.Append(requestMessage.Protocol.AuthorizationHeaderScheme);
authorization.Append(" ");
foreach (var pair in fields) {
string key = Uri.EscapeDataString(pair.Key);
string value = Uri.EscapeDataString(pair.Value);
authorization.Append(key);
authorization.Append("=\"");
authorization.Append(value);
authorization.Append("\",");
}
authorization.Length--; // remove trailing comma
httpRequest.Headers.Add(HttpRequestHeader.Authorization, authorization.ToString());
return httpRequest;
}
/// <summary>
/// Prepares to send a request to the Service Provider as the payload of a POST request.
/// </summary>
/// <param name="requestMessage">The message to be transmitted to the ServiceProvider.</param>
/// <returns>The web request ready to send.</returns>
/// <remarks>
/// This method implements OAuth 1.0 section 5.2, item #2.
/// </remarks>
private HttpWebRequest InitializeRequestAsPost(IDirectedProtocolMessage requestMessage) {
var serializer = MessageSerializer.Get(requestMessage.GetType());
var fields = serializer.Serialize(requestMessage);
HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(requestMessage.Recipient);
httpRequest.Method = "POST";
httpRequest.ContentType = "application/x-www-form-urlencoded";
string requestBody = MessagingUtilities.CreateQueryString(fields);
httpRequest.ContentLength = requestBody.Length;
using (StreamWriter writer = new StreamWriter(httpRequest.GetRequestStream())) {
writer.Write(requestBody);
}
return httpRequest;
}
/// <summary>
/// Prepares to send a request to the Service Provider as the query string in a GET request.
/// </summary>
/// <param name="requestMessage">The message to be transmitted to the ServiceProvider.</param>
/// <returns>The web request ready to send.</returns>
/// <remarks>
/// This method implements OAuth 1.0 section 5.2, item #3.
/// </remarks>
private HttpWebRequest InitializeRequestAsGet(IDirectedProtocolMessage requestMessage) {
var serializer = MessageSerializer.Get(requestMessage.GetType());
var fields = serializer.Serialize(requestMessage);
UriBuilder builder = new UriBuilder(requestMessage.Recipient);
MessagingUtilities.AppendQueryArgs(builder, fields);
HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(builder.Uri);
return httpRequest;
}
}
}
|