1 using Microsoft.AspNetCore.Http.Headers;
2 using Microsoft.Extensions.Primitives;
3 using Microsoft.Net.Http.Headers;
5 using System.Collections.Generic;
7 using System.Net.Http.Headers;
22 public const string ApiVersionHeader =
"Api";
27 public const string InstanceIdHeader =
"Instance";
32 public const string JwtAuthenticationScheme =
"bearer";
37 public const string BasicAuthenticationScheme =
"basic";
42 static readonly AssemblyName AssemblyName = Assembly.GetExecutingAssembly().GetName();
47 public static readonly Version Version = AssemblyName.
Version.Semver();
52 public long? InstanceId {
get;
set; }
57 public ProductHeaderValue?
UserAgent => ProductInfoHeaderValue.TryParse(RawUserAgent, out var userAgent) ? userAgent.Product : null;
62 public string? RawUserAgent {
get; }
67 public Version ApiVersion {
get; }
72 public string? Token {
get; }
77 public string? Username {
get; }
82 public string? Password {
get; }
87 public bool IsTokenAuthentication => Token != null;
94 public static bool CheckCompatibility(Version otherVersion) => Version.Major == (otherVersion?.Major ??
throw new ArgumentNullException(nameof(otherVersion)));
101 public ApiHeaders(ProductHeaderValue userAgent,
string token) : this(userAgent, token, null, null)
103 if (userAgent == null)
104 throw new ArgumentNullException(nameof(userAgent));
106 throw new ArgumentNullException(nameof(token));
115 public ApiHeaders(ProductHeaderValue userAgent,
string username,
string password) : this(userAgent, null, username, password)
117 if (userAgent == null)
118 throw new ArgumentNullException(nameof(userAgent));
119 if (username == null)
120 throw new ArgumentNullException(nameof(username));
121 if (password == null)
122 throw new ArgumentNullException(nameof(password));
132 if (requestHeaders == null)
133 throw new ArgumentNullException(nameof(requestHeaders));
136 var errorBuilder =
new StringBuilder();
138 void AddError(
HeaderTypes headerType,
string message)
141 errorBuilder.Append(Environment.NewLine);
142 badHeaders |= headerType;
143 errorBuilder.Append(message);
146 var jsonAccept =
new Microsoft.Net.Http.Headers.MediaTypeHeaderValue(MediaTypeNames.Application.Json);
147 if (!requestHeaders.Accept.Any(x => jsonAccept.IsSubsetOf(x)))
148 AddError(
HeaderTypes.Accept, $
"Client does not accept {MediaTypeNames.Application.Json}!");
150 if (!requestHeaders.Headers.TryGetValue(HeaderNames.UserAgent, out var userAgentValues) || userAgentValues.Count == 0)
151 AddError(
HeaderTypes.UserAgent, $
"Missing {HeaderNames.UserAgent} header!");
154 RawUserAgent = userAgentValues.First();
155 if (String.IsNullOrWhiteSpace(RawUserAgent))
156 AddError(
HeaderTypes.UserAgent, $
"Malformed {HeaderNames.UserAgent} header!");
160 Version? apiVersion = null;
161 if (!requestHeaders.Headers.TryGetValue(ApiVersionHeader, out var apiUserAgentHeaderValues) || !ProductInfoHeaderValue.TryParse(apiUserAgentHeaderValues.FirstOrDefault(), out var apiUserAgent) || apiUserAgent.Product.Name != AssemblyName.Name)
162 AddError(
HeaderTypes.Api, $
"Missing {ApiVersionHeader} header!");
163 else if (!Version.TryParse(apiUserAgent.Product.Version, out apiVersion))
164 AddError(
HeaderTypes.Api, $
"Malformed {ApiVersionHeader} header!");
166 if (!requestHeaders.Headers.TryGetValue(HeaderNames.Authorization, out StringValues authorization))
167 AddError(
HeaderTypes.Authorization, $
"Missing {HeaderNames.Authorization} header!");
170 var auth = authorization.First();
171 var splits =
new List<string>(auth.Split(
' '));
172 var scheme = splits.First();
173 if (String.IsNullOrWhiteSpace(scheme))
174 AddError(
HeaderTypes.Authorization,
"Missing authentication scheme!");
178 var parameter = String.Concat(splits);
179 if (String.IsNullOrEmpty(parameter))
180 AddError(
HeaderTypes.Authorization,
"Missing authentication parameter!");
183 if (requestHeaders.Headers.TryGetValue(InstanceIdHeader, out var instanceIdValues))
185 var instanceIdString = instanceIdValues.FirstOrDefault();
186 if (instanceIdString !=
default && Int64.TryParse(instanceIdString, out var instanceId))
187 InstanceId = instanceId;
190 #pragma warning disable CA1308 // Normalize strings to uppercase 191 switch (scheme.ToLowerInvariant())
192 #pragma warning restore CA1308
194 case JwtAuthenticationScheme:
197 case BasicAuthenticationScheme:
201 var base64Bytes = Convert.FromBase64String(parameter);
202 joinedString = Encoding.UTF8.GetString(base64Bytes);
206 throw new InvalidOperationException(
"Invalid basic Authorization header!");
209 var basicAuthSplits = joinedString.Split(
new char[] {
':' }, StringSplitOptions.RemoveEmptyEntries);
210 if (basicAuthSplits.Length < 2)
211 throw new InvalidOperationException(
"Invalid basic Authorization header!");
213 Username = basicAuthSplits.First();
214 Password = String.Concat(basicAuthSplits.Skip(1));
217 AddError(
HeaderTypes.Authorization,
"Invalid authentication scheme!");
227 ApiVersion = apiVersion!.Semver();
237 ApiHeaders(ProductHeaderValue userAgent,
string? token,
string? username,
string? password)
239 RawUserAgent = userAgent?.ToString();
243 ApiVersion = Version;
250 public bool Compatible() => CheckCompatibility(ApiVersion);
260 throw new ArgumentNullException(nameof(headers));
261 if (instanceId.HasValue && InstanceId.HasValue && instanceId != InstanceId)
262 throw new InvalidOperationException(
"Specified different instance IDs in constructor and SetRequestHeaders!");
265 headers.Accept.Add(
new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json));
266 if (IsTokenAuthentication)
267 headers.Authorization =
new AuthenticationHeaderValue(JwtAuthenticationScheme, Token);
269 headers.Authorization =
new AuthenticationHeaderValue(
270 BasicAuthenticationScheme,
271 Convert.ToBase64String(Encoding.UTF8.GetBytes($
"{Username}:{Password}")));
273 headers.UserAgent.Add(
new ProductInfoHeaderValue(UserAgent));
274 headers.Add(ApiVersionHeader,
new ProductHeaderValue(AssemblyName.Name, ApiVersion.ToString()).ToString());
275 instanceId ??= InstanceId;
276 if (instanceId.HasValue)
277 headers.Add(InstanceIdHeader, instanceId.ToString());
HeaderTypes
Types of individual ApiHeaders.
Microsoft.Net.Http.Headers.HeaderNames.UserAgent header.