-
Notifications
You must be signed in to change notification settings - Fork 191
Add form and query helpers needed for Facebook auth. #113
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
using System; | ||
using Microsoft.AspNet.Http; | ||
|
||
namespace Microsoft.AspNet.WebUtilities | ||
{ | ||
public static class FormHelpers | ||
{ | ||
/// <summary> | ||
/// Parses an HTTP form body. | ||
/// </summary> | ||
/// <param name="text">The HTTP form body to parse.</param> | ||
/// <returns>The <see cref="T:Microsoft.Owin.IFormCollection" /> object containing the parsed HTTP form body.</returns> | ||
public static IFormCollection ParseForm(string text) | ||
{ | ||
return ParsingHelpers.GetForm(text); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
|
||
namespace Microsoft.AspNet.WebUtilities | ||
{ | ||
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)] | ||
internal sealed class NotNullAttribute : Attribute | ||
{ | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using Microsoft.AspNet.Http; | ||
using Microsoft.AspNet.WebUtilities.Collections; | ||
|
||
namespace Microsoft.AspNet.WebUtilities | ||
{ | ||
internal static partial class ParsingHelpers | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Legacy, these used to be in separate files based on functionality. I'll clean it up. |
||
{ | ||
internal static void ParseDelimited(string text, char[] delimiters, Action<string, string, object> callback, object state) | ||
{ | ||
int textLength = text.Length; | ||
int equalIndex = text.IndexOf('='); | ||
if (equalIndex == -1) | ||
{ | ||
equalIndex = textLength; | ||
} | ||
int scanIndex = 0; | ||
while (scanIndex < textLength) | ||
{ | ||
int delimiterIndex = text.IndexOfAny(delimiters, scanIndex); | ||
if (delimiterIndex == -1) | ||
{ | ||
delimiterIndex = textLength; | ||
} | ||
if (equalIndex < delimiterIndex) | ||
{ | ||
while (scanIndex != equalIndex && char.IsWhiteSpace(text[scanIndex])) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's OK because the content should still be encoded at this point. We decode it a few lines later. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I still highly recommend going with the most correct logic here, per whatever is in the RFC. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Filed as #119 |
||
{ | ||
++scanIndex; | ||
} | ||
string name = text.Substring(scanIndex, equalIndex - scanIndex); | ||
string value = text.Substring(equalIndex + 1, delimiterIndex - equalIndex - 1); | ||
callback( | ||
Uri.UnescapeDataString(name.Replace('+', ' ')), | ||
Uri.UnescapeDataString(value.Replace('+', ' ')), | ||
state); | ||
equalIndex = text.IndexOf('=', delimiterIndex); | ||
if (equalIndex == -1) | ||
{ | ||
equalIndex = textLength; | ||
} | ||
} | ||
scanIndex = delimiterIndex + 1; | ||
} | ||
} | ||
|
||
private static readonly Action<string, string, object> AppendItemCallback = (name, value, state) => | ||
{ | ||
var dictionary = (IDictionary<string, List<String>>)state; | ||
|
||
List<string> existing; | ||
if (!dictionary.TryGetValue(name, out existing)) | ||
{ | ||
dictionary.Add(name, new List<string>(1) { value }); | ||
} | ||
else | ||
{ | ||
existing.Add(value); | ||
} | ||
}; | ||
|
||
internal static IFormCollection GetForm(string text) | ||
{ | ||
IDictionary<string, string[]> form = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase); | ||
var accumulator = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase); | ||
ParseDelimited(text, new[] { '&' }, AppendItemCallback, accumulator); | ||
foreach (var kv in accumulator) | ||
{ | ||
form.Add(kv.Key, kv.Value.ToArray()); | ||
} | ||
return new FormCollection(form); | ||
} | ||
|
||
internal static string GetJoinedValue(IDictionary<string, string[]> store, string key) | ||
{ | ||
string[] values = GetUnmodifiedValues(store, key); | ||
return values == null ? null : string.Join(",", values); | ||
} | ||
|
||
internal static string[] GetUnmodifiedValues(IDictionary<string, string[]> store, string key) | ||
{ | ||
if (store == null) | ||
{ | ||
throw new ArgumentNullException("store"); | ||
} | ||
string[] values; | ||
return store.TryGetValue(key, out values) ? values : null; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
using System; | ||
|
||
namespace Microsoft.AspNet.WebUtilities | ||
{ | ||
public static class QueryHelpers | ||
{ | ||
/// <summary> | ||
/// Append the given query key and value to the uri. | ||
/// </summary> | ||
/// <param name="uri">The base uri.</param> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
/// <param name="name">The name of the query key.</param> | ||
/// <param name="value">The query value.</param> | ||
/// <returns>The combine result.</returns> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
public static string AddQueryString([NotNull] string uri, [NotNull] string name, [NotNull] string value) | ||
{ | ||
bool hasQuery = uri.IndexOf('?') != -1; | ||
return uri + (hasQuery ? "&" : "?") + Uri.EscapeDataString(name) + "=" + Uri.EscapeDataString(value); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is case-sensitivity relevant here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Though I suppose someone could always overwrite the dictionary with whatever they want, so this is probably fine the way it is.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In Katana these were always case sensitive (the default for a new Dictionary). I don't know that there are serious motivations one way or the other.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that's fair.