Revision control
Copy as Markdown
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
import okhttp3.OkHttpClient
import okhttp3.Request
import okio.ByteString
import okio.ByteString.Companion.encodeUtf8
import okio.buffer
import okio.sink
import org.gradle.api.Plugin
import org.gradle.api.Project
import java.io.File
import java.util.TreeSet
/**
* Gradle plugin to update the public suffix list used by the `lib-publicsuffixlist` component.
*
* Base on PublicSuffixListGenerator from OkHttp:
*/
class PublicSuffixListPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.tasks.register("updatePSL") {
doLast {
val filename = project.projectDir.absolutePath + "/src/main/assets/publicsuffixes"
updatePublicSuffixList(filename)
}
}
}
private fun updatePublicSuffixList(destination: String) {
val list = fetchPublicSuffixList()
writeListToDisk(destination, list)
}
private fun writeListToDisk(destination: String, data: PublicSuffixListData) {
val fileSink = File(destination).sink()
fileSink.buffer().use { sink ->
sink.writeInt(data.totalRuleBytes)
for (domain in data.sortedRules) {
sink.write(domain).writeByte('\n'.code)
}
sink.writeInt(data.totalExceptionRuleBytes)
for (domain in data.sortedExceptionRules) {
sink.write(domain).writeByte('\n'.code)
}
}
}
private fun fetchPublicSuffixList(): PublicSuffixListData {
val client = OkHttpClient.Builder().build()
val request = Request.Builder()
.build()
client.newCall(request).execute().use { response ->
val source = response.body!!.source()
val data = PublicSuffixListData()
while (!source.exhausted()) {
val line = source.readUtf8LineStrict()
if (line.trim { it <= ' ' }.isEmpty() || line.startsWith("//")) {
continue
}
if (line.contains(WILDCARD_CHAR)) {
assertWildcardRule(line)
}
var rule = line.encodeUtf8()
if (rule.startsWith(EXCEPTION_RULE_MARKER)) {
rule = rule.substring(1)
// We use '\n' for end of value.
data.totalExceptionRuleBytes += rule.size + 1
data.sortedExceptionRules.add(rule)
} else {
data.totalRuleBytes += rule.size + 1 // We use '\n' for end of value.
data.sortedRules.add(rule)
}
}
return data
}
}
@Suppress("TooGenericExceptionThrown", "ThrowsCount")
private fun assertWildcardRule(rule: String) {
if (rule.indexOf(WILDCARD_CHAR) != 0) {
throw RuntimeException("Wildcard is not not in leftmost position")
}
if (rule.indexOf(WILDCARD_CHAR, 1) != -1) {
throw RuntimeException("Rule contains multiple wildcards")
}
if (rule.length == 1) {
throw RuntimeException("Rule wildcards the first level")
}
}
companion object {
private const val WILDCARD_CHAR = "*"
private val EXCEPTION_RULE_MARKER = "!".encodeUtf8()
}
}
data class PublicSuffixListData(
var totalRuleBytes: Int = 0,
var totalExceptionRuleBytes: Int = 0,
val sortedRules: TreeSet<ByteString> = TreeSet(),
val sortedExceptionRules: TreeSet<ByteString> = TreeSet(),
)