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());
        }
Exemple #2
0
        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());
        }
Exemple #3
0
        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;
                    }
                }
            }