RQDQ
RQDQ

Reputation: 15579

How to parse values from Www-Authenticate

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:

Upvotes: 1

Views: 1957

Answers (3)

Thomas Levesque
Thomas Levesque

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

erdomke
erdomke

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

RQDQ
RQDQ

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

Related Questions