Comprehensive URL manipulation including parsing, building, encoding, and parameter handling with support for all URL components and proper encoding/decoding.
data class URLProtocol(val name: String, val defaultPort: Int)data class URLProtocol(val name: String, val defaultPort: Int) {
companion object {
val HTTP: URLProtocol // http, port 80
val HTTPS: URLProtocol // https, port 443
val WS: URLProtocol // ws, port 80
val WSS: URLProtocol // wss, port 443
val SOCKS: URLProtocol // socks, port 1080
val byName: Map<String, URLProtocol>
fun createOrDefault(name: String): URLProtocol
}
}
fun URLProtocol.isWebsocket(): Boolean
fun URLProtocol.isSecure(): Boolean// Using predefined protocols
val httpProtocol = URLProtocol.HTTP
val httpsProtocol = URLProtocol.HTTPS
// Creating custom protocols
val customProtocol = URLProtocol("custom", 8080)
// Getting or creating protocols
val ftpProtocol = URLProtocol.createOrDefault("ftp")
// Protocol utilities
val isWebSocket = URLProtocol.WS.isWebsocket() // true
val isSecure = URLProtocol.HTTPS.isSecure() // true
val httpNotSecure = URLProtocol.HTTP.isSecure() // false
// Protocol lookup
val httpFromMap = URLProtocol.byName["http"] // Returns URLProtocol.HTTPclass Url {
val protocol: URLProtocol
val host: String
val port: Int
val specifiedPort: Int
val encodedPath: String
val parameters: Parameters
val fragment: String
val user: String?
val password: String?
val trailingQuery: Boolean
// Derived properties
@Deprecated("Use rawSegments or segments instead")
val pathSegments: List<String>
val rawSegments: List<String>
val segments: List<String>
val encodedQuery: String
val encodedPathAndQuery: String
val encodedFragment: String
val encodedUser: String?
val encodedPassword: String?
override fun toString(): String
}URLs are typically created through URLBuilder or parsing functions rather than direct construction.
class URLBuilder {
var protocol: URLProtocol
var host: String
var port: Int
var encodedPath: String
val parameters: ParametersBuilder
var fragment: String
var user: String?
var password: String?
var trailingQuery: Boolean
fun build(): Url
fun clone(): URLBuilder
}// Constructors
fun URLBuilder(url: Url): URLBuilder
fun URLBuilder(url: String): URLBuilder
fun URLBuilder(
protocol: URLProtocol = URLProtocol.HTTP,
host: String = "localhost",
port: Int = protocol.defaultPort,
path: String = "/",
user: String? = null,
password: String? = null,
parameters: Parameters = Parameters.Empty,
fragment: String = "",
trailingQuery: Boolean = false
): URLBuilderfun URLBuilder.appendPathSegments(vararg segments: String): URLBuilder
fun URLBuilder.appendPathSegments(segments: List<String>): URLBuilder
fun URLBuilder.path(vararg components: String): URLBuilder
fun URLBuilder.pathSegments(segments: List<String>): URLBuilder// Encoding functions
fun String.encodeURLQueryComponent(
encodeFull: Boolean = false,
spaceToPlus: Boolean = false
): String
fun String.encodeURLPathPart(): String
fun String.encodeURLPath(
encodeFull: Boolean = false
): String
fun String.encodeOAuth(): String
fun String.encodeURLParameter(spaceToPlus: Boolean = false): String
// Decoding functions
fun String.decodeURLQueryComponent(
decode: Boolean = true,
plusToSpace: Boolean = false
): String
fun String.decodeURLPart(
start: Int = 0,
end: Int = length,
charset: Charset = Charsets.UTF_8
): Stringfun Url(urlString: String): Url
fun takeFrom(url: Url): URLBuilder
fun takeFrom(url: String): URLBuilder// Create URL from string
val url = Url("https://example.com/api/users?page=1&limit=10#section")
// Access URL components
println("Protocol: ${url.protocol}")
println("Host: ${url.host}")
println("Path: ${url.encodedPath}")
println("Query: ${url.encodedQuery}")
println("Fragment: ${url.fragment}")
// Build URL programmatically
val apiUrl = URLBuilder("https://api.example.com")
.appendPathSegments("v1", "users", "123")
.apply {
parameters.append("include", "profile")
parameters.append("format", "json")
}
.build()
println(apiUrl) // https://api.example.com/v1/users/123?include=profile&format=json// Complex URL with all components
val complexUrl = URLBuilder(
protocol = URLProtocol.HTTPS,
host = "secure.example.com",
port = 8443,
user = "admin",
password = "secret"
).apply {
appendPathSegments("admin", "dashboard")
parameters.append("tab", "users")
parameters.append("sort", "name")
fragment = "user-list"
}.build()
// Modify existing URL
val modifiedUrl = URLBuilder(complexUrl).apply {
host = "backup.example.com"
port = 9443
parameters.clear()
parameters.append("backup", "true")
}.build()// Working with path segments
val builder = URLBuilder("https://api.example.com")
builder.path("api", "v2", "resources")
// Results in: /api/v2/resources
// Appending additional segments
builder.appendPathSegments("123", "details")
// Results in: /api/v2/resources/123/details
// Working with parameters
builder.parameters.apply {
append("page", "1")
append("size", "50")
append("sort", "name")
append("sort", "date") // Multiple values for same key
set("filter", "active") // Replace existing values
}
val finalUrl = builder.build()// Query component encoding
val query = "hello world & special chars"
val encoded = query.encodeURLQueryComponent()
println(encoded) // hello%20world%20%26%20special%20chars
// Path segment encoding
val pathSegment = "documents/my file.pdf"
val encodedPath = pathSegment.encodeURLPathPart()
println(encodedPath) // documents%2Fmy%20file.pdf
// OAuth-specific encoding
val oauthParam = "some+special/chars"
val oauthEncoded = oauthParam.encodeOAuth()
println(oauthEncoded) // some%2Bspecial%2Fchars
// Decoding
val decoded = encoded.decodeURLQueryComponent()
println(decoded) // hello world & special charsval originalUrl = URLBuilder("https://example.com/api/users")
.apply {
parameters.append("page", "1")
parameters.append("limit", "10")
}
.build()
// Clone and modify
val modifiedUrl = URLBuilder(originalUrl).apply {
appendPathSegments("123")
parameters.set("page", "2")
parameters.append("include", "profile")
}.build()
println(originalUrl) // https://example.com/api/users?page=1&limit=10
println(modifiedUrl) // https://example.com/api/users/123?page=2&limit=10&include=profiletry {
val url = Url("invalid-url")
} catch (e: URLParserException) {
println("Failed to parse URL: ${e.message}")
}
try {
val decoded = "invalid%encoding".decodeURLPart()
} catch (e: URLDecodeException) {
println("Failed to decode URL part: ${e.message}")
}// Define custom protocol
val customProtocol = URLProtocol("myapp", 9999)
val customUrl = URLBuilder(
protocol = customProtocol,
host = "internal.service",
path = "/custom/endpoint"
).build()
println(customUrl) // myapp://internal.service:9999/custom/endpointfun buildApiUrl(
baseUrl: String,
version: String,
resource: String,
id: String? = null,
queryParams: Map<String, String> = emptyMap()
): Url {
return URLBuilder(baseUrl).apply {
appendPathSegments("api", version, resource)
id?.let { appendPathSegments(it) }
queryParams.forEach { (key, value) ->
parameters.append(key, value)
}
}.build()
}
// Usage
val userUrl = buildApiUrl(
baseUrl = "https://api.example.com",
version = "v1",
resource = "users",
id = "123",
queryParams = mapOf(
"include" to "profile",
"format" to "json"
)
)