public static Handle ( |
||
endpointRegistration | ||
info | IRequestInfo | |
collectionSettings | ||
currentBook | Book | |
리턴 | bool |
// Every path should return false or send a response. // Otherwise we can get a timeout error as the browser waits for a response. // // NOTE: this method gets called on different threads! protected override bool ProcessRequest(IRequestInfo info) { var localPath = GetLocalPathWithoutQuery(info); //enhance: something feeds back these branding logos with a weird URL, that shouldn't be. if (localPath.IndexOf("api/branding") > 20) // this 20 is just arbitrary... the point is, if it doesn't start with api/branding, it is bogus { return(false); } if (localPath.ToLower().StartsWith("api/")) { var endpoint = localPath.Substring(3).ToLowerInvariant().Trim(new char[] { '/' }); foreach (var pair in _endpointRegistrations.Where(pair => Regex.Match(endpoint, "^" + //must match the beginning pair.Key.ToLower() ).Success)) { // A single synchronization object won't do, because when processing a request to create a thumbnail, // we have to load the HTML page the thumbnail is based on. If the page content somehow includes // an api request (api/branding/image is one example), that request will deadlock if the // api/pageTemplateThumbnail request already has the main lock. // To the best of my knowledge, there's no shared data between the thumbnailing process and any // other api requests, so it seems safe to have one lock that prevents working on multiple // thumbnails at the same time, and one that prevents working on other api requests at the same time. var syncOn = SyncObj; if (localPath.ToLowerInvariant().StartsWith("api/pagetemplatethumbnail")) { syncOn = ThumbnailSyncObj; } lock (syncOn) { return(ApiRequest.Handle(pair.Value, info, CurrentCollectionSettings, _bookSelection.CurrentSelection)); } } } //OK, no more obvious simple API requests, dive into the rat's nest of other possibilities if (base.ProcessRequest(info)) { return(true); } if (localPath.Contains("CURRENTPAGE")) //useful when debugging. E.g. http://localhost:8091/bloom/CURRENTPAGE.htm will always show the page we're on. { localPath = _keyToCurrentPage; } string content; bool gotSimulatedPage; lock (_urlToSimulatedPageContent) { gotSimulatedPage = _urlToSimulatedPageContent.TryGetValue(localPath, out content); } if (gotSimulatedPage) { info.ContentType = "text/html"; info.WriteCompleteOutput(content ?? ""); return(true); } if (localPath.StartsWith(OriginalImageMarker) && IsImageTypeThatCanBeDegraded(localPath)) { // Path relative to simulated page file, and we want the file contents without modification. // (Note that the simulated page file's own URL starts with this, so it's important to check // for that BEFORE we do this check.) localPath = localPath.Substring(OriginalImageMarker.Length + 1); return(ProcessAnyFileContent(info, localPath)); } if (localPath.StartsWith("error", StringComparison.InvariantCulture)) { ProcessError(info); return(true); } else if (localPath.StartsWith("i18n/", StringComparison.InvariantCulture)) { if (ProcessI18N(localPath, info)) { return(true); } } else if (localPath.StartsWith("directoryWatcher/", StringComparison.InvariantCulture)) { return(ProcessDirectoryWatcher(info)); } else if (localPath.StartsWith("localhost/", StringComparison.InvariantCulture)) { var temp = LocalHostPathToFilePath(localPath); if (RobustFile.Exists(temp)) { localPath = temp; } } // this is used only by the readium viewer else if (localPath.StartsWith("node_modules/jquery/dist/jquery.js")) { localPath = BloomFileLocator.GetBrowserFile("jquery.min.js"); // Avoid having "output/browser/" removed on Linux developer machines. // GetBrowserFile adds output to the path on developer machines, but not user installs. return(ProcessContent(info, localPath)); } //Firefox debugger, looking for a source map, was prefixing in this unexpected //way. localPath = localPath.Replace("output/browser/", ""); return(ProcessContent(info, localPath)); }
// Every path should return false or send a response. // Otherwise we can get a timeout error as the browser waits for a response. // // NOTE: this method gets called on different threads! public bool ProcessRequest(IRequestInfo info, string localPath) { var localPathLc = localPath.ToLowerInvariant(); if (localPathLc.StartsWith("api/", StringComparison.InvariantCulture)) { var endpointPath = localPath.Substring(3).ToLowerInvariant().Trim(new char[] { '/' }); foreach (var pair in _endpointRegistrations.Where(pair => Regex.Match(endpointPath, "^" + //must match the beginning pair.Key.ToLower() ).Success)) { var endpointRegistration = pair.Value; if (endpointRegistration.RequiresSync) { // A single synchronization object won't do, because when processing a request to create a thumbnail or update a preview, // we have to load the HTML page the thumbnail is based on, or other HTML pages (like one used to figure what's // visible in a preview). If the page content somehow includes // an api request (api/branding/image is one example), that request will deadlock if the // api/pageTemplateThumbnail request already has the main lock. // Another case is the Bloom Reader preview, where the whole UI is rebuilt at the same time as the preview. // This leads to multiple api requests racing with the preview one, and it was possible for all // the server threads to be processing these and waiting for SyncObject while the updatePreview // request held the lock...and the request for the page that would free the lock was sitting in // the queue, waiting for a thread. // To the best of my knowledge, there's no shared data between the thumbnailing and preview processes and any // other api requests, so it seems safe to have one lock that prevents working on multiple // thumbnails/previews at the same time, and one that prevents working on other api requests at the same time. var syncOn = SyncObj; if (localPathLc.StartsWith("api/pagetemplatethumbnail", StringComparison.InvariantCulture) || localPathLc == "api/publish/android/thumbnail" || localPathLc == "api/publish/android/updatepreview") { syncOn = ThumbnailsAndPreviewsSyncObj; } else if (localPathLc.StartsWith("api/i18n/")) { syncOn = I18NLock; } // Basically what lock(syncObj) {} is syntactic sugar for (see its documentation), // but we wrap RegisterThreadBlocking/Unblocked around acquiring the lock. // We need the more complicated structure because we would like RegisterThreadUnblocked // to be called immediately after acquiring the lock (notably, before Handle() is called), // but we also want to handle the case where Monitor.Enter throws an exception. bool lockAcquired = false; try { // Try to acquire lock BloomServer._theOneInstance.RegisterThreadBlocking(); try { // Blocks until it either succeeds (lockAcquired will then always be true) or throws (lockAcquired will stay false) Monitor.Enter(syncOn, ref lockAcquired); } finally { BloomServer._theOneInstance.RegisterThreadUnblocked(); } // Lock has been acquired. ApiRequest.Handle(endpointRegistration, info, CurrentCollectionSettings, _bookSelection.CurrentSelection); // Even if ApiRequest.Handle() fails, return true to indicate that the request was processed and there // is no further need for the caller to continue trying to process the request as a filename. // See https://issues.bloomlibrary.org/youtrack/issue/BL-6763. return(true); } finally { if (lockAcquired) { Monitor.Exit(syncOn); } } } else { // Up to api's that request no sync to do things right! ApiRequest.Handle(endpointRegistration, info, CurrentCollectionSettings, _bookSelection.CurrentSelection); // Even if ApiRequest.Handle() fails, return true to indicate that the request was processed and there // is no further need for the caller to continue trying to process the request as a filename. // See https://issues.bloomlibrary.org/youtrack/issue/BL-6763. return(true); } } } return(false); }
// Every path should return false or send a response. // Otherwise we can get a timeout error as the browser waits for a response. // // NOTE: this method gets called on different threads! public bool ProcessRequest(IRequestInfo info, string localPath) { var localPathLc = localPath.ToLowerInvariant(); if (localPathLc.StartsWith("api/", StringComparison.InvariantCulture)) { var endpoint = localPath.Substring(3).ToLowerInvariant().Trim(new char[] { '/' }); foreach (var pair in _endpointRegistrations.Where(pair => Regex.Match(endpoint, "^" + //must match the beginning pair.Key.ToLower() ).Success)) { if (pair.Value.RequiresSync) { // A single synchronization object won't do, because when processing a request to create a thumbnail or update a preview, // we have to load the HTML page the thumbnail is based on, or other HTML pages (like one used to figure what's // visible in a preview). If the page content somehow includes // an api request (api/branding/image is one example), that request will deadlock if the // api/pageTemplateThumbnail request already has the main lock. // Another case is the Bloom Reader preview, where the whole UI is rebuilt at the same time as the preview. // This leads to multiple api requests racing with the preview one, and it was possible for all // the server threads to be processing these and waiting for SyncObject while the updatePreview // request held the lock...and the request for the page that would free the lock was sitting in // the queue, waiting for a thread. // To the best of my knowledge, there's no shared data between the thumbnailing and preview processes and any // other api requests, so it seems safe to have one lock that prevents working on multiple // thumbnails/previews at the same time, and one that prevents working on other api requests at the same time. var syncOn = SyncObj; if (localPathLc.StartsWith("api/pagetemplatethumbnail", StringComparison.InvariantCulture) || localPathLc == "api/publish/android/thumbnail" || localPathLc == "api/publish/android/updatepreview") { syncOn = ThumbnailsAndPreviewsSyncObj; } else if (localPathLc.StartsWith("api/i18n/")) { syncOn = I18NLock; } lock (syncOn) { ApiRequest.Handle(pair.Value, info, CurrentCollectionSettings, _bookSelection.CurrentSelection); // Even if ApiRequest.Handle() fails, return true to indicate that the request was processed and there // is no further need for the caller to continue trying to process the request as a filename. // See https://issues.bloomlibrary.org/youtrack/issue/BL-6763. return(true); } } else { // Up to api's that request no sync to do things right! ApiRequest.Handle(pair.Value, info, CurrentCollectionSettings, _bookSelection.CurrentSelection); // Even if ApiRequest.Handle() fails, return true to indicate that the request was processed and there // is no further need for the caller to continue trying to process the request as a filename. // See https://issues.bloomlibrary.org/youtrack/issue/BL-6763. return(true); } } } return(false); }