Skip to content

Commit

Permalink
Add Profile field support to HTTP ContentType (resolves warriordog#127)
Browse files Browse the repository at this point in the history
  • Loading branch information
puzzler995 committed Jan 20, 2024
1 parent 485ff03 commit e39b207
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 14 deletions.
24 changes: 19 additions & 5 deletions Source/ActivityPub.Client/ActivityPubClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ public ActivityPubClient(ActivityPubOptions apOptions, IJsonLdSerializer jsonLdS
// https://stackoverflow.com/questions/47176104/c-sharp-add-accept-header-to-httpclient
foreach (var mimeType in apOptions.RequestContentTypes)
{
var mediaType = new MediaTypeWithQualityHeaderValue(mimeType);
var mediaType = new MediaTypeWithQualityHeaderValue(mimeType.MediaType);
if (mimeType.Profile != "") {
mediaType.Parameters.Add(new NameValueHeaderValue("profile", mimeType.Profile));
}
_httpClient.DefaultRequestHeaders.Accept.Add(mediaType);
}
}
Expand Down Expand Up @@ -83,9 +86,9 @@ private async Task<ASType> Get(Uri uri, Type targetType, int? maxRecursion, Canc
if (!resp.IsSuccessStatusCode)
throw new ApplicationException($"Request failed: got status {resp.StatusCode}");

var mediaType = resp.Content.Headers.ContentType?.MediaType;
if (mediaType == null || !_apOptions.ResponseContentTypes.Contains(mediaType))
throw new ApplicationException($"Request failed: unsupported content type {mediaType}");
var contentType = resp.Content.Headers.ContentType;
if (contentType == null || !_apOptions.ResponseContentTypes.Any(expectedContentType => IsContentTypeMatch(contentType, expectedContentType)))
throw new ApplicationException($"Request failed: unsupported content type {contentType?.MediaType}");

var json = await resp.Content.ReadAsStringAsync(cancellationToken);
var jsonObj = _jsonLdSerializer.Deserialize(json, targetType);
Expand All @@ -100,6 +103,17 @@ private async Task<ASType> Get(Uri uri, Type targetType, int? maxRecursion, Canc
return obj;
}

private static bool IsContentTypeMatch(MediaTypeHeaderValue actual, ActivityPubOptions.ContentType expected) {
if (!expected.MediaType.Equals(actual.MediaType)) {
return false;
}

var actualProfile = actual.Parameters.FirstOrDefault(p => p.Name.Equals("profile"))?.Value ?? "";

return string.Equals(actualProfile, expected.Profile,
StringComparison.OrdinalIgnoreCase);
}


#region Dispose

Expand Down Expand Up @@ -130,4 +144,4 @@ protected virtual void Dispose(bool disposing)
private bool _disposed;

#endregion
}
}
27 changes: 18 additions & 9 deletions Source/ActivityPub.Common/Util/ActivityPubOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,31 @@ public class ActivityPubOptions
/// This maps to the Content-Type header.
/// </summary>
/// <seealso href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type" />
public HashSet<string> ResponseContentTypes { get; set; } =
public HashSet<ContentType> ResponseContentTypes { get; set; } =
[
"application/activity+json",
"application/ld+json",
"application/json"
new ContentType("application/ld+json", "https://www.w3.org/ns/activitystreams"),
new ContentType("application/activity+json", ""),
new ContentType("application/ld+json", ""),
new ContentType("application/json", "")
];

/// <summary>
/// Content types to request from remote servers, in priority order.
/// This maps to the Accept header.
/// </summary>
/// <seealso href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept" />
public List<string> RequestContentTypes { get; set; } =
public List<ContentType> RequestContentTypes { get; set; } =
[
"application/activity+json",
"application/ld+json",
"application/json"
new ContentType("application/ld+json", "https://www.w3.org/ns/activitystreams"),
new ContentType("application/activity+json", ""),
new ContentType("application/ld+json", ""),
new ContentType("application/json", "")
];
}

/// <summary>
/// A Record that contains the MediaType and Profile for the HTTP Content-Type and Accept Headers
/// </summary>
/// <param name="MediaType">A MIME type for the header</param>
/// <param name="Profile">A profile to append to the MIME type, leave empty for none</param>
public record struct ContentType(string MediaType, string Profile);
}

0 comments on commit e39b207

Please sign in to comment.