some (not a public API) of org.jetbrains.io classes moved to com.intellij.util.io, part 1

This commit is contained in:
Vladimir Krivosheev
2016-09-08 16:19:44 +02:00
parent fabf9a06e8
commit 771a883e9b
18 changed files with 166 additions and 148 deletions
@@ -25,13 +25,14 @@ import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.VFileProperty
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.PathUtilRt
import com.intellij.util.io.isDirectory
import com.intellij.util.io.*
import io.netty.channel.Channel
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.HttpResponseStatus
import org.jetbrains.io.*
import org.jetbrains.io.orInSafeMode
import org.jetbrains.io.send
import java.nio.file.Path
import java.nio.file.Paths
import java.util.regex.Pattern
@@ -18,13 +18,13 @@ package org.jetbrains.builtInWebServer
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.util.io.host
import com.intellij.util.io.uriScheme
import io.netty.channel.Channel
import io.netty.channel.ChannelHandlerContext
import io.netty.handler.codec.http.*
import org.jetbrains.io.host
import org.jetbrains.io.response
import org.jetbrains.io.send
import org.jetbrains.io.uriScheme
/**
* By default [WebServerPathToFileManager] will be used to map request to file.
@@ -41,6 +41,7 @@ import com.intellij.openapi.wm.IdeFrame;
import com.intellij.util.ExceptionUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.io.NettyKt;
import com.intellij.util.net.NetUtils;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.Unpooled;
@@ -50,7 +51,6 @@ import io.netty.handler.codec.http.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.builtInWebServer.BuiltInWebServerKt;
import org.jetbrains.io.NettyKt;
import org.jetbrains.io.Responses;
import javax.swing.*;
@@ -1,6 +1,7 @@
package org.jetbrains.io.fastCgi
import com.intellij.openapi.util.io.FileUtil
import com.intellij.util.io.writeUtf8
import io.netty.buffer.ByteBuf
import io.netty.buffer.ByteBufAllocator
import io.netty.buffer.ByteBufUtil
@@ -8,7 +9,6 @@ import io.netty.channel.Channel
import io.netty.handler.codec.http.FullHttpRequest
import org.jetbrains.builtInWebServer.PathInfo
import org.jetbrains.io.serverHeaderValue
import org.jetbrains.io.writeUtf8
import java.net.InetSocketAddress
import java.util.*
@@ -15,14 +15,14 @@ import com.intellij.util.ArrayUtil
import com.intellij.util.ArrayUtilRt
import com.intellij.util.Consumer
import com.intellij.util.SmartList
import com.intellij.util.io.releaseIfError
import com.intellij.util.io.writeUtf8
import gnu.trove.THashMap
import gnu.trove.TIntArrayList
import io.netty.buffer.*
import org.jetbrains.concurrency.Promise
import org.jetbrains.io.JsonReaderEx
import org.jetbrains.io.JsonUtil
import org.jetbrains.io.releaseIfError
import org.jetbrains.io.writeUtf8
import java.io.IOException
import java.lang.reflect.Method
import java.util.concurrent.atomic.AtomicInteger
@@ -40,7 +40,7 @@ private val INT_LIST_TYPE_ADAPTER_FACTORY = object : TypeAdapterFactory {
if (typeAdapter == null) {
typeAdapter = IntArrayListTypeAdapter<TIntArrayList>()
}
@Suppress("CAST_NEVER_SUCCEEDS")
@Suppress("UNCHECKED_CAST")
return typeAdapter as TypeAdapter<T>?
}
}
@@ -6,12 +6,13 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.io.NettyKt;
import org.jetbrains.io.jsonRpc.Client;
import org.jetbrains.io.jsonRpc.ClientManager;
import org.jetbrains.io.jsonRpc.ClientManagerKt;
import org.jetbrains.io.jsonRpc.MessageServer;
import static com.intellij.util.io.NettyKt.readUtf8;
@ChannelHandler.Sharable
final class MessageChannelHandler extends WebSocketProtocolHandler {
private final ClientManager clientManager;
@@ -43,7 +44,7 @@ final class MessageChannelHandler extends WebSocketProtocolHandler {
WebSocketClient client = (WebSocketClient)channel.attr(ClientManagerKt.getCLIENT()).get();
CharSequence chars;
try {
chars = NettyKt.readUtf8(message.content());
chars = readUtf8(message.content());
}
catch (Throwable e) {
try {
@@ -1,4 +1,4 @@
package org.jetbrains.io
package com.intellij.util.io
import io.netty.buffer.Unpooled
import org.assertj.core.api.Assertions.assertThat
@@ -1,4 +1,4 @@
package org.jetbrains.io
package com.intellij.util.io
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
@@ -23,10 +23,10 @@ internal abstract class BuiltInServerTestCase {
protected val manager = TestManager(projectRule, tempDirManager)
private val ruleChain = RuleChain(
tempDirManager,
Timeout(60, TimeUnit.SECONDS),
manager,
DisposeModulesRule(projectRule))
tempDirManager,
Timeout(60, TimeUnit.SECONDS),
manager,
DisposeModulesRule(projectRule))
@Rule fun getChain() = ruleChain
protected open val urlPathPrefix = ""
@@ -0,0 +1,131 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util.io
import com.google.common.net.InetAddresses
import com.intellij.util.Url
import com.intellij.util.Urls
import com.intellij.util.net.NetUtils
import io.netty.buffer.ByteBuf
import io.netty.channel.Channel
import io.netty.handler.codec.http.HttpHeaderNames
import io.netty.handler.codec.http.HttpMethod
import io.netty.handler.codec.http.HttpRequest
import io.netty.handler.ssl.SslHandler
import java.io.IOException
import java.net.InetAddress
import java.net.NetworkInterface
val Channel.uriScheme: String
get() = if (pipeline().get(SslHandler::class.java) == null) "http" else "https"
val HttpRequest.host: String?
get() = headers().getAsString(HttpHeaderNames.HOST)
val HttpRequest.origin: String?
get() = headers().getAsString(HttpHeaderNames.ORIGIN)
val HttpRequest.referrer: String?
get() = headers().getAsString(HttpHeaderNames.REFERER)
val HttpRequest.userAgent: String?
get() = headers().getAsString(HttpHeaderNames.USER_AGENT)
inline fun <T> ByteBuf.releaseIfError(task: () -> T): T {
try {
return task()
}
catch (e: Exception) {
try {
release()
}
finally {
throw e
}
}
}
fun isLocalHost(host: String, onlyAnyOrLoopback: Boolean, hostsOnly: Boolean = false): Boolean {
if (NetUtils.isLocalhost(host)) {
return true
}
// if IP address, it is safe to use getByName (not affected by DNS rebinding)
if (onlyAnyOrLoopback && !InetAddresses.isInetAddress(host)) {
return false
}
fun InetAddress.isLocal() = isAnyLocalAddress || isLoopbackAddress || NetworkInterface.getByInetAddress(this) != null
try {
val address = InetAddress.getByName(host)
if (!address.isLocal()) {
return false
}
// be aware - on windows hosts file doesn't contain localhost
// hosts can contain remote addresses, so, we check it
if (hostsOnly && !InetAddresses.isInetAddress(host)) {
return io.netty.resolver.HostsFileEntriesResolver.DEFAULT.address(host).let { it != null && it.isLocal() }
}
else {
return true
}
}
catch (ignored: IOException) {
return false
}
}
@JvmOverloads
fun HttpRequest.isLocalOrigin(onlyAnyOrLoopback: Boolean = true, hostsOnly: Boolean = false) = parseAndCheckIsLocalHost(origin, onlyAnyOrLoopback, hostsOnly) && parseAndCheckIsLocalHost(referrer, onlyAnyOrLoopback, hostsOnly)
private fun isTrustedChromeExtension(url: Url): Boolean {
return url.scheme == "chrome-extension" && (url.authority == "hmhgeddbohgjknpmjagkdomcpobmllji" || url.authority == "offnedcbhjldheanlbojaefbfbllddna")
}
private val Url.host: String?
get() = authority?.let {
val portIndex = it.indexOf(':')
if (portIndex > 0) it.substring(0, portIndex) else it
}
@JvmOverloads
fun parseAndCheckIsLocalHost(uri: String?, onlyAnyOrLoopback: Boolean = true, hostsOnly: Boolean = false): Boolean {
if (uri == null || uri == "about:blank") {
return true
}
try {
val parsedUri = Urls.parse(uri, false) ?: return false
val host = parsedUri.host
return host != null && (isTrustedChromeExtension(parsedUri) || isLocalHost(host, onlyAnyOrLoopback, hostsOnly))
}
catch (ignored: Exception) {
}
return false
}
fun HttpRequest.isRegularBrowser() = userAgent?.startsWith("Mozilla/5.0") ?: false
// forbid POST requests from browser without Origin
fun HttpRequest.isWriteFromBrowserWithoutOrigin(): Boolean {
val method = method()
return origin.isNullOrEmpty() && isRegularBrowser() && (method == HttpMethod.POST || method == HttpMethod.PATCH || method == HttpMethod.PUT || method == HttpMethod.DELETE)
}
fun ByteBuf.readUtf8() = toString(Charsets.UTF_8)
fun ByteBuf.writeUtf8(data: CharSequence) = writeCharSequence(data, Charsets.UTF_8)
@@ -20,11 +20,13 @@ import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import static com.intellij.util.io.NettyKt.readUtf8;
@SuppressWarnings("unused")
@Deprecated
public final class ChannelBufferToString {
@NotNull
public static CharSequence readChars(@NotNull ByteBuf buffer) throws IOException {
return NettyKt.readUtf8(buffer);
return readUtf8(buffer);
}
}
@@ -18,6 +18,7 @@ package org.jetbrains.io
import com.intellij.openapi.application.ex.ApplicationInfoEx
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.IconLoader
import com.intellij.util.io.isWriteFromBrowserWithoutOrigin
import com.intellij.util.ui.UIUtil
import io.netty.buffer.Unpooled
import io.netty.channel.ChannelHandler
@@ -82,7 +83,7 @@ internal class DelegatingHttpRequestHandler : DelegatingHttpRequestHandlerBase()
@Suppress("OverridingDeprecatedMember")
override fun exceptionCaught(context: ChannelHandlerContext, cause: Throwable) {
try {
context.channel().attr(PREV_HANDLER).remove()
context.channel().attr(PREV_HANDLER).set(null)
}
finally {
@Suppress("DEPRECATION")
@@ -15,33 +15,20 @@
*/
package org.jetbrains.io
import com.google.common.net.InetAddresses
import com.intellij.openapi.util.Condition
import com.intellij.openapi.util.Conditions
import com.intellij.util.Url
import com.intellij.util.Urls
import com.intellij.util.net.NetUtils
import io.netty.bootstrap.Bootstrap
import io.netty.bootstrap.ServerBootstrap
import io.netty.buffer.ByteBuf
import io.netty.channel.*
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.oio.OioEventLoopGroup
import io.netty.channel.socket.nio.NioServerSocketChannel
import io.netty.channel.socket.oio.OioServerSocketChannel
import io.netty.channel.socket.oio.OioSocketChannel
import io.netty.handler.codec.http.HttpHeaderNames
import io.netty.handler.codec.http.HttpMethod
import io.netty.handler.codec.http.HttpRequest
import io.netty.handler.ssl.SslHandler
import io.netty.resolver.HostsFileEntriesResolver
import io.netty.util.concurrent.GenericFutureListener
import org.jetbrains.concurrency.AsyncPromise
import org.jetbrains.ide.PooledThreadExecutor
import java.io.IOException
import java.net.InetAddress
import java.net.InetSocketAddress
import java.net.NetworkInterface
import java.util.concurrent.TimeUnit
inline fun Bootstrap.handler(crossinline task: (Channel) -> Unit): Bootstrap {
@@ -99,105 +86,4 @@ fun Bootstrap.connect(remoteAddress: InetSocketAddress, promise: AsyncPromise<*>
promise?.setError(e)
return null
}
}
val Channel.uriScheme: String
get() = if (pipeline().get(SslHandler::class.java) == null) "http" else "https"
val HttpRequest.host: String?
get() = headers().getAsString(HttpHeaderNames.HOST)
val HttpRequest.origin: String?
get() = headers().getAsString(HttpHeaderNames.ORIGIN)
val HttpRequest.referrer: String?
get() = headers().getAsString(HttpHeaderNames.REFERER)
val HttpRequest.userAgent: String?
get() = headers().getAsString(HttpHeaderNames.USER_AGENT)
inline fun <T> ByteBuf.releaseIfError(task: () -> T): T {
try {
return task()
}
catch (e: Exception) {
try {
release()
}
finally {
throw e
}
}
}
fun isLocalHost(host: String, onlyAnyOrLoopback: Boolean, hostsOnly: Boolean = false): Boolean {
if (NetUtils.isLocalhost(host)) {
return true
}
// if IP address, it is safe to use getByName (not affected by DNS rebinding)
if (onlyAnyOrLoopback && !InetAddresses.isInetAddress(host)) {
return false
}
fun InetAddress.isLocal() = isAnyLocalAddress || isLoopbackAddress || NetworkInterface.getByInetAddress(this) != null
try {
val address = InetAddress.getByName(host)
if (!address.isLocal()) {
return false
}
// be aware - on windows hosts file doesn't contain localhost
// hosts can contain remote addresses, so, we check it
if (hostsOnly && !InetAddresses.isInetAddress(host)) {
return HostsFileEntriesResolver.DEFAULT.address(host).let { it != null && it.isLocal() }
}
else {
return true
}
}
catch (ignored: IOException) {
return false
}
}
@JvmOverloads
fun HttpRequest.isLocalOrigin(onlyAnyOrLoopback: Boolean = true, hostsOnly: Boolean = false) = parseAndCheckIsLocalHost(origin, onlyAnyOrLoopback, hostsOnly) && parseAndCheckIsLocalHost(referrer, onlyAnyOrLoopback, hostsOnly)
private fun isTrustedChromeExtension(url: Url): Boolean {
return url.scheme == "chrome-extension" && (url.authority == "hmhgeddbohgjknpmjagkdomcpobmllji" || url.authority == "offnedcbhjldheanlbojaefbfbllddna")
}
private val Url.host: String?
get() = authority?.let {
val portIndex = it.indexOf(':')
if (portIndex > 0) it.substring(0, portIndex) else it
}
@JvmOverloads
fun parseAndCheckIsLocalHost(uri: String?, onlyAnyOrLoopback: Boolean = true, hostsOnly: Boolean = false): Boolean {
if (uri == null || uri == "about:blank") {
return true
}
try {
val parsedUri = Urls.parse(uri, false) ?: return false
val host = parsedUri.host
return host != null && (isTrustedChromeExtension(parsedUri) || isLocalHost(host, onlyAnyOrLoopback, hostsOnly))
}
catch (ignored: Exception) {
}
return false
}
fun HttpRequest.isRegularBrowser() = userAgent?.startsWith("Mozilla/5.0") ?: false
// forbid POST requests from browser without Origin
fun HttpRequest.isWriteFromBrowserWithoutOrigin(): Boolean {
val method = method()
return origin.isNullOrEmpty() && isRegularBrowser() && (method == HttpMethod.POST || method == HttpMethod.PATCH || method == HttpMethod.PUT || method == HttpMethod.DELETE)
}
fun ByteBuf.readUtf8() = toString(Charsets.UTF_8)
fun ByteBuf.writeUtf8(data: CharSequence) = writeCharSequence(data, Charsets.UTF_8)
}
@@ -18,14 +18,13 @@ package org.jetbrains.jsonProtocol
import com.google.gson.stream.JsonWriter
import com.intellij.openapi.vfs.CharsetToolkit
import com.intellij.util.containers.isNullOrEmpty
import com.intellij.util.io.writeUtf8
import gnu.trove.TIntArrayList
import gnu.trove.TIntHashSet
import io.netty.buffer.ByteBuf
import io.netty.buffer.ByteBufAllocator
import io.netty.buffer.ByteBufUtf8Writer
import org.jetbrains.io.JsonUtil
import org.jetbrains.io.writeUtf8
import java.io.IOException
open class OutMessage() {
val buffer: ByteBuf = ByteBufAllocator.DEFAULT.heapBuffer()
@@ -204,19 +203,16 @@ open class OutMessage() {
beginArguments()
writer.name(name).value(value?.toString() ?: null)
}
}
companion object {
@Throws(IOException::class)
fun prepareWriteRaw(message: OutMessage, name: String) {
message.writer.name(name).nullValue()
val itemBuffer = message.buffer
itemBuffer.writerIndex(itemBuffer.writerIndex() - "null".length)
}
fun prepareWriteRaw(message: OutMessage, name: String) {
message.writer.name(name).nullValue()
val itemBuffer = message.buffer
itemBuffer.writerIndex(itemBuffer.writerIndex() - "null".length)
}
fun doWriteRaw(message: OutMessage, rawValue: String) {
message.buffer.writeUtf8(rawValue)
}
}
fun doWriteRaw(message: OutMessage, rawValue: String) {
message.buffer.writeUtf8(rawValue)
}
fun OutMessage.writeEnum(name: String, value: Enum<*>?, defaultValue: Enum<*>?) {
@@ -238,7 +234,7 @@ fun OutMessage.writeString(name: String, value: CharSequence?, defaultValue: Cha
fun OutMessage.writeString(name: String, value: CharSequence) {
beginArguments()
OutMessage.prepareWriteRaw(this, name)
prepareWriteRaw(this, name)
JsonUtil.escape(value, buffer)
}