This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch 10.1.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/10.1.x by this push: new 2c6800111e Refactor CGI servlet to access resources via WebResources 2c6800111e is described below commit 2c6800111e7d8d8d5403c07978ea9bff3db5a5a5 Author: Mark Thomas <ma...@apache.org> AuthorDate: Mon Apr 28 12:58:21 2025 +0100 Refactor CGI servlet to access resources via WebResources --- java/org/apache/catalina/servlets/CGIServlet.java | 233 ++++++++------------- .../catalina/servlets/LocalStrings.properties | 1 - .../catalina/servlets/LocalStrings_fr.properties | 1 - .../catalina/servlets/LocalStrings_ja.properties | 1 - .../catalina/servlets/LocalStrings_ko.properties | 1 - .../servlets/LocalStrings_zh_CN.properties | 1 - webapps/docs/changelog.xml | 4 + 7 files changed, 97 insertions(+), 145 deletions(-) diff --git a/java/org/apache/catalina/servlets/CGIServlet.java b/java/org/apache/catalina/servlets/CGIServlet.java index 3c1b7bd6d2..b005d5621e 100644 --- a/java/org/apache/catalina/servlets/CGIServlet.java +++ b/java/org/apache/catalina/servlets/CGIServlet.java @@ -24,6 +24,8 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URL; import java.net.URLDecoder; import java.nio.file.Files; import java.util.ArrayList; @@ -621,9 +623,6 @@ public final class CGIServlet extends HttpServlet { /** pathInfo for the current request */ private String pathInfo = null; - /** real file system directory of the enclosing servlet's web app */ - private String webAppRootDir = null; - /** tempdir for context - used to expand scripts in unexpanded wars */ private File tmpDir = null; @@ -677,7 +676,6 @@ public final class CGIServlet extends HttpServlet { */ protected void setupFromContext(ServletContext context) { this.context = context; - this.webAppRootDir = context.getRealPath("/"); this.tmpDir = (File) context.getAttribute(ServletContext.TEMPDIR); } @@ -781,10 +779,9 @@ public final class CGIServlet extends HttpServlet { * cgiPathPrefix is defined by setting this servlet's cgiPathPrefix init parameter * </p> * - * @param pathInfo String from HttpServletRequest.getPathInfo() - * @param webAppRootDir String from context.getRealPath("/") * @param contextPath String as from HttpServletRequest.getContextPath() * @param servletPath String as from HttpServletRequest.getServletPath() + * @param pathInfo String from HttpServletRequest.getPathInfo() * @param cgiPathPrefix subdirectory of webAppRootDir below which the web app's CGIs may be stored; can be null. * The CGI search path will start at webAppRootDir + File.separator + cgiPathPrefix (or * webAppRootDir alone if cgiPathPrefix is null). cgiPathPrefix is defined by setting @@ -801,59 +798,108 @@ public final class CGIServlet extends HttpServlet { * found * </ul> */ - protected String[] findCGI(String pathInfo, String webAppRootDir, String contextPath, String servletPath, - String cgiPathPrefix) { - String path; - String name; - String scriptname; - - if (webAppRootDir.lastIndexOf(File.separator) == (webAppRootDir.length() - 1)) { - // strip the trailing "/" from the webAppRootDir - webAppRootDir = webAppRootDir.substring(0, (webAppRootDir.length() - 1)); - } + protected String[] findCGI(String contextPath, String servletPath, String pathInfo, String cgiPathPrefix) { - if (cgiPathPrefix != null) { - webAppRootDir = webAppRootDir + File.separator + cgiPathPrefix; - } + StringBuilder cgiPath = new StringBuilder(); + StringBuilder urlPath = new StringBuilder(); - if (log.isTraceEnabled()) { - log.trace(sm.getString("cgiServlet.find.path", pathInfo, webAppRootDir)); - } + URL cgiScriptURL = null; - File currentLocation = new File(webAppRootDir); - StringTokenizer dirWalker = new StringTokenizer(pathInfo, "/"); - if (log.isTraceEnabled()) { - log.trace(sm.getString("cgiServlet.find.location", currentLocation.getAbsolutePath())); + if (cgiPathPrefix == null || cgiPathPrefix.isEmpty()) { + cgiPath.append(servletPath); + } else { + cgiPath.append('/'); + cgiPath.append(cgiPathPrefix); } - StringBuilder cginameBuilder = new StringBuilder(); - while (!currentLocation.isFile() && dirWalker.hasMoreElements()) { - String nextElement = (String) dirWalker.nextElement(); - currentLocation = new File(currentLocation, nextElement); - cginameBuilder.append('/').append(nextElement); + urlPath.append(servletPath); + + StringTokenizer pathWalker = new StringTokenizer(pathInfo, "/"); + + while (pathWalker.hasMoreElements() && cgiScriptURL == null) { + String urlSegment = pathWalker.nextToken(); + cgiPath.append('/'); + cgiPath.append(urlSegment); + urlPath.append('/'); + urlPath.append(urlSegment); if (log.isTraceEnabled()) { - log.trace(sm.getString("cgiServlet.find.location", currentLocation.getAbsolutePath())); + log.trace(sm.getString("cgiServlet.find.location", cgiPath.toString())); + } + try { + cgiScriptURL = context.getResource(cgiPath.toString()); + } catch (MalformedURLException e) { + // Ignore - should never happen } } - String cginame = cginameBuilder.toString(); - if (!currentLocation.isFile()) { + + // No script was found + if (cgiScriptURL == null) { return new String[] { null, null, null, null }; } - path = currentLocation.getAbsolutePath(); - name = currentLocation.getName(); + // Set-up return values + String path = null; + String scriptName = null; + String cgiName = null; + String name = null; - if (servletPath.startsWith(cginame)) { - scriptname = contextPath + cginame; - } else { - scriptname = contextPath + servletPath + cginame; + path = context.getRealPath(cgiPath.toString()); + if (path == null) { + /* + * The script doesn't exist directly on the file system. It might be located in an archive or similar. + * Such scripts are extracted to the web application's temporary file location. + */ + File tmpCgiFile = new File(tmpDir + cgiPath.toString()); + if (!tmpCgiFile.exists()) { + + // Create directories + File parent = tmpCgiFile.getParentFile(); + if (!parent.mkdirs() && !parent.isDirectory()) { + log.warn(sm.getString("cgiServlet.expandCreateDirFail", parent.getAbsolutePath())); + return new String[] { null, null, null, null }; + } + + try (InputStream is = context.getResourceAsStream(cgiPath.toString())) { + synchronized (expandFileLock) { + // Check if file was created by concurrent request + if (!tmpCgiFile.exists()) { + try { + Files.copy(is, tmpCgiFile.toPath()); + } catch (IOException ioe) { + log.warn(sm.getString("cgiServlet.expandFail", cgiScriptURL, + tmpCgiFile.getAbsolutePath()), ioe); + if (tmpCgiFile.exists()) { + if (!tmpCgiFile.delete()) { + log.warn(sm.getString("cgiServlet.expandDeleteFail", + tmpCgiFile.getAbsolutePath())); + } + } + return new String[] { null, null, null, null }; + } + if (log.isDebugEnabled()) { + log.debug(sm.getString("cgiServlet.expandOk", cgiScriptURL, + tmpCgiFile.getAbsolutePath())); + } + } + } + } catch (IOException ioe) { + log.warn(sm.getString("cgiServlet.expandCloseFail", cgiScriptURL), ioe); + } + } + path = tmpCgiFile.getAbsolutePath(); } + scriptName = urlPath.toString(); + cgiName = scriptName.substring(servletPath.length()); + name = scriptName.substring(scriptName.lastIndexOf('/') + 1); + if (log.isTraceEnabled()) { - log.trace(sm.getString("cgiServlet.find.found", name, path, scriptname, cginame)); + log.trace(sm.getString("cgiServlet.find.found", name, path, scriptName, cgiName)); } - return new String[] { path, scriptname, cginame, name }; + + return new String[] { path, scriptName, cgiName, name }; } + /** * Constructs the CGI environment to be supplied to the invoked CGI script; relies heavily on Servlet API * methods and findCGI @@ -888,13 +934,7 @@ public final class CGIServlet extends HttpServlet { sPathInfoOrig = this.pathInfo; sPathInfoOrig = sPathInfoOrig == null ? "" : sPathInfoOrig; - if (webAppRootDir == null) { - // The app has not been deployed in exploded form - webAppRootDir = tmpDir.toString(); - expandCGIScript(); - } - - sCGINames = findCGI(sPathInfoOrig, webAppRootDir, contextPath, servletPath, cgiPathPrefix); + sCGINames = findCGI(contextPath, servletPath, sPathInfoOrig, cgiPathPrefix); sCGIFullPath = sCGINames[0]; sCGIScriptName = sCGINames[1]; @@ -1020,93 +1060,6 @@ public final class CGIServlet extends HttpServlet { return true; } - /** - * Extracts requested resource from web app archive to context work directory to enable CGI script to be - * executed. - */ - protected void expandCGIScript() { - StringBuilder srcPath = new StringBuilder(); - StringBuilder destPath = new StringBuilder(); - InputStream is = null; - - // paths depend on mapping - if (cgiPathPrefix == null) { - srcPath.append(pathInfo); - is = context.getResourceAsStream(srcPath.toString()); - destPath.append(tmpDir); - destPath.append(pathInfo); - } else { - // essentially same search algorithm as findCGI() - srcPath.append(cgiPathPrefix); - StringTokenizer pathWalker = new StringTokenizer(pathInfo, "/"); - // start with first element - while (pathWalker.hasMoreElements() && (is == null)) { - srcPath.append('/'); - srcPath.append(pathWalker.nextElement()); - is = context.getResourceAsStream(srcPath.toString()); - } - destPath.append(tmpDir); - destPath.append('/'); - destPath.append(srcPath); - } - - if (is == null) { - // didn't find anything, give up now - log.warn(sm.getString("cgiServlet.expandNotFound", srcPath)); - return; - } - - try { - File f = new File(destPath.toString()); - if (f.exists()) { - // Don't need to expand if it already exists - return; - } - - // create directories - File dir = f.getParentFile(); - if (!dir.mkdirs() && !dir.isDirectory()) { - log.warn(sm.getString("cgiServlet.expandCreateDirFail", dir.getAbsolutePath())); - return; - } - - try { - synchronized (expandFileLock) { - // make sure file doesn't exist - if (f.exists()) { - return; - } - - // create file - if (!f.createNewFile()) { - return; - } - - Files.copy(is, f.toPath()); - - if (log.isDebugEnabled()) { - log.debug(sm.getString("cgiServlet.expandOk", srcPath, destPath)); - } - } - } catch (IOException ioe) { - log.warn(sm.getString("cgiServlet.expandFail", srcPath, destPath), ioe); - // delete in case file is corrupted - if (f.exists()) { - if (!f.delete()) { - log.warn(sm.getString("cgiServlet.expandDeleteFail", f.getAbsolutePath())); - } - } - } - } finally { - try { - is.close(); - } catch (IOException e) { - log.warn(sm.getString("cgiServlet.expandCloseFail", srcPath), e); - } - } - } - - /** * Returns important CGI environment information in a multi-line text format. * @@ -1402,9 +1355,9 @@ public final class CGIServlet extends HttpServlet { * <LI><u>Allowed characters in path segments</u>: This implementation does not allow non-terminal NULL segments * in the path -- IOExceptions may be thrown; * <LI><u>"<code>.</code>" and "<code>..</code>" path segments</u>: This implementation does not allow - * "<code>.</code>" and "<code>..</code>" in the path, and such characters will result in an IOException - * being thrown (this should never happen since Tomcat normalises the requestURI before determining the - * contextPath, servletPath and pathInfo); + * "<code>.</code>" and "<code>..</code>" in the path, and such characters will result in an IOException being + * thrown (this should never happen since Tomcat normalises the requestURI before determining the contextPath, + * servletPath and pathInfo); * <LI><u>Implementation limitations</u>: This implementation does not impose any limitations except as * documented above. This implementation may be limited by the servlet container used to house this * implementation. In particular, all the primary CGI variable values are derived either directly or indirectly @@ -1442,7 +1395,7 @@ public final class CGIServlet extends HttpServlet { Runtime rt; BufferedReader cgiHeaderReader = null; InputStream cgiOutput = null; - BufferedReader commandsStdErr ; + BufferedReader commandsStdErr; Thread errReaderThread = null; BufferedOutputStream commandsStdIn; Process proc = null; diff --git a/java/org/apache/catalina/servlets/LocalStrings.properties b/java/org/apache/catalina/servlets/LocalStrings.properties index 5a3158afd3..5fb786b3ba 100644 --- a/java/org/apache/catalina/servlets/LocalStrings.properties +++ b/java/org/apache/catalina/servlets/LocalStrings.properties @@ -21,7 +21,6 @@ cgiServlet.expandCloseFail=Failed to close input stream for script at path [{0}] cgiServlet.expandCreateDirFail=Failed to create destination directory [{0}] for script expansion cgiServlet.expandDeleteFail=Failed to delete file at [{0}] after IOException during expansion cgiServlet.expandFail=Failed to expand script at path [{0}] to [{1}] -cgiServlet.expandNotFound=Unable to expand [{0}] as it could not be found cgiServlet.expandOk=Expanded script at path [{0}] to [{1}] cgiServlet.find.found=Found CGI: name [{0}], path [{1}], script name [{2}] and CGI name [{3}] cgiServlet.find.location=Looking for a file at [{0}] diff --git a/java/org/apache/catalina/servlets/LocalStrings_fr.properties b/java/org/apache/catalina/servlets/LocalStrings_fr.properties index f61a81ce60..328b72eac8 100644 --- a/java/org/apache/catalina/servlets/LocalStrings_fr.properties +++ b/java/org/apache/catalina/servlets/LocalStrings_fr.properties @@ -21,7 +21,6 @@ cgiServlet.expandCloseFail=Impossible de fermer le flux d''entrée du script ave cgiServlet.expandCreateDirFail=Echec de la création du répertoire de destination [{0}] pour la décompression du script cgiServlet.expandDeleteFail=Impossible d''effacer le fichier [{0}] suite à une IOException pendant la décompression cgiServlet.expandFail=Impossible de faire l''expansion du script au chemin [{0}] vers [{1}] -cgiServlet.expandNotFound=Impossible de décompresser [{0}] car il n''a pas été trouvé cgiServlet.expandOk=Extrait le script du chemin [{0}] vers [{1}] cgiServlet.find.found=Trouvé le CGI : nom [{0}], chemin [{1}], nom de script [{2}] et nom du CGI [{3}] cgiServlet.find.location=Recherche d''un fichier en [{0}] diff --git a/java/org/apache/catalina/servlets/LocalStrings_ja.properties b/java/org/apache/catalina/servlets/LocalStrings_ja.properties index 87d7334ad6..f90a9d2fe9 100644 --- a/java/org/apache/catalina/servlets/LocalStrings_ja.properties +++ b/java/org/apache/catalina/servlets/LocalStrings_ja.properties @@ -21,7 +21,6 @@ cgiServlet.expandCloseFail=パス [{0}] のスクリプトの入力ストリー cgiServlet.expandCreateDirFail=スクリプトの展開先ディレクトリ[{0}]の作成に失敗しました。 cgiServlet.expandDeleteFail=拡張中にIOExceptionの後に [{0}] でファイルを削除できませんでした cgiServlet.expandFail=パス [{0}] のスクリプトを [{1}] に展開できませんでした -cgiServlet.expandNotFound=見つけることができなかったため [{0}] を展開できません cgiServlet.expandOk=パス [{0}] の [{1}] に展開されたスクリプト cgiServlet.find.found=見つかったCGI: 名前 [{0}]、パス [{1}]、スクリプト名 [{2}]、CGI名 [{3}] cgiServlet.find.location=ファイル [{0}] を探しています。 diff --git a/java/org/apache/catalina/servlets/LocalStrings_ko.properties b/java/org/apache/catalina/servlets/LocalStrings_ko.properties index aeb3d720d6..547c842722 100644 --- a/java/org/apache/catalina/servlets/LocalStrings_ko.properties +++ b/java/org/apache/catalina/servlets/LocalStrings_ko.properties @@ -21,7 +21,6 @@ cgiServlet.expandCloseFail=경로 [{0}]에 위치한 스크립트를 위한, 입 cgiServlet.expandCreateDirFail=스크립트를 압축해제 하기 위한 대상 디렉토리 [{0}]을(를) 생성하지 못했습니다. cgiServlet.expandDeleteFail=압축해제 중 IOException이 발생한 후, [{0}]에 위치한 해당 파일을 삭제하지 못했습니다. cgiServlet.expandFail=경로 [{0}]의 스크립트를 [{1}](으)로 압축해제 하지 못했습니다. -cgiServlet.expandNotFound=[{0}]을(를) 찾을 수 없어서 압축해제 할 수 없습니다. cgiServlet.expandOk=[{0}] 경로에 있는 스트립트가 [{1}](으)로 압축 해제되었습니다. cgiServlet.find.found=CGI 발견: 이름 [{0}], 경로 [{1}], 스크립트 이름 [{2}], CGI 이름 [{3}] cgiServlet.find.location=[{0}]에 위치한 파일을 찾는 중 diff --git a/java/org/apache/catalina/servlets/LocalStrings_zh_CN.properties b/java/org/apache/catalina/servlets/LocalStrings_zh_CN.properties index 72ee5c9266..700617cccd 100644 --- a/java/org/apache/catalina/servlets/LocalStrings_zh_CN.properties +++ b/java/org/apache/catalina/servlets/LocalStrings_zh_CN.properties @@ -21,7 +21,6 @@ cgiServlet.expandCloseFail=无法关闭路径[{0}]处脚本的输入流 cgiServlet.expandCreateDirFail=无法为脚本扩展创建目标目录[{0}] cgiServlet.expandDeleteFail=扩展期间,发生IOException异常后删除文件[{0}]失败 cgiServlet.expandFail=在路径[{0}] 到[{1}] 展开脚本失败. -cgiServlet.expandNotFound=无法展开[{0}],因为找不到它。 cgiServlet.expandOk=从路径[{0}]到[{1}]扩展脚本 cgiServlet.find.found=找到CGI:name[{0}]、path[{1}]、script name[{2}]和CGI name[{3}] cgiServlet.find.location=在 [{0}] 查找文件 diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 87e68953ad..c823412761 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -138,6 +138,10 @@ <code>org.apache.catalina.connector.InputBuffer</code> with a static, zero length buffer. (markt) </scode> + <scode> + Refactor GCI servlet to access resources via the + <code>WebResource</code> API. (markt) + </scode> </changelog> </subsection> <subsection name="Coyote"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org