IDEA-CR-9966 add test, assert that decoded raw path starts with /

This commit is contained in:
Vladimir Krivosheev
2016-04-07 18:30:19 +02:00
parent 508ac21f3c
commit 437ba9e310
9 changed files with 87 additions and 42 deletions
@@ -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)
@@ -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)
}
@@ -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
}
@@ -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)
}
}
}
@@ -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)
}
}
@@ -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)
}
}
}
@@ -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
}
@@ -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 <T> ByteBuf.releaseIfError(task: () -> T): T {
@@ -226,6 +226,19 @@ inline fun <T> 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