Files: FileHandler

This commit is contained in:
pavelbannov 2020-04-13 14:46:26 +03:00
parent 61762afb13
commit ae0b217172
3 changed files with 139 additions and 66 deletions

View File

@ -34,10 +34,12 @@ using System.Threading;
using System.Threading.Tasks;
using System.Web;
using ASC.Common;
using ASC.Common.Logging;
using ASC.Common.Web;
using ASC.Core;
using ASC.Files.Core;
using ASC.Files.Core.Data;
using ASC.Files.Core.Security;
using ASC.Files.Resources;
using ASC.MessagingSystem;
@ -56,6 +58,7 @@ using ASC.Web.Studio.Utility;
using JWT;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
@ -69,14 +72,32 @@ using SecurityContext = ASC.Core.SecurityContext;
namespace ASC.Web.Files
{
public class FileHandler //: AbstractHttpAsyncHandler
public class FileHandler
{
public RequestDelegate Next { get; }
public IServiceProvider ServiceProvider { get; }
public FileHandler(RequestDelegate next, IServiceProvider serviceProvider)
{
Next = next;
ServiceProvider = serviceProvider;
}
public async Task Invoke(HttpContext context)
{
using var scope = ServiceProvider.CreateScope();
var fileHandlerService = scope.ServiceProvider.GetService<FileHandlerService>();
await fileHandlerService.Invoke(context);
await Next.Invoke(context);
}
}
public class FileHandlerService
{
public string FileHandlerPath
{
get { return FilesLinkUtility.FileHandlerPath; }
}
public RequestDelegate Next { get; }
public FilesLinkUtility FilesLinkUtility { get; }
public TenantExtra TenantExtra { get; }
public AuthContext AuthContext { get; }
@ -103,8 +124,7 @@ namespace ASC.Web.Files
public CookiesManager CookiesManager { get; }
public TenantStatisticsProvider TenantStatisticsProvider { get; }
public FileHandler(
RequestDelegate next,
public FileHandlerService(
FilesLinkUtility filesLinkUtility,
TenantExtra tenantExtra,
CookiesManager cookiesManager,
@ -130,7 +150,6 @@ namespace ASC.Web.Files
FFmpegService fFmpegService,
IServiceProvider serviceProvider)
{
Next = next;
FilesLinkUtility = filesLinkUtility;
TenantExtra = tenantExtra;
AuthContext = authContext;
@ -172,31 +191,31 @@ namespace ASC.Web.Files
{
case "view":
case "download":
DownloadFile(context);
await DownloadFile(context);
break;
case "bulk":
BulkDownloadFile(context);
await BulkDownloadFile(context);
break;
case "stream":
StreamFile(context);
await StreamFile(context);
break;
case "empty":
EmptyFile(context);
await EmptyFile(context);
break;
case "tmp":
TempFile(context);
await TempFile(context);
break;
case "create":
CreateFile(context);
await CreateFile(context);
break;
case "redirect":
Redirect(context);
break;
case "diff":
DifferenceFile(context);
await DifferenceFile(context);
break;
case "track":
TrackFile(context);
await TrackFile(context);
break;
default:
throw new HttpException((int)HttpStatusCode.BadRequest, FilesCommonResource.ErrorMassage_BadRequest);
@ -207,11 +226,9 @@ namespace ASC.Web.Files
{
throw new HttpException((int)HttpStatusCode.InternalServerError, FilesCommonResource.ErrorMassage_BadRequest, e);
}
await Next.Invoke(context);
}
private void BulkDownloadFile(HttpContext context)
private async Task BulkDownloadFile(HttpContext context)
{
if (!SecurityContext.AuthenticateMe(CookiesManager.GetCookies(CookiesType.AuthKey)))
{
@ -250,10 +267,10 @@ namespace ASC.Web.Files
readStream.Seek(offset, SeekOrigin.Begin);
}
SendStreamByChunks(context, length, FileConstant.DownloadTitle + ".zip", readStream, ref flushed);
flushed = await SendStreamByChunksAsync(context, length, FileConstant.DownloadTitle + ".zip", readStream, flushed);
}
context.Response.Body.Flush();
await context.Response.Body.FlushAsync();
//context.Response.SuppressContent = true;
//context.ApplicationInstance.CompleteRequest();
}
@ -264,21 +281,21 @@ namespace ASC.Web.Files
}
}
private void DownloadFile(HttpContext context)
private async Task DownloadFile(HttpContext context)
{
var q = context.Request.Query[FilesLinkUtility.FileId];
if (int.TryParse(q, out var id))
{
DownloadFile(context, id);
await DownloadFile(context, id);
}
else
{
DownloadFile(context, q);
await DownloadFile(context, q);
}
}
private void DownloadFile<T>(HttpContext context, T id)
private async Task DownloadFile<T>(HttpContext context, T id)
{
var flushed = false;
try
@ -415,7 +432,7 @@ namespace ASC.Web.Files
}
}
SendStreamByChunks(context, length, title, fileStream, ref flushed);
flushed = await SendStreamByChunksAsync(context, length, title, fileStream, flushed);
}
else
{
@ -436,7 +453,7 @@ namespace ASC.Web.Files
fileStream.Seek(offset, SeekOrigin.Begin);
}
SendStreamByChunks(context, length, title, fileStream, ref flushed);
flushed = await SendStreamByChunksAsync(context, length, title, fileStream, flushed);
}
}
catch (ThreadAbortException tae)
@ -453,13 +470,13 @@ namespace ASC.Web.Files
if (fileStream != null)
{
fileStream.Close();
fileStream.Dispose();
await fileStream.DisposeAsync();
}
}
try
{
context.Response.Body.Flush();
await context.Response.Body.FlushAsync();
//context.Response.SuppressContent = true;
//context.ApplicationInstance.CompleteRequest();
flushed = true;
@ -487,7 +504,7 @@ namespace ASC.Web.Files
if (!flushed && !context.RequestAborted.IsCancellationRequested)
{
context.Response.StatusCode = 400;
context.Response.WriteAsync(HttpUtility.HtmlEncode(ex.Message)).Wait();
await context.Response.WriteAsync(HttpUtility.HtmlEncode(ex.Message));
}
}
}
@ -525,7 +542,7 @@ namespace ASC.Web.Files
return length;
}
private void SendStreamByChunks(HttpContext context, long toRead, string title, Stream fileStream, ref bool flushed)
private async Task<bool> SendStreamByChunksAsync(HttpContext context, long toRead, string title, Stream fileStream, bool flushed)
{
//context.Response.Buffer = false;
context.Response.Headers.Add("Connection", "Keep-Alive");
@ -541,8 +558,8 @@ namespace ASC.Web.Files
if (!context.RequestAborted.IsCancellationRequested)
{
context.Response.Body.Write(buffer, 0, length);
context.Response.Body.Flush();
await context.Response.Body.WriteAsync(buffer, 0, length);
await context.Response.Body.FlushAsync();
flushed = true;
toRead -= length;
}
@ -552,23 +569,25 @@ namespace ASC.Web.Files
Logger.Warn(string.Format("IsClientConnected is false. Why? Download file {0} Connection is lost. ", title));
}
}
return flushed;
}
private void StreamFile(HttpContext context)
private async Task StreamFile(HttpContext context)
{
var q = context.Request.Query[FilesLinkUtility.FileId];
if (int.TryParse(q, out var id))
{
StreamFile(context, id);
await StreamFile(context, id);
}
else
{
StreamFile(context, q);
await StreamFile(context, q);
}
}
private void StreamFile<T>(HttpContext context, T id)
private async Task StreamFile<T>(HttpContext context, T id)
{
try
{
@ -593,7 +612,7 @@ namespace ASC.Web.Files
Logger.Error($"{FilesLinkUtility.AuthKey} {validateResult}: {context.Request.Url()}", exc);
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
context.Response.WriteAsync(FilesCommonResource.ErrorMassage_SecurityException).Wait();
await context.Response.WriteAsync(FilesCommonResource.ErrorMassage_SecurityException);
return;
}
@ -636,7 +655,7 @@ namespace ASC.Web.Files
{
Logger.Error("Download stream header " + context.Request.Url(), ex);
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
context.Response.WriteAsync(FilesCommonResource.ErrorMassage_SecurityException).Wait();
await context.Response.WriteAsync(FilesCommonResource.ErrorMassage_SecurityException);
return;
}
}
@ -664,7 +683,7 @@ namespace ASC.Web.Files
if (!string.IsNullOrEmpty(file.Error))
{
context.Response.WriteAsync(file.Error).Wait();
await context.Response.WriteAsync(file.Error);
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
return;
}
@ -685,13 +704,13 @@ namespace ASC.Web.Files
{
Logger.Error("Error for: " + context.Request.Url(), ex);
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.WriteAsync(ex.Message).Wait();
await context.Response.WriteAsync(ex.Message);
return;
}
try
{
context.Response.Body.Flush();
await context.Response.Body.FlushAsync();
//context.Response.SuppressContent = true;
//context.ApplicationInstance.CompleteRequest();
}
@ -701,7 +720,7 @@ namespace ASC.Web.Files
}
}
private void EmptyFile(HttpContext context)
private async Task EmptyFile(HttpContext context)
{
try
{
@ -745,7 +764,7 @@ namespace ASC.Web.Files
{
Logger.Error("Download stream header " + context.Request.Url(), ex);
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
context.Response.WriteAsync(FilesCommonResource.ErrorMassage_SecurityException).Wait();
await context.Response.WriteAsync(FilesCommonResource.ErrorMassage_SecurityException);
return;
}
}
@ -761,7 +780,7 @@ namespace ASC.Web.Files
if (!storeTemplate.IsFile("", path))
{
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
context.Response.WriteAsync(FilesCommonResource.ErrorMassage_FileNotFound).Wait();
await context.Response.WriteAsync(FilesCommonResource.ErrorMassage_FileNotFound);
return;
}
@ -779,13 +798,13 @@ namespace ASC.Web.Files
{
Logger.Error("Error for: " + context.Request.Url(), ex);
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.WriteAsync(ex.Message).Wait();
await context.Response.WriteAsync(ex.Message);
return;
}
try
{
context.Response.Body.Flush();
await context.Response.Body.FlushAsync();
//context.Response.SuppressContent = true;
//context.ApplicationInstance.CompleteRequest();
}
@ -795,7 +814,7 @@ namespace ASC.Web.Files
}
}
private void TempFile(HttpContext context)
private async Task TempFile(HttpContext context)
{
var fileName = context.Request.Query[FilesLinkUtility.FileTitle];
var auth = context.Request.Query[FilesLinkUtility.AuthKey].FirstOrDefault();
@ -808,7 +827,7 @@ namespace ASC.Web.Files
Logger.Error($"{FilesLinkUtility.AuthKey} {validateResult}: {context.Request.Url()}", exc);
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
context.Response.WriteAsync(FilesCommonResource.ErrorMassage_SecurityException);
await context.Response.WriteAsync(FilesCommonResource.ErrorMassage_SecurityException);
return;
}
@ -823,7 +842,7 @@ namespace ASC.Web.Files
if (!store.IsFile(FileConstant.StorageDomainTmp, path))
{
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
context.Response.WriteAsync(FilesCommonResource.ErrorMassage_FileNotFound).Wait();
await context.Response.WriteAsync(FilesCommonResource.ErrorMassage_FileNotFound);
return;
}
@ -837,7 +856,7 @@ namespace ASC.Web.Files
try
{
context.Response.Body.Flush();
await context.Response.Body.FlushAsync();
//context.Response.SuppressContent = true;
//context.ApplicationInstance.CompleteRequest();
}
@ -847,21 +866,21 @@ namespace ASC.Web.Files
}
}
private void DifferenceFile(HttpContext context)
private async Task DifferenceFile(HttpContext context)
{
var q = context.Request.Query[FilesLinkUtility.FileId];
if (int.TryParse(q, out var id))
{
DifferenceFile(context, id);
await DifferenceFile(context, id);
}
else
{
DifferenceFile(context, q);
await DifferenceFile(context, q);
}
}
private void DifferenceFile<T>(HttpContext context, T id)
private async Task DifferenceFile<T>(HttpContext context, T id)
{
try
{
@ -881,7 +900,7 @@ namespace ASC.Web.Files
Logger.Error($"{FilesLinkUtility.AuthKey} {validateResult}: {context.Request.Url()}", exc);
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
context.Response.WriteAsync(FilesCommonResource.ErrorMassage_SecurityException).Wait();
await context.Response.WriteAsync(FilesCommonResource.ErrorMassage_SecurityException);
return;
}
}
@ -910,7 +929,7 @@ namespace ASC.Web.Files
if (!string.IsNullOrEmpty(file.Error))
{
context.Response.WriteAsync(file.Error).Wait();
await context.Response.WriteAsync(file.Error);
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
return;
}
@ -927,14 +946,14 @@ namespace ASC.Web.Files
catch (Exception ex)
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.WriteAsync(ex.Message).Wait();
await context.Response.WriteAsync(ex.Message);
Logger.Error("Error for: " + context.Request.Url(), ex);
return;
}
try
{
context.Response.Body.Flush();
await context.Response.Body.FlushAsync();
//context.Response.SuppressContent = true;
//context.ApplicationInstance.CompleteRequest();
}
@ -949,20 +968,20 @@ namespace ASC.Web.Files
return file.ID + ":" + file.Version + ":" + file.Title.GetHashCode() + ":" + file.ContentLength;
}
private void CreateFile(HttpContext context)
private async Task CreateFile(HttpContext context)
{
var folderId = context.Request.Query[FilesLinkUtility.FolderId].FirstOrDefault();
if (string.IsNullOrEmpty(folderId))
{
CreateFile(context, GlobalFolderHelper.FolderMy);
await CreateFile(context, GlobalFolderHelper.FolderMy);
}
else
{
CreateFile(context, folderId);
await CreateFile(context, folderId);
}
}
private void CreateFile<T>(HttpContext context, T folderId)
private async Task CreateFile<T>(HttpContext context, T folderId)
{
var responseMessage = context.Request.Query["response"] == "message";
Folder<T> folder;
@ -993,7 +1012,7 @@ namespace ASC.Web.Files
Logger.Error(ex);
if (responseMessage)
{
context.Response.WriteAsync("error: " + ex.Message).Wait();
await context.Response.WriteAsync("error: " + ex.Message);
return;
}
context.Response.Redirect(PathProvider.StartURL + "#error/" + HttpUtility.UrlEncode(ex.Message), true);
@ -1004,7 +1023,7 @@ namespace ASC.Web.Files
if (responseMessage)
{
context.Response.WriteAsync("ok: " + string.Format(FilesCommonResource.MessageFileCreated, folder.Title)).Wait();
await context.Response.WriteAsync("ok: " + string.Format(FilesCommonResource.MessageFileCreated, folder.Title));
return;
}
@ -1134,7 +1153,7 @@ namespace ASC.Web.Files
context.Response.Redirect(urlRedirect);
}
private void TrackFile(HttpContext context)
private async Task TrackFile(HttpContext context)
{
var auth = context.Request.Query[FilesLinkUtility.AuthKey].FirstOrDefault();
var fileId = context.Request.Query[FilesLinkUtility.FileId].FirstOrDefault();
@ -1240,7 +1259,43 @@ namespace ASC.Web.Files
}
result ??= new DocumentServiceTracker.TrackResponse();
context.Response.WriteAsync(DocumentServiceTracker.TrackResponse.Serialize(result)).Wait();
await context.Response.WriteAsync(DocumentServiceTracker.TrackResponse.Serialize(result));
}
}
public static class FileHandlerExtensions
{
public static DIHelper AddFileHandlerService(this DIHelper services)
{
services.TryAddScoped<FileHandlerService>();
return services
.AddFilesLinkUtilityService()
.AddTenantExtraService()
.AddCookiesManagerService()
.AddAuthContextService()
.AddSecurityContextService()
.AddGlobalStoreService()
.AddDaoFactoryService()
.AddFileSecurityService()
.AddFileMarkerService()
.AddSetupInfo()
.AddFileUtilityService()
.AddGlobalService()
.AddEmailValidationKeyProviderService()
.AddCoreBaseSettingsService()
.AddGlobalFolderHelperService()
.AddPathProviderService()
.AddUserManagerService()
.AddDocumentServiceTrackerHelperService()
.AddFilesMessageService()
.AddFileConverterService()
.AddFileShareLinkService()
.AddFFmpegServiceService();
}
public static IApplicationBuilder UseFileHandler(this IApplicationBuilder builder)
{
return builder.UseMiddleware<FileHandler>();
}
}
}

View File

@ -6,6 +6,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ASC.Common;
using ASC.Common.Logging;
using ASC.Core;
@ -169,4 +170,12 @@ namespace ASC.Web.Files.Services.FFmpegService
}
}
}
public static class FFmpegServiceExtensions
{
public static DIHelper AddFFmpegServiceService(this DIHelper services)
{
services.TryAddSingleton<FFmpegService>();
return services;
}
}
}

View File

@ -8,6 +8,7 @@ using ASC.Api.Documents;
using ASC.Common;
using ASC.Common.DependencyInjection;
using ASC.Common.Logging;
using ASC.Web.Files;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
@ -81,7 +82,8 @@ namespace ASC.Files
diHelper
.AddDocumentsControllerService()
.AddEncryptionControllerService();
.AddEncryptionControllerService()
.AddFileHandlerService();
services.AddAutofac(Configuration, HostEnvironment.ContentRootPath);
}
@ -120,6 +122,13 @@ namespace ASC.Files
endpoints.MapCustom();
});
app.MapWhen(
context => context.Request.Path.ToString().EndsWith("httphandlers/filehandler.ashx"),
appBranch =>
{
appBranch.UseFileHandler();
});
app.UseStaticFiles();
}
}