asciidocfx / AsciidocFX

Asciidoc Editor and Toolchain written with JavaFX 21 (Build PDF, Epub, Mobi and HTML books, documents and slides)
http://www.asciidocfx.com/
Apache License 2.0
1.88k stars 295 forks source link

Absolute path includes do not work on Windows for non-asciidoc files #504

Closed Ayowel closed 3 years ago

Ayowel commented 3 years ago

Hi,

As said in the title, it looks like absolute path includes do not work on windows for asciidoc files. They do seem to work for asciidoc files.

Example assuming a csv file show.csv in the same folder as the asciidoc file in workdir, using docdir to get an absolute path to the workdir:

show.csv

Title,Date
Test 1,2021/01/01

main.adoc

= Show test

[%header]
,===
include::{docdir}\show.csv[]
,===

Error log:

ERROR => UT005023: Exception handling request to /afx/worker/js/resource.afx
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:523)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:590)
    at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
    at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
    at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68)
    at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
    at io.undertow.servlet.handlers.RedirectDirHandler.handleRequest(RedirectDirHandler.java:68)
    at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:132)
    at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
    at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
    at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
    at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
    at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:269)
    at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:78)
    at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:133)
    at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:130)
    at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
    at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
    at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:249)
    at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:78)
    at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:99)
    at io.undertow.server.Connectors.executeRootHandler(Connectors.java:376)
    at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:830)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:835)
Caused by: java.lang.NullPointerException: null
    at java.base/sun.nio.fs.WindowsPath.toWindowsPath(WindowsPath.java:371)
    at java.base/sun.nio.fs.WindowsPath.resolve(WindowsPath.java:580)
    at java.base/sun.nio.fs.WindowsPath.resolve(WindowsPath.java:42)
    at com.kodedu.service.DirectoryService.findPathInWorkdirOrLookup(DirectoryService.java:309)
    at com.kodedu.controller.WebWorkerResource.onrequest(WebWorkerResource.java:102)
    at jdk.internal.reflect.GeneratedMethodAccessor60.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
    ... 47 common frames omitted
Ayowel commented 3 years ago

A quick fix, that works for me but is probably not desirable in the code base:


diff --git a/src/main/java/com/kodedu/controller/WebWorkerResource.java b/src/main/java/com/kodedu/controller/WebWorkerResource.java
index 1ed14be5..a3f9ccc4 100644
--- a/src/main/java/com/kodedu/controller/WebWorkerResource.java
+++ b/src/main/java/com/kodedu/controller/WebWorkerResource.java
@@ -99,6 +99,10 @@ public class WebWorkerResource {
         }

         if (optional.isPresent()) {
+            if(finalURI.startsWith("file:///")) {
+                finalURI = finalURI.replace("file:///", "");
+            }
+
             Path found = directoryService.findPathInWorkdirOrLookup(IOHelper.getPath(finalURI));

             if (Objects.nonNull(found)) {

What happens is that, for some reason, the front-end builds a path with file:// and send a request with it to the back-end when using an absolute path while it produces a 'native' absolute path when using relative paths in the include.

The code diff drops the file:/// prefix on the back-end but the actual fix should probably be implemented on the front-end, however I could not work out where the request was built

rahmanusta commented 3 years ago

Hi,

Seems the change looks safe, but I'm not able to reproduce this issue on last release. What AsciidocFX version do you use ?

image

Ayowel commented 3 years ago

I'm using AsciidocFX 1.7.3 on windows 10. If i print {docdir} it shows as C:\path\to\docdir, if for some reason your docdir is a local path you probably won't have the issue

image

My worry is that the file:// issue might appear on linux too, in which case it probably works now but would be broken by this 'fix' as the linux equivalent would look like file:///path/to/docdir and dropping file:/// would turn it into an invalid local path.

If you do not have control over the parsing/path generation, a fix that should be safer to integrate and is more explicit in what it checks for would be:

diff --git a/src/main/java/com/kodedu/controller/WebWorkerResource.java b/src/main/java/com/kodedu/controller/WebWorkerResource.java
index 1ed14be5..a3f9ccc4 100644
--- a/src/main/java/com/kodedu/controller/WebWorkerResource.java
+++ b/src/main/java/com/kodedu/controller/WebWorkerResource.java
@@ -99,6 +99,10 @@ public class WebWorkerResource {
         }

         if (optional.isPresent()) {
+            if(finalURI.matches("^file:///[A-Z]:[\\\\/].+$")) {
+                finalURI = finalURI.replace("file:///", "");
+            }
+
             Path found = directoryService.findPathInWorkdirOrLookup(IOHelper.getPath(finalURI));

             if (Objects.nonNull(found)) {
Ayowel commented 3 years ago

Fixed in ac80911