diff --git a/platform/built-in-server/intellij.platform.builtInServer.impl.iml b/platform/built-in-server/intellij.platform.builtInServer.impl.iml
index 1321f4d140c0..bc1fc2407149 100644
--- a/platform/built-in-server/intellij.platform.builtInServer.impl.iml
+++ b/platform/built-in-server/intellij.platform.builtInServer.impl.iml
@@ -27,5 +27,6 @@
+
\ No newline at end of file
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 9b1cf66f4362..712c0e97227b 100644
--- a/platform/built-in-server/src/org/jetbrains/builtInWebServer/BuiltInWebServer.kt
+++ b/platform/built-in-server/src/org/jetbrains/builtInWebServer/BuiltInWebServer.kt
@@ -66,7 +66,7 @@ class BuiltInWebServer : HttpRequestHandler() {
override fun isSupported(request: FullHttpRequest): Boolean = super.isSupported(request) || request.method() == HttpMethod.POST
override fun process(urlDecoder: QueryStringDecoder, request: FullHttpRequest, context: ChannelHandlerContext): Boolean {
- var hostName = request.hostName ?: return false
+ var hostName = getHostName(request) ?: return false
val projectName: String?
val isIpv6 = hostName[0] == '[' && hostName.length > 2 && hostName[hostName.length - 1] == ']'
if (isIpv6) {
diff --git a/platform/built-in-server/src/org/jetbrains/ide/AboutHttpService.kt b/platform/built-in-server/src/org/jetbrains/ide/AboutHttpService.kt
index 0d45f5917d49..8a8f87bb8f29 100644
--- a/platform/built-in-server/src/org/jetbrains/ide/AboutHttpService.kt
+++ b/platform/built-in-server/src/org/jetbrains/ide/AboutHttpService.kt
@@ -1,21 +1,24 @@
-// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
-package org.jetbrains.ide;
+// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package org.jetbrains.ide
-import com.google.gson.stream.JsonWriter;
-import com.intellij.ide.IdeAboutInfoUtil;
-import com.intellij.openapi.application.PathManager;
-import com.intellij.openapi.application.ex.ApplicationInfoEx;
-import com.intellij.openapi.fileTypes.FileType;
-import com.intellij.openapi.fileTypes.FileTypeRegistry;
-import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.http.FullHttpRequest;
-import io.netty.handler.codec.http.QueryStringDecoder;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.io.IOException;
-import java.io.OutputStream;
+import com.fasterxml.jackson.core.JsonFactory
+import com.fasterxml.jackson.core.JsonGenerator
+import com.intellij.openapi.application.ApplicationInfo
+import com.intellij.openapi.application.ApplicationNamesInfo
+import com.intellij.openapi.application.PathManager
+import com.intellij.openapi.application.ex.ApplicationInfoEx
+import com.intellij.openapi.fileTypes.FileTypeRegistry
+import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream
+import com.intellij.util.PlatformUtils
+import com.intellij.util.io.jackson.array
+import com.intellij.util.io.jackson.obj
+import com.intellij.util.io.origin
+import io.netty.channel.ChannelHandlerContext
+import io.netty.handler.codec.http.FullHttpRequest
+import io.netty.handler.codec.http.HttpRequest
+import io.netty.handler.codec.http.QueryStringDecoder
+import org.jetbrains.ide.RestService.Companion.getBooleanParameter
+import java.io.OutputStream
/**
* @api {get} /about The application info
@@ -35,61 +38,83 @@ import java.io.OutputStream;
* @apiSuccess {String} registeredFileTypes.description The user-readable description of the file type.
* @apiSuccess {Boolean} registeredFileTypes.isBinary Whether files of the specified type contain binary data.
*
- * * @apiExample Request-Example:
+ * * @apiExample Request-Example:
* /rest/about?registeredFileTypes
*
* @apiUse SuccessExample
* @apiUse SuccessExampleWithRegisteredFileTypes
*/
-public class AboutHttpService extends RestService {
- @NotNull
- @Override
- protected String getServiceName() {
- return "about";
+internal class AboutHttpService : RestService() {
+ override fun getServiceName() = "about"
+
+ override fun isHostTrusted(request: FullHttpRequest, urlDecoder: QueryStringDecoder): Boolean {
+ return isTrustedOrigin(request) || super.isHostTrusted(request, urlDecoder)
}
- @Nullable
- @Override
- public String execute(@NotNull QueryStringDecoder urlDecoder, @NotNull FullHttpRequest request, @NotNull ChannelHandlerContext context) throws IOException {
- BufferExposingByteArrayOutputStream byteOut = new BufferExposingByteArrayOutputStream();
- getAbout(byteOut, urlDecoder);
- send(byteOut, request, context);
- return null;
+ override fun isAccessible(request: HttpRequest): Boolean {
+ return isTrustedOrigin(request) || super.isAccessible(request)
}
- public static void getAbout(@NotNull OutputStream out, @Nullable QueryStringDecoder urlDecoder) throws IOException {
- JsonWriter writer = createJsonWriter(out);
- writer.beginObject();
-
- IdeAboutInfoUtil.writeAboutJson(writer);
-
- if (urlDecoder != null && getBooleanParameter("registeredFileTypes", urlDecoder)) {
- writer.name("registeredFileTypes").beginArray();
- for (FileType fileType : FileTypeRegistry.getInstance().getRegisteredFileTypes()) {
- writer.beginObject();
- writer.name("name").value(fileType.getName());
- writer.name("description").value(fileType.getDescription());
- writer.name("isBinary").value(fileType.isBinary());
- writer.endObject();
- }
- writer.endArray();
- }
-
- if (urlDecoder != null && getBooleanParameter("more", urlDecoder)) {
- ApplicationInfoEx appInfo = ApplicationInfoEx.getInstanceEx();
- writer.name("vendor").value(appInfo.getCompanyName());
- writer.name("isEAP").value(appInfo.isEAP());
- writer.name("productCode").value(appInfo.getBuild().getProductCode());
- writer.name("buildDate").value(appInfo.getBuildDate().getTime().getTime());
- writer.name("isSnapshot").value(appInfo.getBuild().isSnapshot());
- writer.name("configPath").value(PathManager.getConfigPath());
- writer.name("systemPath").value(PathManager.getSystemPath());
- writer.name("binPath").value(PathManager.getBinPath());
- writer.name("logPath").value(PathManager.getLogPath());
- writer.name("homePath").value(PathManager.getHomePath());
- }
-
- writer.endObject();
- writer.close();
+ override fun execute(urlDecoder: QueryStringDecoder, request: FullHttpRequest, context: ChannelHandlerContext): String? {
+ val byteOut = BufferExposingByteArrayOutputStream()
+ writeApplicationInfoJson(byteOut, urlDecoder)
+ send(byteOut, request, context)
+ return null
}
}
+
+private fun isTrustedOrigin(request: HttpRequest): Boolean {
+ val origin = request.origin ?: return false
+ @Suppress("SpellCheckingInspection")
+ return origin == "hyperskill.org"
+}
+
+fun writeApplicationInfoJson(out: OutputStream, urlDecoder: QueryStringDecoder?) {
+ val writer = JsonFactory().createGenerator(out).useDefaultPrettyPrinter()
+ writer.obj {
+ writeAboutJson(writer)
+ if (urlDecoder != null && getBooleanParameter("registeredFileTypes", urlDecoder)) {
+ writer.array("registeredFileTypes") {
+ for (fileType in FileTypeRegistry.getInstance().registeredFileTypes) {
+ writer.obj {
+ writer.writeStringField("name", fileType.name)
+ writer.writeStringField("description", fileType.description)
+ writer.writeBooleanField("isBinary", fileType.isBinary)
+ }
+ }
+ }
+ }
+ if (urlDecoder != null && getBooleanParameter("more", urlDecoder)) {
+ val appInfo = ApplicationInfoEx.getInstanceEx()
+ writer.writeStringField("vendor", appInfo.companyName)
+ writer.writeBooleanField("isEAP", appInfo.isEAP)
+ writer.writeStringField("productCode", appInfo.build.productCode)
+ writer.writeNumberField("buildDate", appInfo.buildDate.time.time)
+ writer.writeBooleanField("isSnapshot", appInfo.build.isSnapshot)
+ writer.writeStringField("configPath", PathManager.getConfigPath())
+ writer.writeStringField("systemPath", PathManager.getSystemPath())
+ writer.writeStringField("binPath", PathManager.getBinPath())
+ writer.writeStringField("logPath", PathManager.getLogPath())
+ writer.writeStringField("homePath", PathManager.getHomePath())
+ }
+ }
+ writer.close()
+}
+
+fun writeAboutJson(writer: JsonGenerator) {
+ var appName = ApplicationInfoEx.getInstanceEx().fullApplicationName
+ if (!PlatformUtils.isIdeaUltimate()) {
+ val productName = ApplicationNamesInfo.getInstance().productName
+ appName = appName
+ .replace("$productName ($productName)", productName)
+ .removePrefix("JetBrains ")
+ }
+ writer.writeStringField("name", appName)
+ writer.writeStringField("productName", ApplicationNamesInfo.getInstance().productName)
+
+ val build = ApplicationInfo.getInstance().build
+ writer.writeNumberField("baselineVersion", build.baselineVersion)
+ if (!build.isSnapshot) {
+ writer.writeStringField("buildNumber", build.asStringWithoutProductCode())
+ }
+}
\ No newline at end of file
diff --git a/platform/built-in-server/src/org/jetbrains/ide/InstallPluginService.kt b/platform/built-in-server/src/org/jetbrains/ide/InstallPluginService.kt
index 74426a7ccd68..ed8892be0985 100644
--- a/platform/built-in-server/src/org/jetbrains/ide/InstallPluginService.kt
+++ b/platform/built-in-server/src/org/jetbrains/ide/InstallPluginService.kt
@@ -1,4 +1,4 @@
-// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.ide
import com.intellij.ide.plugins.marketplace.MarketplaceRequests
@@ -10,12 +10,11 @@ import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.extensions.PluginId
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.updateSettings.impl.pluginsAdvertisement.PluginsAdvertiser
-import com.intellij.openapi.util.BuildNumber
import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream
import com.intellij.openapi.util.text.StringUtil
import com.intellij.ui.AppIcon
import com.intellij.util.PlatformUtils
-import com.intellij.util.io.hostName
+import com.intellij.util.io.getHostName
import com.intellij.util.io.origin
import com.intellij.util.net.NetUtils
import com.intellij.util.text.nullize
@@ -127,7 +126,7 @@ internal class InstallPluginService : RestService() {
return false
}
- val hostName = request.hostName
+ val hostName = getHostName(request)
if (hostName != null && !NetUtils.isLocalhost(hostName)) {
LOG.error("Expected 'request.hostName' to be localhost. hostName='$hostName', origin='$origin'")
}
diff --git a/platform/built-in-server/src/org/jetbrains/ide/StartUpMeasurementService.kt b/platform/built-in-server/src/org/jetbrains/ide/StartUpMeasurementService.kt
index 57033477f98f..f0a0fadd1b2f 100644
--- a/platform/built-in-server/src/org/jetbrains/ide/StartUpMeasurementService.kt
+++ b/platform/built-in-server/src/org/jetbrains/ide/StartUpMeasurementService.kt
@@ -1,11 +1,11 @@
-// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.ide
import com.intellij.diagnostic.StartUpPerformanceService
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ex.ApplicationInfoEx
import com.intellij.openapi.diagnostic.logger
-import com.intellij.util.io.hostName
+import com.intellij.util.io.getHostName
import com.intellij.util.io.origin
import com.intellij.util.net.NetUtils
import io.netty.buffer.Unpooled
@@ -45,7 +45,7 @@ internal class StartUpMeasurementService : RestService() {
}
private fun isTrustedHostName(request: HttpRequest): Boolean {
- val hostName = request.hostName ?: return false
+ val hostName = getHostName(request) ?: return false
if (!NetUtils.isLocalhost(hostName)) {
LOG.error("Expected 'request.hostName' to be localhost. hostName=$hostName, origin=${request.origin}")
}
diff --git a/platform/platform-impl/src/com/intellij/ide/IdeAboutInfoUtil.java b/platform/platform-impl/src/com/intellij/ide/IdeAboutInfoUtil.java
deleted file mode 100644
index 3c695b572931..000000000000
--- a/platform/platform-impl/src/com/intellij/ide/IdeAboutInfoUtil.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
-package com.intellij.ide;
-
-import com.google.gson.stream.JsonWriter;
-import com.intellij.openapi.application.ApplicationInfo;
-import com.intellij.openapi.application.ApplicationNamesInfo;
-import com.intellij.openapi.application.ex.ApplicationInfoEx;
-import com.intellij.openapi.util.BuildNumber;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.util.PlatformUtils;
-
-import java.io.IOException;
-
-public final class IdeAboutInfoUtil {
- public static void writeAboutJson(JsonWriter writer) throws IOException {
- String appName = ApplicationInfoEx.getInstanceEx().getFullApplicationName();
- BuildNumber build = ApplicationInfo.getInstance().getBuild();
-
- if (!PlatformUtils.isIdeaUltimate()) {
- String productName = ApplicationNamesInfo.getInstance().getProductName();
- appName = appName.replace(productName + " (" + productName + ")", productName);
- appName = StringUtil.trimStart(appName, "JetBrains ");
- }
-
- writer.name("name").value(appName);
- writer.name("productName").value(ApplicationNamesInfo.getInstance().getProductName());
- writer.name("baselineVersion").value(build.getBaselineVersion());
- if (!build.isSnapshot()) {
- writer.name("buildNumber").value(build.asStringWithoutProductCode());
- }
- }
-}
diff --git a/platform/platform-util-io/src/com/intellij/util/io/netty.kt b/platform/platform-util-io/src/com/intellij/util/io/netty.kt
index b7af1733291c..b3b5be5c3599 100644
--- a/platform/platform-util-io/src/com/intellij/util/io/netty.kt
+++ b/platform/platform-util-io/src/com/intellij/util/io/netty.kt
@@ -197,12 +197,11 @@ val Channel.uriScheme: String
val HttpRequest.host: String?
get() = headers().getAsString(HttpHeaderNames.HOST)
-val HttpRequest.hostName: String?
- get() {
- val hostAndPort = headers().getAsString(HttpHeaderNames.HOST)?.ifBlank { null } ?: return null
- val portIndex = hostAndPort.lastIndexOf(':')
- return if (portIndex > 0) hostAndPort.substring(0, portIndex).ifBlank { null } else hostAndPort
- }
+fun getHostName(httpRequest: HttpRequest): String? {
+ val hostAndPort = httpRequest.headers().getAsString(HttpHeaderNames.HOST)?.ifBlank { null } ?: return null
+ val portIndex = hostAndPort.lastIndexOf(':')
+ return if (portIndex > 0) hostAndPort.substring(0, portIndex).ifBlank { null } else hostAndPort
+}
val HttpRequest.origin: String?
get() = headers().getAsString(HttpHeaderNames.ORIGIN)
diff --git a/platform/platform-util-io/src/org/jetbrains/ide/HttpRequestHandler.kt b/platform/platform-util-io/src/org/jetbrains/ide/HttpRequestHandler.kt
index c1696e0cb6a7..334d8b35128a 100644
--- a/platform/platform-util-io/src/org/jetbrains/ide/HttpRequestHandler.kt
+++ b/platform/platform-util-io/src/org/jetbrains/ide/HttpRequestHandler.kt
@@ -2,7 +2,7 @@
package org.jetbrains.ide
import com.intellij.openapi.extensions.ExtensionPointName
-import com.intellij.util.io.hostName
+import com.intellij.util.io.getHostName
import com.intellij.util.io.isLocalHost
import com.intellij.util.io.isLocalOrigin
import io.netty.channel.Channel
@@ -42,7 +42,7 @@ abstract class HttpRequestHandler {
*/
@SuppressWarnings("SpellCheckingInspection")
open fun isAccessible(request: HttpRequest): Boolean {
- val hostName = request.hostName
+ val hostName = getHostName(request)
// If attacker.com DNS rebound to 127.0.0.1 and user open site directly - no Origin or Referrer headers.
// So we should check Host header.
return hostName != null && request.isLocalOrigin() && isLocalHost(hostName)