Reputation: 15579
The value that I need to parse is part of a Www-Authenticate
HTTP response header:
realm="https://auth.docker.io/token",service="registry.docker.io",scope="registry:catalog:*"
I would like to get a dictionary (or similar) with the following values:
{ "realm", "https://auth.docker.io/token" },
{ "service", "registry.docker.io" },
{ "scope", "registry:catalog:*"},
Is there some utility built into .NET that will parse this?
This is how I'm getting the value. In making a call to a Web API service, I am getting an Unauthorized response (this is expected):
var httpClient = new HttpClient();
//This is a public url
var response = await httpClient.GetAsync("https://registry-1.docker.io/v2/_catalog")
// This is the value that needs to be parsed
string parameter = response.Headers.WwwAuthenticate.FirstOrDefault().Parameter;
Things I've tried / looked at:
string.Split
. Is this safe? What if there are commas in the quoted values?Upvotes: 1
Views: 1957
Reputation: 292555
A bit late to the party, but I wrote an article a few years ago about how to do this using Sprache. My solution lets you do something like this:
var challenge = Grammar.Challenge.Parse(input);
Console.WriteLine($"Scheme: {challenge.Scheme}");
Console.WriteLine($"Parameters:");
foreach (var p in challenge.Parameters)
{
Console.WriteLine($"- {p.Name} = {p.Value}");
}
Upvotes: 0
Reputation: 5245
Using the schema defined in RFC6750 and RFC2616, a slightly more precise parser implementation is included below. This parser takes into account the possibility that strings might contain =
, ,
, and/or escaped "
.
internal class AuthParamParser
{
private string _buffer;
private int _i;
private AuthParamParser(string param)
{
_buffer = param;
_i = 0;
}
public static Dictionary<string, string> Parse(string param)
{
var state = new AuthParamParser(param);
var result = new Dictionary<string, string>();
var token = state.ReadToken();
while (!string.IsNullOrEmpty(token))
{
if (!state.ReadDelim('='))
return result;
result.Add(token, state.ReadString());
if (!state.ReadDelim(','))
return result;
token = state.ReadToken();
}
return result;
}
private string ReadToken()
{
var start = _i;
while (_i < _buffer.Length && ValidTokenChar(_buffer[_i]))
_i++;
return _buffer.Substring(start, _i - start);
}
private bool ReadDelim(char ch)
{
while (_i < _buffer.Length && char.IsWhiteSpace(_buffer[_i]))
_i++;
if (_i >= _buffer.Length || _buffer[_i] != ch)
return false;
_i++;
while (_i < _buffer.Length && char.IsWhiteSpace(_buffer[_i]))
_i++;
return true;
}
private string ReadString()
{
if (_i < _buffer.Length && _buffer[_i] == '"')
{
var buffer = new StringBuilder();
_i++;
while (_i < _buffer.Length)
{
if (_buffer[_i] == '\\' && (_i + 1) < _buffer.Length)
{
_i++;
buffer.Append(_buffer[_i]);
_i++;
}
else if (_buffer[_i] == '"')
{
_i++;
return buffer.ToString();
}
else
{
buffer.Append(_buffer[_i]);
_i++;
}
}
return buffer.ToString();
}
else
{
return ReadToken();
}
}
private bool ValidTokenChar(char ch)
{
if (ch < 32)
return false;
if (ch == '(' || ch == ')' || ch == '<' || ch == '>' || ch == '@'
|| ch == ',' || ch == ';' || ch == ':' || ch == '\\' || ch == '"'
|| ch == '/' || ch == '[' || ch == ']' || ch == '?' || ch == '='
|| ch == '{' || ch == '}' || ch == 127 || ch == ' ' || ch == '\t')
return false;
return true;
}
}
Upvotes: 2
Reputation: 15579
From the answer that Forty3 directed me to, I came up with the following:
public static class AuthenticateParser
{
public static IDictionary<string, string> Parse(string value)
{
//https://stackoverflow.com/questions/45516717/extracting-and-parsing-the-www-authenticate-header-from-httpresponsemessage-in/45516809#45516809
string[] commaSplit = value.Split(", ".ToCharArray());
return commaSplit
.ToDictionary(GetKey, GetValue);
}
private static string GetKey(string pair)
{
int equalPos = pair.IndexOf("=");
if (equalPos < 1)
throw new FormatException("No '=' found.");
return pair.Substring(0, equalPos);
}
private static string GetValue(string pair)
{
int equalPos = pair.IndexOf("=");
if (equalPos < 1)
throw new FormatException("No '=' found.");
string value = pair.Substring(equalPos + 1).Trim();
//Trim quotes
if (value.StartsWith("\"") && value.EndsWith("\""))
{
value = value.Substring(1, value.Length - 2);
}
return value;
}
}
Upvotes: 0