public async Task <ActionResult> StripeCallbackAsync() { Event stripeEvent; try { stripeEvent = EventUtility.ConstructEvent( await new StreamReader(HttpContext.Request.Body).ReadToEndAsync(), Request.Headers["Stripe-Signature"], _stripeOptions.CurrentValue.WebhookSecret, throwOnApiVersionMismatch: false); } catch (StripeException) { // event construction failed, so request wasn't valid return(ResultUtilities.BadRequest("You're not stripe, are you?")); } switch (stripeEvent.Type) { case Events.CheckoutSessionCompleted when stripeEvent.Data.Object is Session session: await _stripe.HandleSupporterCheckoutCompletedAsync(session); break; default: return(ResultUtilities.BadRequest($"Stripe event {stripeEvent.Type} should not be received by this server.")); } return(Ok()); }
public async Task <ActionResult <DownloadSession> > CreateSessionAsync(CreateSessionRequest request) { var __ = request; var result = await _downloads.CreateSessionAsync(UserId); if (!result.TryPickT0(out var session, out _)) { return(ResultUtilities.BadRequest("Maximum number of download sessions has been reached.")); } return(session.Convert()); }
public override async Task ExecuteResultAsync(ActionContext context) { var cancellationToken = context.HttpContext.RequestAborted; var storage = context.HttpContext.RequestServices.GetService <IStorage>(); var downloads = context.HttpContext.RequestServices.GetService <IDownloadService>(); var memoryManager = context.HttpContext.RequestServices.GetService <RecyclableMemoryStreamManager>(); var sessionId = SessionId; var resourceContext = null as IAsyncDisposable; if (sessionId != null) { var sessionResult = await downloads.GetResourceContextAsync(sessionId, cancellationToken); if (!sessionResult.TryPickT0(out resourceContext, out var sessionError)) { await sessionError.Match( _ => ResultUtilities.NotFound($"Session '{sessionId}' not found."), _ => ResultUtilities.BadRequest("Cannot exceed download concurrency limit.")).ExecuteResultAsync(context); return; } } await using var __ = resourceContext; var result = await storage.ReadAsync(Name, cancellationToken); determineResult: var source = nameof(Storage); // file is already saved in storage if (result.TryPickT0(out var file, out var error)) { try { await using (file) { StorageFileResult.SetHeaders(context, file, CacheControlMode.Aggressive); var response = context.HttpContext.Response; // write to response stream using (_responseTime.Labels(source).Measure()) { await file.Stream.CopyToAsync(response.Body, cancellationToken); await response.CompleteAsync(); } } } catch { _errors.Labels(source).Inc(); throw; } } // file is not found else if (error.TryPickT0(out _, out var exception)) { var locker = context.HttpContext.RequestServices.GetService <IResourceLocker>(); // prevent concurrent downloads await using (await locker.EnterAsync($"scraper:image:{Name}", cancellationToken)) { // file may have been saved to storage while we were awaiting lock result = await storage.ReadAsync(Name, cancellationToken); if (result.TryPickT0(out file, out error) || !error.TryPickT0(out _, out exception)) // found, or error { goto determineResult; } // retrieve image using scraper var(scraper, fileTask) = GetImageAsync(context); source = scraper.Type.ToString(); try { using (_retrieveTime.Labels(source).Measure()) file = await fileTask; if (file == null) { await ResultUtilities.NotFound(Name).ExecuteResultAsync(context); return; } await using (file) { StorageFileResult.SetHeaders(context, file, CacheControlMode.Aggressive); // pipe data to response stream while buffering in memory // buffering is required because certain storage implementations (s3) need to know stream length in advance await using var memory = memoryManager.GetStream(); var response = context.HttpContext.Response.BodyWriter; var buffer = _bufferPool.Rent(_bufferSize); try { var resultSize = _resultSize.Labels(source); using (_responseTime.Labels(source).Measure()) { while (true) { var read = await file.Stream.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None); if (read == 0) { await response.CompleteAsync(); break; } resultSize.Inc(read); memory.Write(buffer, 0, read); await response.WriteAsync(((ReadOnlyMemory <byte>)buffer).Slice(0, read), CancellationToken.None); } } } finally { _bufferPool.Return(buffer); await file.DisposeAsync(); // opportunistic dispose if (resourceContext != null) { await resourceContext.DisposeAsync(); // background storage upload outside session concurrency } } // save to storage await storage.WriteAsync(new StorageFile { Name = Name, MediaType = file.MediaType, Stream = memory }, CancellationToken.None); } } catch { _errors.Labels(source).Inc(); throw; } } }