protected override async Task <FileMetadata> GetFileMetadataImplAsync(string path, FileMetadataGetFlags flags = FileMetadataGetFlags.DefaultAll, CancellationToken cancel = default) { FileMetadata physicalMetadata = await UnderlayFileSystem.GetFileMetadataAsync(path, flags, cancel); try { long headerOffset = 0; long physicalSize = physicalMetadata.Size; using (FileObject physicalFile = await UnderlayFileSystem.OpenAsync(path)) { byte[] bomRead = new byte[3]; Memory <byte> tmp = new byte[3]; if (await physicalFile.ReadRandomAsync(0, tmp, cancel) == tmp.Length) { if (tmp.Span.SequenceEqual(Str.BOM_UTF_8.Span)) { headerOffset = 3; } } physicalSize = await physicalFile.GetFileSizeAsync(true, cancel) - headerOffset; if (physicalSize >= 0) { physicalMetadata.Size = physicalSize; } } } catch { } return(physicalMetadata); }
public async Task <HttpResult> ProcessRequestAsync(IPAddress clientIpAddress, string requestPathAndQueryString, CancellationToken cancel = default) { clientIpAddress = clientIpAddress._UnmapIPv4(); try { // クライアント IP による ACL のチェック if (Options.ClientIpAcl(clientIpAddress) == false) { // ACL error return(new HttpStringResult("403 Forbidden", statusCode: 403)); } // URL のチェック requestPathAndQueryString._ParseUrl(out Uri uri, out QueryStringList qsList); string relativePath; if (this.AbsolutePathPrefix._IsFilled()) { // AbsolutePathPrefix の検査 if (uri.AbsolutePath._TryTrimStartWith(out relativePath, StringComparison.OrdinalIgnoreCase, this.AbsolutePathPrefix) == false) { // Not found return(new HttpStringResult("404 Not Found", statusCode: 404)); } } else { relativePath = uri.AbsolutePath; } if (relativePath.StartsWith("/") == false) { relativePath = "/" + relativePath; } relativePath = PathParser.Linux.NormalizeUnixStylePathWithRemovingRelativeDirectoryElements(relativePath); if (RootFs.IsDirectoryExists(relativePath, cancel)) { // Directory string htmlBody = BuildDirectoryHtml(new DirectoryPath(relativePath, RootFs)); return(new HttpStringResult(htmlBody, contentType: Consts.MimeTypes.HtmlUtf8)); } else if (RootFs.IsFileExists(relativePath, cancel)) { // File string extension = RootFs.PathParser.GetExtension(relativePath); string mimeType = MasterData.ExtensionToMime.Get(extension); FileObject file = await RootFs.OpenAsync(relativePath, cancel : cancel); try { long fileSize = file.Size; long head = qsList._GetStrFirst("head")._ToInt()._NonNegative(); long tail = qsList._GetStrFirst("tail")._ToInt()._NonNegative(); if (head != 0 && tail != 0) { throw new ApplicationException("You can specify either head or tail."); } head = head._Min(fileSize); tail = tail._Min(fileSize); long readStart = 0; long readSize = fileSize; if (head != 0) { readStart = 0; readSize = head; } else if (tail != 0) { readStart = fileSize - tail; readSize = tail; } if (tail != 0) { mimeType = Consts.MimeTypes.Text; } byte[] preData = new byte[0]; if (readSize != 0 && fileSize >= 3) { try { // 元のファイルの先頭に BOM が付いていて、先頭をスキップする場合は、 // 応答データに先頭にも BOM を付ける byte[] bom = new byte[3]; if (await file.ReadRandomAsync(0, bom, cancel) == 3) { if (Str.BOM_UTF_8._MemEquals(bom)) { preData = bom; } } } catch { } } return(new HttpFileResult(file, readStart, readSize, mimeType, preData: preData)); } catch { file._DisposeSafe(); throw; } } else { // Not found return(new HttpStringResult("404 Not Found", statusCode: 404)); } } catch (Exception ex) { return(new HttpStringResult($"HTTP Status Code: 500\r\n" + ex.ToString(), statusCode: 500)); } }