TUN-1734: Pin packages at exact versions
This commit is contained in:
parent
2e2fa29637
commit
bab7583a97
|
@ -2,20 +2,20 @@
|
||||||
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:b16fbfbcc20645cb419f78325bb2e85ec729b338e996a228124d68931a6f2a37"
|
digest = "1:9f3b30d9f8e0d7040f729b82dcbc8f0dead820a133b3147ce355fc451f32d761"
|
||||||
name = "github.com/BurntSushi/toml"
|
name = "github.com/BurntSushi/toml"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "b26d9c308763d68093482582cea63d69be07a0f0"
|
revision = "3012a1dbe2e4bd1391d42b32f0577cb7bbc7f005"
|
||||||
version = "v0.3.0"
|
version = "v0.3.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
|
||||||
digest = "1:d6afaeed1502aa28e80a4ed0981d570ad91b2579193404256ce672ed0a609e0d"
|
digest = "1:d6afaeed1502aa28e80a4ed0981d570ad91b2579193404256ce672ed0a609e0d"
|
||||||
name = "github.com/beorn7/perks"
|
name = "github.com/beorn7/perks"
|
||||||
packages = ["quantile"]
|
packages = ["quantile"]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "3a771d992973f24aa725d07868b467d1ddfceafb"
|
revision = "4b2b341e8d7715fae06375aa633dbb6e91b3fb46"
|
||||||
|
version = "v1.0.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:fed1f537c2f1269fe475a8556c393fe466641682d73ef8fd0491cd3aa1e47bad"
|
digest = "1:fed1f537c2f1269fe475a8556c393fe466641682d73ef8fd0491cd3aa1e47bad"
|
||||||
|
@ -26,7 +26,6 @@
|
||||||
version = "2018.01.18"
|
version = "2018.01.18"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
|
||||||
digest = "1:e5003c19d396d8b3cf1324ea0bf49b00f13e9466d0297d1268b641f1c617c3a2"
|
digest = "1:e5003c19d396d8b3cf1324ea0bf49b00f13e9466d0297d1268b641f1c617c3a2"
|
||||||
name = "github.com/cloudflare/brotli-go"
|
name = "github.com/cloudflare/brotli-go"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
@ -34,7 +33,6 @@
|
||||||
revision = "18c9f6c67e3dfc12e0ddaca748d2887f97a7ac28"
|
revision = "18c9f6c67e3dfc12e0ddaca748d2887f97a7ac28"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
|
||||||
digest = "1:6dbb2bbc7e6333e691c4d82fd86485f0695a35902fbb9b2df5f72e22ab0040f3"
|
digest = "1:6dbb2bbc7e6333e691c4d82fd86485f0695a35902fbb9b2df5f72e22ab0040f3"
|
||||||
name = "github.com/cloudflare/golibs"
|
name = "github.com/cloudflare/golibs"
|
||||||
packages = ["lrucache"]
|
packages = ["lrucache"]
|
||||||
|
@ -42,8 +40,7 @@
|
||||||
revision = "333127dbecfcc23a8db7d9a4f52785d23aff44a1"
|
revision = "333127dbecfcc23a8db7d9a4f52785d23aff44a1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
digest = "1:3f9506ee991cdee1f05bf0cd3e34b5cd922dc00d6a950fb4beb4e07ab1c4d3d1"
|
||||||
digest = "1:af68c8bdac3ceaf46c67f0c43cc1ee5f10e705d8e04f8a63fa41231d0be6b0fb"
|
|
||||||
name = "github.com/coredns/coredns"
|
name = "github.com/coredns/coredns"
|
||||||
packages = [
|
packages = [
|
||||||
"core/dnsserver",
|
"core/dnsserver",
|
||||||
|
@ -72,10 +69,10 @@
|
||||||
"request",
|
"request",
|
||||||
]
|
]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "992e7928c7c258628d2b13b769acc86781b9faea"
|
revision = "2e322f6e8a54f18c6aef9c25a7c432c291a3d9f7"
|
||||||
|
version = "v1.2.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
|
||||||
digest = "1:6a503e232df389d94ebb97dfb22d4ae463b6e2f351660613e11d9e42f57ab6df"
|
digest = "1:6a503e232df389d94ebb97dfb22d4ae463b6e2f351660613e11d9e42f57ab6df"
|
||||||
name = "github.com/coreos/go-oidc"
|
name = "github.com/coreos/go-oidc"
|
||||||
packages = [
|
packages = [
|
||||||
|
@ -93,8 +90,8 @@
|
||||||
name = "github.com/coreos/go-systemd"
|
name = "github.com/coreos/go-systemd"
|
||||||
packages = ["daemon"]
|
packages = ["daemon"]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "39ca1b05acc7ad1220e09f133283b8859a8b71ab"
|
revision = "95778dfbb74eb7e4dbaf43bf7d71809650ef8076"
|
||||||
version = "v17"
|
version = "v19"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:6fda0d7f5e52b081e075775b1ecebf1ea0c923e7be33604ed0225ae078e701b5"
|
digest = "1:6fda0d7f5e52b081e075775b1ecebf1ea0c923e7be33604ed0225ae078e701b5"
|
||||||
|
@ -109,12 +106,12 @@
|
||||||
version = "v4"
|
version = "v4"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39"
|
digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec"
|
||||||
name = "github.com/davecgh/go-spew"
|
name = "github.com/davecgh/go-spew"
|
||||||
packages = ["spew"]
|
packages = ["spew"]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
|
||||||
version = "v1.1.0"
|
version = "v1.1.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
@ -125,8 +122,7 @@
|
||||||
revision = "027aa4915315a0b2825c0f025cea347829b974fa"
|
revision = "027aa4915315a0b2825c0f025cea347829b974fa"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
digest = "1:d4268b2a09b1f736633577c4ac93f2a5356c73742fff5344e2451aeec60a7ad0"
|
||||||
digest = "1:15eadee5930d325154e5e1f3c4f4b28ab62f9c0aa949580e8d9edabaf0b64e85"
|
|
||||||
name = "github.com/equinox-io/equinox"
|
name = "github.com/equinox-io/equinox"
|
||||||
packages = [
|
packages = [
|
||||||
".",
|
".",
|
||||||
|
@ -137,10 +133,10 @@
|
||||||
"proto",
|
"proto",
|
||||||
]
|
]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "f24972fa72facf59d05c91c848b65eac38815915"
|
revision = "5205c98a6c11dc72747ce12fff6cd620a99fde05"
|
||||||
|
version = "v1.2.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
|
||||||
digest = "1:433763f10d88dba9b533a7ea2fe9f5ee11e57e00306eb97a1f6090fd978e8fa1"
|
digest = "1:433763f10d88dba9b533a7ea2fe9f5ee11e57e00306eb97a1f6090fd978e8fa1"
|
||||||
name = "github.com/facebookgo/grace"
|
name = "github.com/facebookgo/grace"
|
||||||
packages = ["gracenet"]
|
packages = ["gracenet"]
|
||||||
|
@ -156,7 +152,6 @@
|
||||||
revision = "3f9db97f856818214da2e1057f8ad84803971cff"
|
revision = "3f9db97f856818214da2e1057f8ad84803971cff"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
|
||||||
digest = "1:d4623fc7bf7e281d9107367cc4a9e76ed3e86b1eec1a4e30630c870bef1fedd0"
|
digest = "1:d4623fc7bf7e281d9107367cc4a9e76ed3e86b1eec1a4e30630c870bef1fedd0"
|
||||||
name = "github.com/getsentry/raven-go"
|
name = "github.com/getsentry/raven-go"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
@ -172,7 +167,7 @@
|
||||||
revision = "604e922904d35e97f98a774db7881f049cd8d970"
|
revision = "604e922904d35e97f98a774db7881f049cd8d970"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:17fe264ee908afc795734e8c4e63db2accabaf57326dbf21763a7d6b86096260"
|
digest = "1:239c4c7fd2159585454003d9be7207167970194216193a8a210b8d29576f19c9"
|
||||||
name = "github.com/golang/protobuf"
|
name = "github.com/golang/protobuf"
|
||||||
packages = [
|
packages = [
|
||||||
"proto",
|
"proto",
|
||||||
|
@ -182,32 +177,24 @@
|
||||||
"ptypes/timestamp",
|
"ptypes/timestamp",
|
||||||
]
|
]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
|
revision = "b5d812f8a3706043e23a9cd5babf2e5423744d30"
|
||||||
version = "v1.1.0"
|
version = "v1.3.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
|
||||||
digest = "1:582b704bebaa06b48c29b0cec224a6058a09c86883aaddabde889cd1a5f73e1b"
|
digest = "1:582b704bebaa06b48c29b0cec224a6058a09c86883aaddabde889cd1a5f73e1b"
|
||||||
name = "github.com/google/uuid"
|
name = "github.com/google/uuid"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "0cd6bf5da1e1c83f8b45653022c74f71af0538a4"
|
revision = "0cd6bf5da1e1c83f8b45653022c74f71af0538a4"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:c79fb010be38a59d657c48c6ba1d003a8aa651fa56b579d959d74573b7dff8e1"
|
|
||||||
name = "github.com/gorilla/context"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "08b5f424b9271eedf6f9f0ce86cb9396ed337a42"
|
|
||||||
version = "v1.1.1"
|
version = "v1.1.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:e73f5b0152105f18bc131fba127d9949305c8693f8a762588a82a48f61756f5f"
|
digest = "1:d5f97fc268267ec1b61c3453058c738246fc3e746f14b1ae25161513b7367b0c"
|
||||||
name = "github.com/gorilla/mux"
|
name = "github.com/gorilla/mux"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf"
|
revision = "c5c6c98bc25355028a63748a498942a6398ccd22"
|
||||||
version = "v1.6.2"
|
version = "v1.7.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:43dd08a10854b2056e615d1b1d22ac94559d822e1f8b6fcc92c1a1057e85188e"
|
digest = "1:43dd08a10854b2056e615d1b1d22ac94559d822e1f8b6fcc92c1a1057e85188e"
|
||||||
|
@ -234,31 +221,32 @@
|
||||||
version = "v0.1.0"
|
version = "v0.1.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
digest = "1:bc1c0be40c67b6b4aee09d7508d5a2a52c1c116b1fa43806dad2b0d6b4d4003b"
|
||||||
digest = "1:37ce7d7d80531b227023331002c0d42b4b4b291a96798c82a049d03a54ba79e4"
|
|
||||||
name = "github.com/lib/pq"
|
name = "github.com/lib/pq"
|
||||||
packages = [
|
packages = [
|
||||||
".",
|
".",
|
||||||
"oid",
|
"oid",
|
||||||
|
"scram",
|
||||||
]
|
]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "90697d60dd844d5ef6ff15135d0203f65d2f53b8"
|
revision = "51e2106eed1cea199c802d2a49e91e2491b02056"
|
||||||
|
version = "v1.1.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:c658e84ad3916da105a761660dcaeb01e63416c8ec7bc62256a9b411a05fcd67"
|
digest = "1:2fa7b0155cd54479a755c629de26f888a918e13f8857a2c442205d825368e084"
|
||||||
name = "github.com/mattn/go-colorable"
|
name = "github.com/mattn/go-colorable"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
|
revision = "3a70a971f94a22f2fa562ffcc7a0eb45f5daf045"
|
||||||
version = "v0.0.9"
|
version = "v0.1.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:d4d17353dbd05cb52a2a52b7fe1771883b682806f68db442b436294926bbfafb"
|
digest = "1:e150b5fafbd7607e2d638e4e5cf43aa4100124e5593385147b0a74e2733d8b0d"
|
||||||
name = "github.com/mattn/go-isatty"
|
name = "github.com/mattn/go-isatty"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
|
revision = "c2a7a6ca930a4cd0bc33a3f298eb71960732a3a7"
|
||||||
version = "v0.0.3"
|
version = "v0.0.7"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc"
|
digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc"
|
||||||
|
@ -269,7 +257,6 @@
|
||||||
version = "v1.0.1"
|
version = "v1.0.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
|
||||||
digest = "1:75fa16a231ef40da3e462d651c20b9df20bde0777bdc1ac0982242c79057ee71"
|
digest = "1:75fa16a231ef40da3e462d651c20b9df20bde0777bdc1ac0982242c79057ee71"
|
||||||
name = "github.com/mholt/caddy"
|
name = "github.com/mholt/caddy"
|
||||||
packages = [
|
packages = [
|
||||||
|
@ -281,25 +268,23 @@
|
||||||
revision = "d3b731e9255b72d4571a5aac125634cf1b6031dc"
|
revision = "d3b731e9255b72d4571a5aac125634cf1b6031dc"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:73f788f2df39614dd7d9e75c8ca5fb8b4a68937b5e6d4a15e4c3a596f6f8c07b"
|
digest = "1:2b4b4b2e5544c2a11a486c1b631357aa2ddf766e50c1b2483cf809da2c511234"
|
||||||
name = "github.com/miekg/dns"
|
name = "github.com/miekg/dns"
|
||||||
packages = [
|
packages = ["."]
|
||||||
".",
|
|
||||||
"internal/socket",
|
|
||||||
]
|
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "6da3249dfb57fbaa16efafcd8744cee8809d80cd"
|
revision = "73601d4aed9d844322611759d7f3619110b7c88e"
|
||||||
|
version = "v1.1.8"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
digest = "1:5d231480e1c64a726869bc4142d270184c419749d34f167646baa21008eb0a79"
|
||||||
digest = "1:8eb17c2ec4df79193ae65b621cd1c0c4697db3bc317fe6afdc76d7f2746abd05"
|
|
||||||
name = "github.com/mitchellh/go-homedir"
|
name = "github.com/mitchellh/go-homedir"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "3864e76763d94a6df2f9960b16a20a33da9f9a66"
|
revision = "af06845cf3004701891bf4fdb884bfe4920b3727"
|
||||||
|
version = "v1.1.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:450b7623b185031f3a456801155c8320209f75d0d4c4e633c6b1e59d44d6e392"
|
digest = "1:11e62d6050198055e6cd87ed57e5d8c669e84f839c16e16f192374d913d1a70d"
|
||||||
name = "github.com/opentracing/opentracing-go"
|
name = "github.com/opentracing/opentracing-go"
|
||||||
packages = [
|
packages = [
|
||||||
".",
|
".",
|
||||||
|
@ -307,8 +292,8 @@
|
||||||
"log",
|
"log",
|
||||||
]
|
]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "1949ddbfd147afd4d964a9f00b24eb291e0e7c38"
|
revision = "659c90643e714681897ec2521c60567dd21da733"
|
||||||
version = "v1.0.2"
|
version = "v1.1.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747"
|
digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747"
|
||||||
|
@ -339,15 +324,14 @@
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:32d10bdfa8f09ecf13598324dba86ab891f11db3c538b6a34d1c3b5b99d7c36b"
|
digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4"
|
||||||
name = "github.com/prometheus/client_model"
|
name = "github.com/prometheus/client_model"
|
||||||
packages = ["go"]
|
packages = ["go"]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c"
|
revision = "fd36f4220a901265f90734c3183c5f0c91daa0b8"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
digest = "1:35cf6bdf68db765988baa9c4f10cc5d7dda1126a54bd62e252dbcd0b1fc8da90"
|
||||||
digest = "1:e469cd65badf7694aeb44874518606d93c1d59e7735d3754ad442782437d3cc3"
|
|
||||||
name = "github.com/prometheus/common"
|
name = "github.com/prometheus/common"
|
||||||
packages = [
|
packages = [
|
||||||
"expfmt",
|
"expfmt",
|
||||||
|
@ -355,48 +339,44 @@
|
||||||
"model",
|
"model",
|
||||||
]
|
]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "7600349dcfe1abd18d72d3a1770870d9800a7801"
|
revision = "a82f4c12f983cc2649298185f296632953e50d3e"
|
||||||
|
version = "v0.3.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:20d9bb50dbee172242f9bcd6ec24a917dd7a5bb17421bf16a79c33111dea7db1"
|
digest = "1:49b09905e781d7775c086604cc00083e1832d0783f1f421b79f42657c457d029"
|
||||||
name = "github.com/prometheus/procfs"
|
name = "github.com/prometheus/procfs"
|
||||||
packages = [
|
packages = ["."]
|
||||||
".",
|
|
||||||
"internal/util",
|
|
||||||
"nfs",
|
|
||||||
"xfs",
|
|
||||||
]
|
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "ae68e2d4c00fed4943b5f6698d504a5fe083da8a"
|
revision = "8368d24ba045f26503eb745b624d930cbe214c79"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:9a6f766efd8d5752adb7052aebb6e3d85255b31a8dff5e58ab4efa740ba9efa0"
|
digest = "1:1a23fdd843129ef761ffe7651bc5fe7c5b09fbe933e92783ab06cc11c37b7b37"
|
||||||
name = "github.com/rifflock/lfshook"
|
name = "github.com/rifflock/lfshook"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "bf539943797a1f34c1f502d07de419b5238ae6c6"
|
revision = "b9218ef580f59a2e72dad1aa33d660150445d05a"
|
||||||
version = "v2.3"
|
version = "v2.4"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:9e9193aa51197513b3abcb108970d831fbcf40ef96aa845c4f03276e1fa316d2"
|
digest = "1:5f2aaa360f48d1711795bd88c7e45a38f86cf81e4bc01453d20983baa67e2d51"
|
||||||
name = "github.com/sirupsen/logrus"
|
name = "github.com/sirupsen/logrus"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc"
|
revision = "f006c2ac4710855cf0f916dd6b77acf6b048dc6e"
|
||||||
version = "v1.0.5"
|
version = "v1.0.3"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:18752d0b95816a1b777505a97f71c7467a8445b8ffb55631a7bf779f6ba4fa83"
|
digest = "1:f85e109eda8f6080877185d1c39e98dd8795e1780c08beca28304b87fd855a1c"
|
||||||
name = "github.com/stretchr/testify"
|
name = "github.com/stretchr/testify"
|
||||||
packages = ["assert"]
|
packages = ["assert"]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686"
|
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
|
||||||
version = "v1.2.2"
|
version = "v1.2.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:1182d12da264c8fe593504f150c3a479297767991fc1c06796cb67e4f61c4366"
|
digest = "1:9afbc3e330bd691e0eb0e704d05cc182b5c62ea71c5691c99fa5e82f06b628b6"
|
||||||
name = "golang.org/x/crypto"
|
name = "golang.org/x/crypto"
|
||||||
packages = [
|
packages = [
|
||||||
"curve25519",
|
"curve25519",
|
||||||
|
@ -410,27 +390,31 @@
|
||||||
"ssh/terminal",
|
"ssh/terminal",
|
||||||
]
|
]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "a49355c7e3f8fe157a85be2f77e6e269a0f89602"
|
revision = "f416ebab96af27ca70b6e5c23d6a0747530da626"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:9513f8a0f1f6918181b7d744225b1931ffacafeb57067a1c26893a51058afb80"
|
digest = "1:52d140f7ab52e491cc1cbc93e6637aa5e9a7f3beae7545d675b02e52ca9d7290"
|
||||||
name = "golang.org/x/net"
|
name = "golang.org/x/net"
|
||||||
packages = [
|
packages = [
|
||||||
|
"bpf",
|
||||||
"context",
|
"context",
|
||||||
"http/httpguts",
|
"http/httpguts",
|
||||||
"http2",
|
"http2",
|
||||||
"http2/hpack",
|
"http2/hpack",
|
||||||
"idna",
|
"idna",
|
||||||
|
"internal/iana",
|
||||||
|
"internal/socket",
|
||||||
"internal/timeseries",
|
"internal/timeseries",
|
||||||
|
"ipv4",
|
||||||
|
"ipv6",
|
||||||
"trace",
|
"trace",
|
||||||
"websocket",
|
"websocket",
|
||||||
]
|
]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "32a936f46389aa10549d60bd7833e54b01685d09"
|
revision = "1da14a5a36f220ea3f03470682b737b1dfd5de22"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
|
||||||
digest = "1:39ebcc2b11457b703ae9ee2e8cca0f68df21969c6102cb3b705f76cca0ea0239"
|
digest = "1:39ebcc2b11457b703ae9ee2e8cca0f68df21969c6102cb3b705f76cca0ea0239"
|
||||||
name = "golang.org/x/sync"
|
name = "golang.org/x/sync"
|
||||||
packages = ["errgroup"]
|
packages = ["errgroup"]
|
||||||
|
@ -439,9 +423,10 @@
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:2e8e74aa73847379a370c2704ff08baf4a7303ae682766e70c23ded3fd9b3f37"
|
digest = "1:77751d02e939d7078faedaeec10c09af575a09c528d84d18f2cb45a84bd1889a"
|
||||||
name = "golang.org/x/sys"
|
name = "golang.org/x/sys"
|
||||||
packages = [
|
packages = [
|
||||||
|
"cpu",
|
||||||
"unix",
|
"unix",
|
||||||
"windows",
|
"windows",
|
||||||
"windows/registry",
|
"windows/registry",
|
||||||
|
@ -450,7 +435,7 @@
|
||||||
"windows/svc/mgr",
|
"windows/svc/mgr",
|
||||||
]
|
]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "ce36f3865eeb42541ce3f87f32f8462c5687befa"
|
revision = "12500544f89f9420afe9529ba8940bf72d294972"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
|
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
|
||||||
|
@ -477,30 +462,38 @@
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:601e63e7d4577f907118bec825902505291918859d223bce015539e79f1160e3"
|
digest = "1:c3076e7defee87de1236f1814beb588f40a75544c60121e6eb38b3b3721783e2"
|
||||||
name = "google.golang.org/genproto"
|
name = "google.golang.org/genproto"
|
||||||
packages = ["googleapis/rpc/status"]
|
packages = ["googleapis/rpc/status"]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "ff3583edef7de132f219f0efc00e097cabcc0ec0"
|
revision = "d1146b9035b912113a38af3b138eb2af567b2c67"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74"
|
digest = "1:31d87f39886fb38a2b6c097ff3b9f985d6960772170d64a68246f7790e955746"
|
||||||
name = "google.golang.org/grpc"
|
name = "google.golang.org/grpc"
|
||||||
packages = [
|
packages = [
|
||||||
".",
|
".",
|
||||||
"balancer",
|
"balancer",
|
||||||
"balancer/base",
|
"balancer/base",
|
||||||
"balancer/roundrobin",
|
"balancer/roundrobin",
|
||||||
|
"binarylog/grpc_binarylog_v1",
|
||||||
"codes",
|
"codes",
|
||||||
"connectivity",
|
"connectivity",
|
||||||
"credentials",
|
"credentials",
|
||||||
|
"credentials/internal",
|
||||||
"encoding",
|
"encoding",
|
||||||
"encoding/proto",
|
"encoding/proto",
|
||||||
"grpclog",
|
"grpclog",
|
||||||
"internal",
|
"internal",
|
||||||
"internal/backoff",
|
"internal/backoff",
|
||||||
|
"internal/balancerload",
|
||||||
|
"internal/binarylog",
|
||||||
"internal/channelz",
|
"internal/channelz",
|
||||||
|
"internal/envconfig",
|
||||||
"internal/grpcrand",
|
"internal/grpcrand",
|
||||||
|
"internal/grpcsync",
|
||||||
|
"internal/syscall",
|
||||||
|
"internal/transport",
|
||||||
"keepalive",
|
"keepalive",
|
||||||
"metadata",
|
"metadata",
|
||||||
"naming",
|
"naming",
|
||||||
|
@ -511,11 +504,10 @@
|
||||||
"stats",
|
"stats",
|
||||||
"status",
|
"status",
|
||||||
"tap",
|
"tap",
|
||||||
"transport",
|
|
||||||
]
|
]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "168a6198bcb0ef175f7dacec0b8691fc141dc9b8"
|
revision = "236199dd5f8031d698fb64091194aecd1c3895b2"
|
||||||
version = "v1.13.0"
|
version = "v1.20.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "altsrc-parse-durations"
|
branch = "altsrc-parse-durations"
|
||||||
|
@ -530,12 +522,12 @@
|
||||||
source = "https://github.com/cbranch/cli"
|
source = "https://github.com/cbranch/cli"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202"
|
digest = "1:4d2e5a73dc1500038e504a8d78b986630e3626dc027bc030ba5c75da257cdb96"
|
||||||
name = "gopkg.in/yaml.v2"
|
name = "gopkg.in/yaml.v2"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
|
revision = "51d6538a90f86fe93ac480b35f37b2be17fef232"
|
||||||
version = "v2.2.1"
|
version = "v2.2.2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:8ffc3ddc31414c0a71220957bb723b16510d7fcb5b3880dc0da4cf6d39c31642"
|
digest = "1:8ffc3ddc31414c0a71220957bb723b16510d7fcb5b3880dc0da4cf6d39c31642"
|
||||||
|
|
36
Gopkg.toml
36
Gopkg.toml
|
@ -10,35 +10,35 @@
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/facebookgo/grace"
|
name = "github.com/facebookgo/grace"
|
||||||
branch = "master"
|
revision = "75cf19382434e82df4dd84953f566b8ad23d6e9e"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/getsentry/raven-go"
|
name = "github.com/getsentry/raven-go"
|
||||||
branch = "master"
|
revision = "ed7bcb39ff10f39ab08e317ce16df282845852fa"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/pkg/errors"
|
name = "github.com/pkg/errors"
|
||||||
version = "0.8.0"
|
version = "=0.8.0"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/prometheus/client_golang"
|
name = "github.com/prometheus/client_golang"
|
||||||
version = "0.9.0-pre1"
|
version = "=0.9.0-pre1"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/sirupsen/logrus"
|
name = "github.com/sirupsen/logrus"
|
||||||
version = "1.0.3"
|
version = "=1.0.3"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/stretchr/testify"
|
name = "github.com/stretchr/testify"
|
||||||
version = "1.2.1"
|
version = "=1.2.1"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "golang.org/x/net"
|
name = "golang.org/x/net"
|
||||||
branch = "master"
|
branch = "master" # master required by github.com/miekg/dns
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "golang.org/x/sync"
|
name = "golang.org/x/sync"
|
||||||
branch = "master"
|
revision = "1d60e4601c6fd243af51cc01ddf169918a5407ca"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "gopkg.in/urfave/cli.v2"
|
name = "gopkg.in/urfave/cli.v2"
|
||||||
|
@ -48,40 +48,40 @@
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "zombiezen.com/go/capnproto2"
|
name = "zombiezen.com/go/capnproto2"
|
||||||
source = "https://github.com/zombiezen/go-capnproto2"
|
source = "https://github.com/zombiezen/go-capnproto2"
|
||||||
version = "2.17.1"
|
version = "=2.17.1"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/gorilla/websocket"
|
name = "github.com/gorilla/websocket"
|
||||||
version = "1.2.0"
|
version = "=1.2.0"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/coredns/coredns"
|
name = "github.com/coredns/coredns"
|
||||||
branch = "master"
|
version = "=1.2.0"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/miekg/dns"
|
name = "github.com/miekg/dns"
|
||||||
revision = "6da3249dfb57fbaa16efafcd8744cee8809d80cd"
|
version = "=1.1.8"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/cloudflare/brotli-go"
|
name = "github.com/cloudflare/brotli-go"
|
||||||
branch = "master"
|
revision = "18c9f6c67e3dfc12e0ddaca748d2887f97a7ac28"
|
||||||
|
|
||||||
[[override]]
|
[[override]]
|
||||||
name = "github.com/mholt/caddy"
|
name = "github.com/mholt/caddy"
|
||||||
branch = "master"
|
revision = "d3b731e9255b72d4571a5aac125634cf1b6031dc"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
branch = "master"
|
|
||||||
name = "github.com/coreos/go-oidc"
|
name = "github.com/coreos/go-oidc"
|
||||||
|
revision = "a93f71fdfe73d2c0f5413c0565eea0af6523a6df"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
branch = "master"
|
|
||||||
name = "golang.org/x/crypto"
|
name = "golang.org/x/crypto"
|
||||||
|
branch = "master" # master required by github.com/miekg/dns
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
branch = "master"
|
|
||||||
name = "github.com/cloudflare/golibs"
|
name = "github.com/cloudflare/golibs"
|
||||||
|
revision = "333127dbecfcc23a8db7d9a4f52785d23aff44a1"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/google/uuid"
|
name = "github.com/google/uuid"
|
||||||
version = "v1.1.1"
|
version = "=1.1.1"
|
||||||
|
|
|
@ -1,14 +1,21 @@
|
||||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
The MIT License (MIT)
|
||||||
Version 2, December 2004
|
|
||||||
|
|
||||||
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
Copyright (c) 2013 TOML authors
|
||||||
|
|
||||||
Everyone is permitted to copy and distribute verbatim or modified
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
copies of this license document, and changing it is allowed as long
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
as the name is changed.
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
The above copyright notice and this permission notice shall be included in
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
|
@ -1,14 +1,21 @@
|
||||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
The MIT License (MIT)
|
||||||
Version 2, December 2004
|
|
||||||
|
|
||||||
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
Copyright (c) 2013 TOML authors
|
||||||
|
|
||||||
Everyone is permitted to copy and distribute verbatim or modified
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
copies of this license document, and changing it is allowed as long
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
as the name is changed.
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
The above copyright notice and this permission notice shall be included in
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
|
@ -1,14 +1,21 @@
|
||||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
The MIT License (MIT)
|
||||||
Version 2, December 2004
|
|
||||||
|
|
||||||
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
Copyright (c) 2013 TOML authors
|
||||||
|
|
||||||
Everyone is permitted to copy and distribute verbatim or modified
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
copies of this license document, and changing it is allowed as long
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
as the name is changed.
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
The above copyright notice and this permission notice shall be included in
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
|
@ -1,14 +1,21 @@
|
||||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
The MIT License (MIT)
|
||||||
Version 2, December 2004
|
|
||||||
|
|
||||||
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
Copyright (c) 2013 TOML authors
|
||||||
|
|
||||||
Everyone is permitted to copy and distribute verbatim or modified
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
copies of this license document, and changing it is allowed as long
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
as the name is changed.
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
The above copyright notice and this permission notice shall be included in
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
|
@ -775,7 +775,7 @@ func lexDatetime(lx *lexer) stateFn {
|
||||||
return lexDatetime
|
return lexDatetime
|
||||||
}
|
}
|
||||||
switch r {
|
switch r {
|
||||||
case '-', 'T', ':', '.', 'Z':
|
case '-', 'T', ':', '.', 'Z', '+':
|
||||||
return lexDatetime
|
return lexDatetime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,6 @@ var Directives = []string{
|
||||||
"auto",
|
"auto",
|
||||||
"secondary",
|
"secondary",
|
||||||
"etcd",
|
"etcd",
|
||||||
"loop",
|
|
||||||
"forward",
|
"forward",
|
||||||
"proxy",
|
"proxy",
|
||||||
"erratic",
|
"erratic",
|
||||||
|
|
|
@ -10,9 +10,7 @@ package log
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
golog "log"
|
golog "log"
|
||||||
"os"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// D controls whether we should output debug logs. If true, we do.
|
// D controls whether we should output debug logs. If true, we do.
|
||||||
|
@ -63,21 +61,9 @@ func Error(v ...interface{}) { log(err, v...) }
|
||||||
// Errorf is equivalent to log.Printf, but prefixed with "[ERROR] ".
|
// Errorf is equivalent to log.Printf, but prefixed with "[ERROR] ".
|
||||||
func Errorf(format string, v ...interface{}) { logf(err, format, v...) }
|
func Errorf(format string, v ...interface{}) { logf(err, format, v...) }
|
||||||
|
|
||||||
// Fatal is equivalent to log.Print, but prefixed with "[FATAL] ", and calling
|
|
||||||
// os.Exit(1).
|
|
||||||
func Fatal(v ...interface{}) { log(fatal, v...); os.Exit(1) }
|
|
||||||
|
|
||||||
// Fatalf is equivalent to log.Printf, but prefixed with "[FATAL] ", and calling
|
|
||||||
// os.Exit(1)
|
|
||||||
func Fatalf(format string, v ...interface{}) { logf(fatal, format, v...); os.Exit(1) }
|
|
||||||
|
|
||||||
// Discard sets the log output to /dev/null.
|
|
||||||
func Discard() { golog.SetOutput(ioutil.Discard) }
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
debug = "[DEBUG] "
|
debug = "[DEBUG] "
|
||||||
err = "[ERROR] "
|
err = "[ERROR] "
|
||||||
fatal = "[FATAL] "
|
|
||||||
info = "[INFO] "
|
|
||||||
warning = "[WARNING] "
|
warning = "[WARNING] "
|
||||||
|
info = "[INFO] "
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,7 +3,6 @@ package log
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
golog "log"
|
golog "log"
|
||||||
"os"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// P is a logger that includes the plugin doing the logging.
|
// P is a logger that includes the plugin doing the logging.
|
||||||
|
@ -59,10 +58,4 @@ func (p P) Error(v ...interface{}) { p.log(err, v...) }
|
||||||
// Errorf logs as log.Errorf.
|
// Errorf logs as log.Errorf.
|
||||||
func (p P) Errorf(format string, v ...interface{}) { p.logf(err, format, v...) }
|
func (p P) Errorf(format string, v ...interface{}) { p.logf(err, format, v...) }
|
||||||
|
|
||||||
// Fatal logs as log.Fatal and calls os.Exit(1).
|
|
||||||
func (p P) Fatal(v ...interface{}) { p.log(fatal, v...); os.Exit(1) }
|
|
||||||
|
|
||||||
// Fatalf logs as log.Fatalf and calls os.Exit(1).
|
|
||||||
func (p P) Fatalf(format string, v ...interface{}) { p.logf(fatal, format, v...); os.Exit(1) }
|
|
||||||
|
|
||||||
func pFormat(s string) string { return "plugin/" + s + ": " }
|
func pFormat(s string) string { return "plugin/" + s + ": " }
|
||||||
|
|
|
@ -2,7 +2,7 @@ ISC License
|
||||||
|
|
||||||
Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
|
Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
|
||||||
|
|
||||||
Permission to use, copy, modify, and distribute this software for any
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
copyright notice and this permission notice appear in all copies.
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,9 @@
|
||||||
// when the code is not running on Google App Engine, compiled by GopherJS, and
|
// when the code is not running on Google App Engine, compiled by GopherJS, and
|
||||||
// "-tags safe" is not added to the go build command line. The "disableunsafe"
|
// "-tags safe" is not added to the go build command line. The "disableunsafe"
|
||||||
// tag is deprecated and thus should not be used.
|
// tag is deprecated and thus should not be used.
|
||||||
// +build !js,!appengine,!safe,!disableunsafe
|
// Go versions prior to 1.4 are disabled because they use a different layout
|
||||||
|
// for interfaces which make the implementation of unsafeReflectValue more complex.
|
||||||
|
// +build !js,!appengine,!safe,!disableunsafe,go1.4
|
||||||
|
|
||||||
package spew
|
package spew
|
||||||
|
|
||||||
|
@ -34,80 +36,49 @@ const (
|
||||||
ptrSize = unsafe.Sizeof((*byte)(nil))
|
ptrSize = unsafe.Sizeof((*byte)(nil))
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
type flag uintptr
|
||||||
// offsetPtr, offsetScalar, and offsetFlag are the offsets for the
|
|
||||||
// internal reflect.Value fields. These values are valid before golang
|
|
||||||
// commit ecccf07e7f9d which changed the format. The are also valid
|
|
||||||
// after commit 82f48826c6c7 which changed the format again to mirror
|
|
||||||
// the original format. Code in the init function updates these offsets
|
|
||||||
// as necessary.
|
|
||||||
offsetPtr = uintptr(ptrSize)
|
|
||||||
offsetScalar = uintptr(0)
|
|
||||||
offsetFlag = uintptr(ptrSize * 2)
|
|
||||||
|
|
||||||
// flagKindWidth and flagKindShift indicate various bits that the
|
var (
|
||||||
// reflect package uses internally to track kind information.
|
// flagRO indicates whether the value field of a reflect.Value
|
||||||
//
|
// is read-only.
|
||||||
// flagRO indicates whether or not the value field of a reflect.Value is
|
flagRO flag
|
||||||
// read-only.
|
|
||||||
//
|
// flagAddr indicates whether the address of the reflect.Value's
|
||||||
// flagIndir indicates whether the value field of a reflect.Value is
|
// value may be taken.
|
||||||
// the actual data or a pointer to the data.
|
flagAddr flag
|
||||||
//
|
|
||||||
// These values are valid before golang commit 90a7c3c86944 which
|
|
||||||
// changed their positions. Code in the init function updates these
|
|
||||||
// flags as necessary.
|
|
||||||
flagKindWidth = uintptr(5)
|
|
||||||
flagKindShift = uintptr(flagKindWidth - 1)
|
|
||||||
flagRO = uintptr(1 << 0)
|
|
||||||
flagIndir = uintptr(1 << 1)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
// flagKindMask holds the bits that make up the kind
|
||||||
// Older versions of reflect.Value stored small integers directly in the
|
// part of the flags field. In all the supported versions,
|
||||||
// ptr field (which is named val in the older versions). Versions
|
// it is in the lower 5 bits.
|
||||||
// between commits ecccf07e7f9d and 82f48826c6c7 added a new field named
|
const flagKindMask = flag(0x1f)
|
||||||
// scalar for this purpose which unfortunately came before the flag
|
|
||||||
// field, so the offset of the flag field is different for those
|
|
||||||
// versions.
|
|
||||||
//
|
|
||||||
// This code constructs a new reflect.Value from a known small integer
|
|
||||||
// and checks if the size of the reflect.Value struct indicates it has
|
|
||||||
// the scalar field. When it does, the offsets are updated accordingly.
|
|
||||||
vv := reflect.ValueOf(0xf00)
|
|
||||||
if unsafe.Sizeof(vv) == (ptrSize * 4) {
|
|
||||||
offsetScalar = ptrSize * 2
|
|
||||||
offsetFlag = ptrSize * 3
|
|
||||||
}
|
|
||||||
|
|
||||||
// Commit 90a7c3c86944 changed the flag positions such that the low
|
// Different versions of Go have used different
|
||||||
// order bits are the kind. This code extracts the kind from the flags
|
// bit layouts for the flags type. This table
|
||||||
// field and ensures it's the correct type. When it's not, the flag
|
// records the known combinations.
|
||||||
// order has been changed to the newer format, so the flags are updated
|
var okFlags = []struct {
|
||||||
// accordingly.
|
ro, addr flag
|
||||||
upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag)
|
}{{
|
||||||
upfv := *(*uintptr)(upf)
|
// From Go 1.4 to 1.5
|
||||||
flagKindMask := uintptr((1<<flagKindWidth - 1) << flagKindShift)
|
ro: 1 << 5,
|
||||||
if (upfv&flagKindMask)>>flagKindShift != uintptr(reflect.Int) {
|
addr: 1 << 7,
|
||||||
flagKindShift = 0
|
}, {
|
||||||
flagRO = 1 << 5
|
// Up to Go tip.
|
||||||
flagIndir = 1 << 6
|
ro: 1<<5 | 1<<6,
|
||||||
|
addr: 1 << 8,
|
||||||
|
}}
|
||||||
|
|
||||||
// Commit adf9b30e5594 modified the flags to separate the
|
var flagValOffset = func() uintptr {
|
||||||
// flagRO flag into two bits which specifies whether or not the
|
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
|
||||||
// field is embedded. This causes flagIndir to move over a bit
|
if !ok {
|
||||||
// and means that flagRO is the combination of either of the
|
panic("reflect.Value has no flag field")
|
||||||
// original flagRO bit and the new bit.
|
|
||||||
//
|
|
||||||
// This code detects the change by extracting what used to be
|
|
||||||
// the indirect bit to ensure it's set. When it's not, the flag
|
|
||||||
// order has been changed to the newer format, so the flags are
|
|
||||||
// updated accordingly.
|
|
||||||
if upfv&flagIndir == 0 {
|
|
||||||
flagRO = 3 << 5
|
|
||||||
flagIndir = 1 << 7
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return field.Offset
|
||||||
|
}()
|
||||||
|
|
||||||
|
// flagField returns a pointer to the flag field of a reflect.Value.
|
||||||
|
func flagField(v *reflect.Value) *flag {
|
||||||
|
return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset))
|
||||||
}
|
}
|
||||||
|
|
||||||
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
|
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
|
||||||
|
@ -119,34 +90,56 @@ func init() {
|
||||||
// This allows us to check for implementations of the Stringer and error
|
// This allows us to check for implementations of the Stringer and error
|
||||||
// interfaces to be used for pretty printing ordinarily unaddressable and
|
// interfaces to be used for pretty printing ordinarily unaddressable and
|
||||||
// inaccessible values such as unexported struct fields.
|
// inaccessible values such as unexported struct fields.
|
||||||
func unsafeReflectValue(v reflect.Value) (rv reflect.Value) {
|
func unsafeReflectValue(v reflect.Value) reflect.Value {
|
||||||
indirects := 1
|
if !v.IsValid() || (v.CanInterface() && v.CanAddr()) {
|
||||||
vt := v.Type()
|
return v
|
||||||
upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr)
|
|
||||||
rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag))
|
|
||||||
if rvf&flagIndir != 0 {
|
|
||||||
vt = reflect.PtrTo(v.Type())
|
|
||||||
indirects++
|
|
||||||
} else if offsetScalar != 0 {
|
|
||||||
// The value is in the scalar field when it's not one of the
|
|
||||||
// reference types.
|
|
||||||
switch vt.Kind() {
|
|
||||||
case reflect.Uintptr:
|
|
||||||
case reflect.Chan:
|
|
||||||
case reflect.Func:
|
|
||||||
case reflect.Map:
|
|
||||||
case reflect.Ptr:
|
|
||||||
case reflect.UnsafePointer:
|
|
||||||
default:
|
|
||||||
upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) +
|
|
||||||
offsetScalar)
|
|
||||||
}
|
}
|
||||||
}
|
flagFieldPtr := flagField(&v)
|
||||||
|
*flagFieldPtr &^= flagRO
|
||||||
pv := reflect.NewAt(vt, upv)
|
*flagFieldPtr |= flagAddr
|
||||||
rv = pv
|
return v
|
||||||
for i := 0; i < indirects; i++ {
|
}
|
||||||
rv = rv.Elem()
|
|
||||||
}
|
// Sanity checks against future reflect package changes
|
||||||
return rv
|
// to the type or semantics of the Value.flag field.
|
||||||
|
func init() {
|
||||||
|
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
|
||||||
|
if !ok {
|
||||||
|
panic("reflect.Value has no flag field")
|
||||||
|
}
|
||||||
|
if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() {
|
||||||
|
panic("reflect.Value flag field has changed kind")
|
||||||
|
}
|
||||||
|
type t0 int
|
||||||
|
var t struct {
|
||||||
|
A t0
|
||||||
|
// t0 will have flagEmbedRO set.
|
||||||
|
t0
|
||||||
|
// a will have flagStickyRO set
|
||||||
|
a t0
|
||||||
|
}
|
||||||
|
vA := reflect.ValueOf(t).FieldByName("A")
|
||||||
|
va := reflect.ValueOf(t).FieldByName("a")
|
||||||
|
vt0 := reflect.ValueOf(t).FieldByName("t0")
|
||||||
|
|
||||||
|
// Infer flagRO from the difference between the flags
|
||||||
|
// for the (otherwise identical) fields in t.
|
||||||
|
flagPublic := *flagField(&vA)
|
||||||
|
flagWithRO := *flagField(&va) | *flagField(&vt0)
|
||||||
|
flagRO = flagPublic ^ flagWithRO
|
||||||
|
|
||||||
|
// Infer flagAddr from the difference between a value
|
||||||
|
// taken from a pointer and not.
|
||||||
|
vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A")
|
||||||
|
flagNoPtr := *flagField(&vA)
|
||||||
|
flagPtr := *flagField(&vPtrA)
|
||||||
|
flagAddr = flagNoPtr ^ flagPtr
|
||||||
|
|
||||||
|
// Check that the inferred flags tally with one of the known versions.
|
||||||
|
for _, f := range okFlags {
|
||||||
|
if flagRO == f.ro && flagAddr == f.addr {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("reflect.Value read-only flag has changed semantics")
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
// when the code is running on Google App Engine, compiled by GopherJS, or
|
// when the code is running on Google App Engine, compiled by GopherJS, or
|
||||||
// "-tags safe" is added to the go build command line. The "disableunsafe"
|
// "-tags safe" is added to the go build command line. The "disableunsafe"
|
||||||
// tag is deprecated and thus should not be used.
|
// tag is deprecated and thus should not be used.
|
||||||
// +build js appengine safe disableunsafe
|
// +build js appengine safe disableunsafe !go1.4
|
||||||
|
|
||||||
package spew
|
package spew
|
||||||
|
|
||||||
|
|
|
@ -180,7 +180,7 @@ func printComplex(w io.Writer, c complex128, floatPrecision int) {
|
||||||
w.Write(closeParenBytes)
|
w.Write(closeParenBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x'
|
// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x'
|
||||||
// prefix to Writer w.
|
// prefix to Writer w.
|
||||||
func printHexPtr(w io.Writer, p uintptr) {
|
func printHexPtr(w io.Writer, p uintptr) {
|
||||||
// Null pointer.
|
// Null pointer.
|
||||||
|
|
|
@ -35,16 +35,16 @@ var (
|
||||||
|
|
||||||
// cCharRE is a regular expression that matches a cgo char.
|
// cCharRE is a regular expression that matches a cgo char.
|
||||||
// It is used to detect character arrays to hexdump them.
|
// It is used to detect character arrays to hexdump them.
|
||||||
cCharRE = regexp.MustCompile("^.*\\._Ctype_char$")
|
cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`)
|
||||||
|
|
||||||
// cUnsignedCharRE is a regular expression that matches a cgo unsigned
|
// cUnsignedCharRE is a regular expression that matches a cgo unsigned
|
||||||
// char. It is used to detect unsigned character arrays to hexdump
|
// char. It is used to detect unsigned character arrays to hexdump
|
||||||
// them.
|
// them.
|
||||||
cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$")
|
cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`)
|
||||||
|
|
||||||
// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
|
// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
|
||||||
// It is used to detect uint8_t arrays to hexdump them.
|
// It is used to detect uint8_t arrays to hexdump them.
|
||||||
cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$")
|
cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`)
|
||||||
)
|
)
|
||||||
|
|
||||||
// dumpState contains information about the state of a dump operation.
|
// dumpState contains information about the state of a dump operation.
|
||||||
|
@ -143,10 +143,10 @@ func (d *dumpState) dumpPtr(v reflect.Value) {
|
||||||
// Display dereferenced value.
|
// Display dereferenced value.
|
||||||
d.w.Write(openParenBytes)
|
d.w.Write(openParenBytes)
|
||||||
switch {
|
switch {
|
||||||
case nilFound == true:
|
case nilFound:
|
||||||
d.w.Write(nilAngleBytes)
|
d.w.Write(nilAngleBytes)
|
||||||
|
|
||||||
case cycleFound == true:
|
case cycleFound:
|
||||||
d.w.Write(circularBytes)
|
d.w.Write(circularBytes)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -182,10 +182,10 @@ func (f *formatState) formatPtr(v reflect.Value) {
|
||||||
|
|
||||||
// Display dereferenced value.
|
// Display dereferenced value.
|
||||||
switch {
|
switch {
|
||||||
case nilFound == true:
|
case nilFound:
|
||||||
f.fs.Write(nilAngleBytes)
|
f.fs.Write(nilAngleBytes)
|
||||||
|
|
||||||
case cycleFound == true:
|
case cycleFound:
|
||||||
f.fs.Write(circularShortBytes)
|
f.fs.Write(circularShortBytes)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
language: go
|
language: go
|
||||||
go:
|
go:
|
||||||
- 1.4
|
- "1.4"
|
||||||
- 1.5
|
- "1.5"
|
||||||
|
- "1.7"
|
||||||
|
- "1.9"
|
||||||
|
- "1.10"
|
||||||
|
- "1.11"
|
||||||
|
- "tip"
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- go: tip
|
||||||
script: go test -v github.com/equinox-io/equinox github.com/equinox-io/equinox/proto
|
script: go test -v github.com/equinox-io/equinox github.com/equinox-io/equinox/proto
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
module github.com/equinox-io/equinox
|
|
@ -131,8 +131,16 @@ func (o *Options) SetPublicKeyPEM(pembytes []byte) error {
|
||||||
// a successful check that found no update from other errors like a failed
|
// a successful check that found no update from other errors like a failed
|
||||||
// network connection.
|
// network connection.
|
||||||
func Check(appID string, opts Options) (Response, error) {
|
func Check(appID string, opts Options) (Response, error) {
|
||||||
var r Response
|
var req, err = checkRequest(appID, &opts)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return Response{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return doCheckRequest(opts, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkRequest(appID string, opts *Options) (*http.Request, error) {
|
||||||
if opts.Channel == "" {
|
if opts.Channel == "" {
|
||||||
opts.Channel = "stable"
|
opts.Channel = "stable"
|
||||||
}
|
}
|
||||||
|
@ -140,7 +148,7 @@ func Check(appID string, opts Options) (Response, error) {
|
||||||
var err error
|
var err error
|
||||||
opts.TargetPath, err = osext.Executable()
|
opts.TargetPath, err = osext.Executable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return r, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if opts.OS == "" {
|
if opts.OS == "" {
|
||||||
|
@ -172,11 +180,17 @@ func Check(appID string, opts Options) (Response, error) {
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", opts.CheckURL, bytes.NewReader(payload))
|
req, err := http.NewRequest("POST", opts.CheckURL, bytes.NewReader(payload))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return r, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Accept", fmt.Sprintf("application/json; q=1; version=%s; charset=utf-8", protocolVersion))
|
req.Header.Set("Accept", fmt.Sprintf("application/json; q=1; version=%s; charset=utf-8", protocolVersion))
|
||||||
req.Header.Set("Content-Type", "application/json; charset=utf-8")
|
req.Header.Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
req.Close = true
|
||||||
|
|
||||||
|
return req, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func doCheckRequest(opts Options, req *http.Request) (r Response, err error) {
|
||||||
resp, err := opts.HTTPClient.Do(req)
|
resp, err := opts.HTTPClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return r, err
|
return r, err
|
||||||
|
@ -236,6 +250,16 @@ func computeChecksum(path string) string {
|
||||||
//
|
//
|
||||||
// Error is nil if and only if the entire update completes successfully.
|
// Error is nil if and only if the entire update completes successfully.
|
||||||
func (r Response) Apply() error {
|
func (r Response) Apply() error {
|
||||||
|
var req, opts, err = r.applyRequest()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.applyUpdate(req, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Response) applyRequest() (*http.Request, update.Options, error) {
|
||||||
opts := update.Options{
|
opts := update.Options{
|
||||||
TargetPath: r.opts.TargetPath,
|
TargetPath: r.opts.TargetPath,
|
||||||
TargetMode: r.opts.TargetMode,
|
TargetMode: r.opts.TargetMode,
|
||||||
|
@ -250,15 +274,16 @@ func (r Response) Apply() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := opts.CheckPermissions(); err != nil {
|
if err := opts.CheckPermissions(); err != nil {
|
||||||
return err
|
return nil, opts, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", r.downloadURL, nil)
|
req, err := http.NewRequest("GET", r.downloadURL, nil)
|
||||||
if err != nil {
|
return req, opts, err
|
||||||
return err
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
func (r Response) applyUpdate(req *http.Request, opts update.Options) error {
|
||||||
// fetch the update
|
// fetch the update
|
||||||
|
req.Close = true
|
||||||
resp, err := r.opts.HTTPClient.Do(req)
|
resp, err := r.opts.HTTPClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
// +build go1.7
|
||||||
|
|
||||||
|
package equinox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CheckContext is like Check but includes a context.
|
||||||
|
func CheckContext(ctx context.Context, appID string, opts Options) (Response, error) {
|
||||||
|
var req, err = checkRequest(appID, &opts)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return Response{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return doCheckRequest(opts, req.WithContext(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyContext is like Apply but includes a context.
|
||||||
|
func (r Response) ApplyContext(ctx context.Context) error {
|
||||||
|
var req, opts, err = r.applyRequest()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.applyUpdate(req.WithContext(ctx), opts)
|
||||||
|
}
|
|
@ -1,7 +1,4 @@
|
||||||
Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
|
|
||||||
Copyright 2010 The Go Authors. All rights reserved.
|
Copyright 2010 The Go Authors. All rights reserved.
|
||||||
https://github.com/golang/protobuf
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are
|
modification, are permitted provided that the following conditions are
|
||||||
|
|
|
@ -186,7 +186,6 @@ func (p *Buffer) DecodeVarint() (x uint64, err error) {
|
||||||
if b&0x80 == 0 {
|
if b&0x80 == 0 {
|
||||||
goto done
|
goto done
|
||||||
}
|
}
|
||||||
// x -= 0x80 << 63 // Always zero.
|
|
||||||
|
|
||||||
return 0, errOverflow
|
return 0, errOverflow
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
// Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
//
|
||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// https://github.com/golang/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
package proto
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
// Deprecated: do not use.
|
||||||
|
type Stats struct{ Emalloc, Dmalloc, Encode, Decode, Chit, Cmiss, Size uint64 }
|
||||||
|
|
||||||
|
// Deprecated: do not use.
|
||||||
|
func GetStats() Stats { return Stats{} }
|
||||||
|
|
||||||
|
// Deprecated: do not use.
|
||||||
|
func MarshalMessageSet(interface{}) ([]byte, error) {
|
||||||
|
return nil, errors.New("proto: not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: do not use.
|
||||||
|
func UnmarshalMessageSet([]byte, interface{}) error {
|
||||||
|
return errors.New("proto: not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: do not use.
|
||||||
|
func MarshalMessageSetJSON(interface{}) ([]byte, error) {
|
||||||
|
return nil, errors.New("proto: not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: do not use.
|
||||||
|
func UnmarshalMessageSetJSON([]byte, interface{}) error {
|
||||||
|
return errors.New("proto: not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: do not use.
|
||||||
|
func RegisterMessageSetType(Message, int32, string) {}
|
|
@ -37,27 +37,9 @@ package proto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RequiredNotSetError is the error returned if Marshal is called with
|
|
||||||
// a protocol buffer struct whose required fields have not
|
|
||||||
// all been initialized. It is also the error returned if Unmarshal is
|
|
||||||
// called with an encoded protocol buffer that does not include all the
|
|
||||||
// required fields.
|
|
||||||
//
|
|
||||||
// When printed, RequiredNotSetError reports the first unset required field in a
|
|
||||||
// message. If the field cannot be precisely determined, it is reported as
|
|
||||||
// "{Unknown}".
|
|
||||||
type RequiredNotSetError struct {
|
|
||||||
field string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *RequiredNotSetError) Error() string {
|
|
||||||
return fmt.Sprintf("proto: required field %q not set", e.field)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// errRepeatedHasNil is the error returned if Marshal is called with
|
// errRepeatedHasNil is the error returned if Marshal is called with
|
||||||
// a struct with a repeated field containing a nil element.
|
// a struct with a repeated field containing a nil element.
|
||||||
|
|
|
@ -246,7 +246,8 @@ func equalExtMap(base reflect.Type, em1, em2 map[int32]Extension) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
m1, m2 := e1.value, e2.value
|
m1 := extensionAsLegacyType(e1.value)
|
||||||
|
m2 := extensionAsLegacyType(e2.value)
|
||||||
|
|
||||||
if m1 == nil && m2 == nil {
|
if m1 == nil && m2 == nil {
|
||||||
// Both have only encoded form.
|
// Both have only encoded form.
|
||||||
|
|
|
@ -186,7 +186,23 @@ type Extension struct {
|
||||||
// accessed using GetExtension (or GetExtensions) desc and value
|
// accessed using GetExtension (or GetExtensions) desc and value
|
||||||
// will be set.
|
// will be set.
|
||||||
desc *ExtensionDesc
|
desc *ExtensionDesc
|
||||||
|
|
||||||
|
// value is a concrete value for the extension field. Let the type of
|
||||||
|
// desc.ExtensionType be the "API type" and the type of Extension.value
|
||||||
|
// be the "storage type". The API type and storage type are the same except:
|
||||||
|
// * For scalars (except []byte), the API type uses *T,
|
||||||
|
// while the storage type uses T.
|
||||||
|
// * For repeated fields, the API type uses []T, while the storage type
|
||||||
|
// uses *[]T.
|
||||||
|
//
|
||||||
|
// The reason for the divergence is so that the storage type more naturally
|
||||||
|
// matches what is expected of when retrieving the values through the
|
||||||
|
// protobuf reflection APIs.
|
||||||
|
//
|
||||||
|
// The value may only be populated if desc is also populated.
|
||||||
value interface{}
|
value interface{}
|
||||||
|
|
||||||
|
// enc is the raw bytes for the extension field.
|
||||||
enc []byte
|
enc []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,7 +350,7 @@ func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) {
|
||||||
// descriptors with the same field number.
|
// descriptors with the same field number.
|
||||||
return nil, errors.New("proto: descriptor conflict")
|
return nil, errors.New("proto: descriptor conflict")
|
||||||
}
|
}
|
||||||
return e.value, nil
|
return extensionAsLegacyType(e.value), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if extension.ExtensionType == nil {
|
if extension.ExtensionType == nil {
|
||||||
|
@ -349,11 +365,11 @@ func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) {
|
||||||
|
|
||||||
// Remember the decoded version and drop the encoded version.
|
// Remember the decoded version and drop the encoded version.
|
||||||
// That way it is safe to mutate what we return.
|
// That way it is safe to mutate what we return.
|
||||||
e.value = v
|
e.value = extensionAsStorageType(v)
|
||||||
e.desc = extension
|
e.desc = extension
|
||||||
e.enc = nil
|
e.enc = nil
|
||||||
emap[extension.Field] = e
|
emap[extension.Field] = e
|
||||||
return e.value, nil
|
return extensionAsLegacyType(e.value), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// defaultExtensionValue returns the default value for extension.
|
// defaultExtensionValue returns the default value for extension.
|
||||||
|
@ -488,7 +504,7 @@ func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error
|
||||||
}
|
}
|
||||||
typ := reflect.TypeOf(extension.ExtensionType)
|
typ := reflect.TypeOf(extension.ExtensionType)
|
||||||
if typ != reflect.TypeOf(value) {
|
if typ != reflect.TypeOf(value) {
|
||||||
return errors.New("proto: bad extension value type")
|
return fmt.Errorf("proto: bad extension value type. got: %T, want: %T", value, extension.ExtensionType)
|
||||||
}
|
}
|
||||||
// nil extension values need to be caught early, because the
|
// nil extension values need to be caught early, because the
|
||||||
// encoder can't distinguish an ErrNil due to a nil extension
|
// encoder can't distinguish an ErrNil due to a nil extension
|
||||||
|
@ -500,7 +516,7 @@ func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
extmap := epb.extensionsWrite()
|
extmap := epb.extensionsWrite()
|
||||||
extmap[extension.Field] = Extension{desc: extension, value: value}
|
extmap[extension.Field] = Extension{desc: extension, value: extensionAsStorageType(value)}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -541,3 +557,51 @@ func RegisterExtension(desc *ExtensionDesc) {
|
||||||
func RegisteredExtensions(pb Message) map[int32]*ExtensionDesc {
|
func RegisteredExtensions(pb Message) map[int32]*ExtensionDesc {
|
||||||
return extensionMaps[reflect.TypeOf(pb).Elem()]
|
return extensionMaps[reflect.TypeOf(pb).Elem()]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extensionAsLegacyType converts an value in the storage type as the API type.
|
||||||
|
// See Extension.value.
|
||||||
|
func extensionAsLegacyType(v interface{}) interface{} {
|
||||||
|
switch rv := reflect.ValueOf(v); rv.Kind() {
|
||||||
|
case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
|
||||||
|
// Represent primitive types as a pointer to the value.
|
||||||
|
rv2 := reflect.New(rv.Type())
|
||||||
|
rv2.Elem().Set(rv)
|
||||||
|
v = rv2.Interface()
|
||||||
|
case reflect.Ptr:
|
||||||
|
// Represent slice types as the value itself.
|
||||||
|
switch rv.Type().Elem().Kind() {
|
||||||
|
case reflect.Slice:
|
||||||
|
if rv.IsNil() {
|
||||||
|
v = reflect.Zero(rv.Type().Elem()).Interface()
|
||||||
|
} else {
|
||||||
|
v = rv.Elem().Interface()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// extensionAsStorageType converts an value in the API type as the storage type.
|
||||||
|
// See Extension.value.
|
||||||
|
func extensionAsStorageType(v interface{}) interface{} {
|
||||||
|
switch rv := reflect.ValueOf(v); rv.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
// Represent slice types as the value itself.
|
||||||
|
switch rv.Type().Elem().Kind() {
|
||||||
|
case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
|
||||||
|
if rv.IsNil() {
|
||||||
|
v = reflect.Zero(rv.Type().Elem()).Interface()
|
||||||
|
} else {
|
||||||
|
v = rv.Elem().Interface()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Slice:
|
||||||
|
// Represent slice types as a pointer to the value.
|
||||||
|
if rv.Type().Elem().Kind() != reflect.Uint8 {
|
||||||
|
rv2 := reflect.New(rv.Type())
|
||||||
|
rv2.Elem().Set(rv)
|
||||||
|
v = rv2.Interface()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
|
@ -265,7 +265,6 @@ package proto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
@ -274,7 +273,66 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
var errInvalidUTF8 = errors.New("proto: invalid UTF-8 string")
|
// RequiredNotSetError is an error type returned by either Marshal or Unmarshal.
|
||||||
|
// Marshal reports this when a required field is not initialized.
|
||||||
|
// Unmarshal reports this when a required field is missing from the wire data.
|
||||||
|
type RequiredNotSetError struct{ field string }
|
||||||
|
|
||||||
|
func (e *RequiredNotSetError) Error() string {
|
||||||
|
if e.field == "" {
|
||||||
|
return fmt.Sprintf("proto: required field not set")
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("proto: required field %q not set", e.field)
|
||||||
|
}
|
||||||
|
func (e *RequiredNotSetError) RequiredNotSet() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type invalidUTF8Error struct{ field string }
|
||||||
|
|
||||||
|
func (e *invalidUTF8Error) Error() string {
|
||||||
|
if e.field == "" {
|
||||||
|
return "proto: invalid UTF-8 detected"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("proto: field %q contains invalid UTF-8", e.field)
|
||||||
|
}
|
||||||
|
func (e *invalidUTF8Error) InvalidUTF8() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// errInvalidUTF8 is a sentinel error to identify fields with invalid UTF-8.
|
||||||
|
// This error should not be exposed to the external API as such errors should
|
||||||
|
// be recreated with the field information.
|
||||||
|
var errInvalidUTF8 = &invalidUTF8Error{}
|
||||||
|
|
||||||
|
// isNonFatal reports whether the error is either a RequiredNotSet error
|
||||||
|
// or a InvalidUTF8 error.
|
||||||
|
func isNonFatal(err error) bool {
|
||||||
|
if re, ok := err.(interface{ RequiredNotSet() bool }); ok && re.RequiredNotSet() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if re, ok := err.(interface{ InvalidUTF8() bool }); ok && re.InvalidUTF8() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type nonFatal struct{ E error }
|
||||||
|
|
||||||
|
// Merge merges err into nf and reports whether it was successful.
|
||||||
|
// Otherwise it returns false for any fatal non-nil errors.
|
||||||
|
func (nf *nonFatal) Merge(err error) (ok bool) {
|
||||||
|
if err == nil {
|
||||||
|
return true // not an error
|
||||||
|
}
|
||||||
|
if !isNonFatal(err) {
|
||||||
|
return false // fatal error
|
||||||
|
}
|
||||||
|
if nf.E == nil {
|
||||||
|
nf.E = err // store first instance of non-fatal error
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Message is implemented by generated protocol buffer messages.
|
// Message is implemented by generated protocol buffer messages.
|
||||||
type Message interface {
|
type Message interface {
|
||||||
|
@ -283,26 +341,6 @@ type Message interface {
|
||||||
ProtoMessage()
|
ProtoMessage()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stats records allocation details about the protocol buffer encoders
|
|
||||||
// and decoders. Useful for tuning the library itself.
|
|
||||||
type Stats struct {
|
|
||||||
Emalloc uint64 // mallocs in encode
|
|
||||||
Dmalloc uint64 // mallocs in decode
|
|
||||||
Encode uint64 // number of encodes
|
|
||||||
Decode uint64 // number of decodes
|
|
||||||
Chit uint64 // number of cache hits
|
|
||||||
Cmiss uint64 // number of cache misses
|
|
||||||
Size uint64 // number of sizes
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set to true to enable stats collection.
|
|
||||||
const collectStats = false
|
|
||||||
|
|
||||||
var stats Stats
|
|
||||||
|
|
||||||
// GetStats returns a copy of the global Stats structure.
|
|
||||||
func GetStats() Stats { return stats }
|
|
||||||
|
|
||||||
// A Buffer is a buffer manager for marshaling and unmarshaling
|
// A Buffer is a buffer manager for marshaling and unmarshaling
|
||||||
// protocol buffers. It may be reused between invocations to
|
// protocol buffers. It may be reused between invocations to
|
||||||
// reduce memory usage. It is not necessary to use a Buffer;
|
// reduce memory usage. It is not necessary to use a Buffer;
|
||||||
|
@ -902,13 +940,19 @@ func isProto3Zero(v reflect.Value) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProtoPackageIsVersion2 is referenced from generated protocol buffer files
|
const (
|
||||||
// to assert that that code is compatible with this version of the proto package.
|
// ProtoPackageIsVersion3 is referenced from generated protocol buffer files
|
||||||
const ProtoPackageIsVersion2 = true
|
// to assert that that code is compatible with this version of the proto package.
|
||||||
|
ProtoPackageIsVersion3 = true
|
||||||
|
|
||||||
// ProtoPackageIsVersion1 is referenced from generated protocol buffer files
|
// ProtoPackageIsVersion2 is referenced from generated protocol buffer files
|
||||||
// to assert that that code is compatible with this version of the proto package.
|
// to assert that that code is compatible with this version of the proto package.
|
||||||
const ProtoPackageIsVersion1 = true
|
ProtoPackageIsVersion2 = true
|
||||||
|
|
||||||
|
// ProtoPackageIsVersion1 is referenced from generated protocol buffer files
|
||||||
|
// to assert that that code is compatible with this version of the proto package.
|
||||||
|
ProtoPackageIsVersion1 = true
|
||||||
|
)
|
||||||
|
|
||||||
// InternalMessageInfo is a type used internally by generated .pb.go files.
|
// InternalMessageInfo is a type used internally by generated .pb.go files.
|
||||||
// This type is not intended to be used by non-generated code.
|
// This type is not intended to be used by non-generated code.
|
||||||
|
|
|
@ -36,13 +36,7 @@ package proto
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// errNoMessageTypeID occurs when a protocol buffer does not have a message type ID.
|
// errNoMessageTypeID occurs when a protocol buffer does not have a message type ID.
|
||||||
|
@ -145,46 +139,9 @@ func skipVarint(buf []byte) []byte {
|
||||||
return buf[i+1:]
|
return buf[i+1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalMessageSet encodes the extension map represented by m in the message set wire format.
|
// unmarshalMessageSet decodes the extension map encoded in buf in the message set wire format.
|
||||||
// It is called by generated Marshal methods on protocol buffer messages with the message_set_wire_format option.
|
|
||||||
func MarshalMessageSet(exts interface{}) ([]byte, error) {
|
|
||||||
return marshalMessageSet(exts, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// marshaMessageSet implements above function, with the opt to turn on / off deterministic during Marshal.
|
|
||||||
func marshalMessageSet(exts interface{}, deterministic bool) ([]byte, error) {
|
|
||||||
switch exts := exts.(type) {
|
|
||||||
case *XXX_InternalExtensions:
|
|
||||||
var u marshalInfo
|
|
||||||
siz := u.sizeMessageSet(exts)
|
|
||||||
b := make([]byte, 0, siz)
|
|
||||||
return u.appendMessageSet(b, exts, deterministic)
|
|
||||||
|
|
||||||
case map[int32]Extension:
|
|
||||||
// This is an old-style extension map.
|
|
||||||
// Wrap it in a new-style XXX_InternalExtensions.
|
|
||||||
ie := XXX_InternalExtensions{
|
|
||||||
p: &struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
extensionMap map[int32]Extension
|
|
||||||
}{
|
|
||||||
extensionMap: exts,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var u marshalInfo
|
|
||||||
siz := u.sizeMessageSet(&ie)
|
|
||||||
b := make([]byte, 0, siz)
|
|
||||||
return u.appendMessageSet(b, &ie, deterministic)
|
|
||||||
|
|
||||||
default:
|
|
||||||
return nil, errors.New("proto: not an extension map")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalMessageSet decodes the extension map encoded in buf in the message set wire format.
|
|
||||||
// It is called by Unmarshal methods on protocol buffer messages with the message_set_wire_format option.
|
// It is called by Unmarshal methods on protocol buffer messages with the message_set_wire_format option.
|
||||||
func UnmarshalMessageSet(buf []byte, exts interface{}) error {
|
func unmarshalMessageSet(buf []byte, exts interface{}) error {
|
||||||
var m map[int32]Extension
|
var m map[int32]Extension
|
||||||
switch exts := exts.(type) {
|
switch exts := exts.(type) {
|
||||||
case *XXX_InternalExtensions:
|
case *XXX_InternalExtensions:
|
||||||
|
@ -222,93 +179,3 @@ func UnmarshalMessageSet(buf []byte, exts interface{}) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalMessageSetJSON encodes the extension map represented by m in JSON format.
|
|
||||||
// It is called by generated MarshalJSON methods on protocol buffer messages with the message_set_wire_format option.
|
|
||||||
func MarshalMessageSetJSON(exts interface{}) ([]byte, error) {
|
|
||||||
var m map[int32]Extension
|
|
||||||
switch exts := exts.(type) {
|
|
||||||
case *XXX_InternalExtensions:
|
|
||||||
var mu sync.Locker
|
|
||||||
m, mu = exts.extensionsRead()
|
|
||||||
if m != nil {
|
|
||||||
// Keep the extensions map locked until we're done marshaling to prevent
|
|
||||||
// races between marshaling and unmarshaling the lazily-{en,de}coded
|
|
||||||
// values.
|
|
||||||
mu.Lock()
|
|
||||||
defer mu.Unlock()
|
|
||||||
}
|
|
||||||
case map[int32]Extension:
|
|
||||||
m = exts
|
|
||||||
default:
|
|
||||||
return nil, errors.New("proto: not an extension map")
|
|
||||||
}
|
|
||||||
var b bytes.Buffer
|
|
||||||
b.WriteByte('{')
|
|
||||||
|
|
||||||
// Process the map in key order for deterministic output.
|
|
||||||
ids := make([]int32, 0, len(m))
|
|
||||||
for id := range m {
|
|
||||||
ids = append(ids, id)
|
|
||||||
}
|
|
||||||
sort.Sort(int32Slice(ids)) // int32Slice defined in text.go
|
|
||||||
|
|
||||||
for i, id := range ids {
|
|
||||||
ext := m[id]
|
|
||||||
msd, ok := messageSetMap[id]
|
|
||||||
if !ok {
|
|
||||||
// Unknown type; we can't render it, so skip it.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if i > 0 && b.Len() > 1 {
|
|
||||||
b.WriteByte(',')
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(&b, `"[%s]":`, msd.name)
|
|
||||||
|
|
||||||
x := ext.value
|
|
||||||
if x == nil {
|
|
||||||
x = reflect.New(msd.t.Elem()).Interface()
|
|
||||||
if err := Unmarshal(ext.enc, x.(Message)); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
d, err := json.Marshal(x)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
b.Write(d)
|
|
||||||
}
|
|
||||||
b.WriteByte('}')
|
|
||||||
return b.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalMessageSetJSON decodes the extension map encoded in buf in JSON format.
|
|
||||||
// It is called by generated UnmarshalJSON methods on protocol buffer messages with the message_set_wire_format option.
|
|
||||||
func UnmarshalMessageSetJSON(buf []byte, exts interface{}) error {
|
|
||||||
// Common-case fast path.
|
|
||||||
if len(buf) == 0 || bytes.Equal(buf, []byte("{}")) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is fairly tricky, and it's not clear that it is needed.
|
|
||||||
return errors.New("TODO: UnmarshalMessageSetJSON not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// A global registry of types that can be used in a MessageSet.
|
|
||||||
|
|
||||||
var messageSetMap = make(map[int32]messageSetDesc)
|
|
||||||
|
|
||||||
type messageSetDesc struct {
|
|
||||||
t reflect.Type // pointer to struct
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterMessageSetType is called from the generated code.
|
|
||||||
func RegisterMessageSetType(m Message, fieldNum int32, name string) {
|
|
||||||
messageSetMap[fieldNum] = messageSetDesc{
|
|
||||||
t: reflect.TypeOf(m),
|
|
||||||
name: name,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -79,10 +79,13 @@ func toPointer(i *Message) pointer {
|
||||||
|
|
||||||
// toAddrPointer converts an interface to a pointer that points to
|
// toAddrPointer converts an interface to a pointer that points to
|
||||||
// the interface data.
|
// the interface data.
|
||||||
func toAddrPointer(i *interface{}, isptr bool) pointer {
|
func toAddrPointer(i *interface{}, isptr, deref bool) pointer {
|
||||||
v := reflect.ValueOf(*i)
|
v := reflect.ValueOf(*i)
|
||||||
u := reflect.New(v.Type())
|
u := reflect.New(v.Type())
|
||||||
u.Elem().Set(v)
|
u.Elem().Set(v)
|
||||||
|
if deref {
|
||||||
|
u = u.Elem()
|
||||||
|
}
|
||||||
return pointer{v: u}
|
return pointer{v: u}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,16 +85,21 @@ func toPointer(i *Message) pointer {
|
||||||
|
|
||||||
// toAddrPointer converts an interface to a pointer that points to
|
// toAddrPointer converts an interface to a pointer that points to
|
||||||
// the interface data.
|
// the interface data.
|
||||||
func toAddrPointer(i *interface{}, isptr bool) pointer {
|
func toAddrPointer(i *interface{}, isptr, deref bool) (p pointer) {
|
||||||
// Super-tricky - read or get the address of data word of interface value.
|
// Super-tricky - read or get the address of data word of interface value.
|
||||||
if isptr {
|
if isptr {
|
||||||
// The interface is of pointer type, thus it is a direct interface.
|
// The interface is of pointer type, thus it is a direct interface.
|
||||||
// The data word is the pointer data itself. We take its address.
|
// The data word is the pointer data itself. We take its address.
|
||||||
return pointer{p: unsafe.Pointer(uintptr(unsafe.Pointer(i)) + ptrSize)}
|
p = pointer{p: unsafe.Pointer(uintptr(unsafe.Pointer(i)) + ptrSize)}
|
||||||
}
|
} else {
|
||||||
// The interface is not of pointer type. The data word is the pointer
|
// The interface is not of pointer type. The data word is the pointer
|
||||||
// to the data.
|
// to the data.
|
||||||
return pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]}
|
p = pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]}
|
||||||
|
}
|
||||||
|
if deref {
|
||||||
|
p.p = *(*unsafe.Pointer)(p.p)
|
||||||
|
}
|
||||||
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
// valToPointer converts v to a pointer. v must be of pointer type.
|
// valToPointer converts v to a pointer. v must be of pointer type.
|
||||||
|
|
|
@ -139,7 +139,7 @@ type Properties struct {
|
||||||
Repeated bool
|
Repeated bool
|
||||||
Packed bool // relevant for repeated primitives only
|
Packed bool // relevant for repeated primitives only
|
||||||
Enum string // set for enum types only
|
Enum string // set for enum types only
|
||||||
proto3 bool // whether this is known to be a proto3 field; set for []byte only
|
proto3 bool // whether this is known to be a proto3 field
|
||||||
oneof bool // whether this is a oneof field
|
oneof bool // whether this is a oneof field
|
||||||
|
|
||||||
Default string // default value
|
Default string // default value
|
||||||
|
@ -149,8 +149,8 @@ type Properties struct {
|
||||||
sprop *StructProperties // set for struct types only
|
sprop *StructProperties // set for struct types only
|
||||||
|
|
||||||
mtype reflect.Type // set for map types only
|
mtype reflect.Type // set for map types only
|
||||||
mkeyprop *Properties // set for map types only
|
MapKeyProp *Properties // set for map types only
|
||||||
mvalprop *Properties // set for map types only
|
MapValProp *Properties // set for map types only
|
||||||
}
|
}
|
||||||
|
|
||||||
// String formats the properties in the protobuf struct field tag style.
|
// String formats the properties in the protobuf struct field tag style.
|
||||||
|
@ -275,16 +275,16 @@ func (p *Properties) setFieldProps(typ reflect.Type, f *reflect.StructField, loc
|
||||||
|
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
p.mtype = t1
|
p.mtype = t1
|
||||||
p.mkeyprop = &Properties{}
|
p.MapKeyProp = &Properties{}
|
||||||
p.mkeyprop.init(reflect.PtrTo(p.mtype.Key()), "Key", f.Tag.Get("protobuf_key"), nil, lockGetProp)
|
p.MapKeyProp.init(reflect.PtrTo(p.mtype.Key()), "Key", f.Tag.Get("protobuf_key"), nil, lockGetProp)
|
||||||
p.mvalprop = &Properties{}
|
p.MapValProp = &Properties{}
|
||||||
vtype := p.mtype.Elem()
|
vtype := p.mtype.Elem()
|
||||||
if vtype.Kind() != reflect.Ptr && vtype.Kind() != reflect.Slice {
|
if vtype.Kind() != reflect.Ptr && vtype.Kind() != reflect.Slice {
|
||||||
// The value type is not a message (*T) or bytes ([]byte),
|
// The value type is not a message (*T) or bytes ([]byte),
|
||||||
// so we need encoders for the pointer to this type.
|
// so we need encoders for the pointer to this type.
|
||||||
vtype = reflect.PtrTo(vtype)
|
vtype = reflect.PtrTo(vtype)
|
||||||
}
|
}
|
||||||
p.mvalprop.init(vtype, "Value", f.Tag.Get("protobuf_val"), nil, lockGetProp)
|
p.MapValProp.init(vtype, "Value", f.Tag.Get("protobuf_val"), nil, lockGetProp)
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.stype != nil {
|
if p.stype != nil {
|
||||||
|
@ -334,9 +334,6 @@ func GetProperties(t reflect.Type) *StructProperties {
|
||||||
sprop, ok := propertiesMap[t]
|
sprop, ok := propertiesMap[t]
|
||||||
propertiesMu.RUnlock()
|
propertiesMu.RUnlock()
|
||||||
if ok {
|
if ok {
|
||||||
if collectStats {
|
|
||||||
stats.Chit++
|
|
||||||
}
|
|
||||||
return sprop
|
return sprop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,17 +343,20 @@ func GetProperties(t reflect.Type) *StructProperties {
|
||||||
return sprop
|
return sprop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
oneofFuncsIface interface {
|
||||||
|
XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{})
|
||||||
|
}
|
||||||
|
oneofWrappersIface interface {
|
||||||
|
XXX_OneofWrappers() []interface{}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// getPropertiesLocked requires that propertiesMu is held.
|
// getPropertiesLocked requires that propertiesMu is held.
|
||||||
func getPropertiesLocked(t reflect.Type) *StructProperties {
|
func getPropertiesLocked(t reflect.Type) *StructProperties {
|
||||||
if prop, ok := propertiesMap[t]; ok {
|
if prop, ok := propertiesMap[t]; ok {
|
||||||
if collectStats {
|
|
||||||
stats.Chit++
|
|
||||||
}
|
|
||||||
return prop
|
return prop
|
||||||
}
|
}
|
||||||
if collectStats {
|
|
||||||
stats.Cmiss++
|
|
||||||
}
|
|
||||||
|
|
||||||
prop := new(StructProperties)
|
prop := new(StructProperties)
|
||||||
// in case of recursive protos, fill this in now.
|
// in case of recursive protos, fill this in now.
|
||||||
|
@ -391,13 +391,14 @@ func getPropertiesLocked(t reflect.Type) *StructProperties {
|
||||||
// Re-order prop.order.
|
// Re-order prop.order.
|
||||||
sort.Sort(prop)
|
sort.Sort(prop)
|
||||||
|
|
||||||
type oneofMessage interface {
|
|
||||||
XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{})
|
|
||||||
}
|
|
||||||
if om, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(oneofMessage); ok {
|
|
||||||
var oots []interface{}
|
var oots []interface{}
|
||||||
_, _, _, oots = om.XXX_OneofFuncs()
|
switch m := reflect.Zero(reflect.PtrTo(t)).Interface().(type) {
|
||||||
|
case oneofFuncsIface:
|
||||||
|
_, _, _, oots = m.XXX_OneofFuncs()
|
||||||
|
case oneofWrappersIface:
|
||||||
|
oots = m.XXX_OneofWrappers()
|
||||||
|
}
|
||||||
|
if len(oots) > 0 {
|
||||||
// Interpret oneof metadata.
|
// Interpret oneof metadata.
|
||||||
prop.OneofTypes = make(map[string]*OneofProperties)
|
prop.OneofTypes = make(map[string]*OneofProperties)
|
||||||
for _, oot := range oots {
|
for _, oot := range oots {
|
||||||
|
|
|
@ -87,6 +87,7 @@ type marshalElemInfo struct {
|
||||||
sizer sizer
|
sizer sizer
|
||||||
marshaler marshaler
|
marshaler marshaler
|
||||||
isptr bool // elem is pointer typed, thus interface of this type is a direct interface (extension only)
|
isptr bool // elem is pointer typed, thus interface of this type is a direct interface (extension only)
|
||||||
|
deref bool // dereference the pointer before operating on it; implies isptr
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -231,7 +232,7 @@ func (u *marshalInfo) marshal(b []byte, ptr pointer, deterministic bool) ([]byte
|
||||||
return b, err
|
return b, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var err, errreq error
|
var err, errLater error
|
||||||
// The old marshaler encodes extensions at beginning.
|
// The old marshaler encodes extensions at beginning.
|
||||||
if u.extensions.IsValid() {
|
if u.extensions.IsValid() {
|
||||||
e := ptr.offset(u.extensions).toExtensions()
|
e := ptr.offset(u.extensions).toExtensions()
|
||||||
|
@ -252,11 +253,13 @@ func (u *marshalInfo) marshal(b []byte, ptr pointer, deterministic bool) ([]byte
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, f := range u.fields {
|
for _, f := range u.fields {
|
||||||
if f.required && errreq == nil {
|
if f.required {
|
||||||
if ptr.offset(f.field).getPointer().isNil() {
|
if ptr.offset(f.field).getPointer().isNil() {
|
||||||
// Required field is not set.
|
// Required field is not set.
|
||||||
// We record the error but keep going, to give a complete marshaling.
|
// We record the error but keep going, to give a complete marshaling.
|
||||||
errreq = &RequiredNotSetError{f.name}
|
if errLater == nil {
|
||||||
|
errLater = &RequiredNotSetError{f.name}
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -269,14 +272,21 @@ func (u *marshalInfo) marshal(b []byte, ptr pointer, deterministic bool) ([]byte
|
||||||
if err1, ok := err.(*RequiredNotSetError); ok {
|
if err1, ok := err.(*RequiredNotSetError); ok {
|
||||||
// Required field in submessage is not set.
|
// Required field in submessage is not set.
|
||||||
// We record the error but keep going, to give a complete marshaling.
|
// We record the error but keep going, to give a complete marshaling.
|
||||||
if errreq == nil {
|
if errLater == nil {
|
||||||
errreq = &RequiredNotSetError{f.name + "." + err1.field}
|
errLater = &RequiredNotSetError{f.name + "." + err1.field}
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err == errRepeatedHasNil {
|
if err == errRepeatedHasNil {
|
||||||
err = errors.New("proto: repeated field " + f.name + " has nil element")
|
err = errors.New("proto: repeated field " + f.name + " has nil element")
|
||||||
}
|
}
|
||||||
|
if err == errInvalidUTF8 {
|
||||||
|
if errLater == nil {
|
||||||
|
fullName := revProtoTypes[reflect.PtrTo(u.typ)] + "." + f.name
|
||||||
|
errLater = &invalidUTF8Error{fullName}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
return b, err
|
return b, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -284,7 +294,7 @@ func (u *marshalInfo) marshal(b []byte, ptr pointer, deterministic bool) ([]byte
|
||||||
s := *ptr.offset(u.unrecognized).toBytes()
|
s := *ptr.offset(u.unrecognized).toBytes()
|
||||||
b = append(b, s...)
|
b = append(b, s...)
|
||||||
}
|
}
|
||||||
return b, errreq
|
return b, errLater
|
||||||
}
|
}
|
||||||
|
|
||||||
// computeMarshalInfo initializes the marshal info.
|
// computeMarshalInfo initializes the marshal info.
|
||||||
|
@ -311,8 +321,11 @@ func (u *marshalInfo) computeMarshalInfo() {
|
||||||
|
|
||||||
// get oneof implementers
|
// get oneof implementers
|
||||||
var oneofImplementers []interface{}
|
var oneofImplementers []interface{}
|
||||||
if m, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(oneofMessage); ok {
|
switch m := reflect.Zero(reflect.PtrTo(t)).Interface().(type) {
|
||||||
|
case oneofFuncsIface:
|
||||||
_, _, _, oneofImplementers = m.XXX_OneofFuncs()
|
_, _, _, oneofImplementers = m.XXX_OneofFuncs()
|
||||||
|
case oneofWrappersIface:
|
||||||
|
oneofImplementers = m.XXX_OneofWrappers()
|
||||||
}
|
}
|
||||||
|
|
||||||
n := t.NumField()
|
n := t.NumField()
|
||||||
|
@ -398,13 +411,22 @@ func (u *marshalInfo) getExtElemInfo(desc *ExtensionDesc) *marshalElemInfo {
|
||||||
panic("tag is not an integer")
|
panic("tag is not an integer")
|
||||||
}
|
}
|
||||||
wt := wiretype(tags[0])
|
wt := wiretype(tags[0])
|
||||||
|
if t.Kind() == reflect.Ptr && t.Elem().Kind() != reflect.Struct {
|
||||||
|
t = t.Elem()
|
||||||
|
}
|
||||||
sizer, marshaler := typeMarshaler(t, tags, false, false)
|
sizer, marshaler := typeMarshaler(t, tags, false, false)
|
||||||
|
var deref bool
|
||||||
|
if t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8 {
|
||||||
|
t = reflect.PtrTo(t)
|
||||||
|
deref = true
|
||||||
|
}
|
||||||
e = &marshalElemInfo{
|
e = &marshalElemInfo{
|
||||||
wiretag: uint64(tag)<<3 | wt,
|
wiretag: uint64(tag)<<3 | wt,
|
||||||
tagsize: SizeVarint(uint64(tag) << 3),
|
tagsize: SizeVarint(uint64(tag) << 3),
|
||||||
sizer: sizer,
|
sizer: sizer,
|
||||||
marshaler: marshaler,
|
marshaler: marshaler,
|
||||||
isptr: t.Kind() == reflect.Ptr,
|
isptr: t.Kind() == reflect.Ptr,
|
||||||
|
deref: deref,
|
||||||
}
|
}
|
||||||
|
|
||||||
// update cache
|
// update cache
|
||||||
|
@ -439,7 +461,7 @@ func (fi *marshalFieldInfo) computeMarshalFieldInfo(f *reflect.StructField) {
|
||||||
|
|
||||||
func (fi *marshalFieldInfo) computeOneofFieldInfo(f *reflect.StructField, oneofImplementers []interface{}) {
|
func (fi *marshalFieldInfo) computeOneofFieldInfo(f *reflect.StructField, oneofImplementers []interface{}) {
|
||||||
fi.field = toField(f)
|
fi.field = toField(f)
|
||||||
fi.wiretag = 1<<31 - 1 // Use a large tag number, make oneofs sorted at the end. This tag will not appear on the wire.
|
fi.wiretag = math.MaxInt32 // Use a large tag number, make oneofs sorted at the end. This tag will not appear on the wire.
|
||||||
fi.isPointer = true
|
fi.isPointer = true
|
||||||
fi.sizer, fi.marshaler = makeOneOfMarshaler(fi, f)
|
fi.sizer, fi.marshaler = makeOneOfMarshaler(fi, f)
|
||||||
fi.oneofElems = make(map[reflect.Type]*marshalElemInfo)
|
fi.oneofElems = make(map[reflect.Type]*marshalElemInfo)
|
||||||
|
@ -467,10 +489,6 @@ func (fi *marshalFieldInfo) computeOneofFieldInfo(f *reflect.StructField, oneofI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type oneofMessage interface {
|
|
||||||
XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// wiretype returns the wire encoding of the type.
|
// wiretype returns the wire encoding of the type.
|
||||||
func wiretype(encoding string) uint64 {
|
func wiretype(encoding string) uint64 {
|
||||||
switch encoding {
|
switch encoding {
|
||||||
|
@ -530,6 +548,7 @@ func typeMarshaler(t reflect.Type, tags []string, nozero, oneof bool) (sizer, ma
|
||||||
|
|
||||||
packed := false
|
packed := false
|
||||||
proto3 := false
|
proto3 := false
|
||||||
|
validateUTF8 := true
|
||||||
for i := 2; i < len(tags); i++ {
|
for i := 2; i < len(tags); i++ {
|
||||||
if tags[i] == "packed" {
|
if tags[i] == "packed" {
|
||||||
packed = true
|
packed = true
|
||||||
|
@ -538,6 +557,7 @@ func typeMarshaler(t reflect.Type, tags []string, nozero, oneof bool) (sizer, ma
|
||||||
proto3 = true
|
proto3 = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
validateUTF8 = validateUTF8 && proto3
|
||||||
|
|
||||||
switch t.Kind() {
|
switch t.Kind() {
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
|
@ -735,6 +755,18 @@ func typeMarshaler(t reflect.Type, tags []string, nozero, oneof bool) (sizer, ma
|
||||||
}
|
}
|
||||||
return sizeFloat64Value, appendFloat64Value
|
return sizeFloat64Value, appendFloat64Value
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
|
if validateUTF8 {
|
||||||
|
if pointer {
|
||||||
|
return sizeStringPtr, appendUTF8StringPtr
|
||||||
|
}
|
||||||
|
if slice {
|
||||||
|
return sizeStringSlice, appendUTF8StringSlice
|
||||||
|
}
|
||||||
|
if nozero {
|
||||||
|
return sizeStringValueNoZero, appendUTF8StringValueNoZero
|
||||||
|
}
|
||||||
|
return sizeStringValue, appendUTF8StringValue
|
||||||
|
}
|
||||||
if pointer {
|
if pointer {
|
||||||
return sizeStringPtr, appendStringPtr
|
return sizeStringPtr, appendStringPtr
|
||||||
}
|
}
|
||||||
|
@ -1984,9 +2016,6 @@ func appendBoolPackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byt
|
||||||
}
|
}
|
||||||
func appendStringValue(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
|
func appendStringValue(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
|
||||||
v := *ptr.toString()
|
v := *ptr.toString()
|
||||||
if !utf8.ValidString(v) {
|
|
||||||
return nil, errInvalidUTF8
|
|
||||||
}
|
|
||||||
b = appendVarint(b, wiretag)
|
b = appendVarint(b, wiretag)
|
||||||
b = appendVarint(b, uint64(len(v)))
|
b = appendVarint(b, uint64(len(v)))
|
||||||
b = append(b, v...)
|
b = append(b, v...)
|
||||||
|
@ -1997,9 +2026,6 @@ func appendStringValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]b
|
||||||
if v == "" {
|
if v == "" {
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
if !utf8.ValidString(v) {
|
|
||||||
return nil, errInvalidUTF8
|
|
||||||
}
|
|
||||||
b = appendVarint(b, wiretag)
|
b = appendVarint(b, wiretag)
|
||||||
b = appendVarint(b, uint64(len(v)))
|
b = appendVarint(b, uint64(len(v)))
|
||||||
b = append(b, v...)
|
b = append(b, v...)
|
||||||
|
@ -2011,9 +2037,6 @@ func appendStringPtr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, err
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
v := *p
|
v := *p
|
||||||
if !utf8.ValidString(v) {
|
|
||||||
return nil, errInvalidUTF8
|
|
||||||
}
|
|
||||||
b = appendVarint(b, wiretag)
|
b = appendVarint(b, wiretag)
|
||||||
b = appendVarint(b, uint64(len(v)))
|
b = appendVarint(b, uint64(len(v)))
|
||||||
b = append(b, v...)
|
b = append(b, v...)
|
||||||
|
@ -2022,12 +2045,74 @@ func appendStringPtr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, err
|
||||||
func appendStringSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
|
func appendStringSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
|
||||||
s := *ptr.toStringSlice()
|
s := *ptr.toStringSlice()
|
||||||
for _, v := range s {
|
for _, v := range s {
|
||||||
|
b = appendVarint(b, wiretag)
|
||||||
|
b = appendVarint(b, uint64(len(v)))
|
||||||
|
b = append(b, v...)
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
func appendUTF8StringValue(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
|
||||||
|
var invalidUTF8 bool
|
||||||
|
v := *ptr.toString()
|
||||||
if !utf8.ValidString(v) {
|
if !utf8.ValidString(v) {
|
||||||
return nil, errInvalidUTF8
|
invalidUTF8 = true
|
||||||
}
|
}
|
||||||
b = appendVarint(b, wiretag)
|
b = appendVarint(b, wiretag)
|
||||||
b = appendVarint(b, uint64(len(v)))
|
b = appendVarint(b, uint64(len(v)))
|
||||||
b = append(b, v...)
|
b = append(b, v...)
|
||||||
|
if invalidUTF8 {
|
||||||
|
return b, errInvalidUTF8
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
func appendUTF8StringValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
|
||||||
|
var invalidUTF8 bool
|
||||||
|
v := *ptr.toString()
|
||||||
|
if v == "" {
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
if !utf8.ValidString(v) {
|
||||||
|
invalidUTF8 = true
|
||||||
|
}
|
||||||
|
b = appendVarint(b, wiretag)
|
||||||
|
b = appendVarint(b, uint64(len(v)))
|
||||||
|
b = append(b, v...)
|
||||||
|
if invalidUTF8 {
|
||||||
|
return b, errInvalidUTF8
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
func appendUTF8StringPtr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
|
||||||
|
var invalidUTF8 bool
|
||||||
|
p := *ptr.toStringPtr()
|
||||||
|
if p == nil {
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
v := *p
|
||||||
|
if !utf8.ValidString(v) {
|
||||||
|
invalidUTF8 = true
|
||||||
|
}
|
||||||
|
b = appendVarint(b, wiretag)
|
||||||
|
b = appendVarint(b, uint64(len(v)))
|
||||||
|
b = append(b, v...)
|
||||||
|
if invalidUTF8 {
|
||||||
|
return b, errInvalidUTF8
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
func appendUTF8StringSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
|
||||||
|
var invalidUTF8 bool
|
||||||
|
s := *ptr.toStringSlice()
|
||||||
|
for _, v := range s {
|
||||||
|
if !utf8.ValidString(v) {
|
||||||
|
invalidUTF8 = true
|
||||||
|
}
|
||||||
|
b = appendVarint(b, wiretag)
|
||||||
|
b = appendVarint(b, uint64(len(v)))
|
||||||
|
b = append(b, v...)
|
||||||
|
}
|
||||||
|
if invalidUTF8 {
|
||||||
|
return b, errInvalidUTF8
|
||||||
}
|
}
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
@ -2107,7 +2192,8 @@ func makeGroupSliceMarshaler(u *marshalInfo) (sizer, marshaler) {
|
||||||
},
|
},
|
||||||
func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) {
|
func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) {
|
||||||
s := ptr.getPointerSlice()
|
s := ptr.getPointerSlice()
|
||||||
var err, errreq error
|
var err error
|
||||||
|
var nerr nonFatal
|
||||||
for _, v := range s {
|
for _, v := range s {
|
||||||
if v.isNil() {
|
if v.isNil() {
|
||||||
return b, errRepeatedHasNil
|
return b, errRepeatedHasNil
|
||||||
|
@ -2115,22 +2201,14 @@ func makeGroupSliceMarshaler(u *marshalInfo) (sizer, marshaler) {
|
||||||
b = appendVarint(b, wiretag) // start group
|
b = appendVarint(b, wiretag) // start group
|
||||||
b, err = u.marshal(b, v, deterministic)
|
b, err = u.marshal(b, v, deterministic)
|
||||||
b = appendVarint(b, wiretag+(WireEndGroup-WireStartGroup)) // end group
|
b = appendVarint(b, wiretag+(WireEndGroup-WireStartGroup)) // end group
|
||||||
if err != nil {
|
if !nerr.Merge(err) {
|
||||||
if _, ok := err.(*RequiredNotSetError); ok {
|
|
||||||
// Required field in submessage is not set.
|
|
||||||
// We record the error but keep going, to give a complete marshaling.
|
|
||||||
if errreq == nil {
|
|
||||||
errreq = err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err == ErrNil {
|
if err == ErrNil {
|
||||||
err = errRepeatedHasNil
|
err = errRepeatedHasNil
|
||||||
}
|
}
|
||||||
return b, err
|
return b, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return b, errreq
|
return b, nerr.E
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2174,7 +2252,8 @@ func makeMessageSliceMarshaler(u *marshalInfo) (sizer, marshaler) {
|
||||||
},
|
},
|
||||||
func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) {
|
func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) {
|
||||||
s := ptr.getPointerSlice()
|
s := ptr.getPointerSlice()
|
||||||
var err, errreq error
|
var err error
|
||||||
|
var nerr nonFatal
|
||||||
for _, v := range s {
|
for _, v := range s {
|
||||||
if v.isNil() {
|
if v.isNil() {
|
||||||
return b, errRepeatedHasNil
|
return b, errRepeatedHasNil
|
||||||
|
@ -2184,22 +2263,14 @@ func makeMessageSliceMarshaler(u *marshalInfo) (sizer, marshaler) {
|
||||||
b = appendVarint(b, uint64(siz))
|
b = appendVarint(b, uint64(siz))
|
||||||
b, err = u.marshal(b, v, deterministic)
|
b, err = u.marshal(b, v, deterministic)
|
||||||
|
|
||||||
if err != nil {
|
if !nerr.Merge(err) {
|
||||||
if _, ok := err.(*RequiredNotSetError); ok {
|
|
||||||
// Required field in submessage is not set.
|
|
||||||
// We record the error but keep going, to give a complete marshaling.
|
|
||||||
if errreq == nil {
|
|
||||||
errreq = err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err == ErrNil {
|
if err == ErrNil {
|
||||||
err = errRepeatedHasNil
|
err = errRepeatedHasNil
|
||||||
}
|
}
|
||||||
return b, err
|
return b, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return b, errreq
|
return b, nerr.E
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2223,14 +2294,33 @@ func makeMapMarshaler(f *reflect.StructField) (sizer, marshaler) {
|
||||||
// value.
|
// value.
|
||||||
// Key cannot be pointer-typed.
|
// Key cannot be pointer-typed.
|
||||||
valIsPtr := valType.Kind() == reflect.Ptr
|
valIsPtr := valType.Kind() == reflect.Ptr
|
||||||
|
|
||||||
|
// If value is a message with nested maps, calling
|
||||||
|
// valSizer in marshal may be quadratic. We should use
|
||||||
|
// cached version in marshal (but not in size).
|
||||||
|
// If value is not message type, we don't have size cache,
|
||||||
|
// but it cannot be nested either. Just use valSizer.
|
||||||
|
valCachedSizer := valSizer
|
||||||
|
if valIsPtr && valType.Elem().Kind() == reflect.Struct {
|
||||||
|
u := getMarshalInfo(valType.Elem())
|
||||||
|
valCachedSizer = func(ptr pointer, tagsize int) int {
|
||||||
|
// Same as message sizer, but use cache.
|
||||||
|
p := ptr.getPointer()
|
||||||
|
if p.isNil() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
siz := u.cachedsize(p)
|
||||||
|
return siz + SizeVarint(uint64(siz)) + tagsize
|
||||||
|
}
|
||||||
|
}
|
||||||
return func(ptr pointer, tagsize int) int {
|
return func(ptr pointer, tagsize int) int {
|
||||||
m := ptr.asPointerTo(t).Elem() // the map
|
m := ptr.asPointerTo(t).Elem() // the map
|
||||||
n := 0
|
n := 0
|
||||||
for _, k := range m.MapKeys() {
|
for _, k := range m.MapKeys() {
|
||||||
ki := k.Interface()
|
ki := k.Interface()
|
||||||
vi := m.MapIndex(k).Interface()
|
vi := m.MapIndex(k).Interface()
|
||||||
kaddr := toAddrPointer(&ki, false) // pointer to key
|
kaddr := toAddrPointer(&ki, false, false) // pointer to key
|
||||||
vaddr := toAddrPointer(&vi, valIsPtr) // pointer to value
|
vaddr := toAddrPointer(&vi, valIsPtr, false) // pointer to value
|
||||||
siz := keySizer(kaddr, 1) + valSizer(vaddr, 1) // tag of key = 1 (size=1), tag of val = 2 (size=1)
|
siz := keySizer(kaddr, 1) + valSizer(vaddr, 1) // tag of key = 1 (size=1), tag of val = 2 (size=1)
|
||||||
n += siz + SizeVarint(uint64(siz)) + tagsize
|
n += siz + SizeVarint(uint64(siz)) + tagsize
|
||||||
}
|
}
|
||||||
|
@ -2243,24 +2333,26 @@ func makeMapMarshaler(f *reflect.StructField) (sizer, marshaler) {
|
||||||
if len(keys) > 1 && deterministic {
|
if len(keys) > 1 && deterministic {
|
||||||
sort.Sort(mapKeys(keys))
|
sort.Sort(mapKeys(keys))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var nerr nonFatal
|
||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
ki := k.Interface()
|
ki := k.Interface()
|
||||||
vi := m.MapIndex(k).Interface()
|
vi := m.MapIndex(k).Interface()
|
||||||
kaddr := toAddrPointer(&ki, false) // pointer to key
|
kaddr := toAddrPointer(&ki, false, false) // pointer to key
|
||||||
vaddr := toAddrPointer(&vi, valIsPtr) // pointer to value
|
vaddr := toAddrPointer(&vi, valIsPtr, false) // pointer to value
|
||||||
b = appendVarint(b, tag)
|
b = appendVarint(b, tag)
|
||||||
siz := keySizer(kaddr, 1) + valSizer(vaddr, 1) // tag of key = 1 (size=1), tag of val = 2 (size=1)
|
siz := keySizer(kaddr, 1) + valCachedSizer(vaddr, 1) // tag of key = 1 (size=1), tag of val = 2 (size=1)
|
||||||
b = appendVarint(b, uint64(siz))
|
b = appendVarint(b, uint64(siz))
|
||||||
b, err = keyMarshaler(b, kaddr, keyWireTag, deterministic)
|
b, err = keyMarshaler(b, kaddr, keyWireTag, deterministic)
|
||||||
if err != nil {
|
if !nerr.Merge(err) {
|
||||||
return b, err
|
return b, err
|
||||||
}
|
}
|
||||||
b, err = valMarshaler(b, vaddr, valWireTag, deterministic)
|
b, err = valMarshaler(b, vaddr, valWireTag, deterministic)
|
||||||
if err != nil && err != ErrNil { // allow nil value in map
|
if err != ErrNil && !nerr.Merge(err) { // allow nil value in map
|
||||||
return b, err
|
return b, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return b, nil
|
return b, nerr.E
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2316,7 +2408,7 @@ func (u *marshalInfo) sizeExtensions(ext *XXX_InternalExtensions) int {
|
||||||
// the last time this function was called.
|
// the last time this function was called.
|
||||||
ei := u.getExtElemInfo(e.desc)
|
ei := u.getExtElemInfo(e.desc)
|
||||||
v := e.value
|
v := e.value
|
||||||
p := toAddrPointer(&v, ei.isptr)
|
p := toAddrPointer(&v, ei.isptr, ei.deref)
|
||||||
n += ei.sizer(p, ei.tagsize)
|
n += ei.sizer(p, ei.tagsize)
|
||||||
}
|
}
|
||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
|
@ -2333,6 +2425,7 @@ func (u *marshalInfo) appendExtensions(b []byte, ext *XXX_InternalExtensions, de
|
||||||
defer mu.Unlock()
|
defer mu.Unlock()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
var nerr nonFatal
|
||||||
|
|
||||||
// Fast-path for common cases: zero or one extensions.
|
// Fast-path for common cases: zero or one extensions.
|
||||||
// Don't bother sorting the keys.
|
// Don't bother sorting the keys.
|
||||||
|
@ -2350,13 +2443,13 @@ func (u *marshalInfo) appendExtensions(b []byte, ext *XXX_InternalExtensions, de
|
||||||
|
|
||||||
ei := u.getExtElemInfo(e.desc)
|
ei := u.getExtElemInfo(e.desc)
|
||||||
v := e.value
|
v := e.value
|
||||||
p := toAddrPointer(&v, ei.isptr)
|
p := toAddrPointer(&v, ei.isptr, ei.deref)
|
||||||
b, err = ei.marshaler(b, p, ei.wiretag, deterministic)
|
b, err = ei.marshaler(b, p, ei.wiretag, deterministic)
|
||||||
if err != nil {
|
if !nerr.Merge(err) {
|
||||||
return b, err
|
return b, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return b, nil
|
return b, nerr.E
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort the keys to provide a deterministic encoding.
|
// Sort the keys to provide a deterministic encoding.
|
||||||
|
@ -2381,13 +2474,13 @@ func (u *marshalInfo) appendExtensions(b []byte, ext *XXX_InternalExtensions, de
|
||||||
|
|
||||||
ei := u.getExtElemInfo(e.desc)
|
ei := u.getExtElemInfo(e.desc)
|
||||||
v := e.value
|
v := e.value
|
||||||
p := toAddrPointer(&v, ei.isptr)
|
p := toAddrPointer(&v, ei.isptr, ei.deref)
|
||||||
b, err = ei.marshaler(b, p, ei.wiretag, deterministic)
|
b, err = ei.marshaler(b, p, ei.wiretag, deterministic)
|
||||||
if err != nil {
|
if !nerr.Merge(err) {
|
||||||
return b, err
|
return b, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return b, nil
|
return b, nerr.E
|
||||||
}
|
}
|
||||||
|
|
||||||
// message set format is:
|
// message set format is:
|
||||||
|
@ -2426,7 +2519,7 @@ func (u *marshalInfo) sizeMessageSet(ext *XXX_InternalExtensions) int {
|
||||||
|
|
||||||
ei := u.getExtElemInfo(e.desc)
|
ei := u.getExtElemInfo(e.desc)
|
||||||
v := e.value
|
v := e.value
|
||||||
p := toAddrPointer(&v, ei.isptr)
|
p := toAddrPointer(&v, ei.isptr, ei.deref)
|
||||||
n += ei.sizer(p, 1) // message, tag = 3 (size=1)
|
n += ei.sizer(p, 1) // message, tag = 3 (size=1)
|
||||||
}
|
}
|
||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
|
@ -2444,6 +2537,7 @@ func (u *marshalInfo) appendMessageSet(b []byte, ext *XXX_InternalExtensions, de
|
||||||
defer mu.Unlock()
|
defer mu.Unlock()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
var nerr nonFatal
|
||||||
|
|
||||||
// Fast-path for common cases: zero or one extensions.
|
// Fast-path for common cases: zero or one extensions.
|
||||||
// Don't bother sorting the keys.
|
// Don't bother sorting the keys.
|
||||||
|
@ -2468,14 +2562,14 @@ func (u *marshalInfo) appendMessageSet(b []byte, ext *XXX_InternalExtensions, de
|
||||||
|
|
||||||
ei := u.getExtElemInfo(e.desc)
|
ei := u.getExtElemInfo(e.desc)
|
||||||
v := e.value
|
v := e.value
|
||||||
p := toAddrPointer(&v, ei.isptr)
|
p := toAddrPointer(&v, ei.isptr, ei.deref)
|
||||||
b, err = ei.marshaler(b, p, 3<<3|WireBytes, deterministic)
|
b, err = ei.marshaler(b, p, 3<<3|WireBytes, deterministic)
|
||||||
if err != nil {
|
if !nerr.Merge(err) {
|
||||||
return b, err
|
return b, err
|
||||||
}
|
}
|
||||||
b = append(b, 1<<3|WireEndGroup)
|
b = append(b, 1<<3|WireEndGroup)
|
||||||
}
|
}
|
||||||
return b, nil
|
return b, nerr.E
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort the keys to provide a deterministic encoding.
|
// Sort the keys to provide a deterministic encoding.
|
||||||
|
@ -2506,14 +2600,14 @@ func (u *marshalInfo) appendMessageSet(b []byte, ext *XXX_InternalExtensions, de
|
||||||
|
|
||||||
ei := u.getExtElemInfo(e.desc)
|
ei := u.getExtElemInfo(e.desc)
|
||||||
v := e.value
|
v := e.value
|
||||||
p := toAddrPointer(&v, ei.isptr)
|
p := toAddrPointer(&v, ei.isptr, ei.deref)
|
||||||
b, err = ei.marshaler(b, p, 3<<3|WireBytes, deterministic)
|
b, err = ei.marshaler(b, p, 3<<3|WireBytes, deterministic)
|
||||||
b = append(b, 1<<3|WireEndGroup)
|
b = append(b, 1<<3|WireEndGroup)
|
||||||
if err != nil {
|
if !nerr.Merge(err) {
|
||||||
return b, err
|
return b, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return b, nil
|
return b, nerr.E
|
||||||
}
|
}
|
||||||
|
|
||||||
// sizeV1Extensions computes the size of encoded data for a V1-API extension field.
|
// sizeV1Extensions computes the size of encoded data for a V1-API extension field.
|
||||||
|
@ -2536,7 +2630,7 @@ func (u *marshalInfo) sizeV1Extensions(m map[int32]Extension) int {
|
||||||
|
|
||||||
ei := u.getExtElemInfo(e.desc)
|
ei := u.getExtElemInfo(e.desc)
|
||||||
v := e.value
|
v := e.value
|
||||||
p := toAddrPointer(&v, ei.isptr)
|
p := toAddrPointer(&v, ei.isptr, ei.deref)
|
||||||
n += ei.sizer(p, ei.tagsize)
|
n += ei.sizer(p, ei.tagsize)
|
||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
|
@ -2556,6 +2650,7 @@ func (u *marshalInfo) appendV1Extensions(b []byte, m map[int32]Extension, determ
|
||||||
sort.Ints(keys)
|
sort.Ints(keys)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
var nerr nonFatal
|
||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
e := m[int32(k)]
|
e := m[int32(k)]
|
||||||
if e.value == nil || e.desc == nil {
|
if e.value == nil || e.desc == nil {
|
||||||
|
@ -2570,13 +2665,13 @@ func (u *marshalInfo) appendV1Extensions(b []byte, m map[int32]Extension, determ
|
||||||
|
|
||||||
ei := u.getExtElemInfo(e.desc)
|
ei := u.getExtElemInfo(e.desc)
|
||||||
v := e.value
|
v := e.value
|
||||||
p := toAddrPointer(&v, ei.isptr)
|
p := toAddrPointer(&v, ei.isptr, ei.deref)
|
||||||
b, err = ei.marshaler(b, p, ei.wiretag, deterministic)
|
b, err = ei.marshaler(b, p, ei.wiretag, deterministic)
|
||||||
if err != nil {
|
if !nerr.Merge(err) {
|
||||||
return b, err
|
return b, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return b, nil
|
return b, nerr.E
|
||||||
}
|
}
|
||||||
|
|
||||||
// newMarshaler is the interface representing objects that can marshal themselves.
|
// newMarshaler is the interface representing objects that can marshal themselves.
|
||||||
|
|
|
@ -97,6 +97,8 @@ type unmarshalFieldInfo struct {
|
||||||
|
|
||||||
// if a required field, contains a single set bit at this field's index in the required field list.
|
// if a required field, contains a single set bit at this field's index in the required field list.
|
||||||
reqMask uint64
|
reqMask uint64
|
||||||
|
|
||||||
|
name string // name of the field, for error reporting
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -134,10 +136,10 @@ func (u *unmarshalInfo) unmarshal(m pointer, b []byte) error {
|
||||||
u.computeUnmarshalInfo()
|
u.computeUnmarshalInfo()
|
||||||
}
|
}
|
||||||
if u.isMessageSet {
|
if u.isMessageSet {
|
||||||
return UnmarshalMessageSet(b, m.offset(u.extensions).toExtensions())
|
return unmarshalMessageSet(b, m.offset(u.extensions).toExtensions())
|
||||||
}
|
}
|
||||||
var reqMask uint64 // bitmask of required fields we've seen.
|
var reqMask uint64 // bitmask of required fields we've seen.
|
||||||
var rnse *RequiredNotSetError // an instance of a RequiredNotSetError returned by a submessage.
|
var errLater error
|
||||||
for len(b) > 0 {
|
for len(b) > 0 {
|
||||||
// Read tag and wire type.
|
// Read tag and wire type.
|
||||||
// Special case 1 and 2 byte varints.
|
// Special case 1 and 2 byte varints.
|
||||||
|
@ -176,11 +178,20 @@ func (u *unmarshalInfo) unmarshal(m pointer, b []byte) error {
|
||||||
if r, ok := err.(*RequiredNotSetError); ok {
|
if r, ok := err.(*RequiredNotSetError); ok {
|
||||||
// Remember this error, but keep parsing. We need to produce
|
// Remember this error, but keep parsing. We need to produce
|
||||||
// a full parse even if a required field is missing.
|
// a full parse even if a required field is missing.
|
||||||
rnse = r
|
if errLater == nil {
|
||||||
|
errLater = r
|
||||||
|
}
|
||||||
reqMask |= f.reqMask
|
reqMask |= f.reqMask
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err != errInternalBadWireType {
|
if err != errInternalBadWireType {
|
||||||
|
if err == errInvalidUTF8 {
|
||||||
|
if errLater == nil {
|
||||||
|
fullName := revProtoTypes[reflect.PtrTo(u.typ)] + "." + f.name
|
||||||
|
errLater = &invalidUTF8Error{fullName}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Fragments with bad wire type are treated as unknown fields.
|
// Fragments with bad wire type are treated as unknown fields.
|
||||||
|
@ -239,20 +250,16 @@ func (u *unmarshalInfo) unmarshal(m pointer, b []byte) error {
|
||||||
emap[int32(tag)] = e
|
emap[int32(tag)] = e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if rnse != nil {
|
if reqMask != u.reqMask && errLater == nil {
|
||||||
// A required field of a submessage/group is missing. Return that error.
|
|
||||||
return rnse
|
|
||||||
}
|
|
||||||
if reqMask != u.reqMask {
|
|
||||||
// A required field of this message is missing.
|
// A required field of this message is missing.
|
||||||
for _, n := range u.reqFields {
|
for _, n := range u.reqFields {
|
||||||
if reqMask&1 == 0 {
|
if reqMask&1 == 0 {
|
||||||
return &RequiredNotSetError{n}
|
errLater = &RequiredNotSetError{n}
|
||||||
}
|
}
|
||||||
reqMask >>= 1
|
reqMask >>= 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return errLater
|
||||||
}
|
}
|
||||||
|
|
||||||
// computeUnmarshalInfo fills in u with information for use
|
// computeUnmarshalInfo fills in u with information for use
|
||||||
|
@ -351,25 +358,34 @@ func (u *unmarshalInfo) computeUnmarshalInfo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the info in the correct slot in the message.
|
// Store the info in the correct slot in the message.
|
||||||
u.setTag(tag, toField(&f), unmarshal, reqMask)
|
u.setTag(tag, toField(&f), unmarshal, reqMask, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find any types associated with oneof fields.
|
// Find any types associated with oneof fields.
|
||||||
// TODO: XXX_OneofFuncs returns more info than we need. Get rid of some of it?
|
var oneofImplementers []interface{}
|
||||||
fn := reflect.Zero(reflect.PtrTo(t)).MethodByName("XXX_OneofFuncs")
|
switch m := reflect.Zero(reflect.PtrTo(t)).Interface().(type) {
|
||||||
if fn.IsValid() {
|
case oneofFuncsIface:
|
||||||
res := fn.Call(nil)[3] // last return value from XXX_OneofFuncs: []interface{}
|
_, _, _, oneofImplementers = m.XXX_OneofFuncs()
|
||||||
for i := res.Len() - 1; i >= 0; i-- {
|
case oneofWrappersIface:
|
||||||
v := res.Index(i) // interface{}
|
oneofImplementers = m.XXX_OneofWrappers()
|
||||||
tptr := reflect.ValueOf(v.Interface()).Type() // *Msg_X
|
}
|
||||||
|
for _, v := range oneofImplementers {
|
||||||
|
tptr := reflect.TypeOf(v) // *Msg_X
|
||||||
typ := tptr.Elem() // Msg_X
|
typ := tptr.Elem() // Msg_X
|
||||||
|
|
||||||
f := typ.Field(0) // oneof implementers have one field
|
f := typ.Field(0) // oneof implementers have one field
|
||||||
baseUnmarshal := fieldUnmarshaler(&f)
|
baseUnmarshal := fieldUnmarshaler(&f)
|
||||||
tagstr := strings.Split(f.Tag.Get("protobuf"), ",")[1]
|
tags := strings.Split(f.Tag.Get("protobuf"), ",")
|
||||||
tag, err := strconv.Atoi(tagstr)
|
fieldNum, err := strconv.Atoi(tags[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("protobuf tag field not an integer: " + tagstr)
|
panic("protobuf tag field not an integer: " + tags[1])
|
||||||
|
}
|
||||||
|
var name string
|
||||||
|
for _, tag := range tags {
|
||||||
|
if strings.HasPrefix(tag, "name=") {
|
||||||
|
name = strings.TrimPrefix(tag, "name=")
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the oneof field that this struct implements.
|
// Find the oneof field that this struct implements.
|
||||||
|
@ -380,14 +396,14 @@ func (u *unmarshalInfo) computeUnmarshalInfo() {
|
||||||
// That lets us know where this struct should be stored
|
// That lets us know where this struct should be stored
|
||||||
// when we encounter it during unmarshaling.
|
// when we encounter it during unmarshaling.
|
||||||
unmarshal := makeUnmarshalOneof(typ, of.ityp, baseUnmarshal)
|
unmarshal := makeUnmarshalOneof(typ, of.ityp, baseUnmarshal)
|
||||||
u.setTag(tag, of.field, unmarshal, 0)
|
u.setTag(fieldNum, of.field, unmarshal, 0, name)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Get extension ranges, if any.
|
// Get extension ranges, if any.
|
||||||
fn = reflect.Zero(reflect.PtrTo(t)).MethodByName("ExtensionRangeArray")
|
fn := reflect.Zero(reflect.PtrTo(t)).MethodByName("ExtensionRangeArray")
|
||||||
if fn.IsValid() {
|
if fn.IsValid() {
|
||||||
if !u.extensions.IsValid() && !u.oldExtensions.IsValid() {
|
if !u.extensions.IsValid() && !u.oldExtensions.IsValid() {
|
||||||
panic("a message with extensions, but no extensions field in " + t.Name())
|
panic("a message with extensions, but no extensions field in " + t.Name())
|
||||||
|
@ -401,7 +417,7 @@ func (u *unmarshalInfo) computeUnmarshalInfo() {
|
||||||
// [0 0] is [tag=0/wiretype=varint varint-encoded-0].
|
// [0 0] is [tag=0/wiretype=varint varint-encoded-0].
|
||||||
u.setTag(0, zeroField, func(b []byte, f pointer, w int) ([]byte, error) {
|
u.setTag(0, zeroField, func(b []byte, f pointer, w int) ([]byte, error) {
|
||||||
return nil, fmt.Errorf("proto: %s: illegal tag 0 (wire type %d)", t, w)
|
return nil, fmt.Errorf("proto: %s: illegal tag 0 (wire type %d)", t, w)
|
||||||
}, 0)
|
}, 0, "")
|
||||||
|
|
||||||
// Set mask for required field check.
|
// Set mask for required field check.
|
||||||
u.reqMask = uint64(1)<<uint(len(u.reqFields)) - 1
|
u.reqMask = uint64(1)<<uint(len(u.reqFields)) - 1
|
||||||
|
@ -413,8 +429,9 @@ func (u *unmarshalInfo) computeUnmarshalInfo() {
|
||||||
// tag = tag # for field
|
// tag = tag # for field
|
||||||
// field/unmarshal = unmarshal info for that field.
|
// field/unmarshal = unmarshal info for that field.
|
||||||
// reqMask = if required, bitmask for field position in required field list. 0 otherwise.
|
// reqMask = if required, bitmask for field position in required field list. 0 otherwise.
|
||||||
func (u *unmarshalInfo) setTag(tag int, field field, unmarshal unmarshaler, reqMask uint64) {
|
// name = short name of the field.
|
||||||
i := unmarshalFieldInfo{field: field, unmarshal: unmarshal, reqMask: reqMask}
|
func (u *unmarshalInfo) setTag(tag int, field field, unmarshal unmarshaler, reqMask uint64, name string) {
|
||||||
|
i := unmarshalFieldInfo{field: field, unmarshal: unmarshal, reqMask: reqMask, name: name}
|
||||||
n := u.typ.NumField()
|
n := u.typ.NumField()
|
||||||
if tag >= 0 && (tag < 16 || tag < 2*n) { // TODO: what are the right numbers here?
|
if tag >= 0 && (tag < 16 || tag < 2*n) { // TODO: what are the right numbers here?
|
||||||
for len(u.dense) <= tag {
|
for len(u.dense) <= tag {
|
||||||
|
@ -442,11 +459,17 @@ func typeUnmarshaler(t reflect.Type, tags string) unmarshaler {
|
||||||
tagArray := strings.Split(tags, ",")
|
tagArray := strings.Split(tags, ",")
|
||||||
encoding := tagArray[0]
|
encoding := tagArray[0]
|
||||||
name := "unknown"
|
name := "unknown"
|
||||||
|
proto3 := false
|
||||||
|
validateUTF8 := true
|
||||||
for _, tag := range tagArray[3:] {
|
for _, tag := range tagArray[3:] {
|
||||||
if strings.HasPrefix(tag, "name=") {
|
if strings.HasPrefix(tag, "name=") {
|
||||||
name = tag[5:]
|
name = tag[5:]
|
||||||
}
|
}
|
||||||
|
if tag == "proto3" {
|
||||||
|
proto3 = true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
validateUTF8 = validateUTF8 && proto3
|
||||||
|
|
||||||
// Figure out packaging (pointer, slice, or both)
|
// Figure out packaging (pointer, slice, or both)
|
||||||
slice := false
|
slice := false
|
||||||
|
@ -594,6 +617,15 @@ func typeUnmarshaler(t reflect.Type, tags string) unmarshaler {
|
||||||
}
|
}
|
||||||
return unmarshalBytesValue
|
return unmarshalBytesValue
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
|
if validateUTF8 {
|
||||||
|
if pointer {
|
||||||
|
return unmarshalUTF8StringPtr
|
||||||
|
}
|
||||||
|
if slice {
|
||||||
|
return unmarshalUTF8StringSlice
|
||||||
|
}
|
||||||
|
return unmarshalUTF8StringValue
|
||||||
|
}
|
||||||
if pointer {
|
if pointer {
|
||||||
return unmarshalStringPtr
|
return unmarshalStringPtr
|
||||||
}
|
}
|
||||||
|
@ -1448,9 +1480,6 @@ func unmarshalStringValue(b []byte, f pointer, w int) ([]byte, error) {
|
||||||
return nil, io.ErrUnexpectedEOF
|
return nil, io.ErrUnexpectedEOF
|
||||||
}
|
}
|
||||||
v := string(b[:x])
|
v := string(b[:x])
|
||||||
if !utf8.ValidString(v) {
|
|
||||||
return nil, errInvalidUTF8
|
|
||||||
}
|
|
||||||
*f.toString() = v
|
*f.toString() = v
|
||||||
return b[x:], nil
|
return b[x:], nil
|
||||||
}
|
}
|
||||||
|
@ -1468,9 +1497,6 @@ func unmarshalStringPtr(b []byte, f pointer, w int) ([]byte, error) {
|
||||||
return nil, io.ErrUnexpectedEOF
|
return nil, io.ErrUnexpectedEOF
|
||||||
}
|
}
|
||||||
v := string(b[:x])
|
v := string(b[:x])
|
||||||
if !utf8.ValidString(v) {
|
|
||||||
return nil, errInvalidUTF8
|
|
||||||
}
|
|
||||||
*f.toStringPtr() = &v
|
*f.toStringPtr() = &v
|
||||||
return b[x:], nil
|
return b[x:], nil
|
||||||
}
|
}
|
||||||
|
@ -1488,14 +1514,72 @@ func unmarshalStringSlice(b []byte, f pointer, w int) ([]byte, error) {
|
||||||
return nil, io.ErrUnexpectedEOF
|
return nil, io.ErrUnexpectedEOF
|
||||||
}
|
}
|
||||||
v := string(b[:x])
|
v := string(b[:x])
|
||||||
if !utf8.ValidString(v) {
|
|
||||||
return nil, errInvalidUTF8
|
|
||||||
}
|
|
||||||
s := f.toStringSlice()
|
s := f.toStringSlice()
|
||||||
*s = append(*s, v)
|
*s = append(*s, v)
|
||||||
return b[x:], nil
|
return b[x:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func unmarshalUTF8StringValue(b []byte, f pointer, w int) ([]byte, error) {
|
||||||
|
if w != WireBytes {
|
||||||
|
return b, errInternalBadWireType
|
||||||
|
}
|
||||||
|
x, n := decodeVarint(b)
|
||||||
|
if n == 0 {
|
||||||
|
return nil, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b = b[n:]
|
||||||
|
if x > uint64(len(b)) {
|
||||||
|
return nil, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
v := string(b[:x])
|
||||||
|
*f.toString() = v
|
||||||
|
if !utf8.ValidString(v) {
|
||||||
|
return b[x:], errInvalidUTF8
|
||||||
|
}
|
||||||
|
return b[x:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalUTF8StringPtr(b []byte, f pointer, w int) ([]byte, error) {
|
||||||
|
if w != WireBytes {
|
||||||
|
return b, errInternalBadWireType
|
||||||
|
}
|
||||||
|
x, n := decodeVarint(b)
|
||||||
|
if n == 0 {
|
||||||
|
return nil, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b = b[n:]
|
||||||
|
if x > uint64(len(b)) {
|
||||||
|
return nil, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
v := string(b[:x])
|
||||||
|
*f.toStringPtr() = &v
|
||||||
|
if !utf8.ValidString(v) {
|
||||||
|
return b[x:], errInvalidUTF8
|
||||||
|
}
|
||||||
|
return b[x:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalUTF8StringSlice(b []byte, f pointer, w int) ([]byte, error) {
|
||||||
|
if w != WireBytes {
|
||||||
|
return b, errInternalBadWireType
|
||||||
|
}
|
||||||
|
x, n := decodeVarint(b)
|
||||||
|
if n == 0 {
|
||||||
|
return nil, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b = b[n:]
|
||||||
|
if x > uint64(len(b)) {
|
||||||
|
return nil, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
v := string(b[:x])
|
||||||
|
s := f.toStringSlice()
|
||||||
|
*s = append(*s, v)
|
||||||
|
if !utf8.ValidString(v) {
|
||||||
|
return b[x:], errInvalidUTF8
|
||||||
|
}
|
||||||
|
return b[x:], nil
|
||||||
|
}
|
||||||
|
|
||||||
var emptyBuf [0]byte
|
var emptyBuf [0]byte
|
||||||
|
|
||||||
func unmarshalBytesValue(b []byte, f pointer, w int) ([]byte, error) {
|
func unmarshalBytesValue(b []byte, f pointer, w int) ([]byte, error) {
|
||||||
|
@ -1674,6 +1758,7 @@ func makeUnmarshalMap(f *reflect.StructField) unmarshaler {
|
||||||
// Maps will be somewhat slow. Oh well.
|
// Maps will be somewhat slow. Oh well.
|
||||||
|
|
||||||
// Read key and value from data.
|
// Read key and value from data.
|
||||||
|
var nerr nonFatal
|
||||||
k := reflect.New(kt)
|
k := reflect.New(kt)
|
||||||
v := reflect.New(vt)
|
v := reflect.New(vt)
|
||||||
for len(b) > 0 {
|
for len(b) > 0 {
|
||||||
|
@ -1694,7 +1779,7 @@ func makeUnmarshalMap(f *reflect.StructField) unmarshaler {
|
||||||
err = errInternalBadWireType // skip unknown tag
|
err = errInternalBadWireType // skip unknown tag
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil {
|
if nerr.Merge(err) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err != errInternalBadWireType {
|
if err != errInternalBadWireType {
|
||||||
|
@ -1717,7 +1802,7 @@ func makeUnmarshalMap(f *reflect.StructField) unmarshaler {
|
||||||
// Insert into map.
|
// Insert into map.
|
||||||
m.SetMapIndex(k.Elem(), v.Elem())
|
m.SetMapIndex(k.Elem(), v.Elem())
|
||||||
|
|
||||||
return r, nil
|
return r, nerr.E
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1743,15 +1828,16 @@ func makeUnmarshalOneof(typ, ityp reflect.Type, unmarshal unmarshaler) unmarshal
|
||||||
// Unmarshal data into holder.
|
// Unmarshal data into holder.
|
||||||
// We unmarshal into the first field of the holder object.
|
// We unmarshal into the first field of the holder object.
|
||||||
var err error
|
var err error
|
||||||
|
var nerr nonFatal
|
||||||
b, err = unmarshal(b, valToPointer(v).offset(field0), w)
|
b, err = unmarshal(b, valToPointer(v).offset(field0), w)
|
||||||
if err != nil {
|
if !nerr.Merge(err) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write pointer to holder into target field.
|
// Write pointer to holder into target field.
|
||||||
f.asPointerTo(ityp).Elem().Set(v)
|
f.asPointerTo(ityp).Elem().Set(v)
|
||||||
|
|
||||||
return b, nil
|
return b, nerr.E
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1864,7 +1950,7 @@ func encodeVarint(b []byte, x uint64) []byte {
|
||||||
// If there is an error, it returns 0,0.
|
// If there is an error, it returns 0,0.
|
||||||
func decodeVarint(b []byte) (uint64, int) {
|
func decodeVarint(b []byte) (uint64, int) {
|
||||||
var x, y uint64
|
var x, y uint64
|
||||||
if len(b) <= 0 {
|
if len(b) == 0 {
|
||||||
goto bad
|
goto bad
|
||||||
}
|
}
|
||||||
x = uint64(b[0])
|
x = uint64(b[0])
|
||||||
|
|
|
@ -353,7 +353,7 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := tm.writeAny(w, key, props.mkeyprop); err != nil {
|
if err := tm.writeAny(w, key, props.MapKeyProp); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := w.WriteByte('\n'); err != nil {
|
if err := w.WriteByte('\n'); err != nil {
|
||||||
|
@ -370,7 +370,7 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := tm.writeAny(w, val, props.mvalprop); err != nil {
|
if err := tm.writeAny(w, val, props.MapValProp); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := w.WriteByte('\n'); err != nil {
|
if err := w.WriteByte('\n'); err != nil {
|
||||||
|
|
|
@ -630,17 +630,17 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
|
||||||
if err := p.consumeToken(":"); err != nil {
|
if err := p.consumeToken(":"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := p.readAny(key, props.mkeyprop); err != nil {
|
if err := p.readAny(key, props.MapKeyProp); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := p.consumeOptionalSeparator(); err != nil {
|
if err := p.consumeOptionalSeparator(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case "value":
|
case "value":
|
||||||
if err := p.checkForColon(props.mvalprop, dst.Type().Elem()); err != nil {
|
if err := p.checkForColon(props.MapValProp, dst.Type().Elem()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := p.readAny(val, props.mvalprop); err != nil {
|
if err := p.readAny(val, props.MapValProp); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := p.consumeOptionalSeparator(); err != nil {
|
if err := p.consumeOptionalSeparator(); err != nil {
|
||||||
|
|
|
@ -130,10 +130,12 @@ func UnmarshalAny(any *any.Any, pb proto.Message) error {
|
||||||
|
|
||||||
// Is returns true if any value contains a given message type.
|
// Is returns true if any value contains a given message type.
|
||||||
func Is(any *any.Any, pb proto.Message) bool {
|
func Is(any *any.Any, pb proto.Message) bool {
|
||||||
aname, err := AnyMessageName(any)
|
// The following is equivalent to AnyMessageName(any) == proto.MessageName(pb),
|
||||||
if err != nil {
|
// but it avoids scanning TypeUrl for the slash.
|
||||||
|
if any == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
name := proto.MessageName(pb)
|
||||||
return aname == proto.MessageName(pb)
|
prefix := len(any.TypeUrl) - len(name)
|
||||||
|
return prefix >= 1 && any.TypeUrl[prefix-1] == '/' && any.TypeUrl[prefix:] == name
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// source: google/protobuf/any.proto
|
// source: google/protobuf/any.proto
|
||||||
|
|
||||||
package any // import "github.com/golang/protobuf/ptypes/any"
|
package any
|
||||||
|
|
||||||
import proto "github.com/golang/protobuf/proto"
|
import (
|
||||||
import fmt "fmt"
|
fmt "fmt"
|
||||||
import math "math"
|
proto "github.com/golang/protobuf/proto"
|
||||||
|
math "math"
|
||||||
|
)
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
var _ = proto.Marshal
|
var _ = proto.Marshal
|
||||||
|
@ -16,7 +18,7 @@ var _ = math.Inf
|
||||||
// is compatible with the proto package it is being compiled against.
|
// is compatible with the proto package it is being compiled against.
|
||||||
// A compilation error at this line likely means your copy of the
|
// A compilation error at this line likely means your copy of the
|
||||||
// proto package needs to be updated.
|
// proto package needs to be updated.
|
||||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||||
|
|
||||||
// `Any` contains an arbitrary serialized protocol buffer message along with a
|
// `Any` contains an arbitrary serialized protocol buffer message along with a
|
||||||
// URL that describes the type of the serialized message.
|
// URL that describes the type of the serialized message.
|
||||||
|
@ -99,17 +101,18 @@ const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
type Any struct {
|
type Any struct {
|
||||||
// A URL/resource name whose content describes the type of the
|
// A URL/resource name that uniquely identifies the type of the serialized
|
||||||
// serialized protocol buffer message.
|
// protocol buffer message. The last segment of the URL's path must represent
|
||||||
|
// the fully qualified name of the type (as in
|
||||||
|
// `path/google.protobuf.Duration`). The name should be in a canonical form
|
||||||
|
// (e.g., leading "." is not accepted).
|
||||||
//
|
//
|
||||||
// For URLs which use the scheme `http`, `https`, or no scheme, the
|
// In practice, teams usually precompile into the binary all types that they
|
||||||
// following restrictions and interpretations apply:
|
// expect it to use in the context of Any. However, for URLs which use the
|
||||||
|
// scheme `http`, `https`, or no scheme, one can optionally set up a type
|
||||||
|
// server that maps type URLs to message definitions as follows:
|
||||||
//
|
//
|
||||||
// * If no scheme is provided, `https` is assumed.
|
// * If no scheme is provided, `https` is assumed.
|
||||||
// * The last segment of the URL's path must represent the fully
|
|
||||||
// qualified name of the type (as in `path/google.protobuf.Duration`).
|
|
||||||
// The name should be in a canonical form (e.g., leading "." is
|
|
||||||
// not accepted).
|
|
||||||
// * An HTTP GET on the URL must yield a [google.protobuf.Type][]
|
// * An HTTP GET on the URL must yield a [google.protobuf.Type][]
|
||||||
// value in binary format, or produce an error.
|
// value in binary format, or produce an error.
|
||||||
// * Applications are allowed to cache lookup results based on the
|
// * Applications are allowed to cache lookup results based on the
|
||||||
|
@ -118,10 +121,14 @@ type Any struct {
|
||||||
// on changes to types. (Use versioned type names to manage
|
// on changes to types. (Use versioned type names to manage
|
||||||
// breaking changes.)
|
// breaking changes.)
|
||||||
//
|
//
|
||||||
|
// Note: this functionality is not currently available in the official
|
||||||
|
// protobuf release, and it is not used for type URLs beginning with
|
||||||
|
// type.googleapis.com.
|
||||||
|
//
|
||||||
// Schemes other than `http`, `https` (or the empty scheme) might be
|
// Schemes other than `http`, `https` (or the empty scheme) might be
|
||||||
// used with implementation specific semantics.
|
// used with implementation specific semantics.
|
||||||
//
|
//
|
||||||
TypeUrl string `protobuf:"bytes,1,opt,name=type_url,json=typeUrl" json:"type_url,omitempty"`
|
TypeUrl string `protobuf:"bytes,1,opt,name=type_url,json=typeUrl,proto3" json:"type_url,omitempty"`
|
||||||
// Must be a valid serialized protocol buffer of the above specified type.
|
// Must be a valid serialized protocol buffer of the above specified type.
|
||||||
Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
|
Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
@ -133,17 +140,19 @@ func (m *Any) Reset() { *m = Any{} }
|
||||||
func (m *Any) String() string { return proto.CompactTextString(m) }
|
func (m *Any) String() string { return proto.CompactTextString(m) }
|
||||||
func (*Any) ProtoMessage() {}
|
func (*Any) ProtoMessage() {}
|
||||||
func (*Any) Descriptor() ([]byte, []int) {
|
func (*Any) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_any_744b9ca530f228db, []int{0}
|
return fileDescriptor_b53526c13ae22eb4, []int{0}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Any) XXX_WellKnownType() string { return "Any" }
|
func (*Any) XXX_WellKnownType() string { return "Any" }
|
||||||
|
|
||||||
func (m *Any) XXX_Unmarshal(b []byte) error {
|
func (m *Any) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_Any.Unmarshal(m, b)
|
return xxx_messageInfo_Any.Unmarshal(m, b)
|
||||||
}
|
}
|
||||||
func (m *Any) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
func (m *Any) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
return xxx_messageInfo_Any.Marshal(b, m, deterministic)
|
return xxx_messageInfo_Any.Marshal(b, m, deterministic)
|
||||||
}
|
}
|
||||||
func (dst *Any) XXX_Merge(src proto.Message) {
|
func (m *Any) XXX_Merge(src proto.Message) {
|
||||||
xxx_messageInfo_Any.Merge(dst, src)
|
xxx_messageInfo_Any.Merge(m, src)
|
||||||
}
|
}
|
||||||
func (m *Any) XXX_Size() int {
|
func (m *Any) XXX_Size() int {
|
||||||
return xxx_messageInfo_Any.Size(m)
|
return xxx_messageInfo_Any.Size(m)
|
||||||
|
@ -172,9 +181,9 @@ func init() {
|
||||||
proto.RegisterType((*Any)(nil), "google.protobuf.Any")
|
proto.RegisterType((*Any)(nil), "google.protobuf.Any")
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { proto.RegisterFile("google/protobuf/any.proto", fileDescriptor_any_744b9ca530f228db) }
|
func init() { proto.RegisterFile("google/protobuf/any.proto", fileDescriptor_b53526c13ae22eb4) }
|
||||||
|
|
||||||
var fileDescriptor_any_744b9ca530f228db = []byte{
|
var fileDescriptor_b53526c13ae22eb4 = []byte{
|
||||||
// 185 bytes of a gzipped FileDescriptorProto
|
// 185 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4c, 0xcf, 0xcf, 0x4f,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4c, 0xcf, 0xcf, 0x4f,
|
||||||
0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0xcc, 0xab, 0xd4,
|
0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0xcc, 0xab, 0xd4,
|
||||||
|
|
|
@ -120,17 +120,18 @@ option objc_class_prefix = "GPB";
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
message Any {
|
message Any {
|
||||||
// A URL/resource name whose content describes the type of the
|
// A URL/resource name that uniquely identifies the type of the serialized
|
||||||
// serialized protocol buffer message.
|
// protocol buffer message. The last segment of the URL's path must represent
|
||||||
|
// the fully qualified name of the type (as in
|
||||||
|
// `path/google.protobuf.Duration`). The name should be in a canonical form
|
||||||
|
// (e.g., leading "." is not accepted).
|
||||||
//
|
//
|
||||||
// For URLs which use the scheme `http`, `https`, or no scheme, the
|
// In practice, teams usually precompile into the binary all types that they
|
||||||
// following restrictions and interpretations apply:
|
// expect it to use in the context of Any. However, for URLs which use the
|
||||||
|
// scheme `http`, `https`, or no scheme, one can optionally set up a type
|
||||||
|
// server that maps type URLs to message definitions as follows:
|
||||||
//
|
//
|
||||||
// * If no scheme is provided, `https` is assumed.
|
// * If no scheme is provided, `https` is assumed.
|
||||||
// * The last segment of the URL's path must represent the fully
|
|
||||||
// qualified name of the type (as in `path/google.protobuf.Duration`).
|
|
||||||
// The name should be in a canonical form (e.g., leading "." is
|
|
||||||
// not accepted).
|
|
||||||
// * An HTTP GET on the URL must yield a [google.protobuf.Type][]
|
// * An HTTP GET on the URL must yield a [google.protobuf.Type][]
|
||||||
// value in binary format, or produce an error.
|
// value in binary format, or produce an error.
|
||||||
// * Applications are allowed to cache lookup results based on the
|
// * Applications are allowed to cache lookup results based on the
|
||||||
|
@ -139,6 +140,10 @@ message Any {
|
||||||
// on changes to types. (Use versioned type names to manage
|
// on changes to types. (Use versioned type names to manage
|
||||||
// breaking changes.)
|
// breaking changes.)
|
||||||
//
|
//
|
||||||
|
// Note: this functionality is not currently available in the official
|
||||||
|
// protobuf release, and it is not used for type URLs beginning with
|
||||||
|
// type.googleapis.com.
|
||||||
|
//
|
||||||
// Schemes other than `http`, `https` (or the empty scheme) might be
|
// Schemes other than `http`, `https` (or the empty scheme) might be
|
||||||
// used with implementation specific semantics.
|
// used with implementation specific semantics.
|
||||||
//
|
//
|
||||||
|
|
|
@ -82,7 +82,7 @@ func Duration(p *durpb.Duration) (time.Duration, error) {
|
||||||
return 0, fmt.Errorf("duration: %v is out of range for time.Duration", p)
|
return 0, fmt.Errorf("duration: %v is out of range for time.Duration", p)
|
||||||
}
|
}
|
||||||
if p.Nanos != 0 {
|
if p.Nanos != 0 {
|
||||||
d += time.Duration(p.Nanos)
|
d += time.Duration(p.Nanos) * time.Nanosecond
|
||||||
if (d < 0) != (p.Nanos < 0) {
|
if (d < 0) != (p.Nanos < 0) {
|
||||||
return 0, fmt.Errorf("duration: %v is out of range for time.Duration", p)
|
return 0, fmt.Errorf("duration: %v is out of range for time.Duration", p)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// source: google/protobuf/duration.proto
|
// source: google/protobuf/duration.proto
|
||||||
|
|
||||||
package duration // import "github.com/golang/protobuf/ptypes/duration"
|
package duration
|
||||||
|
|
||||||
import proto "github.com/golang/protobuf/proto"
|
import (
|
||||||
import fmt "fmt"
|
fmt "fmt"
|
||||||
import math "math"
|
proto "github.com/golang/protobuf/proto"
|
||||||
|
math "math"
|
||||||
|
)
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
var _ = proto.Marshal
|
var _ = proto.Marshal
|
||||||
|
@ -16,7 +18,7 @@ var _ = math.Inf
|
||||||
// is compatible with the proto package it is being compiled against.
|
// is compatible with the proto package it is being compiled against.
|
||||||
// A compilation error at this line likely means your copy of the
|
// A compilation error at this line likely means your copy of the
|
||||||
// proto package needs to be updated.
|
// proto package needs to be updated.
|
||||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||||
|
|
||||||
// A Duration represents a signed, fixed-length span of time represented
|
// A Duration represents a signed, fixed-length span of time represented
|
||||||
// as a count of seconds and fractions of seconds at nanosecond
|
// as a count of seconds and fractions of seconds at nanosecond
|
||||||
|
@ -82,14 +84,14 @@ type Duration struct {
|
||||||
// Signed seconds of the span of time. Must be from -315,576,000,000
|
// Signed seconds of the span of time. Must be from -315,576,000,000
|
||||||
// to +315,576,000,000 inclusive. Note: these bounds are computed from:
|
// to +315,576,000,000 inclusive. Note: these bounds are computed from:
|
||||||
// 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years
|
// 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years
|
||||||
Seconds int64 `protobuf:"varint,1,opt,name=seconds" json:"seconds,omitempty"`
|
Seconds int64 `protobuf:"varint,1,opt,name=seconds,proto3" json:"seconds,omitempty"`
|
||||||
// Signed fractions of a second at nanosecond resolution of the span
|
// Signed fractions of a second at nanosecond resolution of the span
|
||||||
// of time. Durations less than one second are represented with a 0
|
// of time. Durations less than one second are represented with a 0
|
||||||
// `seconds` field and a positive or negative `nanos` field. For durations
|
// `seconds` field and a positive or negative `nanos` field. For durations
|
||||||
// of one second or more, a non-zero value for the `nanos` field must be
|
// of one second or more, a non-zero value for the `nanos` field must be
|
||||||
// of the same sign as the `seconds` field. Must be from -999,999,999
|
// of the same sign as the `seconds` field. Must be from -999,999,999
|
||||||
// to +999,999,999 inclusive.
|
// to +999,999,999 inclusive.
|
||||||
Nanos int32 `protobuf:"varint,2,opt,name=nanos" json:"nanos,omitempty"`
|
Nanos int32 `protobuf:"varint,2,opt,name=nanos,proto3" json:"nanos,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_unrecognized []byte `json:"-"`
|
||||||
XXX_sizecache int32 `json:"-"`
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
@ -99,17 +101,19 @@ func (m *Duration) Reset() { *m = Duration{} }
|
||||||
func (m *Duration) String() string { return proto.CompactTextString(m) }
|
func (m *Duration) String() string { return proto.CompactTextString(m) }
|
||||||
func (*Duration) ProtoMessage() {}
|
func (*Duration) ProtoMessage() {}
|
||||||
func (*Duration) Descriptor() ([]byte, []int) {
|
func (*Duration) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_duration_e7d612259e3f0613, []int{0}
|
return fileDescriptor_23597b2ebd7ac6c5, []int{0}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Duration) XXX_WellKnownType() string { return "Duration" }
|
func (*Duration) XXX_WellKnownType() string { return "Duration" }
|
||||||
|
|
||||||
func (m *Duration) XXX_Unmarshal(b []byte) error {
|
func (m *Duration) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_Duration.Unmarshal(m, b)
|
return xxx_messageInfo_Duration.Unmarshal(m, b)
|
||||||
}
|
}
|
||||||
func (m *Duration) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
func (m *Duration) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
return xxx_messageInfo_Duration.Marshal(b, m, deterministic)
|
return xxx_messageInfo_Duration.Marshal(b, m, deterministic)
|
||||||
}
|
}
|
||||||
func (dst *Duration) XXX_Merge(src proto.Message) {
|
func (m *Duration) XXX_Merge(src proto.Message) {
|
||||||
xxx_messageInfo_Duration.Merge(dst, src)
|
xxx_messageInfo_Duration.Merge(m, src)
|
||||||
}
|
}
|
||||||
func (m *Duration) XXX_Size() int {
|
func (m *Duration) XXX_Size() int {
|
||||||
return xxx_messageInfo_Duration.Size(m)
|
return xxx_messageInfo_Duration.Size(m)
|
||||||
|
@ -138,11 +142,9 @@ func init() {
|
||||||
proto.RegisterType((*Duration)(nil), "google.protobuf.Duration")
|
proto.RegisterType((*Duration)(nil), "google.protobuf.Duration")
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() { proto.RegisterFile("google/protobuf/duration.proto", fileDescriptor_23597b2ebd7ac6c5) }
|
||||||
proto.RegisterFile("google/protobuf/duration.proto", fileDescriptor_duration_e7d612259e3f0613)
|
|
||||||
}
|
|
||||||
|
|
||||||
var fileDescriptor_duration_e7d612259e3f0613 = []byte{
|
var fileDescriptor_23597b2ebd7ac6c5 = []byte{
|
||||||
// 190 bytes of a gzipped FileDescriptorProto
|
// 190 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4b, 0xcf, 0xcf, 0x4f,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4b, 0xcf, 0xcf, 0x4f,
|
||||||
0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0x29, 0x2d, 0x4a,
|
0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0x29, 0x2d, 0x4a,
|
||||||
|
|
|
@ -111,11 +111,9 @@ func TimestampNow() *tspb.Timestamp {
|
||||||
// TimestampProto converts the time.Time to a google.protobuf.Timestamp proto.
|
// TimestampProto converts the time.Time to a google.protobuf.Timestamp proto.
|
||||||
// It returns an error if the resulting Timestamp is invalid.
|
// It returns an error if the resulting Timestamp is invalid.
|
||||||
func TimestampProto(t time.Time) (*tspb.Timestamp, error) {
|
func TimestampProto(t time.Time) (*tspb.Timestamp, error) {
|
||||||
seconds := t.Unix()
|
|
||||||
nanos := int32(t.Sub(time.Unix(seconds, 0)))
|
|
||||||
ts := &tspb.Timestamp{
|
ts := &tspb.Timestamp{
|
||||||
Seconds: seconds,
|
Seconds: t.Unix(),
|
||||||
Nanos: nanos,
|
Nanos: int32(t.Nanosecond()),
|
||||||
}
|
}
|
||||||
if err := validateTimestamp(ts); err != nil {
|
if err := validateTimestamp(ts); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// source: google/protobuf/timestamp.proto
|
// source: google/protobuf/timestamp.proto
|
||||||
|
|
||||||
package timestamp // import "github.com/golang/protobuf/ptypes/timestamp"
|
package timestamp
|
||||||
|
|
||||||
import proto "github.com/golang/protobuf/proto"
|
import (
|
||||||
import fmt "fmt"
|
fmt "fmt"
|
||||||
import math "math"
|
proto "github.com/golang/protobuf/proto"
|
||||||
|
math "math"
|
||||||
|
)
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
var _ = proto.Marshal
|
var _ = proto.Marshal
|
||||||
|
@ -16,7 +18,7 @@ var _ = math.Inf
|
||||||
// is compatible with the proto package it is being compiled against.
|
// is compatible with the proto package it is being compiled against.
|
||||||
// A compilation error at this line likely means your copy of the
|
// A compilation error at this line likely means your copy of the
|
||||||
// proto package needs to be updated.
|
// proto package needs to be updated.
|
||||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||||
|
|
||||||
// A Timestamp represents a point in time independent of any time zone
|
// A Timestamp represents a point in time independent of any time zone
|
||||||
// or calendar, represented as seconds and fractions of seconds at
|
// or calendar, represented as seconds and fractions of seconds at
|
||||||
|
@ -81,7 +83,9 @@ const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
|
// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
|
||||||
// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
|
// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
|
||||||
// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
|
// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
|
||||||
// is required, though only UTC (as indicated by "Z") is presently supported.
|
// is required. A proto3 JSON serializer should always use UTC (as indicated by
|
||||||
|
// "Z") when printing the Timestamp type and a proto3 JSON parser should be
|
||||||
|
// able to accept both UTC and other timezones (as indicated by an offset).
|
||||||
//
|
//
|
||||||
// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past
|
// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past
|
||||||
// 01:30 UTC on January 15, 2017.
|
// 01:30 UTC on January 15, 2017.
|
||||||
|
@ -92,20 +96,20 @@ const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
// to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime)
|
// to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime)
|
||||||
// with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one
|
// with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one
|
||||||
// can use the Joda Time's [`ISODateTimeFormat.dateTime()`](
|
// can use the Joda Time's [`ISODateTimeFormat.dateTime()`](
|
||||||
// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime--)
|
// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime--
|
||||||
// to obtain a formatter capable of generating timestamps in this format.
|
// ) to obtain a formatter capable of generating timestamps in this format.
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
type Timestamp struct {
|
type Timestamp struct {
|
||||||
// Represents seconds of UTC time since Unix epoch
|
// Represents seconds of UTC time since Unix epoch
|
||||||
// 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
|
// 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
|
||||||
// 9999-12-31T23:59:59Z inclusive.
|
// 9999-12-31T23:59:59Z inclusive.
|
||||||
Seconds int64 `protobuf:"varint,1,opt,name=seconds" json:"seconds,omitempty"`
|
Seconds int64 `protobuf:"varint,1,opt,name=seconds,proto3" json:"seconds,omitempty"`
|
||||||
// Non-negative fractions of a second at nanosecond resolution. Negative
|
// Non-negative fractions of a second at nanosecond resolution. Negative
|
||||||
// second values with fractions must still have non-negative nanos values
|
// second values with fractions must still have non-negative nanos values
|
||||||
// that count forward in time. Must be from 0 to 999,999,999
|
// that count forward in time. Must be from 0 to 999,999,999
|
||||||
// inclusive.
|
// inclusive.
|
||||||
Nanos int32 `protobuf:"varint,2,opt,name=nanos" json:"nanos,omitempty"`
|
Nanos int32 `protobuf:"varint,2,opt,name=nanos,proto3" json:"nanos,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_unrecognized []byte `json:"-"`
|
||||||
XXX_sizecache int32 `json:"-"`
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
@ -115,17 +119,19 @@ func (m *Timestamp) Reset() { *m = Timestamp{} }
|
||||||
func (m *Timestamp) String() string { return proto.CompactTextString(m) }
|
func (m *Timestamp) String() string { return proto.CompactTextString(m) }
|
||||||
func (*Timestamp) ProtoMessage() {}
|
func (*Timestamp) ProtoMessage() {}
|
||||||
func (*Timestamp) Descriptor() ([]byte, []int) {
|
func (*Timestamp) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_timestamp_b826e8e5fba671a8, []int{0}
|
return fileDescriptor_292007bbfe81227e, []int{0}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Timestamp) XXX_WellKnownType() string { return "Timestamp" }
|
func (*Timestamp) XXX_WellKnownType() string { return "Timestamp" }
|
||||||
|
|
||||||
func (m *Timestamp) XXX_Unmarshal(b []byte) error {
|
func (m *Timestamp) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_Timestamp.Unmarshal(m, b)
|
return xxx_messageInfo_Timestamp.Unmarshal(m, b)
|
||||||
}
|
}
|
||||||
func (m *Timestamp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
func (m *Timestamp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
return xxx_messageInfo_Timestamp.Marshal(b, m, deterministic)
|
return xxx_messageInfo_Timestamp.Marshal(b, m, deterministic)
|
||||||
}
|
}
|
||||||
func (dst *Timestamp) XXX_Merge(src proto.Message) {
|
func (m *Timestamp) XXX_Merge(src proto.Message) {
|
||||||
xxx_messageInfo_Timestamp.Merge(dst, src)
|
xxx_messageInfo_Timestamp.Merge(m, src)
|
||||||
}
|
}
|
||||||
func (m *Timestamp) XXX_Size() int {
|
func (m *Timestamp) XXX_Size() int {
|
||||||
return xxx_messageInfo_Timestamp.Size(m)
|
return xxx_messageInfo_Timestamp.Size(m)
|
||||||
|
@ -154,11 +160,9 @@ func init() {
|
||||||
proto.RegisterType((*Timestamp)(nil), "google.protobuf.Timestamp")
|
proto.RegisterType((*Timestamp)(nil), "google.protobuf.Timestamp")
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() { proto.RegisterFile("google/protobuf/timestamp.proto", fileDescriptor_292007bbfe81227e) }
|
||||||
proto.RegisterFile("google/protobuf/timestamp.proto", fileDescriptor_timestamp_b826e8e5fba671a8)
|
|
||||||
}
|
|
||||||
|
|
||||||
var fileDescriptor_timestamp_b826e8e5fba671a8 = []byte{
|
var fileDescriptor_292007bbfe81227e = []byte{
|
||||||
// 191 bytes of a gzipped FileDescriptorProto
|
// 191 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4f, 0xcf, 0xcf, 0x4f,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4f, 0xcf, 0xcf, 0x4f,
|
||||||
0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0xc9, 0xcc, 0x4d,
|
0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0xc9, 0xcc, 0x4d,
|
||||||
|
|
|
@ -103,7 +103,9 @@ option objc_class_prefix = "GPB";
|
||||||
// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
|
// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
|
||||||
// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
|
// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
|
||||||
// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
|
// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
|
||||||
// is required, though only UTC (as indicated by "Z") is presently supported.
|
// is required. A proto3 JSON serializer should always use UTC (as indicated by
|
||||||
|
// "Z") when printing the Timestamp type and a proto3 JSON parser should be
|
||||||
|
// able to accept both UTC and other timezones (as indicated by an offset).
|
||||||
//
|
//
|
||||||
// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past
|
// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past
|
||||||
// 01:30 UTC on January 15, 2017.
|
// 01:30 UTC on January 15, 2017.
|
||||||
|
@ -114,8 +116,8 @@ option objc_class_prefix = "GPB";
|
||||||
// to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime)
|
// to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime)
|
||||||
// with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one
|
// with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one
|
||||||
// can use the Joda Time's [`ISODateTimeFormat.dateTime()`](
|
// can use the Joda Time's [`ISODateTimeFormat.dateTime()`](
|
||||||
// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime--)
|
// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime--
|
||||||
// to obtain a formatter capable of generating timestamps in this format.
|
// ) to obtain a formatter capable of generating timestamps in this format.
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
message Timestamp {
|
message Timestamp {
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
language: go
|
|
||||||
sudo: false
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- go: 1.3
|
|
||||||
- go: 1.4
|
|
||||||
- go: 1.5
|
|
||||||
- go: 1.6
|
|
||||||
- go: 1.7
|
|
||||||
- go: tip
|
|
||||||
allow_failures:
|
|
||||||
- go: tip
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go get -t -v ./...
|
|
||||||
- diff -u <(echo -n) <(gofmt -d .)
|
|
||||||
- go vet $(go list ./... | grep -v /vendor/)
|
|
||||||
- go test -v -race ./...
|
|
|
@ -1,27 +0,0 @@
|
||||||
Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
@ -1,10 +0,0 @@
|
||||||
context
|
|
||||||
=======
|
|
||||||
[![Build Status](https://travis-ci.org/gorilla/context.png?branch=master)](https://travis-ci.org/gorilla/context)
|
|
||||||
|
|
||||||
gorilla/context is a general purpose registry for global request variables.
|
|
||||||
|
|
||||||
> Note: gorilla/context, having been born well before `context.Context` existed, does not play well
|
|
||||||
> with the shallow copying of the request that [`http.Request.WithContext`](https://golang.org/pkg/net/http/#Request.WithContext) (added to net/http Go 1.7 onwards) performs. You should either use *just* gorilla/context, or moving forward, the new `http.Request.Context()`.
|
|
||||||
|
|
||||||
Read the full documentation here: http://www.gorillatoolkit.org/pkg/context
|
|
|
@ -1,143 +0,0 @@
|
||||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package context
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
mutex sync.RWMutex
|
|
||||||
data = make(map[*http.Request]map[interface{}]interface{})
|
|
||||||
datat = make(map[*http.Request]int64)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Set stores a value for a given key in a given request.
|
|
||||||
func Set(r *http.Request, key, val interface{}) {
|
|
||||||
mutex.Lock()
|
|
||||||
if data[r] == nil {
|
|
||||||
data[r] = make(map[interface{}]interface{})
|
|
||||||
datat[r] = time.Now().Unix()
|
|
||||||
}
|
|
||||||
data[r][key] = val
|
|
||||||
mutex.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns a value stored for a given key in a given request.
|
|
||||||
func Get(r *http.Request, key interface{}) interface{} {
|
|
||||||
mutex.RLock()
|
|
||||||
if ctx := data[r]; ctx != nil {
|
|
||||||
value := ctx[key]
|
|
||||||
mutex.RUnlock()
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
mutex.RUnlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetOk returns stored value and presence state like multi-value return of map access.
|
|
||||||
func GetOk(r *http.Request, key interface{}) (interface{}, bool) {
|
|
||||||
mutex.RLock()
|
|
||||||
if _, ok := data[r]; ok {
|
|
||||||
value, ok := data[r][key]
|
|
||||||
mutex.RUnlock()
|
|
||||||
return value, ok
|
|
||||||
}
|
|
||||||
mutex.RUnlock()
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAll returns all stored values for the request as a map. Nil is returned for invalid requests.
|
|
||||||
func GetAll(r *http.Request) map[interface{}]interface{} {
|
|
||||||
mutex.RLock()
|
|
||||||
if context, ok := data[r]; ok {
|
|
||||||
result := make(map[interface{}]interface{}, len(context))
|
|
||||||
for k, v := range context {
|
|
||||||
result[k] = v
|
|
||||||
}
|
|
||||||
mutex.RUnlock()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
mutex.RUnlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAllOk returns all stored values for the request as a map and a boolean value that indicates if
|
|
||||||
// the request was registered.
|
|
||||||
func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) {
|
|
||||||
mutex.RLock()
|
|
||||||
context, ok := data[r]
|
|
||||||
result := make(map[interface{}]interface{}, len(context))
|
|
||||||
for k, v := range context {
|
|
||||||
result[k] = v
|
|
||||||
}
|
|
||||||
mutex.RUnlock()
|
|
||||||
return result, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete removes a value stored for a given key in a given request.
|
|
||||||
func Delete(r *http.Request, key interface{}) {
|
|
||||||
mutex.Lock()
|
|
||||||
if data[r] != nil {
|
|
||||||
delete(data[r], key)
|
|
||||||
}
|
|
||||||
mutex.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear removes all values stored for a given request.
|
|
||||||
//
|
|
||||||
// This is usually called by a handler wrapper to clean up request
|
|
||||||
// variables at the end of a request lifetime. See ClearHandler().
|
|
||||||
func Clear(r *http.Request) {
|
|
||||||
mutex.Lock()
|
|
||||||
clear(r)
|
|
||||||
mutex.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear is Clear without the lock.
|
|
||||||
func clear(r *http.Request) {
|
|
||||||
delete(data, r)
|
|
||||||
delete(datat, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Purge removes request data stored for longer than maxAge, in seconds.
|
|
||||||
// It returns the amount of requests removed.
|
|
||||||
//
|
|
||||||
// If maxAge <= 0, all request data is removed.
|
|
||||||
//
|
|
||||||
// This is only used for sanity check: in case context cleaning was not
|
|
||||||
// properly set some request data can be kept forever, consuming an increasing
|
|
||||||
// amount of memory. In case this is detected, Purge() must be called
|
|
||||||
// periodically until the problem is fixed.
|
|
||||||
func Purge(maxAge int) int {
|
|
||||||
mutex.Lock()
|
|
||||||
count := 0
|
|
||||||
if maxAge <= 0 {
|
|
||||||
count = len(data)
|
|
||||||
data = make(map[*http.Request]map[interface{}]interface{})
|
|
||||||
datat = make(map[*http.Request]int64)
|
|
||||||
} else {
|
|
||||||
min := time.Now().Unix() - int64(maxAge)
|
|
||||||
for r := range data {
|
|
||||||
if datat[r] < min {
|
|
||||||
clear(r)
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mutex.Unlock()
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClearHandler wraps an http.Handler and clears request values at the end
|
|
||||||
// of a request lifetime.
|
|
||||||
func ClearHandler(h http.Handler) http.Handler {
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
defer Clear(r)
|
|
||||||
h.ServeHTTP(w, r)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package context stores values shared during a request lifetime.
|
|
||||||
|
|
||||||
Note: gorilla/context, having been born well before `context.Context` existed,
|
|
||||||
does not play well > with the shallow copying of the request that
|
|
||||||
[`http.Request.WithContext`](https://golang.org/pkg/net/http/#Request.WithContext)
|
|
||||||
(added to net/http Go 1.7 onwards) performs. You should either use *just*
|
|
||||||
gorilla/context, or moving forward, the new `http.Request.Context()`.
|
|
||||||
|
|
||||||
For example, a router can set variables extracted from the URL and later
|
|
||||||
application handlers can access those values, or it can be used to store
|
|
||||||
sessions values to be saved at the end of a request. There are several
|
|
||||||
others common uses.
|
|
||||||
|
|
||||||
The idea was posted by Brad Fitzpatrick to the go-nuts mailing list:
|
|
||||||
|
|
||||||
http://groups.google.com/group/golang-nuts/msg/e2d679d303aa5d53
|
|
||||||
|
|
||||||
Here's the basic usage: first define the keys that you will need. The key
|
|
||||||
type is interface{} so a key can be of any type that supports equality.
|
|
||||||
Here we define a key using a custom int type to avoid name collisions:
|
|
||||||
|
|
||||||
package foo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gorilla/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
type key int
|
|
||||||
|
|
||||||
const MyKey key = 0
|
|
||||||
|
|
||||||
Then set a variable. Variables are bound to an http.Request object, so you
|
|
||||||
need a request instance to set a value:
|
|
||||||
|
|
||||||
context.Set(r, MyKey, "bar")
|
|
||||||
|
|
||||||
The application can later access the variable using the same key you provided:
|
|
||||||
|
|
||||||
func MyHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// val is "bar".
|
|
||||||
val := context.Get(r, foo.MyKey)
|
|
||||||
|
|
||||||
// returns ("bar", true)
|
|
||||||
val, ok := context.GetOk(r, foo.MyKey)
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
And that's all about the basic usage. We discuss some other ideas below.
|
|
||||||
|
|
||||||
Any type can be stored in the context. To enforce a given type, make the key
|
|
||||||
private and wrap Get() and Set() to accept and return values of a specific
|
|
||||||
type:
|
|
||||||
|
|
||||||
type key int
|
|
||||||
|
|
||||||
const mykey key = 0
|
|
||||||
|
|
||||||
// GetMyKey returns a value for this package from the request values.
|
|
||||||
func GetMyKey(r *http.Request) SomeType {
|
|
||||||
if rv := context.Get(r, mykey); rv != nil {
|
|
||||||
return rv.(SomeType)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetMyKey sets a value for this package in the request values.
|
|
||||||
func SetMyKey(r *http.Request, val SomeType) {
|
|
||||||
context.Set(r, mykey, val)
|
|
||||||
}
|
|
||||||
|
|
||||||
Variables must be cleared at the end of a request, to remove all values
|
|
||||||
that were stored. This can be done in an http.Handler, after a request was
|
|
||||||
served. Just call Clear() passing the request:
|
|
||||||
|
|
||||||
context.Clear(r)
|
|
||||||
|
|
||||||
...or use ClearHandler(), which conveniently wraps an http.Handler to clear
|
|
||||||
variables at the end of a request lifetime.
|
|
||||||
|
|
||||||
The Routers from the packages gorilla/mux and gorilla/pat call Clear()
|
|
||||||
so if you are using either of them you don't need to clear the context manually.
|
|
||||||
*/
|
|
||||||
package context
|
|
|
@ -1,14 +1,15 @@
|
||||||
language: go
|
language: go
|
||||||
sudo: false
|
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- go: 1.5.x
|
|
||||||
- go: 1.6.x
|
|
||||||
- go: 1.7.x
|
- go: 1.7.x
|
||||||
- go: 1.8.x
|
- go: 1.8.x
|
||||||
- go: 1.9.x
|
- go: 1.9.x
|
||||||
- go: 1.10.x
|
- go: 1.10.x
|
||||||
|
- go: 1.11.x
|
||||||
|
- go: 1.x
|
||||||
|
env: LATEST=true
|
||||||
- go: tip
|
- go: tip
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- go: tip
|
- go: tip
|
||||||
|
@ -19,5 +20,5 @@ install:
|
||||||
script:
|
script:
|
||||||
- go get -t -v ./...
|
- go get -t -v ./...
|
||||||
- diff -u <(echo -n) <(gofmt -d .)
|
- diff -u <(echo -n) <(gofmt -d .)
|
||||||
- go tool vet .
|
- if [[ "$LATEST" = true ]]; then go vet .; fi
|
||||||
- go test -v -race ./...
|
- go test -v -race ./...
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
# This is the official list of gorilla/mux authors for copyright purposes.
|
||||||
|
#
|
||||||
|
# Please keep the list sorted.
|
||||||
|
|
||||||
|
Google LLC (https://opensource.google.com/)
|
||||||
|
Kamil Kisielk <kamil@kamilkisiel.net>
|
||||||
|
Matt Silverlock <matt@eatsleeprepeat.net>
|
||||||
|
Rodrigo Moraes (https://github.com/moraes)
|
|
@ -1,4 +1,4 @@
|
||||||
Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
|
Copyright (c) 2012-2018 The Gorilla Authors. All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are
|
modification, are permitted provided that the following conditions are
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
![Gorilla Logo](http://www.gorillatoolkit.org/static/images/gorilla-icon-64.png)
|
![Gorilla Logo](http://www.gorillatoolkit.org/static/images/gorilla-icon-64.png)
|
||||||
|
|
||||||
http://www.gorillatoolkit.org/pkg/mux
|
https://www.gorillatoolkit.org/pkg/mux
|
||||||
|
|
||||||
Package `gorilla/mux` implements a request router and dispatcher for matching incoming requests to
|
Package `gorilla/mux` implements a request router and dispatcher for matching incoming requests to
|
||||||
their respective handler.
|
their respective handler.
|
||||||
|
@ -88,7 +88,7 @@ r := mux.NewRouter()
|
||||||
// Only matches if domain is "www.example.com".
|
// Only matches if domain is "www.example.com".
|
||||||
r.Host("www.example.com")
|
r.Host("www.example.com")
|
||||||
// Matches a dynamic subdomain.
|
// Matches a dynamic subdomain.
|
||||||
r.Host("{subdomain:[a-z]+}.domain.com")
|
r.Host("{subdomain:[a-z]+}.example.com")
|
||||||
```
|
```
|
||||||
|
|
||||||
There are several other matchers that can be added. To match path prefixes:
|
There are several other matchers that can be added. To match path prefixes:
|
||||||
|
@ -238,13 +238,13 @@ This also works for host and query value variables:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
r.Host("{subdomain}.domain.com").
|
r.Host("{subdomain}.example.com").
|
||||||
Path("/articles/{category}/{id:[0-9]+}").
|
Path("/articles/{category}/{id:[0-9]+}").
|
||||||
Queries("filter", "{filter}").
|
Queries("filter", "{filter}").
|
||||||
HandlerFunc(ArticleHandler).
|
HandlerFunc(ArticleHandler).
|
||||||
Name("article")
|
Name("article")
|
||||||
|
|
||||||
// url.String() will be "http://news.domain.com/articles/technology/42?filter=gorilla"
|
// url.String() will be "http://news.example.com/articles/technology/42?filter=gorilla"
|
||||||
url, err := r.Get("article").URL("subdomain", "news",
|
url, err := r.Get("article").URL("subdomain", "news",
|
||||||
"category", "technology",
|
"category", "technology",
|
||||||
"id", "42",
|
"id", "42",
|
||||||
|
@ -264,7 +264,7 @@ r.HeadersRegexp("Content-Type", "application/(text|json)")
|
||||||
There's also a way to build only the URL host or path for a route: use the methods `URLHost()` or `URLPath()` instead. For the previous route, we would do:
|
There's also a way to build only the URL host or path for a route: use the methods `URLHost()` or `URLPath()` instead. For the previous route, we would do:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// "http://news.domain.com/"
|
// "http://news.example.com/"
|
||||||
host, err := r.Get("article").URLHost("subdomain", "news")
|
host, err := r.Get("article").URLHost("subdomain", "news")
|
||||||
|
|
||||||
// "/articles/technology/42"
|
// "/articles/technology/42"
|
||||||
|
@ -275,12 +275,12 @@ And if you use subrouters, host and path defined separately can be built as well
|
||||||
|
|
||||||
```go
|
```go
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
s := r.Host("{subdomain}.domain.com").Subrouter()
|
s := r.Host("{subdomain}.example.com").Subrouter()
|
||||||
s.Path("/articles/{category}/{id:[0-9]+}").
|
s.Path("/articles/{category}/{id:[0-9]+}").
|
||||||
HandlerFunc(ArticleHandler).
|
HandlerFunc(ArticleHandler).
|
||||||
Name("article")
|
Name("article")
|
||||||
|
|
||||||
// "http://news.domain.com/articles/technology/42"
|
// "http://news.example.com/articles/technology/42"
|
||||||
url, err := r.Get("article").URL("subdomain", "news",
|
url, err := r.Get("article").URL("subdomain", "news",
|
||||||
"category", "technology",
|
"category", "technology",
|
||||||
"id", "42")
|
"id", "42")
|
||||||
|
@ -503,8 +503,8 @@ package main
|
||||||
|
|
||||||
func HealthCheckHandler(w http.ResponseWriter, r *http.Request) {
|
func HealthCheckHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
// A very simple health check.
|
// A very simple health check.
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
// In the future we could report back on the status of our DB, or our cache
|
// In the future we could report back on the status of our DB, or our cache
|
||||||
// (e.g. Redis) by performing a simple PING, and include them in the response.
|
// (e.g. Redis) by performing a simple PING, and include them in the response.
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
// +build go1.7
|
|
||||||
|
|
||||||
package mux
|
package mux
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -18,7 +16,3 @@ func contextSet(r *http.Request, key, val interface{}) *http.Request {
|
||||||
|
|
||||||
return r.WithContext(context.WithValue(r.Context(), key, val))
|
return r.WithContext(context.WithValue(r.Context(), key, val))
|
||||||
}
|
}
|
||||||
|
|
||||||
func contextClear(r *http.Request) {
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
// +build !go1.7
|
|
||||||
|
|
||||||
package mux
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/gorilla/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
func contextGet(r *http.Request, key interface{}) interface{} {
|
|
||||||
return context.Get(r, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func contextSet(r *http.Request, key, val interface{}) *http.Request {
|
|
||||||
if val == nil {
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Set(r, key, val)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func contextClear(r *http.Request) {
|
|
||||||
context.Clear(r)
|
|
||||||
}
|
|
|
@ -0,0 +1 @@
|
||||||
|
module github.com/gorilla/mux
|
|
@ -22,7 +22,7 @@ var (
|
||||||
|
|
||||||
// NewRouter returns a new router instance.
|
// NewRouter returns a new router instance.
|
||||||
func NewRouter() *Router {
|
func NewRouter() *Router {
|
||||||
return &Router{namedRoutes: make(map[string]*Route), KeepContext: false}
|
return &Router{namedRoutes: make(map[string]*Route)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Router registers routes to be matched and dispatches a handler.
|
// Router registers routes to be matched and dispatches a handler.
|
||||||
|
@ -50,24 +50,78 @@ type Router struct {
|
||||||
// Configurable Handler to be used when the request method does not match the route.
|
// Configurable Handler to be used when the request method does not match the route.
|
||||||
MethodNotAllowedHandler http.Handler
|
MethodNotAllowedHandler http.Handler
|
||||||
|
|
||||||
// Parent route, if this is a subrouter.
|
|
||||||
parent parentRoute
|
|
||||||
// Routes to be matched, in order.
|
// Routes to be matched, in order.
|
||||||
routes []*Route
|
routes []*Route
|
||||||
|
|
||||||
// Routes by name for URL building.
|
// Routes by name for URL building.
|
||||||
namedRoutes map[string]*Route
|
namedRoutes map[string]*Route
|
||||||
// See Router.StrictSlash(). This defines the flag for new routes.
|
|
||||||
strictSlash bool
|
|
||||||
// See Router.SkipClean(). This defines the flag for new routes.
|
|
||||||
skipClean bool
|
|
||||||
// If true, do not clear the request context after handling the request.
|
// If true, do not clear the request context after handling the request.
|
||||||
// This has no effect when go1.7+ is used, since the context is stored
|
//
|
||||||
|
// Deprecated: No effect when go1.7+ is used, since the context is stored
|
||||||
// on the request itself.
|
// on the request itself.
|
||||||
KeepContext bool
|
KeepContext bool
|
||||||
// see Router.UseEncodedPath(). This defines a flag for all routes.
|
|
||||||
useEncodedPath bool
|
|
||||||
// Slice of middlewares to be called after a match is found
|
// Slice of middlewares to be called after a match is found
|
||||||
middlewares []middleware
|
middlewares []middleware
|
||||||
|
|
||||||
|
// configuration shared with `Route`
|
||||||
|
routeConf
|
||||||
|
}
|
||||||
|
|
||||||
|
// common route configuration shared between `Router` and `Route`
|
||||||
|
type routeConf struct {
|
||||||
|
// If true, "/path/foo%2Fbar/to" will match the path "/path/{var}/to"
|
||||||
|
useEncodedPath bool
|
||||||
|
|
||||||
|
// If true, when the path pattern is "/path/", accessing "/path" will
|
||||||
|
// redirect to the former and vice versa.
|
||||||
|
strictSlash bool
|
||||||
|
|
||||||
|
// If true, when the path pattern is "/path//to", accessing "/path//to"
|
||||||
|
// will not redirect
|
||||||
|
skipClean bool
|
||||||
|
|
||||||
|
// Manager for the variables from host and path.
|
||||||
|
regexp routeRegexpGroup
|
||||||
|
|
||||||
|
// List of matchers.
|
||||||
|
matchers []matcher
|
||||||
|
|
||||||
|
// The scheme used when building URLs.
|
||||||
|
buildScheme string
|
||||||
|
|
||||||
|
buildVarsFunc BuildVarsFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns an effective deep copy of `routeConf`
|
||||||
|
func copyRouteConf(r routeConf) routeConf {
|
||||||
|
c := r
|
||||||
|
|
||||||
|
if r.regexp.path != nil {
|
||||||
|
c.regexp.path = copyRouteRegexp(r.regexp.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.regexp.host != nil {
|
||||||
|
c.regexp.host = copyRouteRegexp(r.regexp.host)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.regexp.queries = make([]*routeRegexp, 0, len(r.regexp.queries))
|
||||||
|
for _, q := range r.regexp.queries {
|
||||||
|
c.regexp.queries = append(c.regexp.queries, copyRouteRegexp(q))
|
||||||
|
}
|
||||||
|
|
||||||
|
c.matchers = make([]matcher, 0, len(r.matchers))
|
||||||
|
for _, m := range r.matchers {
|
||||||
|
c.matchers = append(c.matchers, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyRouteRegexp(r *routeRegexp) *routeRegexp {
|
||||||
|
c := *r
|
||||||
|
return &c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match attempts to match the given request against the router's registered routes.
|
// Match attempts to match the given request against the router's registered routes.
|
||||||
|
@ -155,22 +209,18 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
handler = http.NotFoundHandler()
|
handler = http.NotFoundHandler()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !r.KeepContext {
|
|
||||||
defer contextClear(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
handler.ServeHTTP(w, req)
|
handler.ServeHTTP(w, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns a route registered with the given name.
|
// Get returns a route registered with the given name.
|
||||||
func (r *Router) Get(name string) *Route {
|
func (r *Router) Get(name string) *Route {
|
||||||
return r.getNamedRoutes()[name]
|
return r.namedRoutes[name]
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRoute returns a route registered with the given name. This method
|
// GetRoute returns a route registered with the given name. This method
|
||||||
// was renamed to Get() and remains here for backwards compatibility.
|
// was renamed to Get() and remains here for backwards compatibility.
|
||||||
func (r *Router) GetRoute(name string) *Route {
|
func (r *Router) GetRoute(name string) *Route {
|
||||||
return r.getNamedRoutes()[name]
|
return r.namedRoutes[name]
|
||||||
}
|
}
|
||||||
|
|
||||||
// StrictSlash defines the trailing slash behavior for new routes. The initial
|
// StrictSlash defines the trailing slash behavior for new routes. The initial
|
||||||
|
@ -221,55 +271,24 @@ func (r *Router) UseEncodedPath() *Router {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// parentRoute
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
func (r *Router) getBuildScheme() string {
|
|
||||||
if r.parent != nil {
|
|
||||||
return r.parent.getBuildScheme()
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// getNamedRoutes returns the map where named routes are registered.
|
|
||||||
func (r *Router) getNamedRoutes() map[string]*Route {
|
|
||||||
if r.namedRoutes == nil {
|
|
||||||
if r.parent != nil {
|
|
||||||
r.namedRoutes = r.parent.getNamedRoutes()
|
|
||||||
} else {
|
|
||||||
r.namedRoutes = make(map[string]*Route)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return r.namedRoutes
|
|
||||||
}
|
|
||||||
|
|
||||||
// getRegexpGroup returns regexp definitions from the parent route, if any.
|
|
||||||
func (r *Router) getRegexpGroup() *routeRegexpGroup {
|
|
||||||
if r.parent != nil {
|
|
||||||
return r.parent.getRegexpGroup()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Router) buildVars(m map[string]string) map[string]string {
|
|
||||||
if r.parent != nil {
|
|
||||||
m = r.parent.buildVars(m)
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Route factories
|
// Route factories
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
// NewRoute registers an empty route.
|
// NewRoute registers an empty route.
|
||||||
func (r *Router) NewRoute() *Route {
|
func (r *Router) NewRoute() *Route {
|
||||||
route := &Route{parent: r, strictSlash: r.strictSlash, skipClean: r.skipClean, useEncodedPath: r.useEncodedPath}
|
// initialize a route with a copy of the parent router's configuration
|
||||||
|
route := &Route{routeConf: copyRouteConf(r.routeConf), namedRoutes: r.namedRoutes}
|
||||||
r.routes = append(r.routes, route)
|
r.routes = append(r.routes, route)
|
||||||
return route
|
return route
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Name registers a new route with a name.
|
||||||
|
// See Route.Name().
|
||||||
|
func (r *Router) Name(name string) *Route {
|
||||||
|
return r.NewRoute().Name(name)
|
||||||
|
}
|
||||||
|
|
||||||
// Handle registers a new route with a matcher for the URL path.
|
// Handle registers a new route with a matcher for the URL path.
|
||||||
// See Route.Path() and Route.Handler().
|
// See Route.Path() and Route.Handler().
|
||||||
func (r *Router) Handle(path string, handler http.Handler) *Route {
|
func (r *Router) Handle(path string, handler http.Handler) *Route {
|
||||||
|
|
|
@ -267,7 +267,7 @@ type routeRegexpGroup struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// setMatch extracts the variables from the URL once a route matches.
|
// setMatch extracts the variables from the URL once a route matches.
|
||||||
func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) {
|
func (v routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) {
|
||||||
// Store host variables.
|
// Store host variables.
|
||||||
if v.host != nil {
|
if v.host != nil {
|
||||||
host := getHost(req)
|
host := getHost(req)
|
||||||
|
@ -296,7 +296,7 @@ func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route)
|
||||||
} else {
|
} else {
|
||||||
u.Path += "/"
|
u.Path += "/"
|
||||||
}
|
}
|
||||||
m.Handler = http.RedirectHandler(u.String(), 301)
|
m.Handler = http.RedirectHandler(u.String(), http.StatusMovedPermanently)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -312,17 +312,13 @@ func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getHost tries its best to return the request host.
|
// getHost tries its best to return the request host.
|
||||||
|
// According to section 14.23 of RFC 2616 the Host header
|
||||||
|
// can include the port number if the default value of 80 is not used.
|
||||||
func getHost(r *http.Request) string {
|
func getHost(r *http.Request) string {
|
||||||
if r.URL.IsAbs() {
|
if r.URL.IsAbs() {
|
||||||
return r.URL.Host
|
return r.URL.Host
|
||||||
}
|
}
|
||||||
host := r.Host
|
return r.Host
|
||||||
// Slice off any port information.
|
|
||||||
if i := strings.Index(host, ":"); i != -1 {
|
|
||||||
host = host[:i]
|
|
||||||
}
|
|
||||||
return host
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractVars(input string, matches []int, names []string, output map[string]string) {
|
func extractVars(input string, matches []int, names []string, output map[string]string) {
|
||||||
|
|
|
@ -15,24 +15,8 @@ import (
|
||||||
|
|
||||||
// Route stores information to match a request and build URLs.
|
// Route stores information to match a request and build URLs.
|
||||||
type Route struct {
|
type Route struct {
|
||||||
// Parent where the route was registered (a Router).
|
|
||||||
parent parentRoute
|
|
||||||
// Request handler for the route.
|
// Request handler for the route.
|
||||||
handler http.Handler
|
handler http.Handler
|
||||||
// List of matchers.
|
|
||||||
matchers []matcher
|
|
||||||
// Manager for the variables from host and path.
|
|
||||||
regexp *routeRegexpGroup
|
|
||||||
// If true, when the path pattern is "/path/", accessing "/path" will
|
|
||||||
// redirect to the former and vice versa.
|
|
||||||
strictSlash bool
|
|
||||||
// If true, when the path pattern is "/path//to", accessing "/path//to"
|
|
||||||
// will not redirect
|
|
||||||
skipClean bool
|
|
||||||
// If true, "/path/foo%2Fbar/to" will match the path "/path/{var}/to"
|
|
||||||
useEncodedPath bool
|
|
||||||
// The scheme used when building URLs.
|
|
||||||
buildScheme string
|
|
||||||
// If true, this route never matches: it is only used to build URLs.
|
// If true, this route never matches: it is only used to build URLs.
|
||||||
buildOnly bool
|
buildOnly bool
|
||||||
// The name used to build URLs.
|
// The name used to build URLs.
|
||||||
|
@ -40,7 +24,11 @@ type Route struct {
|
||||||
// Error resulted from building a route.
|
// Error resulted from building a route.
|
||||||
err error
|
err error
|
||||||
|
|
||||||
buildVarsFunc BuildVarsFunc
|
// "global" reference to all named routes
|
||||||
|
namedRoutes map[string]*Route
|
||||||
|
|
||||||
|
// config possibly passed in from `Router`
|
||||||
|
routeConf
|
||||||
}
|
}
|
||||||
|
|
||||||
// SkipClean reports whether path cleaning is enabled for this route via
|
// SkipClean reports whether path cleaning is enabled for this route via
|
||||||
|
@ -64,6 +52,18 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
|
||||||
matchErr = ErrMethodMismatch
|
matchErr = ErrMethodMismatch
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ignore ErrNotFound errors. These errors arise from match call
|
||||||
|
// to Subrouters.
|
||||||
|
//
|
||||||
|
// This prevents subsequent matching subrouters from failing to
|
||||||
|
// run middleware. If not ignored, the middleware would see a
|
||||||
|
// non-nil MatchErr and be skipped, even when there was a
|
||||||
|
// matching route.
|
||||||
|
if match.MatchErr == ErrNotFound {
|
||||||
|
match.MatchErr = nil
|
||||||
|
}
|
||||||
|
|
||||||
matchErr = nil
|
matchErr = nil
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -93,9 +93,7 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set variables.
|
// Set variables.
|
||||||
if r.regexp != nil {
|
|
||||||
r.regexp.setMatch(req, match, r)
|
r.regexp.setMatch(req, match, r)
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +135,7 @@ func (r *Route) GetHandler() http.Handler {
|
||||||
// Name -----------------------------------------------------------------------
|
// Name -----------------------------------------------------------------------
|
||||||
|
|
||||||
// Name sets the name for the route, used to build URLs.
|
// Name sets the name for the route, used to build URLs.
|
||||||
// If the name was registered already it will be overwritten.
|
// It is an error to call Name more than once on a route.
|
||||||
func (r *Route) Name(name string) *Route {
|
func (r *Route) Name(name string) *Route {
|
||||||
if r.name != "" {
|
if r.name != "" {
|
||||||
r.err = fmt.Errorf("mux: route already has name %q, can't set %q",
|
r.err = fmt.Errorf("mux: route already has name %q, can't set %q",
|
||||||
|
@ -145,7 +143,7 @@ func (r *Route) Name(name string) *Route {
|
||||||
}
|
}
|
||||||
if r.err == nil {
|
if r.err == nil {
|
||||||
r.name = name
|
r.name = name
|
||||||
r.getNamedRoutes()[name] = r
|
r.namedRoutes[name] = r
|
||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
@ -177,7 +175,6 @@ func (r *Route) addRegexpMatcher(tpl string, typ regexpType) error {
|
||||||
if r.err != nil {
|
if r.err != nil {
|
||||||
return r.err
|
return r.err
|
||||||
}
|
}
|
||||||
r.regexp = r.getRegexpGroup()
|
|
||||||
if typ == regexpTypePath || typ == regexpTypePrefix {
|
if typ == regexpTypePath || typ == regexpTypePrefix {
|
||||||
if len(tpl) > 0 && tpl[0] != '/' {
|
if len(tpl) > 0 && tpl[0] != '/' {
|
||||||
return fmt.Errorf("mux: path must start with a slash, got %q", tpl)
|
return fmt.Errorf("mux: path must start with a slash, got %q", tpl)
|
||||||
|
@ -386,7 +383,7 @@ func (r *Route) PathPrefix(tpl string) *Route {
|
||||||
// The above route will only match if the URL contains the defined queries
|
// The above route will only match if the URL contains the defined queries
|
||||||
// values, e.g.: ?foo=bar&id=42.
|
// values, e.g.: ?foo=bar&id=42.
|
||||||
//
|
//
|
||||||
// It the value is an empty string, it will match any value if the key is set.
|
// If the value is an empty string, it will match any value if the key is set.
|
||||||
//
|
//
|
||||||
// Variables can define an optional regexp pattern to be matched:
|
// Variables can define an optional regexp pattern to be matched:
|
||||||
//
|
//
|
||||||
|
@ -424,7 +421,7 @@ func (r *Route) Schemes(schemes ...string) *Route {
|
||||||
for k, v := range schemes {
|
for k, v := range schemes {
|
||||||
schemes[k] = strings.ToLower(v)
|
schemes[k] = strings.ToLower(v)
|
||||||
}
|
}
|
||||||
if r.buildScheme == "" && len(schemes) > 0 {
|
if len(schemes) > 0 {
|
||||||
r.buildScheme = schemes[0]
|
r.buildScheme = schemes[0]
|
||||||
}
|
}
|
||||||
return r.addMatcher(schemeMatcher(schemes))
|
return r.addMatcher(schemeMatcher(schemes))
|
||||||
|
@ -439,7 +436,15 @@ type BuildVarsFunc func(map[string]string) map[string]string
|
||||||
// BuildVarsFunc adds a custom function to be used to modify build variables
|
// BuildVarsFunc adds a custom function to be used to modify build variables
|
||||||
// before a route's URL is built.
|
// before a route's URL is built.
|
||||||
func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route {
|
func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route {
|
||||||
|
if r.buildVarsFunc != nil {
|
||||||
|
// compose the old and new functions
|
||||||
|
old := r.buildVarsFunc
|
||||||
|
r.buildVarsFunc = func(m map[string]string) map[string]string {
|
||||||
|
return f(old(m))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
r.buildVarsFunc = f
|
r.buildVarsFunc = f
|
||||||
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,7 +463,8 @@ func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route {
|
||||||
// Here, the routes registered in the subrouter won't be tested if the host
|
// Here, the routes registered in the subrouter won't be tested if the host
|
||||||
// doesn't match.
|
// doesn't match.
|
||||||
func (r *Route) Subrouter() *Router {
|
func (r *Route) Subrouter() *Router {
|
||||||
router := &Router{parent: r, strictSlash: r.strictSlash}
|
// initialize a subrouter with a copy of the parent route's configuration
|
||||||
|
router := &Router{routeConf: copyRouteConf(r.routeConf), namedRoutes: r.namedRoutes}
|
||||||
r.addMatcher(router)
|
r.addMatcher(router)
|
||||||
return router
|
return router
|
||||||
}
|
}
|
||||||
|
@ -502,9 +508,6 @@ func (r *Route) URL(pairs ...string) (*url.URL, error) {
|
||||||
if r.err != nil {
|
if r.err != nil {
|
||||||
return nil, r.err
|
return nil, r.err
|
||||||
}
|
}
|
||||||
if r.regexp == nil {
|
|
||||||
return nil, errors.New("mux: route doesn't have a host or path")
|
|
||||||
}
|
|
||||||
values, err := r.prepareVars(pairs...)
|
values, err := r.prepareVars(pairs...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -516,8 +519,8 @@ func (r *Route) URL(pairs ...string) (*url.URL, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
scheme = "http"
|
scheme = "http"
|
||||||
if s := r.getBuildScheme(); s != "" {
|
if r.buildScheme != "" {
|
||||||
scheme = s
|
scheme = r.buildScheme
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if r.regexp.path != nil {
|
if r.regexp.path != nil {
|
||||||
|
@ -547,7 +550,7 @@ func (r *Route) URLHost(pairs ...string) (*url.URL, error) {
|
||||||
if r.err != nil {
|
if r.err != nil {
|
||||||
return nil, r.err
|
return nil, r.err
|
||||||
}
|
}
|
||||||
if r.regexp == nil || r.regexp.host == nil {
|
if r.regexp.host == nil {
|
||||||
return nil, errors.New("mux: route doesn't have a host")
|
return nil, errors.New("mux: route doesn't have a host")
|
||||||
}
|
}
|
||||||
values, err := r.prepareVars(pairs...)
|
values, err := r.prepareVars(pairs...)
|
||||||
|
@ -562,8 +565,8 @@ func (r *Route) URLHost(pairs ...string) (*url.URL, error) {
|
||||||
Scheme: "http",
|
Scheme: "http",
|
||||||
Host: host,
|
Host: host,
|
||||||
}
|
}
|
||||||
if s := r.getBuildScheme(); s != "" {
|
if r.buildScheme != "" {
|
||||||
u.Scheme = s
|
u.Scheme = r.buildScheme
|
||||||
}
|
}
|
||||||
return u, nil
|
return u, nil
|
||||||
}
|
}
|
||||||
|
@ -575,7 +578,7 @@ func (r *Route) URLPath(pairs ...string) (*url.URL, error) {
|
||||||
if r.err != nil {
|
if r.err != nil {
|
||||||
return nil, r.err
|
return nil, r.err
|
||||||
}
|
}
|
||||||
if r.regexp == nil || r.regexp.path == nil {
|
if r.regexp.path == nil {
|
||||||
return nil, errors.New("mux: route doesn't have a path")
|
return nil, errors.New("mux: route doesn't have a path")
|
||||||
}
|
}
|
||||||
values, err := r.prepareVars(pairs...)
|
values, err := r.prepareVars(pairs...)
|
||||||
|
@ -600,7 +603,7 @@ func (r *Route) GetPathTemplate() (string, error) {
|
||||||
if r.err != nil {
|
if r.err != nil {
|
||||||
return "", r.err
|
return "", r.err
|
||||||
}
|
}
|
||||||
if r.regexp == nil || r.regexp.path == nil {
|
if r.regexp.path == nil {
|
||||||
return "", errors.New("mux: route doesn't have a path")
|
return "", errors.New("mux: route doesn't have a path")
|
||||||
}
|
}
|
||||||
return r.regexp.path.template, nil
|
return r.regexp.path.template, nil
|
||||||
|
@ -614,7 +617,7 @@ func (r *Route) GetPathRegexp() (string, error) {
|
||||||
if r.err != nil {
|
if r.err != nil {
|
||||||
return "", r.err
|
return "", r.err
|
||||||
}
|
}
|
||||||
if r.regexp == nil || r.regexp.path == nil {
|
if r.regexp.path == nil {
|
||||||
return "", errors.New("mux: route does not have a path")
|
return "", errors.New("mux: route does not have a path")
|
||||||
}
|
}
|
||||||
return r.regexp.path.regexp.String(), nil
|
return r.regexp.path.regexp.String(), nil
|
||||||
|
@ -629,7 +632,7 @@ func (r *Route) GetQueriesRegexp() ([]string, error) {
|
||||||
if r.err != nil {
|
if r.err != nil {
|
||||||
return nil, r.err
|
return nil, r.err
|
||||||
}
|
}
|
||||||
if r.regexp == nil || r.regexp.queries == nil {
|
if r.regexp.queries == nil {
|
||||||
return nil, errors.New("mux: route doesn't have queries")
|
return nil, errors.New("mux: route doesn't have queries")
|
||||||
}
|
}
|
||||||
var queries []string
|
var queries []string
|
||||||
|
@ -648,7 +651,7 @@ func (r *Route) GetQueriesTemplates() ([]string, error) {
|
||||||
if r.err != nil {
|
if r.err != nil {
|
||||||
return nil, r.err
|
return nil, r.err
|
||||||
}
|
}
|
||||||
if r.regexp == nil || r.regexp.queries == nil {
|
if r.regexp.queries == nil {
|
||||||
return nil, errors.New("mux: route doesn't have queries")
|
return nil, errors.New("mux: route doesn't have queries")
|
||||||
}
|
}
|
||||||
var queries []string
|
var queries []string
|
||||||
|
@ -683,7 +686,7 @@ func (r *Route) GetHostTemplate() (string, error) {
|
||||||
if r.err != nil {
|
if r.err != nil {
|
||||||
return "", r.err
|
return "", r.err
|
||||||
}
|
}
|
||||||
if r.regexp == nil || r.regexp.host == nil {
|
if r.regexp.host == nil {
|
||||||
return "", errors.New("mux: route doesn't have a host")
|
return "", errors.New("mux: route doesn't have a host")
|
||||||
}
|
}
|
||||||
return r.regexp.host.template, nil
|
return r.regexp.host.template, nil
|
||||||
|
@ -700,64 +703,8 @@ func (r *Route) prepareVars(pairs ...string) (map[string]string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Route) buildVars(m map[string]string) map[string]string {
|
func (r *Route) buildVars(m map[string]string) map[string]string {
|
||||||
if r.parent != nil {
|
|
||||||
m = r.parent.buildVars(m)
|
|
||||||
}
|
|
||||||
if r.buildVarsFunc != nil {
|
if r.buildVarsFunc != nil {
|
||||||
m = r.buildVarsFunc(m)
|
m = r.buildVarsFunc(m)
|
||||||
}
|
}
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// parentRoute
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// parentRoute allows routes to know about parent host and path definitions.
|
|
||||||
type parentRoute interface {
|
|
||||||
getBuildScheme() string
|
|
||||||
getNamedRoutes() map[string]*Route
|
|
||||||
getRegexpGroup() *routeRegexpGroup
|
|
||||||
buildVars(map[string]string) map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Route) getBuildScheme() string {
|
|
||||||
if r.buildScheme != "" {
|
|
||||||
return r.buildScheme
|
|
||||||
}
|
|
||||||
if r.parent != nil {
|
|
||||||
return r.parent.getBuildScheme()
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// getNamedRoutes returns the map where named routes are registered.
|
|
||||||
func (r *Route) getNamedRoutes() map[string]*Route {
|
|
||||||
if r.parent == nil {
|
|
||||||
// During tests router is not always set.
|
|
||||||
r.parent = NewRouter()
|
|
||||||
}
|
|
||||||
return r.parent.getNamedRoutes()
|
|
||||||
}
|
|
||||||
|
|
||||||
// getRegexpGroup returns regexp definitions from this route.
|
|
||||||
func (r *Route) getRegexpGroup() *routeRegexpGroup {
|
|
||||||
if r.regexp == nil {
|
|
||||||
if r.parent == nil {
|
|
||||||
// During tests router is not always set.
|
|
||||||
r.parent = NewRouter()
|
|
||||||
}
|
|
||||||
regexp := r.parent.getRegexpGroup()
|
|
||||||
if regexp == nil {
|
|
||||||
r.regexp = new(routeRegexpGroup)
|
|
||||||
} else {
|
|
||||||
// Copy.
|
|
||||||
r.regexp = &routeRegexpGroup{
|
|
||||||
host: regexp.host,
|
|
||||||
path: regexp.path,
|
|
||||||
queries: regexp.queries,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return r.regexp
|
|
||||||
}
|
|
||||||
|
|
|
@ -80,7 +80,7 @@ megacheck_install() {
|
||||||
}
|
}
|
||||||
|
|
||||||
golint_install() {
|
golint_install() {
|
||||||
go get github.com/golang/lint/golint
|
go get golang.org/x/lint/golint
|
||||||
}
|
}
|
||||||
|
|
||||||
$1
|
$1
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
language: go
|
language: go
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- 1.8.x
|
|
||||||
- 1.9.x
|
- 1.9.x
|
||||||
- 1.10.x
|
- 1.10.x
|
||||||
|
- 1.11.x
|
||||||
- master
|
- master
|
||||||
|
|
||||||
sudo: true
|
sudo: true
|
||||||
|
@ -44,7 +44,7 @@ script:
|
||||||
- >
|
- >
|
||||||
goimports -d -e $(find -name '*.go') | awk '{ print } END { exit NR == 0 ? 0 : 1 }'
|
goimports -d -e $(find -name '*.go') | awk '{ print } END { exit NR == 0 ? 0 : 1 }'
|
||||||
- go vet ./...
|
- go vet ./...
|
||||||
- megacheck -go 1.8 ./...
|
- megacheck -go 1.9 ./...
|
||||||
- golint ./...
|
- golint ./...
|
||||||
- PQTEST_BINARY_PARAMETERS=no go test -race -v ./...
|
- PQTEST_BINARY_PARAMETERS=no go test -race -v ./...
|
||||||
- PQTEST_BINARY_PARAMETERS=yes go test -race -v ./...
|
- PQTEST_BINARY_PARAMETERS=yes go test -race -v ./...
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
## Docs
|
## Docs
|
||||||
|
|
||||||
For detailed documentation and basic usage examples, please see the package
|
For detailed documentation and basic usage examples, please see the package
|
||||||
documentation at <http://godoc.org/github.com/lib/pq>.
|
documentation at <https://godoc.org/github.com/lib/pq>.
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,9 @@ package pq
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"context"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
|
"crypto/sha256"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
@ -20,6 +22,7 @@ import (
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"github.com/lib/pq/oid"
|
"github.com/lib/pq/oid"
|
||||||
|
"github.com/lib/pq/scram"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Common error types
|
// Common error types
|
||||||
|
@ -89,13 +92,24 @@ type Dialer interface {
|
||||||
DialTimeout(network, address string, timeout time.Duration) (net.Conn, error)
|
DialTimeout(network, address string, timeout time.Duration) (net.Conn, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type defaultDialer struct{}
|
type DialerContext interface {
|
||||||
|
DialContext(ctx context.Context, network, address string) (net.Conn, error)
|
||||||
func (d defaultDialer) Dial(ntw, addr string) (net.Conn, error) {
|
|
||||||
return net.Dial(ntw, addr)
|
|
||||||
}
|
}
|
||||||
func (d defaultDialer) DialTimeout(ntw, addr string, timeout time.Duration) (net.Conn, error) {
|
|
||||||
return net.DialTimeout(ntw, addr, timeout)
|
type defaultDialer struct {
|
||||||
|
d net.Dialer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d defaultDialer) Dial(network, address string) (net.Conn, error) {
|
||||||
|
return d.d.Dial(network, address)
|
||||||
|
}
|
||||||
|
func (d defaultDialer) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
|
defer cancel()
|
||||||
|
return d.DialContext(ctx, network, address)
|
||||||
|
}
|
||||||
|
func (d defaultDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
|
return d.d.DialContext(ctx, network, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
type conn struct {
|
type conn struct {
|
||||||
|
@ -244,90 +258,35 @@ func (cn *conn) writeBuf(b byte) *writeBuf {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open opens a new connection to the database. name is a connection string.
|
// Open opens a new connection to the database. dsn is a connection string.
|
||||||
// Most users should only use it through database/sql package from the standard
|
// Most users should only use it through database/sql package from the standard
|
||||||
// library.
|
// library.
|
||||||
func Open(name string) (_ driver.Conn, err error) {
|
func Open(dsn string) (_ driver.Conn, err error) {
|
||||||
return DialOpen(defaultDialer{}, name)
|
return DialOpen(defaultDialer{}, dsn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialOpen opens a new connection to the database using a dialer.
|
// DialOpen opens a new connection to the database using a dialer.
|
||||||
func DialOpen(d Dialer, name string) (_ driver.Conn, err error) {
|
func DialOpen(d Dialer, dsn string) (_ driver.Conn, err error) {
|
||||||
|
c, err := NewConnector(dsn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c.dialer = d
|
||||||
|
return c.open(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Connector) open(ctx context.Context) (cn *conn, err error) {
|
||||||
// Handle any panics during connection initialization. Note that we
|
// Handle any panics during connection initialization. Note that we
|
||||||
// specifically do *not* want to use errRecover(), as that would turn any
|
// specifically do *not* want to use errRecover(), as that would turn any
|
||||||
// connection errors into ErrBadConns, hiding the real error message from
|
// connection errors into ErrBadConns, hiding the real error message from
|
||||||
// the user.
|
// the user.
|
||||||
defer errRecoverNoErrBadConn(&err)
|
defer errRecoverNoErrBadConn(&err)
|
||||||
|
|
||||||
o := make(values)
|
o := c.opts
|
||||||
|
|
||||||
// A number of defaults are applied here, in this order:
|
cn = &conn{
|
||||||
//
|
|
||||||
// * Very low precedence defaults applied in every situation
|
|
||||||
// * Environment variables
|
|
||||||
// * Explicitly passed connection information
|
|
||||||
o["host"] = "localhost"
|
|
||||||
o["port"] = "5432"
|
|
||||||
// N.B.: Extra float digits should be set to 3, but that breaks
|
|
||||||
// Postgres 8.4 and older, where the max is 2.
|
|
||||||
o["extra_float_digits"] = "2"
|
|
||||||
for k, v := range parseEnviron(os.Environ()) {
|
|
||||||
o[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(name, "postgres://") || strings.HasPrefix(name, "postgresql://") {
|
|
||||||
name, err = ParseURL(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := parseOpts(name, o); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the "fallback" application name if necessary
|
|
||||||
if fallback, ok := o["fallback_application_name"]; ok {
|
|
||||||
if _, ok := o["application_name"]; !ok {
|
|
||||||
o["application_name"] = fallback
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We can't work with any client_encoding other than UTF-8 currently.
|
|
||||||
// However, we have historically allowed the user to set it to UTF-8
|
|
||||||
// explicitly, and there's no reason to break such programs, so allow that.
|
|
||||||
// Note that the "options" setting could also set client_encoding, but
|
|
||||||
// parsing its value is not worth it. Instead, we always explicitly send
|
|
||||||
// client_encoding as a separate run-time parameter, which should override
|
|
||||||
// anything set in options.
|
|
||||||
if enc, ok := o["client_encoding"]; ok && !isUTF8(enc) {
|
|
||||||
return nil, errors.New("client_encoding must be absent or 'UTF8'")
|
|
||||||
}
|
|
||||||
o["client_encoding"] = "UTF8"
|
|
||||||
// DateStyle needs a similar treatment.
|
|
||||||
if datestyle, ok := o["datestyle"]; ok {
|
|
||||||
if datestyle != "ISO, MDY" {
|
|
||||||
panic(fmt.Sprintf("setting datestyle must be absent or %v; got %v",
|
|
||||||
"ISO, MDY", datestyle))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
o["datestyle"] = "ISO, MDY"
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a user is not provided by any other means, the last
|
|
||||||
// resort is to use the current operating system provided user
|
|
||||||
// name.
|
|
||||||
if _, ok := o["user"]; !ok {
|
|
||||||
u, err := userCurrent()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
o["user"] = u
|
|
||||||
}
|
|
||||||
|
|
||||||
cn := &conn{
|
|
||||||
opts: o,
|
opts: o,
|
||||||
dialer: d,
|
dialer: c.dialer,
|
||||||
}
|
}
|
||||||
err = cn.handleDriverSettings(o)
|
err = cn.handleDriverSettings(o)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -335,7 +294,7 @@ func DialOpen(d Dialer, name string) (_ driver.Conn, err error) {
|
||||||
}
|
}
|
||||||
cn.handlePgpass(o)
|
cn.handlePgpass(o)
|
||||||
|
|
||||||
cn.c, err = dial(d, o)
|
cn.c, err = dial(ctx, c.dialer, o)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -364,10 +323,10 @@ func DialOpen(d Dialer, name string) (_ driver.Conn, err error) {
|
||||||
return cn, err
|
return cn, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func dial(d Dialer, o values) (net.Conn, error) {
|
func dial(ctx context.Context, d Dialer, o values) (net.Conn, error) {
|
||||||
ntw, addr := network(o)
|
network, address := network(o)
|
||||||
// SSL is not necessary or supported over UNIX domain sockets
|
// SSL is not necessary or supported over UNIX domain sockets
|
||||||
if ntw == "unix" {
|
if network == "unix" {
|
||||||
o["sslmode"] = "disable"
|
o["sslmode"] = "disable"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -378,19 +337,30 @@ func dial(d Dialer, o values) (net.Conn, error) {
|
||||||
return nil, fmt.Errorf("invalid value for parameter connect_timeout: %s", err)
|
return nil, fmt.Errorf("invalid value for parameter connect_timeout: %s", err)
|
||||||
}
|
}
|
||||||
duration := time.Duration(seconds) * time.Second
|
duration := time.Duration(seconds) * time.Second
|
||||||
|
|
||||||
// connect_timeout should apply to the entire connection establishment
|
// connect_timeout should apply to the entire connection establishment
|
||||||
// procedure, so we both use a timeout for the TCP connection
|
// procedure, so we both use a timeout for the TCP connection
|
||||||
// establishment and set a deadline for doing the initial handshake.
|
// establishment and set a deadline for doing the initial handshake.
|
||||||
// The deadline is then reset after startup() is done.
|
// The deadline is then reset after startup() is done.
|
||||||
deadline := time.Now().Add(duration)
|
deadline := time.Now().Add(duration)
|
||||||
conn, err := d.DialTimeout(ntw, addr, duration)
|
var conn net.Conn
|
||||||
|
if dctx, ok := d.(DialerContext); ok {
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, duration)
|
||||||
|
defer cancel()
|
||||||
|
conn, err = dctx.DialContext(ctx, network, address)
|
||||||
|
} else {
|
||||||
|
conn, err = d.DialTimeout(network, address, duration)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = conn.SetDeadline(deadline)
|
err = conn.SetDeadline(deadline)
|
||||||
return conn, err
|
return conn, err
|
||||||
}
|
}
|
||||||
return d.Dial(ntw, addr)
|
if dctx, ok := d.(DialerContext); ok {
|
||||||
|
return dctx.DialContext(ctx, network, address)
|
||||||
|
}
|
||||||
|
return d.Dial(network, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
func network(o values) (string, string) {
|
func network(o values) (string, string) {
|
||||||
|
@ -704,7 +674,7 @@ func (cn *conn) simpleQuery(q string) (res *rows, err error) {
|
||||||
// res might be non-nil here if we received a previous
|
// res might be non-nil here if we received a previous
|
||||||
// CommandComplete, but that's fine; just overwrite it
|
// CommandComplete, but that's fine; just overwrite it
|
||||||
res = &rows{cn: cn}
|
res = &rows{cn: cn}
|
||||||
res.colNames, res.colFmts, res.colTyps = parsePortalRowDescribe(r)
|
res.rowsHeader = parsePortalRowDescribe(r)
|
||||||
|
|
||||||
// To work around a bug in QueryRow in Go 1.2 and earlier, wait
|
// To work around a bug in QueryRow in Go 1.2 and earlier, wait
|
||||||
// until the first DataRow has been received.
|
// until the first DataRow has been received.
|
||||||
|
@ -861,7 +831,7 @@ func (cn *conn) query(query string, args []driver.Value) (_ *rows, err error) {
|
||||||
cn.readParseResponse()
|
cn.readParseResponse()
|
||||||
cn.readBindResponse()
|
cn.readBindResponse()
|
||||||
rows := &rows{cn: cn}
|
rows := &rows{cn: cn}
|
||||||
rows.colNames, rows.colFmts, rows.colTyps = cn.readPortalDescribeResponse()
|
rows.rowsHeader = cn.readPortalDescribeResponse()
|
||||||
cn.postExecuteWorkaround()
|
cn.postExecuteWorkaround()
|
||||||
return rows, nil
|
return rows, nil
|
||||||
}
|
}
|
||||||
|
@ -869,9 +839,7 @@ func (cn *conn) query(query string, args []driver.Value) (_ *rows, err error) {
|
||||||
st.exec(args)
|
st.exec(args)
|
||||||
return &rows{
|
return &rows{
|
||||||
cn: cn,
|
cn: cn,
|
||||||
colNames: st.colNames,
|
rowsHeader: st.rowsHeader,
|
||||||
colTyps: st.colTyps,
|
|
||||||
colFmts: st.colFmts,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -992,7 +960,6 @@ func (cn *conn) recv() (t byte, r *readBuf) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch t {
|
switch t {
|
||||||
case 'E':
|
case 'E':
|
||||||
panic(parseError(r))
|
panic(parseError(r))
|
||||||
|
@ -1163,6 +1130,55 @@ func (cn *conn) auth(r *readBuf, o values) {
|
||||||
if r.int32() != 0 {
|
if r.int32() != 0 {
|
||||||
errorf("unexpected authentication response: %q", t)
|
errorf("unexpected authentication response: %q", t)
|
||||||
}
|
}
|
||||||
|
case 10:
|
||||||
|
sc := scram.NewClient(sha256.New, o["user"], o["password"])
|
||||||
|
sc.Step(nil)
|
||||||
|
if sc.Err() != nil {
|
||||||
|
errorf("SCRAM-SHA-256 error: %s", sc.Err().Error())
|
||||||
|
}
|
||||||
|
scOut := sc.Out()
|
||||||
|
|
||||||
|
w := cn.writeBuf('p')
|
||||||
|
w.string("SCRAM-SHA-256")
|
||||||
|
w.int32(len(scOut))
|
||||||
|
w.bytes(scOut)
|
||||||
|
cn.send(w)
|
||||||
|
|
||||||
|
t, r := cn.recv()
|
||||||
|
if t != 'R' {
|
||||||
|
errorf("unexpected password response: %q", t)
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.int32() != 11 {
|
||||||
|
errorf("unexpected authentication response: %q", t)
|
||||||
|
}
|
||||||
|
|
||||||
|
nextStep := r.next(len(*r))
|
||||||
|
sc.Step(nextStep)
|
||||||
|
if sc.Err() != nil {
|
||||||
|
errorf("SCRAM-SHA-256 error: %s", sc.Err().Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
scOut = sc.Out()
|
||||||
|
w = cn.writeBuf('p')
|
||||||
|
w.bytes(scOut)
|
||||||
|
cn.send(w)
|
||||||
|
|
||||||
|
t, r = cn.recv()
|
||||||
|
if t != 'R' {
|
||||||
|
errorf("unexpected password response: %q", t)
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.int32() != 12 {
|
||||||
|
errorf("unexpected authentication response: %q", t)
|
||||||
|
}
|
||||||
|
|
||||||
|
nextStep = r.next(len(*r))
|
||||||
|
sc.Step(nextStep)
|
||||||
|
if sc.Err() != nil {
|
||||||
|
errorf("SCRAM-SHA-256 error: %s", sc.Err().Error())
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
errorf("unknown authentication response: %d", code)
|
errorf("unknown authentication response: %d", code)
|
||||||
}
|
}
|
||||||
|
@ -1182,10 +1198,8 @@ var colFmtDataAllText = []byte{0, 0}
|
||||||
type stmt struct {
|
type stmt struct {
|
||||||
cn *conn
|
cn *conn
|
||||||
name string
|
name string
|
||||||
colNames []string
|
rowsHeader
|
||||||
colFmts []format
|
|
||||||
colFmtData []byte
|
colFmtData []byte
|
||||||
colTyps []fieldDesc
|
|
||||||
paramTyps []oid.Oid
|
paramTyps []oid.Oid
|
||||||
closed bool
|
closed bool
|
||||||
}
|
}
|
||||||
|
@ -1232,9 +1246,7 @@ func (st *stmt) Query(v []driver.Value) (r driver.Rows, err error) {
|
||||||
st.exec(v)
|
st.exec(v)
|
||||||
return &rows{
|
return &rows{
|
||||||
cn: st.cn,
|
cn: st.cn,
|
||||||
colNames: st.colNames,
|
rowsHeader: st.rowsHeader,
|
||||||
colTyps: st.colTyps,
|
|
||||||
colFmts: st.colFmts,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1344,16 +1356,22 @@ func (cn *conn) parseComplete(commandTag string) (driver.Result, string) {
|
||||||
return driver.RowsAffected(n), commandTag
|
return driver.RowsAffected(n), commandTag
|
||||||
}
|
}
|
||||||
|
|
||||||
type rows struct {
|
type rowsHeader struct {
|
||||||
cn *conn
|
|
||||||
finish func()
|
|
||||||
colNames []string
|
colNames []string
|
||||||
colTyps []fieldDesc
|
colTyps []fieldDesc
|
||||||
colFmts []format
|
colFmts []format
|
||||||
|
}
|
||||||
|
|
||||||
|
type rows struct {
|
||||||
|
cn *conn
|
||||||
|
finish func()
|
||||||
|
rowsHeader
|
||||||
done bool
|
done bool
|
||||||
rb readBuf
|
rb readBuf
|
||||||
result driver.Result
|
result driver.Result
|
||||||
tag string
|
tag string
|
||||||
|
|
||||||
|
next *rowsHeader
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rs *rows) Close() error {
|
func (rs *rows) Close() error {
|
||||||
|
@ -1440,7 +1458,8 @@ func (rs *rows) Next(dest []driver.Value) (err error) {
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
case 'T':
|
case 'T':
|
||||||
rs.colNames, rs.colFmts, rs.colTyps = parsePortalRowDescribe(&rs.rb)
|
next := parsePortalRowDescribe(&rs.rb)
|
||||||
|
rs.next = &next
|
||||||
return io.EOF
|
return io.EOF
|
||||||
default:
|
default:
|
||||||
errorf("unexpected message after execute: %q", t)
|
errorf("unexpected message after execute: %q", t)
|
||||||
|
@ -1449,10 +1468,16 @@ func (rs *rows) Next(dest []driver.Value) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rs *rows) HasNextResultSet() bool {
|
func (rs *rows) HasNextResultSet() bool {
|
||||||
return !rs.done
|
hasNext := rs.next != nil && !rs.done
|
||||||
|
return hasNext
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rs *rows) NextResultSet() error {
|
func (rs *rows) NextResultSet() error {
|
||||||
|
if rs.next == nil {
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
rs.rowsHeader = *rs.next
|
||||||
|
rs.next = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1630,13 +1655,13 @@ func (cn *conn) readStatementDescribeResponse() (paramTyps []oid.Oid, colNames [
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cn *conn) readPortalDescribeResponse() (colNames []string, colFmts []format, colTyps []fieldDesc) {
|
func (cn *conn) readPortalDescribeResponse() rowsHeader {
|
||||||
t, r := cn.recv1()
|
t, r := cn.recv1()
|
||||||
switch t {
|
switch t {
|
||||||
case 'T':
|
case 'T':
|
||||||
return parsePortalRowDescribe(r)
|
return parsePortalRowDescribe(r)
|
||||||
case 'n':
|
case 'n':
|
||||||
return nil, nil, nil
|
return rowsHeader{}
|
||||||
case 'E':
|
case 'E':
|
||||||
err := parseError(r)
|
err := parseError(r)
|
||||||
cn.readReadyForQuery()
|
cn.readReadyForQuery()
|
||||||
|
@ -1742,11 +1767,11 @@ func parseStatementRowDescribe(r *readBuf) (colNames []string, colTyps []fieldDe
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func parsePortalRowDescribe(r *readBuf) (colNames []string, colFmts []format, colTyps []fieldDesc) {
|
func parsePortalRowDescribe(r *readBuf) rowsHeader {
|
||||||
n := r.int16()
|
n := r.int16()
|
||||||
colNames = make([]string, n)
|
colNames := make([]string, n)
|
||||||
colFmts = make([]format, n)
|
colFmts := make([]format, n)
|
||||||
colTyps = make([]fieldDesc, n)
|
colTyps := make([]fieldDesc, n)
|
||||||
for i := range colNames {
|
for i := range colNames {
|
||||||
colNames[i] = r.string()
|
colNames[i] = r.string()
|
||||||
r.next(6)
|
r.next(6)
|
||||||
|
@ -1755,7 +1780,11 @@ func parsePortalRowDescribe(r *readBuf) (colNames []string, colFmts []format, co
|
||||||
colTyps[i].Mod = r.int32()
|
colTyps[i].Mod = r.int32()
|
||||||
colFmts[i] = format(r.int16())
|
colFmts[i] = format(r.int16())
|
||||||
}
|
}
|
||||||
return
|
return rowsHeader{
|
||||||
|
colNames: colNames,
|
||||||
|
colFmts: colFmts,
|
||||||
|
colTyps: colTyps,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseEnviron tries to mimic some of libpq's environment handling
|
// parseEnviron tries to mimic some of libpq's environment handling
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
// +build go1.8
|
|
||||||
|
|
||||||
package pq
|
package pq
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -9,6 +7,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Implement the "QueryerContext" interface
|
// Implement the "QueryerContext" interface
|
||||||
|
@ -76,13 +75,32 @@ func (cn *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx,
|
||||||
return tx, nil
|
return tx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cn *conn) Ping(ctx context.Context) error {
|
||||||
|
if finish := cn.watchCancel(ctx); finish != nil {
|
||||||
|
defer finish()
|
||||||
|
}
|
||||||
|
rows, err := cn.simpleQuery("SELECT 'lib/pq ping test';")
|
||||||
|
if err != nil {
|
||||||
|
return driver.ErrBadConn // https://golang.org/pkg/database/sql/driver/#Pinger
|
||||||
|
}
|
||||||
|
rows.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (cn *conn) watchCancel(ctx context.Context) func() {
|
func (cn *conn) watchCancel(ctx context.Context) func() {
|
||||||
if done := ctx.Done(); done != nil {
|
if done := ctx.Done(); done != nil {
|
||||||
finished := make(chan struct{})
|
finished := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
select {
|
select {
|
||||||
case <-done:
|
case <-done:
|
||||||
_ = cn.cancel()
|
// At this point the function level context is canceled,
|
||||||
|
// so it must not be used for the additional network
|
||||||
|
// request to cancel the query.
|
||||||
|
// Create a new context to pass into the dial.
|
||||||
|
ctxCancel, cancel := context.WithTimeout(context.Background(), time.Second*10)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
_ = cn.cancel(ctxCancel)
|
||||||
finished <- struct{}{}
|
finished <- struct{}{}
|
||||||
case <-finished:
|
case <-finished:
|
||||||
}
|
}
|
||||||
|
@ -97,8 +115,8 @@ func (cn *conn) watchCancel(ctx context.Context) func() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cn *conn) cancel() error {
|
func (cn *conn) cancel(ctx context.Context) error {
|
||||||
c, err := dial(cn.dialer, cn.opts)
|
c, err := dial(ctx, cn.dialer, cn.opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
// +build go1.10
|
|
||||||
|
|
||||||
package pq
|
package pq
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Connector represents a fixed configuration for the pq driver with a given
|
// Connector represents a fixed configuration for the pq driver with a given
|
||||||
|
@ -14,30 +16,95 @@ import (
|
||||||
//
|
//
|
||||||
// See https://golang.org/pkg/database/sql/driver/#Connector.
|
// See https://golang.org/pkg/database/sql/driver/#Connector.
|
||||||
// See https://golang.org/pkg/database/sql/#OpenDB.
|
// See https://golang.org/pkg/database/sql/#OpenDB.
|
||||||
type connector struct {
|
type Connector struct {
|
||||||
name string
|
opts values
|
||||||
|
dialer Dialer
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect returns a connection to the database using the fixed configuration
|
// Connect returns a connection to the database using the fixed configuration
|
||||||
// of this Connector. Context is not used.
|
// of this Connector. Context is not used.
|
||||||
func (c *connector) Connect(_ context.Context) (driver.Conn, error) {
|
func (c *Connector) Connect(ctx context.Context) (driver.Conn, error) {
|
||||||
return (&Driver{}).Open(c.name)
|
return c.open(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Driver returnst the underlying driver of this Connector.
|
// Driver returnst the underlying driver of this Connector.
|
||||||
func (c *connector) Driver() driver.Driver {
|
func (c *Connector) Driver() driver.Driver {
|
||||||
return &Driver{}
|
return &Driver{}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ driver.Connector = &connector{}
|
|
||||||
|
|
||||||
// NewConnector returns a connector for the pq driver in a fixed configuration
|
// NewConnector returns a connector for the pq driver in a fixed configuration
|
||||||
// with the given name. The returned connector can be used to create any number
|
// with the given dsn. The returned connector can be used to create any number
|
||||||
// of equivalent Conn's. The returned connector is intended to be used with
|
// of equivalent Conn's. The returned connector is intended to be used with
|
||||||
// database/sql.OpenDB.
|
// database/sql.OpenDB.
|
||||||
//
|
//
|
||||||
// See https://golang.org/pkg/database/sql/driver/#Connector.
|
// See https://golang.org/pkg/database/sql/driver/#Connector.
|
||||||
// See https://golang.org/pkg/database/sql/#OpenDB.
|
// See https://golang.org/pkg/database/sql/#OpenDB.
|
||||||
func NewConnector(name string) (driver.Connector, error) {
|
func NewConnector(dsn string) (*Connector, error) {
|
||||||
return &connector{name: name}, nil
|
var err error
|
||||||
|
o := make(values)
|
||||||
|
|
||||||
|
// A number of defaults are applied here, in this order:
|
||||||
|
//
|
||||||
|
// * Very low precedence defaults applied in every situation
|
||||||
|
// * Environment variables
|
||||||
|
// * Explicitly passed connection information
|
||||||
|
o["host"] = "localhost"
|
||||||
|
o["port"] = "5432"
|
||||||
|
// N.B.: Extra float digits should be set to 3, but that breaks
|
||||||
|
// Postgres 8.4 and older, where the max is 2.
|
||||||
|
o["extra_float_digits"] = "2"
|
||||||
|
for k, v := range parseEnviron(os.Environ()) {
|
||||||
|
o[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(dsn, "postgres://") || strings.HasPrefix(dsn, "postgresql://") {
|
||||||
|
dsn, err = ParseURL(dsn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := parseOpts(dsn, o); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the "fallback" application name if necessary
|
||||||
|
if fallback, ok := o["fallback_application_name"]; ok {
|
||||||
|
if _, ok := o["application_name"]; !ok {
|
||||||
|
o["application_name"] = fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can't work with any client_encoding other than UTF-8 currently.
|
||||||
|
// However, we have historically allowed the user to set it to UTF-8
|
||||||
|
// explicitly, and there's no reason to break such programs, so allow that.
|
||||||
|
// Note that the "options" setting could also set client_encoding, but
|
||||||
|
// parsing its value is not worth it. Instead, we always explicitly send
|
||||||
|
// client_encoding as a separate run-time parameter, which should override
|
||||||
|
// anything set in options.
|
||||||
|
if enc, ok := o["client_encoding"]; ok && !isUTF8(enc) {
|
||||||
|
return nil, errors.New("client_encoding must be absent or 'UTF8'")
|
||||||
|
}
|
||||||
|
o["client_encoding"] = "UTF8"
|
||||||
|
// DateStyle needs a similar treatment.
|
||||||
|
if datestyle, ok := o["datestyle"]; ok {
|
||||||
|
if datestyle != "ISO, MDY" {
|
||||||
|
return nil, fmt.Errorf("setting datestyle must be absent or %v; got %v", "ISO, MDY", datestyle)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
o["datestyle"] = "ISO, MDY"
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a user is not provided by any other means, the last
|
||||||
|
// resort is to use the current operating system provided user
|
||||||
|
// name.
|
||||||
|
if _, ok := o["user"]; !ok {
|
||||||
|
u, err := userCurrent()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
o["user"] = u
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Connector{opts: o, dialer: defaultDialer{}}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -239,7 +239,7 @@ for more information). Note that the channel name will be truncated to 63
|
||||||
bytes by the PostgreSQL server.
|
bytes by the PostgreSQL server.
|
||||||
|
|
||||||
You can find a complete, working example of Listener usage at
|
You can find a complete, working example of Listener usage at
|
||||||
http://godoc.org/github.com/lib/pq/example/listen.
|
https://godoc.org/github.com/lib/pq/example/listen.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
package pq
|
package pq
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
module github.com/lib/pq
|
|
@ -725,6 +725,9 @@ func (l *Listener) Close() error {
|
||||||
}
|
}
|
||||||
l.isClosed = true
|
l.isClosed = true
|
||||||
|
|
||||||
|
// Unblock calls to Listen()
|
||||||
|
l.reconnectCond.Broadcast()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,264 @@
|
||||||
|
// Copyright (c) 2014 - Gustavo Niemeyer <gustavo@niemeyer.net>
|
||||||
|
//
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
// list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer in the documentation
|
||||||
|
// and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// Pacakage scram implements a SCRAM-{SHA-1,etc} client per RFC5802.
|
||||||
|
//
|
||||||
|
// http://tools.ietf.org/html/rfc5802
|
||||||
|
//
|
||||||
|
package scram
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"hash"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client implements a SCRAM-* client (SCRAM-SHA-1, SCRAM-SHA-256, etc).
|
||||||
|
//
|
||||||
|
// A Client may be used within a SASL conversation with logic resembling:
|
||||||
|
//
|
||||||
|
// var in []byte
|
||||||
|
// var client = scram.NewClient(sha1.New, user, pass)
|
||||||
|
// for client.Step(in) {
|
||||||
|
// out := client.Out()
|
||||||
|
// // send out to server
|
||||||
|
// in := serverOut
|
||||||
|
// }
|
||||||
|
// if client.Err() != nil {
|
||||||
|
// // auth failed
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
type Client struct {
|
||||||
|
newHash func() hash.Hash
|
||||||
|
|
||||||
|
user string
|
||||||
|
pass string
|
||||||
|
step int
|
||||||
|
out bytes.Buffer
|
||||||
|
err error
|
||||||
|
|
||||||
|
clientNonce []byte
|
||||||
|
serverNonce []byte
|
||||||
|
saltedPass []byte
|
||||||
|
authMsg bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient returns a new SCRAM-* client with the provided hash algorithm.
|
||||||
|
//
|
||||||
|
// For SCRAM-SHA-256, for example, use:
|
||||||
|
//
|
||||||
|
// client := scram.NewClient(sha256.New, user, pass)
|
||||||
|
//
|
||||||
|
func NewClient(newHash func() hash.Hash, user, pass string) *Client {
|
||||||
|
c := &Client{
|
||||||
|
newHash: newHash,
|
||||||
|
user: user,
|
||||||
|
pass: pass,
|
||||||
|
}
|
||||||
|
c.out.Grow(256)
|
||||||
|
c.authMsg.Grow(256)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Out returns the data to be sent to the server in the current step.
|
||||||
|
func (c *Client) Out() []byte {
|
||||||
|
if c.out.Len() == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.out.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err returns the error that ocurred, or nil if there were no errors.
|
||||||
|
func (c *Client) Err() error {
|
||||||
|
return c.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNonce sets the client nonce to the provided value.
|
||||||
|
// If not set, the nonce is generated automatically out of crypto/rand on the first step.
|
||||||
|
func (c *Client) SetNonce(nonce []byte) {
|
||||||
|
c.clientNonce = nonce
|
||||||
|
}
|
||||||
|
|
||||||
|
var escaper = strings.NewReplacer("=", "=3D", ",", "=2C")
|
||||||
|
|
||||||
|
// Step processes the incoming data from the server and makes the
|
||||||
|
// next round of data for the server available via Client.Out.
|
||||||
|
// Step returns false if there are no errors and more data is
|
||||||
|
// still expected.
|
||||||
|
func (c *Client) Step(in []byte) bool {
|
||||||
|
c.out.Reset()
|
||||||
|
if c.step > 2 || c.err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
c.step++
|
||||||
|
switch c.step {
|
||||||
|
case 1:
|
||||||
|
c.err = c.step1(in)
|
||||||
|
case 2:
|
||||||
|
c.err = c.step2(in)
|
||||||
|
case 3:
|
||||||
|
c.err = c.step3(in)
|
||||||
|
}
|
||||||
|
return c.step > 2 || c.err != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) step1(in []byte) error {
|
||||||
|
if len(c.clientNonce) == 0 {
|
||||||
|
const nonceLen = 16
|
||||||
|
buf := make([]byte, nonceLen+b64.EncodedLen(nonceLen))
|
||||||
|
if _, err := rand.Read(buf[:nonceLen]); err != nil {
|
||||||
|
return fmt.Errorf("cannot read random SCRAM-SHA-256 nonce from operating system: %v", err)
|
||||||
|
}
|
||||||
|
c.clientNonce = buf[nonceLen:]
|
||||||
|
b64.Encode(c.clientNonce, buf[:nonceLen])
|
||||||
|
}
|
||||||
|
c.authMsg.WriteString("n=")
|
||||||
|
escaper.WriteString(&c.authMsg, c.user)
|
||||||
|
c.authMsg.WriteString(",r=")
|
||||||
|
c.authMsg.Write(c.clientNonce)
|
||||||
|
|
||||||
|
c.out.WriteString("n,,")
|
||||||
|
c.out.Write(c.authMsg.Bytes())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var b64 = base64.StdEncoding
|
||||||
|
|
||||||
|
func (c *Client) step2(in []byte) error {
|
||||||
|
c.authMsg.WriteByte(',')
|
||||||
|
c.authMsg.Write(in)
|
||||||
|
|
||||||
|
fields := bytes.Split(in, []byte(","))
|
||||||
|
if len(fields) != 3 {
|
||||||
|
return fmt.Errorf("expected 3 fields in first SCRAM-SHA-256 server message, got %d: %q", len(fields), in)
|
||||||
|
}
|
||||||
|
if !bytes.HasPrefix(fields[0], []byte("r=")) || len(fields[0]) < 2 {
|
||||||
|
return fmt.Errorf("server sent an invalid SCRAM-SHA-256 nonce: %q", fields[0])
|
||||||
|
}
|
||||||
|
if !bytes.HasPrefix(fields[1], []byte("s=")) || len(fields[1]) < 6 {
|
||||||
|
return fmt.Errorf("server sent an invalid SCRAM-SHA-256 salt: %q", fields[1])
|
||||||
|
}
|
||||||
|
if !bytes.HasPrefix(fields[2], []byte("i=")) || len(fields[2]) < 6 {
|
||||||
|
return fmt.Errorf("server sent an invalid SCRAM-SHA-256 iteration count: %q", fields[2])
|
||||||
|
}
|
||||||
|
|
||||||
|
c.serverNonce = fields[0][2:]
|
||||||
|
if !bytes.HasPrefix(c.serverNonce, c.clientNonce) {
|
||||||
|
return fmt.Errorf("server SCRAM-SHA-256 nonce is not prefixed by client nonce: got %q, want %q+\"...\"", c.serverNonce, c.clientNonce)
|
||||||
|
}
|
||||||
|
|
||||||
|
salt := make([]byte, b64.DecodedLen(len(fields[1][2:])))
|
||||||
|
n, err := b64.Decode(salt, fields[1][2:])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot decode SCRAM-SHA-256 salt sent by server: %q", fields[1])
|
||||||
|
}
|
||||||
|
salt = salt[:n]
|
||||||
|
iterCount, err := strconv.Atoi(string(fields[2][2:]))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("server sent an invalid SCRAM-SHA-256 iteration count: %q", fields[2])
|
||||||
|
}
|
||||||
|
c.saltPassword(salt, iterCount)
|
||||||
|
|
||||||
|
c.authMsg.WriteString(",c=biws,r=")
|
||||||
|
c.authMsg.Write(c.serverNonce)
|
||||||
|
|
||||||
|
c.out.WriteString("c=biws,r=")
|
||||||
|
c.out.Write(c.serverNonce)
|
||||||
|
c.out.WriteString(",p=")
|
||||||
|
c.out.Write(c.clientProof())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) step3(in []byte) error {
|
||||||
|
var isv, ise bool
|
||||||
|
var fields = bytes.Split(in, []byte(","))
|
||||||
|
if len(fields) == 1 {
|
||||||
|
isv = bytes.HasPrefix(fields[0], []byte("v="))
|
||||||
|
ise = bytes.HasPrefix(fields[0], []byte("e="))
|
||||||
|
}
|
||||||
|
if ise {
|
||||||
|
return fmt.Errorf("SCRAM-SHA-256 authentication error: %s", fields[0][2:])
|
||||||
|
} else if !isv {
|
||||||
|
return fmt.Errorf("unsupported SCRAM-SHA-256 final message from server: %q", in)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(c.serverSignature(), fields[0][2:]) {
|
||||||
|
return fmt.Errorf("cannot authenticate SCRAM-SHA-256 server signature: %q", fields[0][2:])
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) saltPassword(salt []byte, iterCount int) {
|
||||||
|
mac := hmac.New(c.newHash, []byte(c.pass))
|
||||||
|
mac.Write(salt)
|
||||||
|
mac.Write([]byte{0, 0, 0, 1})
|
||||||
|
ui := mac.Sum(nil)
|
||||||
|
hi := make([]byte, len(ui))
|
||||||
|
copy(hi, ui)
|
||||||
|
for i := 1; i < iterCount; i++ {
|
||||||
|
mac.Reset()
|
||||||
|
mac.Write(ui)
|
||||||
|
mac.Sum(ui[:0])
|
||||||
|
for j, b := range ui {
|
||||||
|
hi[j] ^= b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.saltedPass = hi
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) clientProof() []byte {
|
||||||
|
mac := hmac.New(c.newHash, c.saltedPass)
|
||||||
|
mac.Write([]byte("Client Key"))
|
||||||
|
clientKey := mac.Sum(nil)
|
||||||
|
hash := c.newHash()
|
||||||
|
hash.Write(clientKey)
|
||||||
|
storedKey := hash.Sum(nil)
|
||||||
|
mac = hmac.New(c.newHash, storedKey)
|
||||||
|
mac.Write(c.authMsg.Bytes())
|
||||||
|
clientProof := mac.Sum(nil)
|
||||||
|
for i, b := range clientKey {
|
||||||
|
clientProof[i] ^= b
|
||||||
|
}
|
||||||
|
clientProof64 := make([]byte, b64.EncodedLen(len(clientProof)))
|
||||||
|
b64.Encode(clientProof64, clientProof)
|
||||||
|
return clientProof64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) serverSignature() []byte {
|
||||||
|
mac := hmac.New(c.newHash, c.saltedPass)
|
||||||
|
mac.Write([]byte("Server Key"))
|
||||||
|
serverKey := mac.Sum(nil)
|
||||||
|
|
||||||
|
mac = hmac.New(c.newHash, serverKey)
|
||||||
|
mac.Write(c.authMsg.Bytes())
|
||||||
|
serverSignature := mac.Sum(nil)
|
||||||
|
|
||||||
|
encoded := make([]byte, b64.EncodedLen(len(serverSignature)))
|
||||||
|
b64.Encode(encoded, serverSignature)
|
||||||
|
return encoded
|
||||||
|
}
|
|
@ -58,7 +58,13 @@ func ssl(o values) (func(net.Conn) (net.Conn, error), error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
sslRenegotiation(&tlsConf)
|
|
||||||
|
// Accept renegotiation requests initiated by the backend.
|
||||||
|
//
|
||||||
|
// Renegotiation was deprecated then removed from PostgreSQL 9.5, but
|
||||||
|
// the default configuration of older versions has it enabled. Redshift
|
||||||
|
// also initiates renegotiations and cannot be reconfigured.
|
||||||
|
tlsConf.Renegotiation = tls.RenegotiateFreelyAsClient
|
||||||
|
|
||||||
return func(conn net.Conn) (net.Conn, error) {
|
return func(conn net.Conn) (net.Conn, error) {
|
||||||
client := tls.Client(conn, &tlsConf)
|
client := tls.Client(conn, &tlsConf)
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
// +build go1.7
|
|
||||||
|
|
||||||
package pq
|
|
||||||
|
|
||||||
import "crypto/tls"
|
|
||||||
|
|
||||||
// Accept renegotiation requests initiated by the backend.
|
|
||||||
//
|
|
||||||
// Renegotiation was deprecated then removed from PostgreSQL 9.5, but
|
|
||||||
// the default configuration of older versions has it enabled. Redshift
|
|
||||||
// also initiates renegotiations and cannot be reconfigured.
|
|
||||||
func sslRenegotiation(conf *tls.Config) {
|
|
||||||
conf.Renegotiation = tls.RenegotiateFreelyAsClient
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
// +build !go1.7
|
|
||||||
|
|
||||||
package pq
|
|
||||||
|
|
||||||
import "crypto/tls"
|
|
||||||
|
|
||||||
// Renegotiation is not supported by crypto/tls until Go 1.7.
|
|
||||||
func sslRenegotiation(*tls.Config) {}
|
|
|
@ -29,6 +29,15 @@ const (
|
||||||
backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity)
|
backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
genericRead = 0x80000000
|
||||||
|
genericWrite = 0x40000000
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
consoleTextmodeBuffer = 0x1
|
||||||
|
)
|
||||||
|
|
||||||
type wchar uint16
|
type wchar uint16
|
||||||
type short int16
|
type short int16
|
||||||
type dword uint32
|
type dword uint32
|
||||||
|
@ -69,14 +78,17 @@ var (
|
||||||
procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo")
|
procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo")
|
||||||
procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo")
|
procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo")
|
||||||
procSetConsoleTitle = kernel32.NewProc("SetConsoleTitleW")
|
procSetConsoleTitle = kernel32.NewProc("SetConsoleTitleW")
|
||||||
|
procCreateConsoleScreenBuffer = kernel32.NewProc("CreateConsoleScreenBuffer")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Writer provide colorable Writer to the console
|
// Writer provide colorable Writer to the console
|
||||||
type Writer struct {
|
type Writer struct {
|
||||||
out io.Writer
|
out io.Writer
|
||||||
handle syscall.Handle
|
handle syscall.Handle
|
||||||
|
althandle syscall.Handle
|
||||||
oldattr word
|
oldattr word
|
||||||
oldpos coord
|
oldpos coord
|
||||||
|
rest bytes.Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewColorable return new instance of Writer which handle escape sequence from File.
|
// NewColorable return new instance of Writer which handle escape sequence from File.
|
||||||
|
@ -407,7 +419,18 @@ func (w *Writer) Write(data []byte) (n int, err error) {
|
||||||
var csbi consoleScreenBufferInfo
|
var csbi consoleScreenBufferInfo
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
|
|
||||||
er := bytes.NewReader(data)
|
handle := w.handle
|
||||||
|
|
||||||
|
var er *bytes.Reader
|
||||||
|
if w.rest.Len() > 0 {
|
||||||
|
var rest bytes.Buffer
|
||||||
|
w.rest.WriteTo(&rest)
|
||||||
|
w.rest.Reset()
|
||||||
|
rest.Write(data)
|
||||||
|
er = bytes.NewReader(rest.Bytes())
|
||||||
|
} else {
|
||||||
|
er = bytes.NewReader(data)
|
||||||
|
}
|
||||||
var bw [1]byte
|
var bw [1]byte
|
||||||
loop:
|
loop:
|
||||||
for {
|
for {
|
||||||
|
@ -425,29 +448,55 @@ loop:
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
|
|
||||||
if c2 == ']' {
|
switch c2 {
|
||||||
if err := doTitleSequence(er); err != nil {
|
case '>':
|
||||||
|
continue
|
||||||
|
case ']':
|
||||||
|
w.rest.WriteByte(c1)
|
||||||
|
w.rest.WriteByte(c2)
|
||||||
|
er.WriteTo(&w.rest)
|
||||||
|
if bytes.IndexByte(w.rest.Bytes(), 0x07) == -1 {
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
continue
|
er = bytes.NewReader(w.rest.Bytes()[2:])
|
||||||
}
|
err := doTitleSequence(er)
|
||||||
if c2 != 0x5b {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
var m byte
|
|
||||||
for {
|
|
||||||
c, err := er.ReadByte()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
|
w.rest.Reset()
|
||||||
|
continue
|
||||||
|
// https://github.com/mattn/go-colorable/issues/27
|
||||||
|
case '7':
|
||||||
|
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
|
w.oldpos = csbi.cursorPosition
|
||||||
|
continue
|
||||||
|
case '8':
|
||||||
|
procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos)))
|
||||||
|
continue
|
||||||
|
case 0x5b:
|
||||||
|
// execute part after switch
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
w.rest.WriteByte(c1)
|
||||||
|
w.rest.WriteByte(c2)
|
||||||
|
er.WriteTo(&w.rest)
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
var m byte
|
||||||
|
for i, c := range w.rest.Bytes()[2:] {
|
||||||
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
|
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
|
||||||
m = c
|
m = c
|
||||||
|
er = bytes.NewReader(w.rest.Bytes()[2+i+1:])
|
||||||
|
w.rest.Reset()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
buf.Write([]byte(string(c)))
|
buf.Write([]byte(string(c)))
|
||||||
}
|
}
|
||||||
|
if m == 0 {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
|
||||||
switch m {
|
switch m {
|
||||||
case 'A':
|
case 'A':
|
||||||
|
@ -455,61 +504,64 @@ loop:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
csbi.cursorPosition.y -= short(n)
|
csbi.cursorPosition.y -= short(n)
|
||||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||||
case 'B':
|
case 'B':
|
||||||
n, err = strconv.Atoi(buf.String())
|
n, err = strconv.Atoi(buf.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
csbi.cursorPosition.y += short(n)
|
csbi.cursorPosition.y += short(n)
|
||||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||||
case 'C':
|
case 'C':
|
||||||
n, err = strconv.Atoi(buf.String())
|
n, err = strconv.Atoi(buf.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
csbi.cursorPosition.x += short(n)
|
csbi.cursorPosition.x += short(n)
|
||||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||||
case 'D':
|
case 'D':
|
||||||
n, err = strconv.Atoi(buf.String())
|
n, err = strconv.Atoi(buf.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
csbi.cursorPosition.x -= short(n)
|
csbi.cursorPosition.x -= short(n)
|
||||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
if csbi.cursorPosition.x < 0 {
|
||||||
|
csbi.cursorPosition.x = 0
|
||||||
|
}
|
||||||
|
procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||||
case 'E':
|
case 'E':
|
||||||
n, err = strconv.Atoi(buf.String())
|
n, err = strconv.Atoi(buf.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
csbi.cursorPosition.x = 0
|
csbi.cursorPosition.x = 0
|
||||||
csbi.cursorPosition.y += short(n)
|
csbi.cursorPosition.y += short(n)
|
||||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||||
case 'F':
|
case 'F':
|
||||||
n, err = strconv.Atoi(buf.String())
|
n, err = strconv.Atoi(buf.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
csbi.cursorPosition.x = 0
|
csbi.cursorPosition.x = 0
|
||||||
csbi.cursorPosition.y -= short(n)
|
csbi.cursorPosition.y -= short(n)
|
||||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||||
case 'G':
|
case 'G':
|
||||||
n, err = strconv.Atoi(buf.String())
|
n, err = strconv.Atoi(buf.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
csbi.cursorPosition.x = short(n - 1)
|
csbi.cursorPosition.x = short(n - 1)
|
||||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||||
case 'H', 'f':
|
case 'H', 'f':
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
if buf.Len() > 0 {
|
if buf.Len() > 0 {
|
||||||
token := strings.Split(buf.String(), ";")
|
token := strings.Split(buf.String(), ";")
|
||||||
switch len(token) {
|
switch len(token) {
|
||||||
|
@ -534,7 +586,7 @@ loop:
|
||||||
} else {
|
} else {
|
||||||
csbi.cursorPosition.y = 0
|
csbi.cursorPosition.y = 0
|
||||||
}
|
}
|
||||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||||
case 'J':
|
case 'J':
|
||||||
n := 0
|
n := 0
|
||||||
if buf.Len() > 0 {
|
if buf.Len() > 0 {
|
||||||
|
@ -545,20 +597,20 @@ loop:
|
||||||
}
|
}
|
||||||
var count, written dword
|
var count, written dword
|
||||||
var cursor coord
|
var cursor coord
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
switch n {
|
switch n {
|
||||||
case 0:
|
case 0:
|
||||||
cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
|
cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
|
||||||
count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x)
|
count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x)
|
||||||
case 1:
|
case 1:
|
||||||
cursor = coord{x: csbi.window.left, y: csbi.window.top}
|
cursor = coord{x: csbi.window.left, y: csbi.window.top}
|
||||||
count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.window.top-csbi.cursorPosition.y)*csbi.size.x)
|
count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.window.top-csbi.cursorPosition.y)*dword(csbi.size.x)
|
||||||
case 2:
|
case 2:
|
||||||
cursor = coord{x: csbi.window.left, y: csbi.window.top}
|
cursor = coord{x: csbi.window.left, y: csbi.window.top}
|
||||||
count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x)
|
count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x)
|
||||||
}
|
}
|
||||||
procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||||
procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||||
case 'K':
|
case 'K':
|
||||||
n := 0
|
n := 0
|
||||||
if buf.Len() > 0 {
|
if buf.Len() > 0 {
|
||||||
|
@ -567,28 +619,28 @@ loop:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
var cursor coord
|
var cursor coord
|
||||||
var count, written dword
|
var count, written dword
|
||||||
switch n {
|
switch n {
|
||||||
case 0:
|
case 0:
|
||||||
cursor = coord{x: csbi.cursorPosition.x + 1, y: csbi.cursorPosition.y}
|
cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
|
||||||
count = dword(csbi.size.x - csbi.cursorPosition.x - 1)
|
count = dword(csbi.size.x - csbi.cursorPosition.x)
|
||||||
case 1:
|
case 1:
|
||||||
cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y}
|
cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y}
|
||||||
count = dword(csbi.size.x - csbi.cursorPosition.x)
|
count = dword(csbi.size.x - csbi.cursorPosition.x)
|
||||||
case 2:
|
case 2:
|
||||||
cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y}
|
cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y}
|
||||||
count = dword(csbi.size.x)
|
count = dword(csbi.size.x)
|
||||||
}
|
}
|
||||||
procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||||
procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||||
case 'm':
|
case 'm':
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
attr := csbi.attributes
|
attr := csbi.attributes
|
||||||
cs := buf.String()
|
cs := buf.String()
|
||||||
if cs == "" {
|
if cs == "" {
|
||||||
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(w.oldattr))
|
procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(w.oldattr))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
token := strings.Split(cs, ";")
|
token := strings.Split(cs, ";")
|
||||||
|
@ -627,6 +679,21 @@ loop:
|
||||||
attr |= n256foreAttr[n256]
|
attr |= n256foreAttr[n256]
|
||||||
i += 2
|
i += 2
|
||||||
}
|
}
|
||||||
|
} else if len(token) == 5 && token[i+1] == "2" {
|
||||||
|
var r, g, b int
|
||||||
|
r, _ = strconv.Atoi(token[i+2])
|
||||||
|
g, _ = strconv.Atoi(token[i+3])
|
||||||
|
b, _ = strconv.Atoi(token[i+4])
|
||||||
|
i += 4
|
||||||
|
if r > 127 {
|
||||||
|
attr |= foregroundRed
|
||||||
|
}
|
||||||
|
if g > 127 {
|
||||||
|
attr |= foregroundGreen
|
||||||
|
}
|
||||||
|
if b > 127 {
|
||||||
|
attr |= foregroundBlue
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
attr = attr & (w.oldattr & backgroundMask)
|
attr = attr & (w.oldattr & backgroundMask)
|
||||||
}
|
}
|
||||||
|
@ -654,6 +721,21 @@ loop:
|
||||||
attr |= n256backAttr[n256]
|
attr |= n256backAttr[n256]
|
||||||
i += 2
|
i += 2
|
||||||
}
|
}
|
||||||
|
} else if len(token) == 5 && token[i+1] == "2" {
|
||||||
|
var r, g, b int
|
||||||
|
r, _ = strconv.Atoi(token[i+2])
|
||||||
|
g, _ = strconv.Atoi(token[i+3])
|
||||||
|
b, _ = strconv.Atoi(token[i+4])
|
||||||
|
i += 4
|
||||||
|
if r > 127 {
|
||||||
|
attr |= backgroundRed
|
||||||
|
}
|
||||||
|
if g > 127 {
|
||||||
|
attr |= backgroundGreen
|
||||||
|
}
|
||||||
|
if b > 127 {
|
||||||
|
attr |= backgroundBlue
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
attr = attr & (w.oldattr & foregroundMask)
|
attr = attr & (w.oldattr & foregroundMask)
|
||||||
}
|
}
|
||||||
|
@ -685,38 +767,52 @@ loop:
|
||||||
attr |= backgroundBlue
|
attr |= backgroundBlue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr))
|
procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(attr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 'h':
|
case 'h':
|
||||||
var ci consoleCursorInfo
|
var ci consoleCursorInfo
|
||||||
cs := buf.String()
|
cs := buf.String()
|
||||||
if cs == "5>" {
|
if cs == "5>" {
|
||||||
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
|
||||||
ci.visible = 0
|
ci.visible = 0
|
||||||
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
|
||||||
} else if cs == "?25" {
|
} else if cs == "?25" {
|
||||||
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
|
||||||
ci.visible = 1
|
ci.visible = 1
|
||||||
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
|
||||||
|
} else if cs == "?1049" {
|
||||||
|
if w.althandle == 0 {
|
||||||
|
h, _, _ := procCreateConsoleScreenBuffer.Call(uintptr(genericRead|genericWrite), 0, 0, uintptr(consoleTextmodeBuffer), 0, 0)
|
||||||
|
w.althandle = syscall.Handle(h)
|
||||||
|
if w.althandle != 0 {
|
||||||
|
handle = w.althandle
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case 'l':
|
case 'l':
|
||||||
var ci consoleCursorInfo
|
var ci consoleCursorInfo
|
||||||
cs := buf.String()
|
cs := buf.String()
|
||||||
if cs == "5>" {
|
if cs == "5>" {
|
||||||
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
|
||||||
ci.visible = 1
|
ci.visible = 1
|
||||||
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
|
||||||
} else if cs == "?25" {
|
} else if cs == "?25" {
|
||||||
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
|
||||||
ci.visible = 0
|
ci.visible = 0
|
||||||
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
|
||||||
|
} else if cs == "?1049" {
|
||||||
|
if w.althandle != 0 {
|
||||||
|
syscall.CloseHandle(w.althandle)
|
||||||
|
w.althandle = 0
|
||||||
|
handle = w.handle
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case 's':
|
case 's':
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
w.oldpos = csbi.cursorPosition
|
w.oldpos = csbi.cursorPosition
|
||||||
case 'u':
|
case 'u':
|
||||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&w.oldpos)))
|
procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
module github.com/mattn/go-colorable
|
||||||
|
|
||||||
|
require github.com/mattn/go-isatty v0.0.5
|
|
@ -0,0 +1,4 @@
|
||||||
|
github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw=
|
||||||
|
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
|
||||||
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
@ -2,6 +2,10 @@ language: go
|
||||||
go:
|
go:
|
||||||
- tip
|
- tip
|
||||||
|
|
||||||
|
os:
|
||||||
|
- linux
|
||||||
|
- osx
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- go get github.com/mattn/goveralls
|
- go get github.com/mattn/goveralls
|
||||||
- go get golang.org/x/tools/cmd/cover
|
- go get golang.org/x/tools/cmd/cover
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
module github.com/mattn/go-isatty
|
||||||
|
|
||||||
|
require golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223
|
|
@ -0,0 +1,2 @@
|
||||||
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
|
||||||
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
@ -1,12 +1,10 @@
|
||||||
// +build linux
|
// +build android
|
||||||
// +build ppc64 ppc64le
|
|
||||||
|
|
||||||
package isatty
|
package isatty
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
syscall "golang.org/x/sys/unix"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const ioctlReadTermios = syscall.TCGETS
|
const ioctlReadTermios = syscall.TCGETS
|
||||||
|
@ -17,3 +15,9 @@ func IsTerminal(fd uintptr) bool {
|
||||||
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||||
return err == 0
|
return err == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
||||||
|
// terminal. This is also always false on this environment.
|
||||||
|
func IsCygwinTerminal(fd uintptr) bool {
|
||||||
|
return false
|
||||||
|
}
|
|
@ -1,15 +0,0 @@
|
||||||
// +build appengine
|
|
||||||
|
|
||||||
package isatty
|
|
||||||
|
|
||||||
// IsTerminal returns true if the file descriptor is terminal which
|
|
||||||
// is always false on on appengine classic which is a sandboxed PaaS.
|
|
||||||
func IsTerminal(fd uintptr) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
|
|
||||||
// terminal. This is also always false on this environment.
|
|
||||||
func IsCygwinTerminal(fd uintptr) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
|
@ -16,3 +16,9 @@ func IsTerminal(fd uintptr) bool {
|
||||||
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||||
return err == 0
|
return err == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
||||||
|
// terminal. This is also always false on this environment.
|
||||||
|
func IsCygwinTerminal(fd uintptr) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
// +build linux
|
// +build linux
|
||||||
// +build !appengine,!ppc64,!ppc64le
|
// +build !appengine
|
||||||
|
// +build !android
|
||||||
|
|
||||||
package isatty
|
package isatty
|
||||||
|
|
||||||
import (
|
import "golang.org/x/sys/unix"
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
const ioctlReadTermios = syscall.TCGETS
|
|
||||||
|
|
||||||
// IsTerminal return true if the file descriptor is terminal.
|
// IsTerminal return true if the file descriptor is terminal.
|
||||||
func IsTerminal(fd uintptr) bool {
|
func IsTerminal(fd uintptr) bool {
|
||||||
var termios syscall.Termios
|
_, err := unix.IoctlGetTermios(int(fd), unix.TCGETS)
|
||||||
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
return err == nil
|
||||||
return err == 0
|
}
|
||||||
|
|
||||||
|
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
||||||
|
// terminal. This is also always false on this environment.
|
||||||
|
func IsCygwinTerminal(fd uintptr) bool {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
// +build !windows
|
// +build appengine js
|
||||||
// +build !appengine
|
|
||||||
|
|
||||||
package isatty
|
package isatty
|
||||||
|
|
||||||
|
// IsTerminal returns true if the file descriptor is terminal which
|
||||||
|
// is always false on js and appengine classic which is a sandboxed PaaS.
|
||||||
|
func IsTerminal(fd uintptr) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
|
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
|
||||||
// terminal. This is also always false on this environment.
|
// terminal. This is also always false on this environment.
|
||||||
func IsCygwinTerminal(fd uintptr) bool {
|
func IsCygwinTerminal(fd uintptr) bool {
|
||||||
|
|
|
@ -14,3 +14,9 @@ func IsTerminal(fd uintptr) bool {
|
||||||
err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio)
|
err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio)
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
||||||
|
// terminal. This is also always false on this environment.
|
||||||
|
func IsCygwinTerminal(fd uintptr) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
coverage:
|
||||||
|
status:
|
||||||
|
project:
|
||||||
|
default:
|
||||||
|
target: 40%
|
||||||
|
threshold: null
|
||||||
|
patch: false
|
||||||
|
changes: false
|
|
@ -1,12 +1,11 @@
|
||||||
language: go
|
language: go
|
||||||
sudo: false
|
sudo: false
|
||||||
go:
|
|
||||||
- 1.9.x
|
|
||||||
- tip
|
|
||||||
|
|
||||||
env:
|
go:
|
||||||
- TESTS="-race -v -bench=. -coverprofile=coverage.txt -covermode=atomic"
|
- 1.10.x
|
||||||
- TESTS="-race -v ./..."
|
- 1.11.x
|
||||||
|
- 1.12.x
|
||||||
|
- tip
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
# don't use the miekg/dns when testing forks
|
# don't use the miekg/dns when testing forks
|
||||||
|
@ -14,7 +13,7 @@ before_install:
|
||||||
- ln -s $TRAVIS_BUILD_DIR $GOPATH/src/github.com/miekg/ || true
|
- ln -s $TRAVIS_BUILD_DIR $GOPATH/src/github.com/miekg/ || true
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- go test $TESTS
|
- go test -race -v -bench=. -coverprofile=coverage.txt -covermode=atomic ./...
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
- bash <(curl -s https://codecov.io/bash)
|
- bash <(curl -s https://codecov.io/bash)
|
||||||
|
|
|
@ -3,13 +3,55 @@
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
digest = "1:6914c49eed986dfb8dffb33516fa129c49929d4d873f41e073c83c11c372b870"
|
||||||
name = "golang.org/x/crypto"
|
name = "golang.org/x/crypto"
|
||||||
packages = ["ed25519","ed25519/internal/edwards25519"]
|
packages = [
|
||||||
revision = "b080dc9a8c480b08e698fb1219160d598526310f"
|
"ed25519",
|
||||||
|
"ed25519/internal/edwards25519",
|
||||||
|
]
|
||||||
|
pruneopts = ""
|
||||||
|
revision = "e3636079e1a4c1f337f212cc5cd2aca108f6c900"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:08e41d63f8dac84d83797368b56cf0b339e42d0224e5e56668963c28aec95685"
|
||||||
|
name = "golang.org/x/net"
|
||||||
|
packages = [
|
||||||
|
"bpf",
|
||||||
|
"context",
|
||||||
|
"internal/iana",
|
||||||
|
"internal/socket",
|
||||||
|
"ipv4",
|
||||||
|
"ipv6",
|
||||||
|
]
|
||||||
|
pruneopts = ""
|
||||||
|
revision = "4dfa2610cdf3b287375bbba5b8f2a14d3b01d8de"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:b2ea75de0ccb2db2ac79356407f8a4cd8f798fe15d41b381c00abf3ae8e55ed1"
|
||||||
|
name = "golang.org/x/sync"
|
||||||
|
packages = ["errgroup"]
|
||||||
|
pruneopts = ""
|
||||||
|
revision = "1d60e4601c6fd243af51cc01ddf169918a5407ca"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:149a432fabebb8221a80f77731b1cd63597197ded4f14af606ebe3a0959004ec"
|
||||||
|
name = "golang.org/x/sys"
|
||||||
|
packages = ["unix"]
|
||||||
|
pruneopts = ""
|
||||||
|
revision = "e4b3c5e9061176387e7cea65e4dc5853801f3fb7"
|
||||||
|
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "5046e265393bd5e54f570ce29ae8bc6fa3f30ef5110e922996540400f287c64a"
|
input-imports = [
|
||||||
|
"golang.org/x/crypto/ed25519",
|
||||||
|
"golang.org/x/net/ipv4",
|
||||||
|
"golang.org/x/net/ipv6",
|
||||||
|
"golang.org/x/sync/errgroup",
|
||||||
|
"golang.org/x/sys/unix",
|
||||||
|
]
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
|
|
@ -24,3 +24,15 @@
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "golang.org/x/crypto"
|
name = "golang.org/x/crypto"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
branch = "master"
|
||||||
|
name = "golang.org/x/net"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
branch = "master"
|
||||||
|
name = "golang.org/x/sys"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
branch = "master"
|
||||||
|
name = "golang.org/x/sync"
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
# Makefile for releasing.
|
||||||
|
#
|
||||||
|
# The release is controlled from version.go. The version found there is
|
||||||
|
# used to tag the git repo, we're not building any artifects so there is nothing
|
||||||
|
# to upload to github.
|
||||||
|
#
|
||||||
|
# * Up the version in version.go
|
||||||
|
# * Run: make -f Makefile.release release
|
||||||
|
# * will *commit* your change with 'Release $VERSION'
|
||||||
|
# * push to github
|
||||||
|
#
|
||||||
|
|
||||||
|
define GO
|
||||||
|
//+build ignore
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println(dns.Version.String())
|
||||||
|
}
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(file > version_release.go,$(GO))
|
||||||
|
VERSION:=$(shell go run version_release.go)
|
||||||
|
TAG="v$(VERSION)"
|
||||||
|
|
||||||
|
all:
|
||||||
|
@echo Use the \'release\' target to start a release $(VERSION)
|
||||||
|
rm -f version_release.go
|
||||||
|
|
||||||
|
.PHONY: release
|
||||||
|
release: commit push
|
||||||
|
@echo Released $(VERSION)
|
||||||
|
rm -f version_release.go
|
||||||
|
|
||||||
|
.PHONY: commit
|
||||||
|
commit:
|
||||||
|
@echo Committing release $(VERSION)
|
||||||
|
git commit -am"Release $(VERSION)"
|
||||||
|
git tag $(TAG)
|
||||||
|
|
||||||
|
.PHONY: push
|
||||||
|
push:
|
||||||
|
@echo Pushing release $(VERSION) to master
|
||||||
|
git push --tags
|
||||||
|
git push
|
|
@ -7,10 +7,10 @@
|
||||||
|
|
||||||
> Less is more.
|
> Less is more.
|
||||||
|
|
||||||
Complete and usable DNS library. All widely used Resource Records are supported, including the
|
Complete and usable DNS library. All Resource Records are supported, including the DNSSEC types.
|
||||||
DNSSEC types. It follows a lean and mean philosophy. If there is stuff you should know as a DNS
|
It follows a lean and mean philosophy. If there is stuff you should know as a DNS programmer there
|
||||||
programmer there isn't a convenience function for it. Server side and client side programming is
|
isn't a convenience function for it. Server side and client side programming is supported, i.e. you
|
||||||
supported, i.e. you can build servers and resolvers with it.
|
can build servers and resolvers with it.
|
||||||
|
|
||||||
We try to keep the "master" branch as sane as possible and at the bleeding edge of standards,
|
We try to keep the "master" branch as sane as possible and at the bleeding edge of standards,
|
||||||
avoiding breaking changes wherever reasonable. We support the last two versions of Go.
|
avoiding breaking changes wherever reasonable. We support the last two versions of Go.
|
||||||
|
@ -42,10 +42,9 @@ A not-so-up-to-date-list-that-may-be-actually-current:
|
||||||
* https://github.com/tianon/rawdns
|
* https://github.com/tianon/rawdns
|
||||||
* https://mesosphere.github.io/mesos-dns/
|
* https://mesosphere.github.io/mesos-dns/
|
||||||
* https://pulse.turbobytes.com/
|
* https://pulse.turbobytes.com/
|
||||||
* https://play.google.com/store/apps/details?id=com.turbobytes.dig
|
|
||||||
* https://github.com/fcambus/statzone
|
* https://github.com/fcambus/statzone
|
||||||
* https://github.com/benschw/dns-clb-go
|
* https://github.com/benschw/dns-clb-go
|
||||||
* https://github.com/corny/dnscheck for http://public-dns.info/
|
* https://github.com/corny/dnscheck for <http://public-dns.info/>
|
||||||
* https://namesmith.io
|
* https://namesmith.io
|
||||||
* https://github.com/miekg/unbound
|
* https://github.com/miekg/unbound
|
||||||
* https://github.com/miekg/exdns
|
* https://github.com/miekg/exdns
|
||||||
|
@ -56,35 +55,41 @@ A not-so-up-to-date-list-that-may-be-actually-current:
|
||||||
* https://github.com/bamarni/dockness
|
* https://github.com/bamarni/dockness
|
||||||
* https://github.com/fffaraz/microdns
|
* https://github.com/fffaraz/microdns
|
||||||
* http://kelda.io
|
* http://kelda.io
|
||||||
* https://github.com/ipdcode/hades (JD.COM)
|
* https://github.com/ipdcode/hades <https://jd.com>
|
||||||
* https://github.com/StackExchange/dnscontrol/
|
* https://github.com/StackExchange/dnscontrol/
|
||||||
* https://www.dnsperf.com/
|
* https://www.dnsperf.com/
|
||||||
* https://dnssectest.net/
|
* https://dnssectest.net/
|
||||||
* https://dns.apebits.com
|
* https://dns.apebits.com
|
||||||
* https://github.com/oif/apex
|
* https://github.com/oif/apex
|
||||||
|
* https://github.com/jedisct1/dnscrypt-proxy
|
||||||
|
* https://github.com/jedisct1/rpdns
|
||||||
|
* https://github.com/xor-gate/sshfp
|
||||||
|
* https://github.com/rs/dnstrace
|
||||||
|
* https://blitiri.com.ar/p/dnss ([github mirror](https://github.com/albertito/dnss))
|
||||||
|
* https://github.com/semihalev/sdns
|
||||||
|
* https://render.com
|
||||||
|
* https://github.com/peterzen/goresolver
|
||||||
|
|
||||||
Send pull request if you want to be listed here.
|
Send pull request if you want to be listed here.
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
|
|
||||||
* UDP/TCP queries, IPv4 and IPv6;
|
* UDP/TCP queries, IPv4 and IPv6
|
||||||
* RFC 1035 zone file parsing ($INCLUDE, $ORIGIN, $TTL and $GENERATE (for all record types) are supported;
|
* RFC 1035 zone file parsing ($INCLUDE, $ORIGIN, $TTL and $GENERATE (for all record types) are supported
|
||||||
* Fast:
|
* Fast
|
||||||
* Reply speed around ~ 80K qps (faster hardware results in more qps);
|
* Server side programming (mimicking the net/http package)
|
||||||
* Parsing RRs ~ 100K RR/s, that's 5M records in about 50 seconds;
|
* Client side programming
|
||||||
* Server side programming (mimicking the net/http package);
|
* DNSSEC: signing, validating and key generation for DSA, RSA, ECDSA and Ed25519
|
||||||
* Client side programming;
|
* EDNS0, NSID, Cookies
|
||||||
* DNSSEC: signing, validating and key generation for DSA, RSA, ECDSA and Ed25519;
|
* AXFR/IXFR
|
||||||
* EDNS0, NSID, Cookies;
|
* TSIG, SIG(0)
|
||||||
* AXFR/IXFR;
|
* DNS over TLS (DoT): encrypted connection between client and server over TCP
|
||||||
* TSIG, SIG(0);
|
* DNS name compression
|
||||||
* DNS over TLS: optional encrypted connection between client and server;
|
|
||||||
* DNS name compression;
|
|
||||||
* Depends only on the standard library.
|
|
||||||
|
|
||||||
Have fun!
|
Have fun!
|
||||||
|
|
||||||
Miek Gieben - 2010-2012 - <miek@miek.nl>
|
Miek Gieben - 2010-2012 - <miek@miek.nl>
|
||||||
|
DNS Authors 2012-
|
||||||
|
|
||||||
# Building
|
# Building
|
||||||
|
|
||||||
|
@ -96,8 +101,8 @@ work:
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
A short "how to use the API" is at the beginning of doc.go (this also will show
|
A short "how to use the API" is at the beginning of doc.go (this also will show when you call `godoc
|
||||||
when you call `godoc github.com/miekg/dns`).
|
github.com/miekg/dns`).
|
||||||
|
|
||||||
Example programs can be found in the `github.com/miekg/exdns` repository.
|
Example programs can be found in the `github.com/miekg/exdns` repository.
|
||||||
|
|
||||||
|
@ -155,12 +160,13 @@ Example programs can be found in the `github.com/miekg/exdns` repository.
|
||||||
* 7553 - URI record
|
* 7553 - URI record
|
||||||
* 7858 - DNS over TLS: Initiation and Performance Considerations
|
* 7858 - DNS over TLS: Initiation and Performance Considerations
|
||||||
* 7871 - EDNS0 Client Subnet
|
* 7871 - EDNS0 Client Subnet
|
||||||
* 7873 - Domain Name System (DNS) Cookies (draft-ietf-dnsop-cookies)
|
* 7873 - Domain Name System (DNS) Cookies
|
||||||
* 8080 - EdDSA for DNSSEC
|
* 8080 - EdDSA for DNSSEC
|
||||||
|
* 8499 - DNS Terminology
|
||||||
|
|
||||||
## Loosely based upon
|
## Loosely Based Upon
|
||||||
|
|
||||||
* `ldns`
|
* ldns - <https://nlnetlabs.nl/projects/ldns/about/>
|
||||||
* `NSD`
|
* NSD - <https://nlnetlabs.nl/projects/nsd/about/>
|
||||||
* `Net::DNS`
|
* Net::DNS - <http://www.net-dns.org/>
|
||||||
* `GRONG`
|
* GRONG - <https://github.com/bortzmeyer/grong>
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
package dns
|
||||||
|
|
||||||
|
// MsgAcceptFunc is used early in the server code to accept or reject a message with RcodeFormatError.
|
||||||
|
// It returns a MsgAcceptAction to indicate what should happen with the message.
|
||||||
|
type MsgAcceptFunc func(dh Header) MsgAcceptAction
|
||||||
|
|
||||||
|
// DefaultMsgAcceptFunc checks the request and will reject if:
|
||||||
|
//
|
||||||
|
// * isn't a request (don't respond in that case).
|
||||||
|
// * opcode isn't OpcodeQuery or OpcodeNotify
|
||||||
|
// * Zero bit isn't zero
|
||||||
|
// * has more than 1 question in the question section
|
||||||
|
// * has more than 1 RR in the Answer section
|
||||||
|
// * has more than 0 RRs in the Authority section
|
||||||
|
// * has more than 2 RRs in the Additional section
|
||||||
|
var DefaultMsgAcceptFunc MsgAcceptFunc = defaultMsgAcceptFunc
|
||||||
|
|
||||||
|
// MsgAcceptAction represents the action to be taken.
|
||||||
|
type MsgAcceptAction int
|
||||||
|
|
||||||
|
const (
|
||||||
|
MsgAccept MsgAcceptAction = iota // Accept the message
|
||||||
|
MsgReject // Reject the message with a RcodeFormatError
|
||||||
|
MsgIgnore // Ignore the error and send nothing back.
|
||||||
|
)
|
||||||
|
|
||||||
|
func defaultMsgAcceptFunc(dh Header) MsgAcceptAction {
|
||||||
|
if isResponse := dh.Bits&_QR != 0; isResponse {
|
||||||
|
return MsgIgnore
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't allow dynamic updates, because then the sections can contain a whole bunch of RRs.
|
||||||
|
opcode := int(dh.Bits>>11) & 0xF
|
||||||
|
if opcode != OpcodeQuery && opcode != OpcodeNotify {
|
||||||
|
return MsgReject
|
||||||
|
}
|
||||||
|
|
||||||
|
if isZero := dh.Bits&_Z != 0; isZero {
|
||||||
|
return MsgReject
|
||||||
|
}
|
||||||
|
if dh.Qdcount != 1 {
|
||||||
|
return MsgReject
|
||||||
|
}
|
||||||
|
// NOTIFY requests can have a SOA in the ANSWER section. See RFC 1996 Section 3.7 and 3.11.
|
||||||
|
if dh.Ancount > 1 {
|
||||||
|
return MsgReject
|
||||||
|
}
|
||||||
|
// IXFR request could have one SOA RR in the NS section. See RFC 1995, section 3.
|
||||||
|
if dh.Nscount > 1 {
|
||||||
|
return MsgReject
|
||||||
|
}
|
||||||
|
if dh.Arcount > 2 {
|
||||||
|
return MsgReject
|
||||||
|
}
|
||||||
|
return MsgAccept
|
||||||
|
}
|
|
@ -3,7 +3,6 @@ package dns
|
||||||
// A client implementation.
|
// A client implementation.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
@ -13,16 +12,16 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const dnsTimeout time.Duration = 2 * time.Second
|
const (
|
||||||
const tcpIdleTimeout time.Duration = 8 * time.Second
|
dnsTimeout time.Duration = 2 * time.Second
|
||||||
|
tcpIdleTimeout time.Duration = 8 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
// A Conn represents a connection to a DNS server.
|
// A Conn represents a connection to a DNS server.
|
||||||
type Conn struct {
|
type Conn struct {
|
||||||
net.Conn // a net.Conn holding the connection
|
net.Conn // a net.Conn holding the connection
|
||||||
UDPSize uint16 // minimum receive buffer for UDP messages
|
UDPSize uint16 // minimum receive buffer for UDP messages
|
||||||
TsigSecret map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)
|
TsigSecret map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)
|
||||||
rtt time.Duration
|
|
||||||
t time.Time
|
|
||||||
tsigRequestMAC string
|
tsigRequestMAC string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,33 +82,22 @@ func (c *Client) Dial(address string) (conn *Conn, err error) {
|
||||||
// create a new dialer with the appropriate timeout
|
// create a new dialer with the appropriate timeout
|
||||||
var d net.Dialer
|
var d net.Dialer
|
||||||
if c.Dialer == nil {
|
if c.Dialer == nil {
|
||||||
d = net.Dialer{}
|
d = net.Dialer{Timeout: c.getTimeoutForRequest(c.dialTimeout())}
|
||||||
} else {
|
} else {
|
||||||
d = net.Dialer(*c.Dialer)
|
d = *c.Dialer
|
||||||
}
|
}
|
||||||
d.Timeout = c.getTimeoutForRequest(c.writeTimeout())
|
|
||||||
|
|
||||||
network := "udp"
|
network := c.Net
|
||||||
useTLS := false
|
if network == "" {
|
||||||
|
network = "udp"
|
||||||
|
}
|
||||||
|
|
||||||
switch c.Net {
|
useTLS := strings.HasPrefix(network, "tcp") && strings.HasSuffix(network, "-tls")
|
||||||
case "tcp-tls":
|
|
||||||
network = "tcp"
|
|
||||||
useTLS = true
|
|
||||||
case "tcp4-tls":
|
|
||||||
network = "tcp4"
|
|
||||||
useTLS = true
|
|
||||||
case "tcp6-tls":
|
|
||||||
network = "tcp6"
|
|
||||||
useTLS = true
|
|
||||||
default:
|
|
||||||
if c.Net != "" {
|
|
||||||
network = c.Net
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
conn = new(Conn)
|
conn = new(Conn)
|
||||||
if useTLS {
|
if useTLS {
|
||||||
|
network = strings.TrimSuffix(network, "-tls")
|
||||||
|
|
||||||
conn.Conn, err = tls.DialWithDialer(&d, network, address, c.TLSConfig)
|
conn.Conn, err = tls.DialWithDialer(&d, network, address, c.TLSConfig)
|
||||||
} else {
|
} else {
|
||||||
conn.Conn, err = d.Dial(network, address)
|
conn.Conn, err = d.Dial(network, address)
|
||||||
|
@ -117,6 +105,7 @@ func (c *Client) Dial(address string) (conn *Conn, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,8 +166,9 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro
|
||||||
}
|
}
|
||||||
|
|
||||||
co.TsigSecret = c.TsigSecret
|
co.TsigSecret = c.TsigSecret
|
||||||
|
t := time.Now()
|
||||||
// write with the appropriate write timeout
|
// write with the appropriate write timeout
|
||||||
co.SetWriteDeadline(time.Now().Add(c.getTimeoutForRequest(c.writeTimeout())))
|
co.SetWriteDeadline(t.Add(c.getTimeoutForRequest(c.writeTimeout())))
|
||||||
if err = co.WriteMsg(m); err != nil {
|
if err = co.WriteMsg(m); err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
@ -188,12 +178,15 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro
|
||||||
if err == nil && r.Id != m.Id {
|
if err == nil && r.Id != m.Id {
|
||||||
err = ErrId
|
err = ErrId
|
||||||
}
|
}
|
||||||
return r, co.rtt, err
|
rtt = time.Since(t)
|
||||||
|
return r, rtt, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadMsg reads a message from the connection co.
|
// ReadMsg reads a message from the connection co.
|
||||||
// If the received message contains a TSIG record the transaction
|
// If the received message contains a TSIG record the transaction signature
|
||||||
// signature is verified.
|
// is verified. This method always tries to return the message, however if an
|
||||||
|
// error is returned there are no guarantees that the returned message is a
|
||||||
|
// valid representation of the packet read.
|
||||||
func (co *Conn) ReadMsg() (*Msg, error) {
|
func (co *Conn) ReadMsg() (*Msg, error) {
|
||||||
p, err := co.ReadMsgHeader(nil)
|
p, err := co.ReadMsgHeader(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -202,14 +195,11 @@ func (co *Conn) ReadMsg() (*Msg, error) {
|
||||||
|
|
||||||
m := new(Msg)
|
m := new(Msg)
|
||||||
if err := m.Unpack(p); err != nil {
|
if err := m.Unpack(p); err != nil {
|
||||||
// If ErrTruncated was returned, we still want to allow the user to use
|
// If an error was returned, we still want to allow the user to use
|
||||||
// the message, but naively they can just check err if they don't want
|
// the message, but naively they can just check err if they don't want
|
||||||
// to use a truncated message
|
// to use an erroneous message
|
||||||
if err == ErrTruncated {
|
|
||||||
return m, err
|
return m, err
|
||||||
}
|
}
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if t := m.IsTsig(); t != nil {
|
if t := m.IsTsig(); t != nil {
|
||||||
if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
|
if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
|
||||||
return m, ErrSecret
|
return m, ErrSecret
|
||||||
|
@ -229,19 +219,15 @@ func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) {
|
||||||
n int
|
n int
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
switch co.Conn.(type) {
|
||||||
switch t := co.Conn.(type) {
|
|
||||||
case *net.TCPConn, *tls.Conn:
|
case *net.TCPConn, *tls.Conn:
|
||||||
r := t.(io.Reader)
|
var length uint16
|
||||||
|
if err := binary.Read(co.Conn, binary.BigEndian, &length); err != nil {
|
||||||
// First two bytes specify the length of the entire message.
|
|
||||||
l, err := tcpMsgLen(r)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
p = make([]byte, l)
|
|
||||||
n, err = tcpRead(r, p)
|
p = make([]byte, length)
|
||||||
co.rtt = time.Since(co.t)
|
n, err = io.ReadFull(co.Conn, p)
|
||||||
default:
|
default:
|
||||||
if co.UDPSize > MinMsgSize {
|
if co.UDPSize > MinMsgSize {
|
||||||
p = make([]byte, co.UDPSize)
|
p = make([]byte, co.UDPSize)
|
||||||
|
@ -249,7 +235,6 @@ func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) {
|
||||||
p = make([]byte, MinMsgSize)
|
p = make([]byte, MinMsgSize)
|
||||||
}
|
}
|
||||||
n, err = co.Read(p)
|
n, err = co.Read(p)
|
||||||
co.rtt = time.Since(co.t)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -269,78 +254,27 @@ func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) {
|
||||||
return p, err
|
return p, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// tcpMsgLen is a helper func to read first two bytes of stream as uint16 packet length.
|
|
||||||
func tcpMsgLen(t io.Reader) (int, error) {
|
|
||||||
p := []byte{0, 0}
|
|
||||||
n, err := t.Read(p)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// As seen with my local router/switch, returns 1 byte on the above read,
|
|
||||||
// resulting a a ShortRead. Just write it out (instead of loop) and read the
|
|
||||||
// other byte.
|
|
||||||
if n == 1 {
|
|
||||||
n1, err := t.Read(p[1:])
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
n += n1
|
|
||||||
}
|
|
||||||
|
|
||||||
if n != 2 {
|
|
||||||
return 0, ErrShortRead
|
|
||||||
}
|
|
||||||
l := binary.BigEndian.Uint16(p)
|
|
||||||
if l == 0 {
|
|
||||||
return 0, ErrShortRead
|
|
||||||
}
|
|
||||||
return int(l), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// tcpRead calls TCPConn.Read enough times to fill allocated buffer.
|
|
||||||
func tcpRead(t io.Reader, p []byte) (int, error) {
|
|
||||||
n, err := t.Read(p)
|
|
||||||
if err != nil {
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
for n < len(p) {
|
|
||||||
j, err := t.Read(p[n:])
|
|
||||||
if err != nil {
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
n += j
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read implements the net.Conn read method.
|
// Read implements the net.Conn read method.
|
||||||
func (co *Conn) Read(p []byte) (n int, err error) {
|
func (co *Conn) Read(p []byte) (n int, err error) {
|
||||||
if co.Conn == nil {
|
if co.Conn == nil {
|
||||||
return 0, ErrConnEmpty
|
return 0, ErrConnEmpty
|
||||||
}
|
}
|
||||||
if len(p) < 2 {
|
|
||||||
return 0, io.ErrShortBuffer
|
|
||||||
}
|
|
||||||
switch t := co.Conn.(type) {
|
|
||||||
case *net.TCPConn, *tls.Conn:
|
|
||||||
r := t.(io.Reader)
|
|
||||||
|
|
||||||
l, err := tcpMsgLen(r)
|
switch co.Conn.(type) {
|
||||||
if err != nil {
|
case *net.TCPConn, *tls.Conn:
|
||||||
|
var length uint16
|
||||||
|
if err := binary.Read(co.Conn, binary.BigEndian, &length); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
if l > len(p) {
|
if int(length) > len(p) {
|
||||||
return int(l), io.ErrShortBuffer
|
return 0, io.ErrShortBuffer
|
||||||
}
|
}
|
||||||
return tcpRead(r, p[:l])
|
|
||||||
|
return io.ReadFull(co.Conn, p[:length])
|
||||||
}
|
}
|
||||||
|
|
||||||
// UDP connection
|
// UDP connection
|
||||||
n, err = co.Conn.Read(p)
|
return co.Conn.Read(p)
|
||||||
if err != nil {
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteMsg sends a message through the connection co.
|
// WriteMsg sends a message through the connection co.
|
||||||
|
@ -362,34 +296,26 @@ func (co *Conn) WriteMsg(m *Msg) (err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
co.t = time.Now()
|
_, err = co.Write(out)
|
||||||
if _, err = co.Write(out); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write implements the net.Conn Write method.
|
// Write implements the net.Conn Write method.
|
||||||
func (co *Conn) Write(p []byte) (n int, err error) {
|
func (co *Conn) Write(p []byte) (n int, err error) {
|
||||||
switch t := co.Conn.(type) {
|
switch co.Conn.(type) {
|
||||||
case *net.TCPConn, *tls.Conn:
|
case *net.TCPConn, *tls.Conn:
|
||||||
w := t.(io.Writer)
|
if len(p) > MaxMsgSize {
|
||||||
|
|
||||||
lp := len(p)
|
|
||||||
if lp < 2 {
|
|
||||||
return 0, io.ErrShortBuffer
|
|
||||||
}
|
|
||||||
if lp > MaxMsgSize {
|
|
||||||
return 0, &Error{err: "message too large"}
|
return 0, &Error{err: "message too large"}
|
||||||
}
|
}
|
||||||
l := make([]byte, 2, lp+2)
|
|
||||||
binary.BigEndian.PutUint16(l, uint16(lp))
|
l := make([]byte, 2)
|
||||||
p = append(l, p...)
|
binary.BigEndian.PutUint16(l, uint16(len(p)))
|
||||||
n, err := io.Copy(w, bytes.NewReader(p))
|
|
||||||
|
n, err := (&net.Buffers{l, p}).WriteTo(co.Conn)
|
||||||
return int(n), err
|
return int(n), err
|
||||||
}
|
}
|
||||||
n, err = co.Conn.Write(p)
|
|
||||||
return n, err
|
return co.Conn.Write(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the appropriate timeout for a specific request
|
// Return the appropriate timeout for a specific request
|
||||||
|
@ -432,7 +358,7 @@ func ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, err error)
|
||||||
|
|
||||||
// ExchangeConn performs a synchronous query. It sends the message m via the connection
|
// ExchangeConn performs a synchronous query. It sends the message m via the connection
|
||||||
// c and waits for a reply. The connection c is not closed by ExchangeConn.
|
// c and waits for a reply. The connection c is not closed by ExchangeConn.
|
||||||
// This function is going away, but can easily be mimicked:
|
// Deprecated: This function is going away, but can easily be mimicked:
|
||||||
//
|
//
|
||||||
// co := &dns.Conn{Conn: c} // c is your net.Conn
|
// co := &dns.Conn{Conn: c} // c is your net.Conn
|
||||||
// co.WriteMsg(m)
|
// co.WriteMsg(m)
|
||||||
|
@ -456,11 +382,7 @@ func ExchangeConn(c net.Conn, m *Msg) (r *Msg, err error) {
|
||||||
// DialTimeout acts like Dial but takes a timeout.
|
// DialTimeout acts like Dial but takes a timeout.
|
||||||
func DialTimeout(network, address string, timeout time.Duration) (conn *Conn, err error) {
|
func DialTimeout(network, address string, timeout time.Duration) (conn *Conn, err error) {
|
||||||
client := Client{Net: network, Dialer: &net.Dialer{Timeout: timeout}}
|
client := Client{Net: network, Dialer: &net.Dialer{Timeout: timeout}}
|
||||||
conn, err = client.Dial(address)
|
return client.Dial(address)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialWithTLS connects to the address on the named network with TLS.
|
// DialWithTLS connects to the address on the named network with TLS.
|
||||||
|
@ -469,12 +391,7 @@ func DialWithTLS(network, address string, tlsConfig *tls.Config) (conn *Conn, er
|
||||||
network += "-tls"
|
network += "-tls"
|
||||||
}
|
}
|
||||||
client := Client{Net: network, TLSConfig: tlsConfig}
|
client := Client{Net: network, TLSConfig: tlsConfig}
|
||||||
conn, err = client.Dial(address)
|
return client.Dial(address)
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialTimeoutWithTLS acts like DialWithTLS but takes a timeout.
|
// DialTimeoutWithTLS acts like DialWithTLS but takes a timeout.
|
||||||
|
@ -483,11 +400,7 @@ func DialTimeoutWithTLS(network, address string, tlsConfig *tls.Config, timeout
|
||||||
network += "-tls"
|
network += "-tls"
|
||||||
}
|
}
|
||||||
client := Client{Net: network, Dialer: &net.Dialer{Timeout: timeout}, TLSConfig: tlsConfig}
|
client := Client{Net: network, Dialer: &net.Dialer{Timeout: timeout}, TLSConfig: tlsConfig}
|
||||||
conn, err = client.Dial(address)
|
return client.Dial(address)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExchangeContext acts like Exchange, but honors the deadline on the provided
|
// ExchangeContext acts like Exchange, but honors the deadline on the provided
|
||||||
|
@ -498,10 +411,11 @@ func (c *Client) ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg,
|
||||||
if deadline, ok := ctx.Deadline(); !ok {
|
if deadline, ok := ctx.Deadline(); !ok {
|
||||||
timeout = 0
|
timeout = 0
|
||||||
} else {
|
} else {
|
||||||
timeout = deadline.Sub(time.Now())
|
timeout = time.Until(deadline)
|
||||||
}
|
}
|
||||||
// not passing the context to the underlying calls, as the API does not support
|
// not passing the context to the underlying calls, as the API does not support
|
||||||
// context. For timeouts you should set up Client.Dialer and call Client.Exchange.
|
// context. For timeouts you should set up Client.Dialer and call Client.Exchange.
|
||||||
|
// TODO(tmthrgd,miekg): this is a race condition.
|
||||||
c.Dialer = &net.Dialer{Timeout: timeout}
|
c.Dialer = &net.Dialer{Timeout: timeout}
|
||||||
return c.Exchange(m, a)
|
return c.Exchange(m, a)
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,14 +68,10 @@ func ClientConfigFromReader(resolvconf io.Reader) (*ClientConfig, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case "search": // set search path to given servers
|
case "search": // set search path to given servers
|
||||||
c.Search = make([]string, len(f)-1)
|
c.Search = append([]string(nil), f[1:]...)
|
||||||
for i := 0; i < len(c.Search); i++ {
|
|
||||||
c.Search[i] = f[i+1]
|
|
||||||
}
|
|
||||||
|
|
||||||
case "options": // magic options
|
case "options": // magic options
|
||||||
for i := 1; i < len(f); i++ {
|
for _, s := range f[1:] {
|
||||||
s := f[i]
|
|
||||||
switch {
|
switch {
|
||||||
case len(s) >= 6 && s[:6] == "ndots:":
|
case len(s) >= 6 && s[:6] == "ndots:":
|
||||||
n, _ := strconv.Atoi(s[6:])
|
n, _ := strconv.Atoi(s[6:])
|
||||||
|
@ -91,7 +87,7 @@ func ClientConfigFromReader(resolvconf io.Reader) (*ClientConfig, error) {
|
||||||
n = 1
|
n = 1
|
||||||
}
|
}
|
||||||
c.Timeout = n
|
c.Timeout = n
|
||||||
case len(s) >= 8 && s[:9] == "attempts:":
|
case len(s) >= 9 && s[:9] == "attempts:":
|
||||||
n, _ := strconv.Atoi(s[9:])
|
n, _ := strconv.Atoi(s[9:])
|
||||||
if n < 1 {
|
if n < 1 {
|
||||||
n = 1
|
n = 1
|
||||||
|
|
|
@ -1,189 +0,0 @@
|
||||||
//+build ignore
|
|
||||||
|
|
||||||
// compression_generate.go is meant to run with go generate. It will use
|
|
||||||
// go/{importer,types} to track down all the RR struct types. Then for each type
|
|
||||||
// it will look to see if there are (compressible) names, if so it will add that
|
|
||||||
// type to compressionLenHelperType and comressionLenSearchType which "fake" the
|
|
||||||
// compression so that Len() is fast.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"go/format"
|
|
||||||
"go/importer"
|
|
||||||
"go/types"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
var packageHdr = `
|
|
||||||
// *** DO NOT MODIFY ***
|
|
||||||
// AUTOGENERATED BY go generate from compress_generate.go
|
|
||||||
|
|
||||||
package dns
|
|
||||||
|
|
||||||
`
|
|
||||||
|
|
||||||
// getTypeStruct will take a type and the package scope, and return the
|
|
||||||
// (innermost) struct if the type is considered a RR type (currently defined as
|
|
||||||
// those structs beginning with a RR_Header, could be redefined as implementing
|
|
||||||
// the RR interface). The bool return value indicates if embedded structs were
|
|
||||||
// resolved.
|
|
||||||
func getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) {
|
|
||||||
st, ok := t.Underlying().(*types.Struct)
|
|
||||||
if !ok {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
if st.Field(0).Type() == scope.Lookup("RR_Header").Type() {
|
|
||||||
return st, false
|
|
||||||
}
|
|
||||||
if st.Field(0).Anonymous() {
|
|
||||||
st, _ := getTypeStruct(st.Field(0).Type(), scope)
|
|
||||||
return st, true
|
|
||||||
}
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// Import and type-check the package
|
|
||||||
pkg, err := importer.Default().Import("github.com/miekg/dns")
|
|
||||||
fatalIfErr(err)
|
|
||||||
scope := pkg.Scope()
|
|
||||||
|
|
||||||
var domainTypes []string // Types that have a domain name in them (either compressible or not).
|
|
||||||
var cdomainTypes []string // Types that have a compressible domain name in them (subset of domainType)
|
|
||||||
Names:
|
|
||||||
for _, name := range scope.Names() {
|
|
||||||
o := scope.Lookup(name)
|
|
||||||
if o == nil || !o.Exported() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
st, _ := getTypeStruct(o.Type(), scope)
|
|
||||||
if st == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if name == "PrivateRR" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if scope.Lookup("Type"+o.Name()) == nil && o.Name() != "RFC3597" {
|
|
||||||
log.Fatalf("Constant Type%s does not exist.", o.Name())
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 1; i < st.NumFields(); i++ {
|
|
||||||
if _, ok := st.Field(i).Type().(*types.Slice); ok {
|
|
||||||
if st.Tag(i) == `dns:"domain-name"` {
|
|
||||||
domainTypes = append(domainTypes, o.Name())
|
|
||||||
continue Names
|
|
||||||
}
|
|
||||||
if st.Tag(i) == `dns:"cdomain-name"` {
|
|
||||||
cdomainTypes = append(cdomainTypes, o.Name())
|
|
||||||
domainTypes = append(domainTypes, o.Name())
|
|
||||||
continue Names
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case st.Tag(i) == `dns:"domain-name"`:
|
|
||||||
domainTypes = append(domainTypes, o.Name())
|
|
||||||
continue Names
|
|
||||||
case st.Tag(i) == `dns:"cdomain-name"`:
|
|
||||||
cdomainTypes = append(cdomainTypes, o.Name())
|
|
||||||
domainTypes = append(domainTypes, o.Name())
|
|
||||||
continue Names
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
b.WriteString(packageHdr)
|
|
||||||
|
|
||||||
// compressionLenHelperType - all types that have domain-name/cdomain-name can be used for compressing names
|
|
||||||
|
|
||||||
fmt.Fprint(b, "func compressionLenHelperType(c map[string]int, r RR) {\n")
|
|
||||||
fmt.Fprint(b, "switch x := r.(type) {\n")
|
|
||||||
for _, name := range domainTypes {
|
|
||||||
o := scope.Lookup(name)
|
|
||||||
st, _ := getTypeStruct(o.Type(), scope)
|
|
||||||
|
|
||||||
fmt.Fprintf(b, "case *%s:\n", name)
|
|
||||||
for i := 1; i < st.NumFields(); i++ {
|
|
||||||
out := func(s string) { fmt.Fprintf(b, "compressionLenHelper(c, x.%s)\n", st.Field(i).Name()) }
|
|
||||||
|
|
||||||
if _, ok := st.Field(i).Type().(*types.Slice); ok {
|
|
||||||
switch st.Tag(i) {
|
|
||||||
case `dns:"domain-name"`:
|
|
||||||
fallthrough
|
|
||||||
case `dns:"cdomain-name"`:
|
|
||||||
// For HIP we need to slice over the elements in this slice.
|
|
||||||
fmt.Fprintf(b, `for i := range x.%s {
|
|
||||||
compressionLenHelper(c, x.%s[i])
|
|
||||||
}
|
|
||||||
`, st.Field(i).Name(), st.Field(i).Name())
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case st.Tag(i) == `dns:"cdomain-name"`:
|
|
||||||
fallthrough
|
|
||||||
case st.Tag(i) == `dns:"domain-name"`:
|
|
||||||
out(st.Field(i).Name())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Fprintln(b, "}\n}\n\n")
|
|
||||||
|
|
||||||
// compressionLenSearchType - search cdomain-tags types for compressible names.
|
|
||||||
|
|
||||||
fmt.Fprint(b, "func compressionLenSearchType(c map[string]int, r RR) (int, bool) {\n")
|
|
||||||
fmt.Fprint(b, "switch x := r.(type) {\n")
|
|
||||||
for _, name := range cdomainTypes {
|
|
||||||
o := scope.Lookup(name)
|
|
||||||
st, _ := getTypeStruct(o.Type(), scope)
|
|
||||||
|
|
||||||
fmt.Fprintf(b, "case *%s:\n", name)
|
|
||||||
j := 1
|
|
||||||
for i := 1; i < st.NumFields(); i++ {
|
|
||||||
out := func(s string, j int) {
|
|
||||||
fmt.Fprintf(b, "k%d, ok%d := compressionLenSearch(c, x.%s)\n", j, j, st.Field(i).Name())
|
|
||||||
}
|
|
||||||
|
|
||||||
// There are no slice types with names that can be compressed.
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case st.Tag(i) == `dns:"cdomain-name"`:
|
|
||||||
out(st.Field(i).Name(), j)
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
k := "k1"
|
|
||||||
ok := "ok1"
|
|
||||||
for i := 2; i < j; i++ {
|
|
||||||
k += fmt.Sprintf(" + k%d", i)
|
|
||||||
ok += fmt.Sprintf(" && ok%d", i)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(b, "return %s, %s\n", k, ok)
|
|
||||||
}
|
|
||||||
fmt.Fprintln(b, "}\nreturn 0, false\n}\n\n")
|
|
||||||
|
|
||||||
// gofmt
|
|
||||||
res, err := format.Source(b.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
b.WriteTo(os.Stderr)
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := os.Create("zcompress.go")
|
|
||||||
fatalIfErr(err)
|
|
||||||
defer f.Close()
|
|
||||||
f.Write(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func fatalIfErr(err error) {
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const hexDigit = "0123456789abcdef"
|
const hexDigit = "0123456789abcdef"
|
||||||
|
@ -145,10 +146,9 @@ func (dns *Msg) IsTsig() *TSIG {
|
||||||
// record in the additional section will do. It returns the OPT record
|
// record in the additional section will do. It returns the OPT record
|
||||||
// found or nil.
|
// found or nil.
|
||||||
func (dns *Msg) IsEdns0() *OPT {
|
func (dns *Msg) IsEdns0() *OPT {
|
||||||
// EDNS0 is at the end of the additional section, start there.
|
// RFC 6891, Section 6.1.1 allows the OPT record to appear
|
||||||
// We might want to change this to *only* look at the last two
|
// anywhere in the additional record section, but it's usually at
|
||||||
// records. So we see TSIG and/or OPT - this a slightly bigger
|
// the end so start there.
|
||||||
// change though.
|
|
||||||
for i := len(dns.Extra) - 1; i >= 0; i-- {
|
for i := len(dns.Extra) - 1; i >= 0; i-- {
|
||||||
if dns.Extra[i].Header().Rrtype == TypeOPT {
|
if dns.Extra[i].Header().Rrtype == TypeOPT {
|
||||||
return dns.Extra[i].(*OPT)
|
return dns.Extra[i].(*OPT)
|
||||||
|
@ -157,17 +157,93 @@ func (dns *Msg) IsEdns0() *OPT {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// popEdns0 is like IsEdns0, but it removes the record from the message.
|
||||||
|
func (dns *Msg) popEdns0() *OPT {
|
||||||
|
// RFC 6891, Section 6.1.1 allows the OPT record to appear
|
||||||
|
// anywhere in the additional record section, but it's usually at
|
||||||
|
// the end so start there.
|
||||||
|
for i := len(dns.Extra) - 1; i >= 0; i-- {
|
||||||
|
if dns.Extra[i].Header().Rrtype == TypeOPT {
|
||||||
|
opt := dns.Extra[i].(*OPT)
|
||||||
|
dns.Extra = append(dns.Extra[:i], dns.Extra[i+1:]...)
|
||||||
|
return opt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// IsDomainName checks if s is a valid domain name, it returns the number of
|
// IsDomainName checks if s is a valid domain name, it returns the number of
|
||||||
// labels and true, when a domain name is valid. Note that non fully qualified
|
// labels and true, when a domain name is valid. Note that non fully qualified
|
||||||
// domain name is considered valid, in this case the last label is counted in
|
// domain name is considered valid, in this case the last label is counted in
|
||||||
// the number of labels. When false is returned the number of labels is not
|
// the number of labels. When false is returned the number of labels is not
|
||||||
// defined. Also note that this function is extremely liberal; almost any
|
// defined. Also note that this function is extremely liberal; almost any
|
||||||
// string is a valid domain name as the DNS is 8 bit protocol. It checks if each
|
// string is a valid domain name as the DNS is 8 bit protocol. It checks if each
|
||||||
// label fits in 63 characters, but there is no length check for the entire
|
// label fits in 63 characters and that the entire name will fit into the 255
|
||||||
// string s. I.e. a domain name longer than 255 characters is considered valid.
|
// octet wire format limit.
|
||||||
func IsDomainName(s string) (labels int, ok bool) {
|
func IsDomainName(s string) (labels int, ok bool) {
|
||||||
_, labels, err := packDomainName(s, nil, 0, nil, false)
|
// XXX: The logic in this function was copied from packDomainName and
|
||||||
return labels, err == nil
|
// should be kept in sync with that function.
|
||||||
|
|
||||||
|
const lenmsg = 256
|
||||||
|
|
||||||
|
if len(s) == 0 { // Ok, for instance when dealing with update RR without any rdata.
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
s = Fqdn(s)
|
||||||
|
|
||||||
|
// Each dot ends a segment of the name. Except for escaped dots (\.), which
|
||||||
|
// are normal dots.
|
||||||
|
|
||||||
|
var (
|
||||||
|
off int
|
||||||
|
begin int
|
||||||
|
wasDot bool
|
||||||
|
)
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
switch s[i] {
|
||||||
|
case '\\':
|
||||||
|
if off+1 > lenmsg {
|
||||||
|
return labels, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for \DDD
|
||||||
|
if i+3 < len(s) && isDigit(s[i+1]) && isDigit(s[i+2]) && isDigit(s[i+3]) {
|
||||||
|
i += 3
|
||||||
|
begin += 3
|
||||||
|
} else {
|
||||||
|
i++
|
||||||
|
begin++
|
||||||
|
}
|
||||||
|
|
||||||
|
wasDot = false
|
||||||
|
case '.':
|
||||||
|
if wasDot {
|
||||||
|
// two dots back to back is not legal
|
||||||
|
return labels, false
|
||||||
|
}
|
||||||
|
wasDot = true
|
||||||
|
|
||||||
|
labelLen := i - begin
|
||||||
|
if labelLen >= 1<<6 { // top two bits of length must be clear
|
||||||
|
return labels, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// off can already (we're in a loop) be bigger than lenmsg
|
||||||
|
// this happens when a name isn't fully qualified
|
||||||
|
off += 1 + labelLen
|
||||||
|
if off > lenmsg {
|
||||||
|
return labels, false
|
||||||
|
}
|
||||||
|
|
||||||
|
labels++
|
||||||
|
begin = i + 1
|
||||||
|
default:
|
||||||
|
wasDot = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return labels, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsSubDomain checks if child is indeed a child of the parent. If child and parent
|
// IsSubDomain checks if child is indeed a child of the parent. If child and parent
|
||||||
|
@ -181,7 +257,7 @@ func IsSubDomain(parent, child string) bool {
|
||||||
// The checking is performed on the binary payload.
|
// The checking is performed on the binary payload.
|
||||||
func IsMsg(buf []byte) error {
|
func IsMsg(buf []byte) error {
|
||||||
// Header
|
// Header
|
||||||
if len(buf) < 12 {
|
if len(buf) < headerSize {
|
||||||
return errors.New("dns: bad message header")
|
return errors.New("dns: bad message header")
|
||||||
}
|
}
|
||||||
// Header: Opcode
|
// Header: Opcode
|
||||||
|
@ -191,11 +267,18 @@ func IsMsg(buf []byte) error {
|
||||||
|
|
||||||
// IsFqdn checks if a domain name is fully qualified.
|
// IsFqdn checks if a domain name is fully qualified.
|
||||||
func IsFqdn(s string) bool {
|
func IsFqdn(s string) bool {
|
||||||
l := len(s)
|
s2 := strings.TrimSuffix(s, ".")
|
||||||
if l == 0 {
|
if s == s2 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return s[l-1] == '.'
|
|
||||||
|
i := strings.LastIndexFunc(s2, func(r rune) bool {
|
||||||
|
return r != '\\'
|
||||||
|
})
|
||||||
|
|
||||||
|
// Test whether we have an even number of escape sequences before
|
||||||
|
// the dot or none.
|
||||||
|
return (len(s2)-i)%2 != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsRRset checks if a set of RRs is a valid RRset as defined by RFC 2181.
|
// IsRRset checks if a set of RRs is a valid RRset as defined by RFC 2181.
|
||||||
|
@ -244,12 +327,19 @@ func ReverseAddr(addr string) (arpa string, err error) {
|
||||||
if ip == nil {
|
if ip == nil {
|
||||||
return "", &Error{err: "unrecognized address: " + addr}
|
return "", &Error{err: "unrecognized address: " + addr}
|
||||||
}
|
}
|
||||||
if ip.To4() != nil {
|
if v4 := ip.To4(); v4 != nil {
|
||||||
return strconv.Itoa(int(ip[15])) + "." + strconv.Itoa(int(ip[14])) + "." + strconv.Itoa(int(ip[13])) + "." +
|
buf := make([]byte, 0, net.IPv4len*4+len("in-addr.arpa."))
|
||||||
strconv.Itoa(int(ip[12])) + ".in-addr.arpa.", nil
|
// Add it, in reverse, to the buffer
|
||||||
|
for i := len(v4) - 1; i >= 0; i-- {
|
||||||
|
buf = strconv.AppendInt(buf, int64(v4[i]), 10)
|
||||||
|
buf = append(buf, '.')
|
||||||
|
}
|
||||||
|
// Append "in-addr.arpa." and return (buf already has the final .)
|
||||||
|
buf = append(buf, "in-addr.arpa."...)
|
||||||
|
return string(buf), nil
|
||||||
}
|
}
|
||||||
// Must be IPv6
|
// Must be IPv6
|
||||||
buf := make([]byte, 0, len(ip)*4+len("ip6.arpa."))
|
buf := make([]byte, 0, net.IPv6len*4+len("ip6.arpa."))
|
||||||
// Add it, in reverse, to the buffer
|
// Add it, in reverse, to the buffer
|
||||||
for i := len(ip) - 1; i >= 0; i-- {
|
for i := len(ip) - 1; i >= 0; i-- {
|
||||||
v := ip[i]
|
v := ip[i]
|
||||||
|
@ -273,8 +363,11 @@ func (t Type) String() string {
|
||||||
|
|
||||||
// String returns the string representation for the class c.
|
// String returns the string representation for the class c.
|
||||||
func (c Class) String() string {
|
func (c Class) String() string {
|
||||||
if c1, ok := ClassToString[uint16(c)]; ok {
|
if s, ok := ClassToString[uint16(c)]; ok {
|
||||||
return c1
|
// Only emit mnemonics when they are unambiguous, specically ANY is in both.
|
||||||
|
if _, ok := StringToType[s]; !ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return "CLASS" + strconv.Itoa(int(c))
|
return "CLASS" + strconv.Itoa(int(c))
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,10 +34,30 @@ type RR interface {
|
||||||
|
|
||||||
// copy returns a copy of the RR
|
// copy returns a copy of the RR
|
||||||
copy() RR
|
copy() RR
|
||||||
// len returns the length (in octets) of the uncompressed RR in wire format.
|
|
||||||
len() int
|
// len returns the length (in octets) of the compressed or uncompressed RR in wire format.
|
||||||
// pack packs an RR into wire format.
|
//
|
||||||
pack([]byte, int, map[string]int, bool) (int, error)
|
// If compression is nil, the uncompressed size will be returned, otherwise the compressed
|
||||||
|
// size will be returned and domain names will be added to the map for future compression.
|
||||||
|
len(off int, compression map[string]struct{}) int
|
||||||
|
|
||||||
|
// pack packs the records RDATA into wire format. The header will
|
||||||
|
// already have been packed into msg.
|
||||||
|
pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error)
|
||||||
|
|
||||||
|
// unpack unpacks an RR from wire format.
|
||||||
|
//
|
||||||
|
// This will only be called on a new and empty RR type with only the header populated. It
|
||||||
|
// will only be called if the record's RDATA is non-empty.
|
||||||
|
unpack(msg []byte, off int) (off1 int, err error)
|
||||||
|
|
||||||
|
// parse parses an RR from zone file format.
|
||||||
|
//
|
||||||
|
// This will only be called on a new and empty RR type with only the header populated.
|
||||||
|
parse(c *zlexer, origin, file string) *ParseError
|
||||||
|
|
||||||
|
// isDuplicate returns whether the two RRs are duplicates.
|
||||||
|
isDuplicate(r2 RR) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// RR_Header is the header all DNS resource records share.
|
// RR_Header is the header all DNS resource records share.
|
||||||
|
@ -55,16 +75,6 @@ func (h *RR_Header) Header() *RR_Header { return h }
|
||||||
// Just to implement the RR interface.
|
// Just to implement the RR interface.
|
||||||
func (h *RR_Header) copy() RR { return nil }
|
func (h *RR_Header) copy() RR { return nil }
|
||||||
|
|
||||||
func (h *RR_Header) copyHeader() *RR_Header {
|
|
||||||
r := new(RR_Header)
|
|
||||||
r.Name = h.Name
|
|
||||||
r.Rrtype = h.Rrtype
|
|
||||||
r.Class = h.Class
|
|
||||||
r.Ttl = h.Ttl
|
|
||||||
r.Rdlength = h.Rdlength
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *RR_Header) String() string {
|
func (h *RR_Header) String() string {
|
||||||
var s string
|
var s string
|
||||||
|
|
||||||
|
@ -80,28 +90,45 @@ func (h *RR_Header) String() string {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *RR_Header) len() int {
|
func (h *RR_Header) len(off int, compression map[string]struct{}) int {
|
||||||
l := len(h.Name) + 1
|
l := domainNameLen(h.Name, off, compression, true)
|
||||||
l += 10 // rrtype(2) + class(2) + ttl(4) + rdlength(2)
|
l += 10 // rrtype(2) + class(2) + ttl(4) + rdlength(2)
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *RR_Header) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
|
||||||
|
// RR_Header has no RDATA to pack.
|
||||||
|
return off, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *RR_Header) unpack(msg []byte, off int) (int, error) {
|
||||||
|
panic("dns: internal error: unpack should never be called on RR_Header")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *RR_Header) parse(c *zlexer, origin, file string) *ParseError {
|
||||||
|
panic("dns: internal error: parse should never be called on RR_Header")
|
||||||
|
}
|
||||||
|
|
||||||
// ToRFC3597 converts a known RR to the unknown RR representation from RFC 3597.
|
// ToRFC3597 converts a known RR to the unknown RR representation from RFC 3597.
|
||||||
func (rr *RFC3597) ToRFC3597(r RR) error {
|
func (rr *RFC3597) ToRFC3597(r RR) error {
|
||||||
buf := make([]byte, r.len()*2)
|
buf := make([]byte, Len(r)*2)
|
||||||
off, err := PackRR(r, buf, 0, nil, false)
|
headerEnd, off, err := packRR(r, buf, 0, compressionMap{}, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
buf = buf[:off]
|
buf = buf[:off]
|
||||||
if int(r.Header().Rdlength) > off {
|
|
||||||
return ErrBuf
|
*rr = RFC3597{Hdr: *r.Header()}
|
||||||
|
rr.Hdr.Rdlength = uint16(off - headerEnd)
|
||||||
|
|
||||||
|
if noRdata(rr.Hdr) {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
rfc3597, _, err := unpackRFC3597(*r.Header(), buf, off-int(r.Header().Rdlength))
|
_, err = rr.unpack(buf, headerEnd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
*rr = *rfc3597.(*RFC3597)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,12 +67,10 @@ var AlgorithmToString = map[uint8]string{
|
||||||
PRIVATEOID: "PRIVATEOID",
|
PRIVATEOID: "PRIVATEOID",
|
||||||
}
|
}
|
||||||
|
|
||||||
// StringToAlgorithm is the reverse of AlgorithmToString.
|
|
||||||
var StringToAlgorithm = reverseInt8(AlgorithmToString)
|
|
||||||
|
|
||||||
// AlgorithmToHash is a map of algorithm crypto hash IDs to crypto.Hash's.
|
// AlgorithmToHash is a map of algorithm crypto hash IDs to crypto.Hash's.
|
||||||
var AlgorithmToHash = map[uint8]crypto.Hash{
|
var AlgorithmToHash = map[uint8]crypto.Hash{
|
||||||
RSAMD5: crypto.MD5, // Deprecated in RFC 6725
|
RSAMD5: crypto.MD5, // Deprecated in RFC 6725
|
||||||
|
DSA: crypto.SHA1,
|
||||||
RSASHA1: crypto.SHA1,
|
RSASHA1: crypto.SHA1,
|
||||||
RSASHA1NSEC3SHA1: crypto.SHA1,
|
RSASHA1NSEC3SHA1: crypto.SHA1,
|
||||||
RSASHA256: crypto.SHA256,
|
RSASHA256: crypto.SHA256,
|
||||||
|
@ -101,9 +99,6 @@ var HashToString = map[uint8]string{
|
||||||
SHA512: "SHA512",
|
SHA512: "SHA512",
|
||||||
}
|
}
|
||||||
|
|
||||||
// StringToHash is a map of names to hash IDs.
|
|
||||||
var StringToHash = reverseInt8(HashToString)
|
|
||||||
|
|
||||||
// DNSKEY flag values.
|
// DNSKEY flag values.
|
||||||
const (
|
const (
|
||||||
SEP = 1
|
SEP = 1
|
||||||
|
@ -172,7 +167,7 @@ func (k *DNSKEY) KeyTag() uint16 {
|
||||||
keytag += int(v) << 8
|
keytag += int(v) << 8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
keytag += (keytag >> 16) & 0xFFFF
|
keytag += keytag >> 16 & 0xFFFF
|
||||||
keytag &= 0xFFFF
|
keytag &= 0xFFFF
|
||||||
}
|
}
|
||||||
return uint16(keytag)
|
return uint16(keytag)
|
||||||
|
@ -239,7 +234,7 @@ func (k *DNSKEY) ToDS(h uint8) *DS {
|
||||||
// ToCDNSKEY converts a DNSKEY record to a CDNSKEY record.
|
// ToCDNSKEY converts a DNSKEY record to a CDNSKEY record.
|
||||||
func (k *DNSKEY) ToCDNSKEY() *CDNSKEY {
|
func (k *DNSKEY) ToCDNSKEY() *CDNSKEY {
|
||||||
c := &CDNSKEY{DNSKEY: *k}
|
c := &CDNSKEY{DNSKEY: *k}
|
||||||
c.Hdr = *k.Hdr.copyHeader()
|
c.Hdr = k.Hdr
|
||||||
c.Hdr.Rrtype = TypeCDNSKEY
|
c.Hdr.Rrtype = TypeCDNSKEY
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
@ -247,7 +242,7 @@ func (k *DNSKEY) ToCDNSKEY() *CDNSKEY {
|
||||||
// ToCDS converts a DS record to a CDS record.
|
// ToCDS converts a DS record to a CDS record.
|
||||||
func (d *DS) ToCDS() *CDS {
|
func (d *DS) ToCDS() *CDS {
|
||||||
c := &CDS{DS: *d}
|
c := &CDS{DS: *d}
|
||||||
c.Hdr = *d.Hdr.copyHeader()
|
c.Hdr = d.Hdr
|
||||||
c.Hdr.Rrtype = TypeCDS
|
c.Hdr.Rrtype = TypeCDS
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
@ -267,16 +262,17 @@ func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error {
|
||||||
return ErrKey
|
return ErrKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h0 := rrset[0].Header()
|
||||||
rr.Hdr.Rrtype = TypeRRSIG
|
rr.Hdr.Rrtype = TypeRRSIG
|
||||||
rr.Hdr.Name = rrset[0].Header().Name
|
rr.Hdr.Name = h0.Name
|
||||||
rr.Hdr.Class = rrset[0].Header().Class
|
rr.Hdr.Class = h0.Class
|
||||||
if rr.OrigTtl == 0 { // If set don't override
|
if rr.OrigTtl == 0 { // If set don't override
|
||||||
rr.OrigTtl = rrset[0].Header().Ttl
|
rr.OrigTtl = h0.Ttl
|
||||||
}
|
}
|
||||||
rr.TypeCovered = rrset[0].Header().Rrtype
|
rr.TypeCovered = h0.Rrtype
|
||||||
rr.Labels = uint8(CountLabel(rrset[0].Header().Name))
|
rr.Labels = uint8(CountLabel(h0.Name))
|
||||||
|
|
||||||
if strings.HasPrefix(rrset[0].Header().Name, "*") {
|
if strings.HasPrefix(h0.Name, "*") {
|
||||||
rr.Labels-- // wildcard, remove from label count
|
rr.Labels-- // wildcard, remove from label count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -400,7 +396,7 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
|
||||||
if rr.Algorithm != k.Algorithm {
|
if rr.Algorithm != k.Algorithm {
|
||||||
return ErrKey
|
return ErrKey
|
||||||
}
|
}
|
||||||
if strings.ToLower(rr.SignerName) != strings.ToLower(k.Hdr.Name) {
|
if !strings.EqualFold(rr.SignerName, k.Hdr.Name) {
|
||||||
return ErrKey
|
return ErrKey
|
||||||
}
|
}
|
||||||
if k.Protocol != 3 {
|
if k.Protocol != 3 {
|
||||||
|
@ -410,10 +406,7 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
|
||||||
// IsRRset checked that we have at least one RR and that the RRs in
|
// IsRRset checked that we have at least one RR and that the RRs in
|
||||||
// the set have consistent type, class, and name. Also check that type and
|
// the set have consistent type, class, and name. Also check that type and
|
||||||
// class matches the RRSIG record.
|
// class matches the RRSIG record.
|
||||||
if rrset[0].Header().Class != rr.Hdr.Class {
|
if h0 := rrset[0].Header(); h0.Class != rr.Hdr.Class || h0.Rrtype != rr.TypeCovered {
|
||||||
return ErrRRset
|
|
||||||
}
|
|
||||||
if rrset[0].Header().Rrtype != rr.TypeCovered {
|
|
||||||
return ErrRRset
|
return ErrRRset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -511,8 +504,8 @@ func (rr *RRSIG) ValidityPeriod(t time.Time) bool {
|
||||||
}
|
}
|
||||||
modi := (int64(rr.Inception) - utc) / year68
|
modi := (int64(rr.Inception) - utc) / year68
|
||||||
mode := (int64(rr.Expiration) - utc) / year68
|
mode := (int64(rr.Expiration) - utc) / year68
|
||||||
ti := int64(rr.Inception) + (modi * year68)
|
ti := int64(rr.Inception) + modi*year68
|
||||||
te := int64(rr.Expiration) + (mode * year68)
|
te := int64(rr.Expiration) + mode*year68
|
||||||
return ti <= utc && utc <= te
|
return ti <= utc && utc <= te
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -532,6 +525,11 @@ func (k *DNSKEY) publicKeyRSA() *rsa.PublicKey {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(keybuf) < 1+1+64 {
|
||||||
|
// Exponent must be at least 1 byte and modulus at least 64
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// RFC 2537/3110, section 2. RSA Public KEY Resource Records
|
// RFC 2537/3110, section 2. RSA Public KEY Resource Records
|
||||||
// Length is in the 0th byte, unless its zero, then it
|
// Length is in the 0th byte, unless its zero, then it
|
||||||
// it in bytes 1 and 2 and its a 16 bit number
|
// it in bytes 1 and 2 and its a 16 bit number
|
||||||
|
@ -541,25 +539,35 @@ func (k *DNSKEY) publicKeyRSA() *rsa.PublicKey {
|
||||||
explen = uint16(keybuf[1])<<8 | uint16(keybuf[2])
|
explen = uint16(keybuf[1])<<8 | uint16(keybuf[2])
|
||||||
keyoff = 3
|
keyoff = 3
|
||||||
}
|
}
|
||||||
pubkey := new(rsa.PublicKey)
|
|
||||||
|
|
||||||
pubkey.N = big.NewInt(0)
|
if explen > 4 || explen == 0 || keybuf[keyoff] == 0 {
|
||||||
shift := uint64((explen - 1) * 8)
|
// Exponent larger than supported by the crypto package,
|
||||||
expo := uint64(0)
|
// empty, or contains prohibited leading zero.
|
||||||
for i := int(explen - 1); i > 0; i-- {
|
|
||||||
expo += uint64(keybuf[keyoff+i]) << shift
|
|
||||||
shift -= 8
|
|
||||||
}
|
|
||||||
// Remainder
|
|
||||||
expo += uint64(keybuf[keyoff])
|
|
||||||
if expo > (2<<31)+1 {
|
|
||||||
// Larger expo than supported.
|
|
||||||
// println("dns: F5 primes (or larger) are not supported")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
pubkey.E = int(expo)
|
|
||||||
|
|
||||||
pubkey.N.SetBytes(keybuf[keyoff+int(explen):])
|
modoff := keyoff + int(explen)
|
||||||
|
modlen := len(keybuf) - modoff
|
||||||
|
if modlen < 64 || modlen > 512 || keybuf[modoff] == 0 {
|
||||||
|
// Modulus is too small, large, or contains prohibited leading zero.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pubkey := new(rsa.PublicKey)
|
||||||
|
|
||||||
|
var expo uint64
|
||||||
|
// The exponent of length explen is between keyoff and modoff.
|
||||||
|
for _, v := range keybuf[keyoff:modoff] {
|
||||||
|
expo <<= 8
|
||||||
|
expo |= uint64(v)
|
||||||
|
}
|
||||||
|
if expo > 1<<31-1 {
|
||||||
|
// Larger exponent than supported by the crypto package.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pubkey.E = int(expo)
|
||||||
|
pubkey.N = new(big.Int).SetBytes(keybuf[modoff:])
|
||||||
return pubkey
|
return pubkey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -584,10 +592,8 @@ func (k *DNSKEY) publicKeyECDSA() *ecdsa.PublicKey {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pubkey.X = big.NewInt(0)
|
pubkey.X = new(big.Int).SetBytes(keybuf[:len(keybuf)/2])
|
||||||
pubkey.X.SetBytes(keybuf[:len(keybuf)/2])
|
pubkey.Y = new(big.Int).SetBytes(keybuf[len(keybuf)/2:])
|
||||||
pubkey.Y = big.NewInt(0)
|
|
||||||
pubkey.Y.SetBytes(keybuf[len(keybuf)/2:])
|
|
||||||
return pubkey
|
return pubkey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -608,10 +614,10 @@ func (k *DNSKEY) publicKeyDSA() *dsa.PublicKey {
|
||||||
p, keybuf := keybuf[:size], keybuf[size:]
|
p, keybuf := keybuf[:size], keybuf[size:]
|
||||||
g, y := keybuf[:size], keybuf[size:]
|
g, y := keybuf[:size], keybuf[size:]
|
||||||
pubkey := new(dsa.PublicKey)
|
pubkey := new(dsa.PublicKey)
|
||||||
pubkey.Parameters.Q = big.NewInt(0).SetBytes(q)
|
pubkey.Parameters.Q = new(big.Int).SetBytes(q)
|
||||||
pubkey.Parameters.P = big.NewInt(0).SetBytes(p)
|
pubkey.Parameters.P = new(big.Int).SetBytes(p)
|
||||||
pubkey.Parameters.G = big.NewInt(0).SetBytes(g)
|
pubkey.Parameters.G = new(big.Int).SetBytes(g)
|
||||||
pubkey.Y = big.NewInt(0).SetBytes(y)
|
pubkey.Y = new(big.Int).SetBytes(y)
|
||||||
return pubkey
|
return pubkey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -641,15 +647,16 @@ func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) {
|
||||||
wires := make(wireSlice, len(rrset))
|
wires := make(wireSlice, len(rrset))
|
||||||
for i, r := range rrset {
|
for i, r := range rrset {
|
||||||
r1 := r.copy()
|
r1 := r.copy()
|
||||||
r1.Header().Ttl = s.OrigTtl
|
h := r1.Header()
|
||||||
labels := SplitDomainName(r1.Header().Name)
|
h.Ttl = s.OrigTtl
|
||||||
|
labels := SplitDomainName(h.Name)
|
||||||
// 6.2. Canonical RR Form. (4) - wildcards
|
// 6.2. Canonical RR Form. (4) - wildcards
|
||||||
if len(labels) > int(s.Labels) {
|
if len(labels) > int(s.Labels) {
|
||||||
// Wildcard
|
// Wildcard
|
||||||
r1.Header().Name = "*." + strings.Join(labels[len(labels)-int(s.Labels):], ".") + "."
|
h.Name = "*." + strings.Join(labels[len(labels)-int(s.Labels):], ".") + "."
|
||||||
}
|
}
|
||||||
// RFC 4034: 6.2. Canonical RR Form. (2) - domain name to lowercase
|
// RFC 4034: 6.2. Canonical RR Form. (2) - domain name to lowercase
|
||||||
r1.Header().Name = strings.ToLower(r1.Header().Name)
|
h.Name = strings.ToLower(h.Name)
|
||||||
// 6.2. Canonical RR Form. (3) - domain rdata to lowercase.
|
// 6.2. Canonical RR Form. (3) - domain rdata to lowercase.
|
||||||
// NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,
|
// NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,
|
||||||
// HINFO, MINFO, MX, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,
|
// HINFO, MINFO, MX, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,
|
||||||
|
@ -707,7 +714,7 @@ func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) {
|
||||||
x.Target = strings.ToLower(x.Target)
|
x.Target = strings.ToLower(x.Target)
|
||||||
}
|
}
|
||||||
// 6.2. Canonical RR Form. (5) - origTTL
|
// 6.2. Canonical RR Form. (5) - origTTL
|
||||||
wire := make([]byte, r1.len()+1) // +1 to be safe(r)
|
wire := make([]byte, Len(r1)+1) // +1 to be safe(r)
|
||||||
off, err1 := PackRR(r1, wire, 0, nil, false)
|
off, err1 := PackRR(r1, wire, 0, nil, false)
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
return nil, err1
|
return nil, err1
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bufio"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/dsa"
|
"crypto/dsa"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
@ -109,21 +109,16 @@ func readPrivateKeyRSA(m map[string]string) (*rsa.PrivateKey, error) {
|
||||||
}
|
}
|
||||||
switch k {
|
switch k {
|
||||||
case "modulus":
|
case "modulus":
|
||||||
p.PublicKey.N = big.NewInt(0)
|
p.PublicKey.N = new(big.Int).SetBytes(v1)
|
||||||
p.PublicKey.N.SetBytes(v1)
|
|
||||||
case "publicexponent":
|
case "publicexponent":
|
||||||
i := big.NewInt(0)
|
i := new(big.Int).SetBytes(v1)
|
||||||
i.SetBytes(v1)
|
|
||||||
p.PublicKey.E = int(i.Int64()) // int64 should be large enough
|
p.PublicKey.E = int(i.Int64()) // int64 should be large enough
|
||||||
case "privateexponent":
|
case "privateexponent":
|
||||||
p.D = big.NewInt(0)
|
p.D = new(big.Int).SetBytes(v1)
|
||||||
p.D.SetBytes(v1)
|
|
||||||
case "prime1":
|
case "prime1":
|
||||||
p.Primes[0] = big.NewInt(0)
|
p.Primes[0] = new(big.Int).SetBytes(v1)
|
||||||
p.Primes[0].SetBytes(v1)
|
|
||||||
case "prime2":
|
case "prime2":
|
||||||
p.Primes[1] = big.NewInt(0)
|
p.Primes[1] = new(big.Int).SetBytes(v1)
|
||||||
p.Primes[1].SetBytes(v1)
|
|
||||||
}
|
}
|
||||||
case "exponent1", "exponent2", "coefficient":
|
case "exponent1", "exponent2", "coefficient":
|
||||||
// not used in Go (yet)
|
// not used in Go (yet)
|
||||||
|
@ -136,7 +131,7 @@ func readPrivateKeyRSA(m map[string]string) (*rsa.PrivateKey, error) {
|
||||||
|
|
||||||
func readPrivateKeyDSA(m map[string]string) (*dsa.PrivateKey, error) {
|
func readPrivateKeyDSA(m map[string]string) (*dsa.PrivateKey, error) {
|
||||||
p := new(dsa.PrivateKey)
|
p := new(dsa.PrivateKey)
|
||||||
p.X = big.NewInt(0)
|
p.X = new(big.Int)
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
switch k {
|
switch k {
|
||||||
case "private_value(x)":
|
case "private_value(x)":
|
||||||
|
@ -154,7 +149,7 @@ func readPrivateKeyDSA(m map[string]string) (*dsa.PrivateKey, error) {
|
||||||
|
|
||||||
func readPrivateKeyECDSA(m map[string]string) (*ecdsa.PrivateKey, error) {
|
func readPrivateKeyECDSA(m map[string]string) (*ecdsa.PrivateKey, error) {
|
||||||
p := new(ecdsa.PrivateKey)
|
p := new(ecdsa.PrivateKey)
|
||||||
p.D = big.NewInt(0)
|
p.D = new(big.Int)
|
||||||
// TODO: validate that the required flags are present
|
// TODO: validate that the required flags are present
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
switch k {
|
switch k {
|
||||||
|
@ -181,22 +176,10 @@ func readPrivateKeyED25519(m map[string]string) (ed25519.PrivateKey, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(p1) != 32 {
|
if len(p1) != ed25519.SeedSize {
|
||||||
return nil, ErrPrivKey
|
return nil, ErrPrivKey
|
||||||
}
|
}
|
||||||
// RFC 8080 and Golang's x/crypto/ed25519 differ as to how the
|
p = ed25519.NewKeyFromSeed(p1)
|
||||||
// private keys are represented. RFC 8080 specifies that private
|
|
||||||
// keys be stored solely as the seed value (p1 above) while the
|
|
||||||
// ed25519 package represents them as the seed value concatenated
|
|
||||||
// to the public key, which is derived from the seed value.
|
|
||||||
//
|
|
||||||
// ed25519.GenerateKey reads exactly 32 bytes from the passed in
|
|
||||||
// io.Reader and uses them as the seed. It also derives the
|
|
||||||
// public key and produces a compatible private key.
|
|
||||||
_, p, err = ed25519.GenerateKey(bytes.NewReader(p1))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
case "created", "publish", "activate":
|
case "created", "publish", "activate":
|
||||||
/* not used in Go (yet) */
|
/* not used in Go (yet) */
|
||||||
}
|
}
|
||||||
|
@ -207,23 +190,12 @@ func readPrivateKeyED25519(m map[string]string) (ed25519.PrivateKey, error) {
|
||||||
// parseKey reads a private key from r. It returns a map[string]string,
|
// parseKey reads a private key from r. It returns a map[string]string,
|
||||||
// with the key-value pairs, or an error when the file is not correct.
|
// with the key-value pairs, or an error when the file is not correct.
|
||||||
func parseKey(r io.Reader, file string) (map[string]string, error) {
|
func parseKey(r io.Reader, file string) (map[string]string, error) {
|
||||||
s, cancel := scanInit(r)
|
|
||||||
m := make(map[string]string)
|
m := make(map[string]string)
|
||||||
c := make(chan lex)
|
var k string
|
||||||
k := ""
|
|
||||||
defer func() {
|
c := newKLexer(r)
|
||||||
cancel()
|
|
||||||
// zlexer can send up to two tokens, the next one and possibly 1 remainders.
|
for l, ok := c.Next(); ok; l, ok = c.Next() {
|
||||||
// Do a non-blocking read.
|
|
||||||
_, ok := <-c
|
|
||||||
_, ok = <-c
|
|
||||||
if !ok {
|
|
||||||
// too bad
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
// Start the lexer
|
|
||||||
go klexer(s, c)
|
|
||||||
for l := range c {
|
|
||||||
// It should alternate
|
// It should alternate
|
||||||
switch l.value {
|
switch l.value {
|
||||||
case zKey:
|
case zKey:
|
||||||
|
@ -232,41 +204,111 @@ func parseKey(r io.Reader, file string) (map[string]string, error) {
|
||||||
if k == "" {
|
if k == "" {
|
||||||
return nil, &ParseError{file, "no private key seen", l}
|
return nil, &ParseError{file, "no private key seen", l}
|
||||||
}
|
}
|
||||||
//println("Setting", strings.ToLower(k), "to", l.token, "b")
|
|
||||||
m[strings.ToLower(k)] = l.token
|
m[strings.ToLower(k)] = l.token
|
||||||
k = ""
|
k = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Surface any read errors from r.
|
||||||
|
if err := c.Err(); err != nil {
|
||||||
|
return nil, &ParseError{file: file, err: err.Error()}
|
||||||
|
}
|
||||||
|
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// klexer scans the sourcefile and returns tokens on the channel c.
|
type klexer struct {
|
||||||
func klexer(s *scan, c chan lex) {
|
br io.ByteReader
|
||||||
var l lex
|
|
||||||
str := "" // Hold the current read text
|
readErr error
|
||||||
commt := false
|
|
||||||
key := true
|
line int
|
||||||
x, err := s.tokenText()
|
column int
|
||||||
defer close(c)
|
|
||||||
for err == nil {
|
key bool
|
||||||
l.column = s.position.Column
|
|
||||||
l.line = s.position.Line
|
eol bool // end-of-line
|
||||||
|
}
|
||||||
|
|
||||||
|
func newKLexer(r io.Reader) *klexer {
|
||||||
|
br, ok := r.(io.ByteReader)
|
||||||
|
if !ok {
|
||||||
|
br = bufio.NewReaderSize(r, 1024)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &klexer{
|
||||||
|
br: br,
|
||||||
|
|
||||||
|
line: 1,
|
||||||
|
|
||||||
|
key: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kl *klexer) Err() error {
|
||||||
|
if kl.readErr == io.EOF {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return kl.readErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// readByte returns the next byte from the input
|
||||||
|
func (kl *klexer) readByte() (byte, bool) {
|
||||||
|
if kl.readErr != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := kl.br.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
kl.readErr = err
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// delay the newline handling until the next token is delivered,
|
||||||
|
// fixes off-by-one errors when reporting a parse error.
|
||||||
|
if kl.eol {
|
||||||
|
kl.line++
|
||||||
|
kl.column = 0
|
||||||
|
kl.eol = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if c == '\n' {
|
||||||
|
kl.eol = true
|
||||||
|
} else {
|
||||||
|
kl.column++
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kl *klexer) Next() (lex, bool) {
|
||||||
|
var (
|
||||||
|
l lex
|
||||||
|
|
||||||
|
str strings.Builder
|
||||||
|
|
||||||
|
commt bool
|
||||||
|
)
|
||||||
|
|
||||||
|
for x, ok := kl.readByte(); ok; x, ok = kl.readByte() {
|
||||||
|
l.line, l.column = kl.line, kl.column
|
||||||
|
|
||||||
switch x {
|
switch x {
|
||||||
case ':':
|
case ':':
|
||||||
if commt {
|
if commt || !kl.key {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
l.token = str
|
|
||||||
if key {
|
kl.key = false
|
||||||
l.value = zKey
|
|
||||||
c <- l
|
|
||||||
// Next token is a space, eat it
|
// Next token is a space, eat it
|
||||||
s.tokenText()
|
kl.readByte()
|
||||||
key = false
|
|
||||||
str = ""
|
l.value = zKey
|
||||||
} else {
|
l.token = str.String()
|
||||||
l.value = zValue
|
return l, true
|
||||||
}
|
|
||||||
case ';':
|
case ';':
|
||||||
commt = true
|
commt = true
|
||||||
case '\n':
|
case '\n':
|
||||||
|
@ -274,24 +316,37 @@ func klexer(s *scan, c chan lex) {
|
||||||
// Reset a comment
|
// Reset a comment
|
||||||
commt = false
|
commt = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if kl.key && str.Len() == 0 {
|
||||||
|
// ignore empty lines
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
kl.key = true
|
||||||
|
|
||||||
l.value = zValue
|
l.value = zValue
|
||||||
l.token = str
|
l.token = str.String()
|
||||||
c <- l
|
return l, true
|
||||||
str = ""
|
|
||||||
commt = false
|
|
||||||
key = true
|
|
||||||
default:
|
default:
|
||||||
if commt {
|
if commt {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
str += string(x)
|
|
||||||
|
str.WriteByte(x)
|
||||||
}
|
}
|
||||||
x, err = s.tokenText()
|
|
||||||
}
|
}
|
||||||
if len(str) > 0 {
|
|
||||||
|
if kl.readErr != nil && kl.readErr != io.EOF {
|
||||||
|
// Don't return any tokens after a read error occurs.
|
||||||
|
return lex{value: zEOF}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if str.Len() > 0 {
|
||||||
// Send remainder
|
// Send remainder
|
||||||
l.token = str
|
|
||||||
l.value = zValue
|
l.value = zValue
|
||||||
c <- l
|
l.token = str.String()
|
||||||
|
return l, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return lex{value: zEOF}, false
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,8 @@ import (
|
||||||
|
|
||||||
const format = "Private-key-format: v1.3\n"
|
const format = "Private-key-format: v1.3\n"
|
||||||
|
|
||||||
|
var bigIntOne = big.NewInt(1)
|
||||||
|
|
||||||
// PrivateKeyString converts a PrivateKey to a string. This string has the same
|
// PrivateKeyString converts a PrivateKey to a string. This string has the same
|
||||||
// format as the private-key-file of BIND9 (Private-key-format: v1.3).
|
// format as the private-key-file of BIND9 (Private-key-format: v1.3).
|
||||||
// It needs some info from the key (the algorithm), so its a method of the DNSKEY
|
// It needs some info from the key (the algorithm), so its a method of the DNSKEY
|
||||||
|
@ -31,12 +33,11 @@ func (r *DNSKEY) PrivateKeyString(p crypto.PrivateKey) string {
|
||||||
prime2 := toBase64(p.Primes[1].Bytes())
|
prime2 := toBase64(p.Primes[1].Bytes())
|
||||||
// Calculate Exponent1/2 and Coefficient as per: http://en.wikipedia.org/wiki/RSA#Using_the_Chinese_remainder_algorithm
|
// Calculate Exponent1/2 and Coefficient as per: http://en.wikipedia.org/wiki/RSA#Using_the_Chinese_remainder_algorithm
|
||||||
// and from: http://code.google.com/p/go/issues/detail?id=987
|
// and from: http://code.google.com/p/go/issues/detail?id=987
|
||||||
one := big.NewInt(1)
|
p1 := new(big.Int).Sub(p.Primes[0], bigIntOne)
|
||||||
p1 := big.NewInt(0).Sub(p.Primes[0], one)
|
q1 := new(big.Int).Sub(p.Primes[1], bigIntOne)
|
||||||
q1 := big.NewInt(0).Sub(p.Primes[1], one)
|
exp1 := new(big.Int).Mod(p.D, p1)
|
||||||
exp1 := big.NewInt(0).Mod(p.D, p1)
|
exp2 := new(big.Int).Mod(p.D, q1)
|
||||||
exp2 := big.NewInt(0).Mod(p.D, q1)
|
coeff := new(big.Int).ModInverse(p.Primes[1], p.Primes[0])
|
||||||
coeff := big.NewInt(0).ModInverse(p.Primes[1], p.Primes[0])
|
|
||||||
|
|
||||||
exponent1 := toBase64(exp1.Bytes())
|
exponent1 := toBase64(exp1.Bytes())
|
||||||
exponent2 := toBase64(exp2.Bytes())
|
exponent2 := toBase64(exp2.Bytes())
|
||||||
|
@ -82,7 +83,7 @@ func (r *DNSKEY) PrivateKeyString(p crypto.PrivateKey) string {
|
||||||
"Public_value(y): " + pub + "\n"
|
"Public_value(y): " + pub + "\n"
|
||||||
|
|
||||||
case ed25519.PrivateKey:
|
case ed25519.PrivateKey:
|
||||||
private := toBase64(p[:32])
|
private := toBase64(p.Seed())
|
||||||
return format +
|
return format +
|
||||||
"Algorithm: " + algorithm + "\n" +
|
"Algorithm: " + algorithm + "\n" +
|
||||||
"PrivateKey: " + private + "\n"
|
"PrivateKey: " + private + "\n"
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
/*
|
/*
|
||||||
Package dns implements a full featured interface to the Domain Name System.
|
Package dns implements a full featured interface to the Domain Name System.
|
||||||
Server- and client-side programming is supported.
|
Both server- and client-side programming is supported. The package allows
|
||||||
The package allows complete control over what is sent out to the DNS. The package
|
complete control over what is sent out to the DNS. The API follows the
|
||||||
API follows the less-is-more principle, by presenting a small, clean interface.
|
less-is-more principle, by presenting a small, clean interface.
|
||||||
|
|
||||||
The package dns supports (asynchronous) querying/replying, incoming/outgoing zone transfers,
|
It supports (asynchronous) querying/replying, incoming/outgoing zone transfers,
|
||||||
TSIG, EDNS0, dynamic updates, notifies and DNSSEC validation/signing.
|
TSIG, EDNS0, dynamic updates, notifies and DNSSEC validation/signing.
|
||||||
Note that domain names MUST be fully qualified, before sending them, unqualified
|
|
||||||
|
Note that domain names MUST be fully qualified before sending them, unqualified
|
||||||
names in a message will result in a packing failure.
|
names in a message will result in a packing failure.
|
||||||
|
|
||||||
Resource records are native types. They are not stored in wire format.
|
Resource records are native types. They are not stored in wire format. Basic
|
||||||
Basic usage pattern for creating a new resource record:
|
usage pattern for creating a new resource record:
|
||||||
|
|
||||||
r := new(dns.MX)
|
r := new(dns.MX)
|
||||||
r.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeMX,
|
r.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 3600}
|
||||||
Class: dns.ClassINET, Ttl: 3600}
|
|
||||||
r.Preference = 10
|
r.Preference = 10
|
||||||
r.Mx = "mx.miek.nl."
|
r.Mx = "mx.miek.nl."
|
||||||
|
|
||||||
|
@ -30,8 +30,8 @@ Or even:
|
||||||
|
|
||||||
mx, err := dns.NewRR("$ORIGIN nl.\nmiek 1H IN MX 10 mx.miek")
|
mx, err := dns.NewRR("$ORIGIN nl.\nmiek 1H IN MX 10 mx.miek")
|
||||||
|
|
||||||
In the DNS messages are exchanged, these messages contain resource
|
In the DNS messages are exchanged, these messages contain resource records
|
||||||
records (sets). Use pattern for creating a message:
|
(sets). Use pattern for creating a message:
|
||||||
|
|
||||||
m := new(dns.Msg)
|
m := new(dns.Msg)
|
||||||
m.SetQuestion("miek.nl.", dns.TypeMX)
|
m.SetQuestion("miek.nl.", dns.TypeMX)
|
||||||
|
@ -40,8 +40,8 @@ Or when not certain if the domain name is fully qualified:
|
||||||
|
|
||||||
m.SetQuestion(dns.Fqdn("miek.nl"), dns.TypeMX)
|
m.SetQuestion(dns.Fqdn("miek.nl"), dns.TypeMX)
|
||||||
|
|
||||||
The message m is now a message with the question section set to ask
|
The message m is now a message with the question section set to ask the MX
|
||||||
the MX records for the miek.nl. zone.
|
records for the miek.nl. zone.
|
||||||
|
|
||||||
The following is slightly more verbose, but more flexible:
|
The following is slightly more verbose, but more flexible:
|
||||||
|
|
||||||
|
@ -51,9 +51,8 @@ The following is slightly more verbose, but more flexible:
|
||||||
m1.Question = make([]dns.Question, 1)
|
m1.Question = make([]dns.Question, 1)
|
||||||
m1.Question[0] = dns.Question{"miek.nl.", dns.TypeMX, dns.ClassINET}
|
m1.Question[0] = dns.Question{"miek.nl.", dns.TypeMX, dns.ClassINET}
|
||||||
|
|
||||||
After creating a message it can be sent.
|
After creating a message it can be sent. Basic use pattern for synchronous
|
||||||
Basic use pattern for synchronous querying the DNS at a
|
querying the DNS at a server configured on 127.0.0.1 and port 53:
|
||||||
server configured on 127.0.0.1 and port 53:
|
|
||||||
|
|
||||||
c := new(dns.Client)
|
c := new(dns.Client)
|
||||||
in, rtt, err := c.Exchange(m1, "127.0.0.1:53")
|
in, rtt, err := c.Exchange(m1, "127.0.0.1:53")
|
||||||
|
@ -73,11 +72,11 @@ and port to use for the connection:
|
||||||
Port: 12345,
|
Port: 12345,
|
||||||
Zone: "",
|
Zone: "",
|
||||||
}
|
}
|
||||||
d := net.Dialer{
|
c.Dialer := &net.Dialer{
|
||||||
Timeout: 200 * time.Millisecond,
|
Timeout: 200 * time.Millisecond,
|
||||||
LocalAddr: &laddr,
|
LocalAddr: &laddr,
|
||||||
}
|
}
|
||||||
in, rtt, err := c.ExchangeWithDialer(&d, m1, "8.8.8.8:53")
|
in, rtt, err := c.Exchange(m1, "8.8.8.8:53")
|
||||||
|
|
||||||
If these "advanced" features are not needed, a simple UDP query can be sent,
|
If these "advanced" features are not needed, a simple UDP query can be sent,
|
||||||
with:
|
with:
|
||||||
|
@ -99,25 +98,24 @@ the Answer section:
|
||||||
|
|
||||||
Domain Name and TXT Character String Representations
|
Domain Name and TXT Character String Representations
|
||||||
|
|
||||||
Both domain names and TXT character strings are converted to presentation
|
Both domain names and TXT character strings are converted to presentation form
|
||||||
form both when unpacked and when converted to strings.
|
both when unpacked and when converted to strings.
|
||||||
|
|
||||||
For TXT character strings, tabs, carriage returns and line feeds will be
|
For TXT character strings, tabs, carriage returns and line feeds will be
|
||||||
converted to \t, \r and \n respectively. Back slashes and quotations marks
|
converted to \t, \r and \n respectively. Back slashes and quotations marks will
|
||||||
will be escaped. Bytes below 32 and above 127 will be converted to \DDD
|
be escaped. Bytes below 32 and above 127 will be converted to \DDD form.
|
||||||
form.
|
|
||||||
|
|
||||||
For domain names, in addition to the above rules brackets, periods,
|
For domain names, in addition to the above rules brackets, periods, spaces,
|
||||||
spaces, semicolons and the at symbol are escaped.
|
semicolons and the at symbol are escaped.
|
||||||
|
|
||||||
DNSSEC
|
DNSSEC
|
||||||
|
|
||||||
DNSSEC (DNS Security Extension) adds a layer of security to the DNS. It
|
DNSSEC (DNS Security Extension) adds a layer of security to the DNS. It uses
|
||||||
uses public key cryptography to sign resource records. The
|
public key cryptography to sign resource records. The public keys are stored in
|
||||||
public keys are stored in DNSKEY records and the signatures in RRSIG records.
|
DNSKEY records and the signatures in RRSIG records.
|
||||||
|
|
||||||
Requesting DNSSEC information for a zone is done by adding the DO (DNSSEC OK) bit
|
Requesting DNSSEC information for a zone is done by adding the DO (DNSSEC OK)
|
||||||
to a request.
|
bit to a request.
|
||||||
|
|
||||||
m := new(dns.Msg)
|
m := new(dns.Msg)
|
||||||
m.SetEdns0(4096, true)
|
m.SetEdns0(4096, true)
|
||||||
|
@ -126,9 +124,9 @@ Signature generation, signature verification and key generation are all supporte
|
||||||
|
|
||||||
DYNAMIC UPDATES
|
DYNAMIC UPDATES
|
||||||
|
|
||||||
Dynamic updates reuses the DNS message format, but renames three of
|
Dynamic updates reuses the DNS message format, but renames three of the
|
||||||
the sections. Question is Zone, Answer is Prerequisite, Authority is
|
sections. Question is Zone, Answer is Prerequisite, Authority is Update, only
|
||||||
Update, only the Additional is not renamed. See RFC 2136 for the gory details.
|
the Additional is not renamed. See RFC 2136 for the gory details.
|
||||||
|
|
||||||
You can set a rather complex set of rules for the existence of absence of
|
You can set a rather complex set of rules for the existence of absence of
|
||||||
certain resource records or names in a zone to specify if resource records
|
certain resource records or names in a zone to specify if resource records
|
||||||
|
@ -145,10 +143,9 @@ DNS function shows which functions exist to specify the prerequisites.
|
||||||
NONE rrset empty RRset does not exist dns.RRsetNotUsed
|
NONE rrset empty RRset does not exist dns.RRsetNotUsed
|
||||||
zone rrset rr RRset exists (value dep) dns.Used
|
zone rrset rr RRset exists (value dep) dns.Used
|
||||||
|
|
||||||
The prerequisite section can also be left empty.
|
The prerequisite section can also be left empty. If you have decided on the
|
||||||
If you have decided on the prerequisites you can tell what RRs should
|
prerequisites you can tell what RRs should be added or deleted. The next table
|
||||||
be added or deleted. The next table shows the options you have and
|
shows the options you have and what functions to call.
|
||||||
what functions to call.
|
|
||||||
|
|
||||||
3.4.2.6 - Table Of Metavalues Used In Update Section
|
3.4.2.6 - Table Of Metavalues Used In Update Section
|
||||||
|
|
||||||
|
@ -181,10 +178,10 @@ changes to the RRset after calling SetTsig() the signature will be incorrect.
|
||||||
...
|
...
|
||||||
// When sending the TSIG RR is calculated and filled in before sending
|
// When sending the TSIG RR is calculated and filled in before sending
|
||||||
|
|
||||||
When requesting an zone transfer (almost all TSIG usage is when requesting zone transfers), with
|
When requesting an zone transfer (almost all TSIG usage is when requesting zone
|
||||||
TSIG, this is the basic use pattern. In this example we request an AXFR for
|
transfers), with TSIG, this is the basic use pattern. In this example we
|
||||||
miek.nl. with TSIG key named "axfr." and secret "so6ZGir4GPAqINNh9U5c3A=="
|
request an AXFR for miek.nl. with TSIG key named "axfr." and secret
|
||||||
and using the server 176.58.119.54:
|
"so6ZGir4GPAqINNh9U5c3A==" and using the server 176.58.119.54:
|
||||||
|
|
||||||
t := new(dns.Transfer)
|
t := new(dns.Transfer)
|
||||||
m := new(dns.Msg)
|
m := new(dns.Msg)
|
||||||
|
@ -194,8 +191,8 @@ and using the server 176.58.119.54:
|
||||||
c, err := t.In(m, "176.58.119.54:53")
|
c, err := t.In(m, "176.58.119.54:53")
|
||||||
for r := range c { ... }
|
for r := range c { ... }
|
||||||
|
|
||||||
You can now read the records from the transfer as they come in. Each envelope is checked with TSIG.
|
You can now read the records from the transfer as they come in. Each envelope
|
||||||
If something is not correct an error is returned.
|
is checked with TSIG. If something is not correct an error is returned.
|
||||||
|
|
||||||
Basic use pattern validating and replying to a message that has TSIG set.
|
Basic use pattern validating and replying to a message that has TSIG set.
|
||||||
|
|
||||||
|
@ -220,29 +217,30 @@ Basic use pattern validating and replying to a message that has TSIG set.
|
||||||
|
|
||||||
PRIVATE RRS
|
PRIVATE RRS
|
||||||
|
|
||||||
RFC 6895 sets aside a range of type codes for private use. This range
|
RFC 6895 sets aside a range of type codes for private use. This range is 65,280
|
||||||
is 65,280 - 65,534 (0xFF00 - 0xFFFE). When experimenting with new Resource Records these
|
- 65,534 (0xFF00 - 0xFFFE). When experimenting with new Resource Records these
|
||||||
can be used, before requesting an official type code from IANA.
|
can be used, before requesting an official type code from IANA.
|
||||||
|
|
||||||
see http://miek.nl/2014/September/21/idn-and-private-rr-in-go-dns/ for more
|
See https://miek.nl/2014/September/21/idn-and-private-rr-in-go-dns/ for more
|
||||||
information.
|
information.
|
||||||
|
|
||||||
EDNS0
|
EDNS0
|
||||||
|
|
||||||
EDNS0 is an extension mechanism for the DNS defined in RFC 2671 and updated
|
EDNS0 is an extension mechanism for the DNS defined in RFC 2671 and updated by
|
||||||
by RFC 6891. It defines an new RR type, the OPT RR, which is then completely
|
RFC 6891. It defines an new RR type, the OPT RR, which is then completely
|
||||||
abused.
|
abused.
|
||||||
|
|
||||||
Basic use pattern for creating an (empty) OPT RR:
|
Basic use pattern for creating an (empty) OPT RR:
|
||||||
|
|
||||||
o := new(dns.OPT)
|
o := new(dns.OPT)
|
||||||
o.Hdr.Name = "." // MUST be the root zone, per definition.
|
o.Hdr.Name = "." // MUST be the root zone, per definition.
|
||||||
o.Hdr.Rrtype = dns.TypeOPT
|
o.Hdr.Rrtype = dns.TypeOPT
|
||||||
|
|
||||||
The rdata of an OPT RR consists out of a slice of EDNS0 (RFC 6891)
|
The rdata of an OPT RR consists out of a slice of EDNS0 (RFC 6891) interfaces.
|
||||||
interfaces. Currently only a few have been standardized: EDNS0_NSID
|
Currently only a few have been standardized: EDNS0_NSID (RFC 5001) and
|
||||||
(RFC 5001) and EDNS0_SUBNET (draft-vandergaast-edns-client-subnet-02). Note
|
EDNS0_SUBNET (draft-vandergaast-edns-client-subnet-02). Note that these options
|
||||||
that these options may be combined in an OPT RR.
|
may be combined in an OPT RR. Basic use pattern for a server to check if (and
|
||||||
Basic use pattern for a server to check if (and which) options are set:
|
which) options are set:
|
||||||
|
|
||||||
// o is a dns.OPT
|
// o is a dns.OPT
|
||||||
for _, s := range o.Option {
|
for _, s := range o.Option {
|
||||||
|
@ -262,10 +260,9 @@ From RFC 2931:
|
||||||
... protection for glue records, DNS requests, protection for message headers
|
... protection for glue records, DNS requests, protection for message headers
|
||||||
on requests and responses, and protection of the overall integrity of a response.
|
on requests and responses, and protection of the overall integrity of a response.
|
||||||
|
|
||||||
It works like TSIG, except that SIG(0) uses public key cryptography, instead of the shared
|
It works like TSIG, except that SIG(0) uses public key cryptography, instead of
|
||||||
secret approach in TSIG.
|
the shared secret approach in TSIG. Supported algorithms: DSA, ECDSAP256SHA256,
|
||||||
Supported algorithms: DSA, ECDSAP256SHA256, ECDSAP384SHA384, RSASHA1, RSASHA256 and
|
ECDSAP384SHA384, RSASHA1, RSASHA256 and RSASHA512.
|
||||||
RSASHA512.
|
|
||||||
|
|
||||||
Signing subsequent messages in multi-message sessions is not implemented.
|
Signing subsequent messages in multi-message sessions is not implemented.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
package dns
|
||||||
|
|
||||||
|
//go:generate go run duplicate_generate.go
|
||||||
|
|
||||||
|
// IsDuplicate checks of r1 and r2 are duplicates of each other, excluding the TTL.
|
||||||
|
// So this means the header data is equal *and* the RDATA is the same. Return true
|
||||||
|
// is so, otherwise false.
|
||||||
|
// It's is a protocol violation to have identical RRs in a message.
|
||||||
|
func IsDuplicate(r1, r2 RR) bool {
|
||||||
|
// Check whether the record header is identical.
|
||||||
|
if !r1.Header().isDuplicate(r2.Header()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether the RDATA is identical.
|
||||||
|
return r1.isDuplicate(r2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r1 *RR_Header) isDuplicate(_r2 RR) bool {
|
||||||
|
r2, ok := _r2.(*RR_Header)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if r1.Class != r2.Class {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if r1.Rrtype != r2.Rrtype {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !isDuplicateName(r1.Name, r2.Name) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// ignore TTL
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// isDuplicateName checks if the domain names s1 and s2 are equal.
|
||||||
|
func isDuplicateName(s1, s2 string) bool { return equal(s1, s2) }
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue