public async Task Render(HttpRequest request, HttpResponse response, APEntity toRender) { response.ContentType = ConverterHelpers.GetBestMatch(_factory.MimeTypes, request.Headers["Accept"]); if (toRender.Type.Contains("Tombstone")) { response.StatusCode = 410; } if (request.Method == "POST") { response.StatusCode = 201; response.Headers.Add("Location", toRender.Id); } var depth = Math.Min(int.Parse(request.Query["depth"].FirstOrDefault() ?? "3"), 5); var unflattened = await _flattener.Unflatten(_entityStore, toRender, depth, isOwner : toRender.IsOwner); await response.WriteAsync(unflattened.Serialize(true).ToString()); }
public async Task Invoke(HttpContext context, IServiceProvider serviceProvider, EntityData entityData, IEntityStore store) { var handler = ActivatorUtilities.CreateInstance <GetEntityHandler>(serviceProvider, context.User); if (entityData.RewriteRequestScheme) { context.Request.Scheme = "https"; } var fullpath = $"{context.Request.Scheme}://{context.Request.Host}{context.Request.Path}"; foreach (var converterFactory in _converters) { if (converterFactory.FileExtension != null && fullpath.EndsWith("." + converterFactory.FileExtension)) { fullpath = fullpath.Substring(0, fullpath.Length - 1 - converterFactory.FileExtension.Length); context.Request.Headers.Remove("Accept"); context.Request.Headers.Add("Accept", converterFactory.RenderMimeType); break; } } /* && ConverterHelpers.GetBestMatch(_converters[0].MimeTypes, context.Request.Headers["Accept"]) != null */ if (context.Request.Method == "OPTIONS") { context.Response.StatusCode = 200; context.Response.Headers.Add("Access-Control-Allow-Credentials", "true"); context.Response.Headers.Add("Access-Control-Allow-Headers", "Authorization"); context.Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST"); context.Response.Headers.Add("Access-Control-Allow-Origin", context.Request.Headers["Origin"]); context.Response.Headers.Add("Vary", "Origin"); return; } Console.WriteLine(fullpath); foreach (var line in context.Request.Headers["Accept"]) { Console.WriteLine($"---- {line}"); } if (context.Request.Headers["Accept"].Contains("text/event-stream")) { await handler.EventStream(context, fullpath); return; } if (context.WebSockets.IsWebSocketRequest) { await handler.WebSocket(context, fullpath); return; } if (context.Request.Method == "POST" && context.Request.ContentType.StartsWith("multipart/form-data")) { context.Items.Add("fullPath", fullpath); context.Request.Path = "/settings/uploadMedia"; await _next(context); return; } if (context.Request.QueryString.Value == "?hub") { context.Items.Add("fullPath", fullpath); context.Request.Path = "/.well-known/hub"; context.Request.QueryString = QueryString.Empty; await _next(context); return; } IConverter readConverter = null; IConverter writeConverter = null; bool needRead = context.Request.Method == "POST"; var target = fullpath; APEntity targetEntity = null; targetEntity = await store.GetEntity(target, false); if (needRead) { if (targetEntity?.Type == "_inbox") { target = (string)targetEntity.Data["attributedTo"].Single().Primitive; } } if (targetEntity == null) { await _next(context); return; } foreach (var converterFactory in _converters) { bool worksForWrite = converterFactory.CanRender && ConverterHelpers.GetBestMatch(converterFactory.MimeTypes, context.Request.Headers["Accept"]) != null; bool worksForRead = needRead && converterFactory.CanParse && ConverterHelpers.GetBestMatch(converterFactory.MimeTypes, context.Request.ContentType) != null; if (worksForRead && worksForWrite && readConverter == null && writeConverter == null) { readConverter = writeConverter = converterFactory.Build(serviceProvider, target); break; } if (worksForRead && readConverter == null) { readConverter = converterFactory.Build(serviceProvider, target); } if (worksForWrite && writeConverter == null) { writeConverter = converterFactory.Build(serviceProvider, target); } } ASObject data = null; if (readConverter != null) { data = await readConverter.Parse(context.Request.Body); } if (needRead && readConverter != null && writeConverter == null) { writeConverter = readConverter; } if (data == null && needRead && targetEntity != null) { context.Response.StatusCode = 415; await context.Response.WriteAsync("Unknown mime type " + context.Request.ContentType); return; } var arguments = context.Request.Query; try { if (context.Request.Method == "GET" || context.Request.Method == "HEAD" || context.Request.Method == "OPTIONS") { data = await handler.Get(fullpath, arguments, context); } else if (context.Request.Method == "POST" && data != null) { data = await handler.Post(context, fullpath, data); } } catch (UnauthorizedAccessException e) { context.Response.StatusCode = 403; await context.Response.WriteAsync(e.Message); } catch (InvalidOperationException e) { context.Response.StatusCode = 401; await context.Response.WriteAsync(e.Message); } if (context.Response.HasStarted) { return; } if (data != null) { if (context.Request.Method == "HEAD") { context.Response.StatusCode = 200; return; } if (writeConverter != null) { await writeConverter.Render(context.Request, context.Response, data); } else if (context.Request.ContentType == "application/magic-envelope+xml") { context.Response.StatusCode = 202; await context.Response.WriteAsync("accepted"); } else { context.Request.Method = "GET"; context.Request.Path = "/render"; context.Items["object"] = APEntity.From(data); await _next(context); } return; } if (!context.Response.HasStarted) { await _next(context); } }
public async Task <APEntity> GetEntity(string id, bool doRemote) { if (id == "https://www.w3.org/ns/activitystreams#Public") { var aso = new ASObject(); aso.Type.Add("https://www.w3.org/ns/activitystreams#Collection"); aso.Id = "https://www.w3.org/ns/activitystreams#Public"; var ent = APEntity.From(aso); return(ent); } try { if ((new Uri(id)).Host == "localhost") { return(await Bypass.GetEntity(id, doRemote)); } } catch (UriFormatException) { /* nom */ } APEntity entity = null; if (Bypass != null) { entity = await Bypass.GetEntity(id, doRemote); } if (entity != null && !entity.IsOwner && entity.Data.Type.Any(_collections.Contains) && doRemote) { entity = null; } if (entity?.Type == "_:LazyLoad" && !doRemote) { return(null); } if ((entity != null && entity.Type != "_:LazyLoad") || !doRemote) { return(entity); } var loadUrl = id; if (entity?.Type == "_:LazyLoad") { loadUrl = (string)entity.Data["href"].First().Primitive; } if (loadUrl.StartsWith("tag:")) { return(null); } var htc = new HttpClient(); htc.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "application/activity+json; application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\", application/json, application/atom+xml, text/html"); if (_context != null) { var signatureVerifier = _serviceProvider.GetRequiredService <SignatureVerifier>(); var user = await Bypass.GetEntity(_context.User.FindFirstValue(JwtTokenSettings.ActorClaim), false); if (user != null) { var jwt = await signatureVerifier.BuildJWS(user, id); if (jwt != null) { htc.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", jwt); } } } HttpResponseMessage response = null; try { response = await htc.GetAsync(loadUrl); if (!response.IsSuccessStatusCode) { response = await htc.GetAsync(loadUrl + ".atom"); // hack! if (!response.IsSuccessStatusCode) { return(null); } } } catch (TaskCanceledException) { return(null); // timeout } catch (HttpRequestException) { return(null); } var converters = new List <IConverterFactory> { new AS2ConverterFactory(), new AtomConverterFactory(false) }; tryAgain: ASObject data = null; foreach (var converter in converters) { if (converter.CanParse && ConverterHelpers.GetBestMatch(converter.MimeTypes, response.Content.Headers.ContentType.ToString()) != null) { data = await converter.Build(_serviceProvider, null).Parse(await response.Content.ReadAsStreamAsync()); break; } } if (data == null) { if (response.Headers.Contains("Link")) { var split = string.Join(", ", response.Headers.GetValues("Link")).Split(new[] { ", " }, StringSplitOptions.RemoveEmptyEntries); foreach (var spl in split) { var args = spl.Split(';').Select(a => a.Trim()).ToArray(); if (args.Contains("rel=\"alternate\"") && args.Contains("type=\"application/atom+xml\"")) { response = await htc.GetAsync(args[0].Substring(1, args[0].Length - 2)); goto tryAgain; } } } try { var links = (await response.Content.ReadAsStringAsync()).Split('\n'); var alt = links.FirstOrDefault(a => a.Contains("application/atom+xml") && a.Contains("alternate")); if (alt == null) { return(null); } var l = XDocument.Parse(alt + "</link>").Root; if (l.Attribute(XNamespace.None + "type")?.Value == "application/atom+xml") { response = await htc.GetAsync(l.Attribute(XNamespace.None + "href")?.Value); goto tryAgain; } } catch (Exception) { } return(null); } // forces out the old lazy load, if used await _entityFlattener.FlattenAndStore(Bypass, data, false); return(await Bypass.GetEntity(id, true)); }
public async Task <APEntity> GetEntity(string id, bool doRemote) { if (id == "https://www.w3.org/ns/activitystreams#Public") { var aso = new ASObject(); aso.Type.Add("https://www.w3.org/ns/activitystreams#Collection"); aso.Id = "https://www.w3.org/ns/activitystreams#Public"; var ent = APEntity.From(aso); return(ent); } string origin = id; try { var uri = new Uri(id); if (uri.Host == "localhost") { return(await Bypass.GetEntity(id, doRemote)); } origin = uri.GetLeftPart(UriPartial.Authority); } catch (UriFormatException) { /* nom */ } APEntity entity = null; if (Bypass != null) { entity = await Bypass.GetEntity(id, doRemote); } /* if (entity == null) * { * var possibilities = (await _getRE().Query(new RelevantEntitiesService.ContainsAnyStatement("https://www.w3.org/ns/activitystreams#url") { id })).Where(a => Uri.IsWellFormedUriString(a.Id, UriKind.Absolute) && (new Uri(a.Id)).GetLeftPart(UriPartial.Authority) == origin).ToList(); * if (possibilities.Count == 1) entity = possibilities.First(); * }*/ if (entity != null && !entity.IsOwner && entity.Data.Type.Any(_collections.Contains) && doRemote) { entity = null; } if (entity != null || !doRemote) { return(entity); } var htc = new HttpClient(); var request = new HttpRequestMessage(HttpMethod.Get, id); request.Headers.TryAddWithoutValidation("Accept", "application/activity+json; application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\", application/json, text/html"); if (_context != null) { var signatureVerifier = _serviceProvider.GetRequiredService <SignatureVerifier>(); var user = await Bypass.GetEntity(_context.User.FindFirstValue("actor"), false); if (user != null) { var jwt = await signatureVerifier.BuildJWS(user, id); if (jwt != null) { request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", jwt); } request.Headers.TryAddWithoutValidation("Signature", await _keyService.BuildHTTPSignature(user.Id, request)); } } HttpResponseMessage response = null; try { response = await htc.SendAsync(request); } catch (TaskCanceledException) { return(null); // timeout } catch (HttpRequestException) { return(null); } ASObject data = null; await response.Content.LoadIntoBufferAsync(); foreach (var converter in ServerConfig.Converters) { if (converter.CanParse && ConverterHelpers.GetBestMatch(converter.MimeTypes, response.Content.Headers.ContentType.ToString()) != null) { try { data = await converter.Build(_serviceProvider, null).Parse(await response.Content.ReadAsStreamAsync()); break; } catch (NullReferenceException e) { Console.WriteLine(e); /* nom */ } } } if (data == null) { return(null); } // forces out the old lazy load, if used await _entityFlattener.FlattenAndStore(Bypass, data, false); return(await Bypass.GetEntity(id, true)); }