diff --git a/platform/built-in-server/src/org/jetbrains/builtInWebServer/BuiltInWebServer.kt b/platform/built-in-server/src/org/jetbrains/builtInWebServer/BuiltInWebServer.kt index 212c68468bc8..88bd40f01657 100644 --- a/platform/built-in-server/src/org/jetbrains/builtInWebServer/BuiltInWebServer.kt +++ b/platform/built-in-server/src/org/jetbrains/builtInWebServer/BuiltInWebServer.kt @@ -50,7 +50,7 @@ class BuiltInWebServer : HttpRequestHandler() { return false } - val portIndex = host.indexOf(':') + val portIndex = host!!.indexOf(':') if (portIndex > 0) { host = host.substring(0, portIndex) } @@ -134,8 +134,7 @@ private fun doProcess(request: FullHttpRequest, context: ChannelHandlerContext, return true } - // must be absolute path (relative to DOCUMENT_ROOT, i.e. scheme://authority/) to properly canonicalize - val path = FileUtil.toCanonicalPath(decodedPath.substring(offset), '/').substring(1) + val path = toIdeaPath(decodedPath, offset) for (pathHandler in WebServerPathHandler.EP_NAME.extensions) { LOG.catchAndLog { if (pathHandler.process(path, project, request, context, projectName, decodedPath, isCustomHost)) { @@ -146,6 +145,15 @@ private fun doProcess(request: FullHttpRequest, context: ChannelHandlerContext, return false } +private fun toIdeaPath(decodedPath: String, offset: Int): String { + // must be absolute path (relative to DOCUMENT_ROOT, i.e. scheme://authority/) to properly canonicalize + val path = decodedPath.substring(offset) + if (!path.startsWith('/')) { + throw AssertionError("Path must be absolute") + } + return FileUtil.toCanonicalPath(path, '/').substring(1) +} + fun compareNameAndProjectBasePath(projectName: String, project: Project): Boolean { val basePath = project.basePath return basePath != null && endsWithName(basePath, projectName) diff --git a/platform/built-in-server/src/org/jetbrains/builtInWebServer/WebServerPathHandler.kt b/platform/built-in-server/src/org/jetbrains/builtInWebServer/WebServerPathHandler.kt index 3a5ca625fbe9..455b4fec054e 100644 --- a/platform/built-in-server/src/org/jetbrains/builtInWebServer/WebServerPathHandler.kt +++ b/platform/built-in-server/src/org/jetbrains/builtInWebServer/WebServerPathHandler.kt @@ -50,7 +50,7 @@ abstract class WebServerPathHandler { fun redirectToDirectory(request: HttpRequest, channel: Channel, path: String) { val response = Responses.response(HttpResponseStatus.MOVED_PERMANENTLY) - val url = VfsUtil.toUri("${channel.uriScheme}://${request.host}/$path/")!! + val url = VfsUtil.toUri("${channel.uriScheme}://${request.host!!}/$path/")!! response.headers().add(HttpHeaderNames.LOCATION, url.toASCIIString()) Responses.send(response, channel, request) } \ No newline at end of file diff --git a/platform/built-in-server/testSrc/BuiltInServerTestCase.kt b/platform/built-in-server/testSrc/BuiltInServerTestCase.kt index bacf2a03e4f7..b30634beb5a4 100644 --- a/platform/built-in-server/testSrc/BuiltInServerTestCase.kt +++ b/platform/built-in-server/testSrc/BuiltInServerTestCase.kt @@ -43,16 +43,18 @@ internal abstract class BuiltInServerTestCase { url += ":$column" } - val connection = URL(url).openConnection() as HttpURLConnection val expectedStatus = HttpResponseStatus.valueOf(manager.annotation?.status ?: 200) - assertThat(HttpResponseStatus.valueOf(connection.responseCode)).isEqualTo(expectedStatus) - + val connection = testUrl(url, expectedStatus) check(serviceUrl, expectedStatus) - if (additionalCheck != null) { - additionalCheck(connection) - } + additionalCheck?.invoke(connection) } protected open fun check(serviceUrl: String, expectedStatus: HttpResponseStatus) { } +} + +internal fun testUrl(url: String, expectedStatus: HttpResponseStatus): HttpURLConnection { + val connection = URL(url).openConnection() as HttpURLConnection + assertThat(HttpResponseStatus.valueOf(connection.responseCode)).isEqualTo(expectedStatus) + return connection } \ No newline at end of file diff --git a/platform/built-in-server/testSrc/BuiltInWebServerTest.kt b/platform/built-in-server/testSrc/BuiltInWebServerTest.kt index 92bc079235ca..519cc7b4f440 100644 --- a/platform/built-in-server/testSrc/BuiltInWebServerTest.kt +++ b/platform/built-in-server/testSrc/BuiltInWebServerTest.kt @@ -1,14 +1,20 @@ package org.jetbrains.ide +import com.google.common.net.UrlEscapers import com.intellij.openapi.application.runWriteAction import com.intellij.openapi.module.EmptyModuleType import com.intellij.openapi.module.ModuleManager +import com.intellij.openapi.project.Project import com.intellij.openapi.roots.ModuleRootModificationUtil -import com.intellij.testFramework.runInEdtAndWait -import com.intellij.util.refreshVfs -import com.intellij.util.systemIndependentPath -import com.intellij.util.writeChild +import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.util.text.StringUtil +import com.intellij.openapi.vfs.LocalFileSystem +import com.intellij.testFramework.* +import com.intellij.util.* +import io.netty.handler.codec.http.HttpResponseStatus import org.assertj.core.api.Assertions.assertThat +import org.junit.ClassRule +import org.junit.Rule import org.junit.Test internal class BuiltInWebServerTest : BuiltInServerTestCase() { @@ -39,13 +45,7 @@ internal class BuiltInWebServerTest : BuiltInServerTestCase() { newPath.writeChild(manager.filePath!!, "hello") newPath.refreshVfs() - runInEdtAndWait { - runWriteAction { - val systemIndependentPath = newPath.systemIndependentPath - val module = ModuleManager.getInstance(project).newModule("$systemIndependentPath/test.iml", EmptyModuleType.EMPTY_MODULE) - ModuleRootModificationUtil.addContentRoot(module, systemIndependentPath) - } - } + createModule(newPath.systemIndependentPath, project) for (path in paths) { doTest(path) { @@ -53,4 +53,40 @@ internal class BuiltInWebServerTest : BuiltInServerTestCase() { } } } +} + +private fun createModule(systemIndependentPath: String, project: Project) { + runInEdtAndWait { + runWriteAction { + val module = ModuleManager.getInstance(project).newModule("$systemIndependentPath/test.iml", EmptyModuleType.EMPTY_MODULE) + ModuleRootModificationUtil.addContentRoot(module, systemIndependentPath) + } + } +} + +internal class HeavyBuiltInWebServerTest { + companion object { + @JvmField + @ClassRule val appRule = ProjectRule() + } + + @Rule + @JvmField + val tempDirManager = TemporaryDirectory() + + @Test + fun `path outside of project`() { + val projectDir = tempDirManager.newPath().resolve("foo/bar") + val projectDirPath = projectDir.systemIndependentPath + createHeavyProject("$projectDirPath/test.ipr").use { project -> + projectDir.createDirectories() + LocalFileSystem.getInstance().refreshAndFindFileByPath(projectDirPath) + createModule(projectDirPath, project) + + val path = tempDirManager.newPath("doNotExposeMe.txt").write("doNotExposeMe").systemIndependentPath + val relativePath = FileUtil.getRelativePath(project.basePath!!, path, '/') + val webPath = StringUtil.replace(UrlEscapers.urlPathSegmentEscaper().escape("${project.name}/$relativePath"), "%2F", "/") + testUrl("http://localhost:${BuiltInServerManager.getInstance().port}/$webPath", HttpResponseStatus.NOT_FOUND) + } + } } \ No newline at end of file diff --git a/platform/configuration-store-impl/src/ProjectStoreImpl.kt b/platform/configuration-store-impl/src/ProjectStoreImpl.kt index 8eff2e832ccd..7427c2ced579 100644 --- a/platform/configuration-store-impl/src/ProjectStoreImpl.kt +++ b/platform/configuration-store-impl/src/ProjectStoreImpl.kt @@ -26,7 +26,6 @@ import com.intellij.openapi.components.* import com.intellij.openapi.components.StateStorage.SaveSession import com.intellij.openapi.components.impl.stores.IComponentStore import com.intellij.openapi.components.impl.stores.IProjectStore -import com.intellij.openapi.fileTypes.FileTypeManager import com.intellij.openapi.module.Module import com.intellij.openapi.module.ModuleManager import com.intellij.openapi.project.Project @@ -246,16 +245,7 @@ private open class ProjectStoreImpl(project: ProjectImpl, private val pathMacroM return PathUtilRt.getFileName(baseDir).replace(":", "") } else { - var temp = PathUtilRt.getFileName(projectFilePath) - val fileType = FileTypeManager.getInstance().getFileTypeByFileName(temp) - if (fileType is ProjectFileType) { - temp = temp.substring(0, temp.length - fileType.defaultExtension.length - 1) - } - val i = temp.lastIndexOf(File.separatorChar) - if (i >= 0) { - temp = temp.substring(i + 1, temp.length - i + 1) - } - return temp + return PathUtilRt.getFileName(projectFilePath).removeSuffix(ProjectFileType.DOT_DEFAULT_EXTENSION) } } diff --git a/platform/configuration-store-impl/testSrc/ProjectStoreTest.kt b/platform/configuration-store-impl/testSrc/ProjectStoreTest.kt index 894db84357cd..05e833007c0a 100644 --- a/platform/configuration-store-impl/testSrc/ProjectStoreTest.kt +++ b/platform/configuration-store-impl/testSrc/ProjectStoreTest.kt @@ -50,7 +50,7 @@ fun loadAndUseProject(tempDirManager: TemporaryDirectory, projectCreator: ((Virt private fun createOrLoadProject(tempDirManager: TemporaryDirectory, task: (Project) -> Unit, projectCreator: ((VirtualFile) -> String)? = null, directoryBased: Boolean) { runInEdtAndWait { - var filePath: String + val filePath: String if (projectCreator == null) { filePath = tempDirManager.newPath("test${if (directoryBased) "" else ProjectFileType.DOT_DEFAULT_EXTENSION}").systemIndependentPath } @@ -59,15 +59,9 @@ private fun createOrLoadProject(tempDirManager: TemporaryDirectory, task: (Proje } val projectManager = ProjectManagerEx.getInstanceEx() as ProjectManagerImpl - var project = if (projectCreator == null) projectManager.newProject(null, filePath, true, false)!! else projectManager.loadProject(filePath)!! + val project = if (projectCreator == null) createHeavyProject(filePath, true) else projectManager.loadProject(filePath)!! project.runInLoadComponentStateMode { - try { - projectManager.openTestProject(project) - task(project) - } - finally { - projectManager.closeProject(project, false, true, false) - } + project.use(task) } } } diff --git a/platform/platform-impl/src/com/intellij/util/path.kt b/platform/platform-impl/src/com/intellij/util/path.kt index 8dfd631a41d3..7f5baf7f884d 100644 --- a/platform/platform-impl/src/com/intellij/util/path.kt +++ b/platform/platform-impl/src/com/intellij/util/path.kt @@ -95,6 +95,8 @@ fun Path.write(data: ByteArray, offset: Int = 0, length: Int = data.size): Path } fun Path.write(data: String): Path { + parent?.createDirectories() + Files.write(this, data.toByteArray()) return this } diff --git a/platform/platform-impl/src/org/jetbrains/io/netty.kt b/platform/platform-impl/src/org/jetbrains/io/netty.kt index 2076f4806ac0..ceb922cc1a34 100644 --- a/platform/platform-impl/src/org/jetbrains/io/netty.kt +++ b/platform/platform-impl/src/org/jetbrains/io/netty.kt @@ -95,7 +95,7 @@ fun Bootstrap.connect(remoteAddress: InetSocketAddress, promise: AsyncPromise<*> val Channel.uriScheme: String get() = if (pipeline().get(SslHandler::class.java) == null) "http" else "https" -val HttpRequest.host: String +val HttpRequest.host: String? get() = headers().getAsString(HttpHeaderNames.HOST) inline fun ByteBuf.releaseIfError(task: () -> T): T { diff --git a/platform/testFramework/src/com/intellij/testFramework/FixtureRule.kt b/platform/testFramework/src/com/intellij/testFramework/FixtureRule.kt index cbba936fc3e4..f4997c6ae49b 100644 --- a/platform/testFramework/src/com/intellij/testFramework/FixtureRule.kt +++ b/platform/testFramework/src/com/intellij/testFramework/FixtureRule.kt @@ -226,6 +226,19 @@ inline fun Project.runInLoadComponentStateMode(task: () -> T): T { } } +fun createHeavyProject(path: String, useDefaultProjectSettings: Boolean = false) = ProjectManagerEx.getInstanceEx().newProject(null, path, useDefaultProjectSettings, false)!! + +fun Project.use(task: (Project) -> Unit) { + val projectManager = ProjectManagerEx.getInstanceEx() as ProjectManagerImpl + try { + runInEdtAndWait { projectManager.openTestProject(this) } + task(this) + } + finally { + runInEdtAndWait { projectManager.closeProject(this, false, true, false) } + } +} + class DisposeNonLightProjectsRule() : ExternalResource() { override fun after() { val projectManager = if (ApplicationManager.getApplication().isDisposed) null else ProjectManager.getInstance() as ProjectManagerImpl