mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
godoc/dl, godoc/proxy, godoc/short, internal/memcache: delete
These packages existed only to power cmd/godoc for the purpose of serving the golang.org website. That functionality has moved into x/website as part of golang/go#29206. x/website has become the canonical source of golang.org in CL 162157, the golang.org-serving code was removed from cmd/godoc in CL 162400, and these packages can be deleted too now. This removes the last dependency on the cloud.google.com/go module, which results in a significant reduction of the number of indirect dependencies in x/tools (this is due to issue golang/go#29935, which affects the current version of the cloud.google.com/go module). Run go mod tidy (using Go 1.12 RC 1). Updates golang/go#29206 Updates golang/go#29981 Change-Id: If07e3ccae8538b3ebd51af64b6af5be5463f4906 Reviewed-on: https://go-review.googlesource.com/c/162401 Reviewed-by: Bryan C. Mills <bcmills@google.com> Reviewed-by: Channing Kimble-Brown <channing@golang.org> Reviewed-by: Michael Matloob <matloob@golang.org>
This commit is contained in:
parent
b4f3f03986
commit
97f80cd550
3
go.mod
3
go.mod
@ -1,8 +1,7 @@
|
||||
module golang.org/x/tools
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.36.0
|
||||
github.com/gomodule/redigo v2.0.0+incompatible
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f // indirect
|
||||
google.golang.org/appengine v1.4.0
|
||||
)
|
||||
|
140
go.sum
140
go.sum
@ -1,150 +1,10 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.36.0 h1:+aCSj7tOo2LODWVEuZDZeGCckdt6MlSF+X/rB3wUiS8=
|
||||
cloud.google.com/go v0.36.0/go.mod h1:RUoy9p/M4ge0HzT8L+SDZ8jg+Q6fth0CiBuhFJpSV40=
|
||||
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
|
||||
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
|
||||
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
|
||||
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU=
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||
github.com/googleapis/gax-go/v2 v2.0.3 h1:siORttZ36U2R/WjiJuDz8znElWBiAlO9rVt+mqJt0Cc=
|
||||
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
|
||||
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
|
||||
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
|
||||
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
|
||||
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
|
||||
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
|
||||
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
|
||||
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
|
||||
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
|
||||
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
|
||||
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
|
||||
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
|
||||
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
|
||||
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
|
||||
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
|
||||
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
|
||||
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
|
||||
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
|
||||
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
|
||||
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
go.opencensus.io v0.18.0 h1:Mk5rgZcggtbvtAun5aJzAtjKKN/t0R3jJPlWILlv938=
|
||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd h1:HuTn7WObtcDo9uEEU7rEqL0jYthdXAmZ6PP+meazmaU=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890 h1:uESlIz09WIHT2I+pasSXcpLYqYK8wHcdCetU3VuMBJE=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497 h1:GXMDsk4xWZCVzkAWCabrabzCCVmfiYSw72f1K/S9QIY=
|
||||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.1.0 h1:K6z2u68e86TPdSdefXdzvXgR1zEMa+459vBSfWYAZkI=
|
||||
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
||||
google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922 h1:mBVYJnbrXLA/ZCBTCe7PtEgAUP+1bg92qTaFoPHdz+8=
|
||||
google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
google.golang.org/grpc v1.17.0 h1:TRJYBgMclJvGYn2rIMjj+h9KtMt5r1Ij7ODVRIZkwhk=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
|
||||
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
||||
|
353
godoc/dl/dl.go
353
godoc/dl/dl.go
@ -1,353 +0,0 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package dl implements a simple downloads frontend server.
|
||||
//
|
||||
// It accepts HTTP POST requests to create a new download metadata entity, and
|
||||
// lists entities with sorting and filtering.
|
||||
// It is designed to run only on the instance of godoc that serves golang.org.
|
||||
package dl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
downloadBaseURL = "https://dl.google.com/go/"
|
||||
cacheKey = "download_list_3" // increment if listTemplateData changes
|
||||
cacheDuration = time.Hour
|
||||
)
|
||||
|
||||
// File represents a file on the golang.org downloads page.
|
||||
// It should be kept in sync with the upload code in x/build/cmd/release.
|
||||
type File struct {
|
||||
Filename string `json:"filename"`
|
||||
OS string `json:"os"`
|
||||
Arch string `json:"arch"`
|
||||
Version string `json:"version"`
|
||||
Checksum string `json:"-" datastore:",noindex"` // SHA1; deprecated
|
||||
ChecksumSHA256 string `json:"sha256" datastore:",noindex"`
|
||||
Size int64 `json:"size" datastore:",noindex"`
|
||||
Kind string `json:"kind"` // "archive", "installer", "source"
|
||||
Uploaded time.Time `json:"-"`
|
||||
}
|
||||
|
||||
func (f File) ChecksumType() string {
|
||||
if f.ChecksumSHA256 != "" {
|
||||
return "SHA256"
|
||||
}
|
||||
return "SHA1"
|
||||
}
|
||||
|
||||
func (f File) PrettyChecksum() string {
|
||||
if f.ChecksumSHA256 != "" {
|
||||
return f.ChecksumSHA256
|
||||
}
|
||||
return f.Checksum
|
||||
}
|
||||
|
||||
func (f File) PrettyOS() string {
|
||||
if f.OS == "darwin" {
|
||||
switch {
|
||||
case strings.Contains(f.Filename, "osx10.8"):
|
||||
return "OS X 10.8+"
|
||||
case strings.Contains(f.Filename, "osx10.6"):
|
||||
return "OS X 10.6+"
|
||||
}
|
||||
}
|
||||
return pretty(f.OS)
|
||||
}
|
||||
|
||||
func (f File) PrettySize() string {
|
||||
const mb = 1 << 20
|
||||
if f.Size == 0 {
|
||||
return ""
|
||||
}
|
||||
if f.Size < mb {
|
||||
// All Go releases are >1mb, but handle this case anyway.
|
||||
return fmt.Sprintf("%v bytes", f.Size)
|
||||
}
|
||||
return fmt.Sprintf("%.0fMB", float64(f.Size)/mb)
|
||||
}
|
||||
|
||||
var primaryPorts = map[string]bool{
|
||||
"darwin/amd64": true,
|
||||
"linux/386": true,
|
||||
"linux/amd64": true,
|
||||
"linux/armv6l": true,
|
||||
"windows/386": true,
|
||||
"windows/amd64": true,
|
||||
}
|
||||
|
||||
func (f File) PrimaryPort() bool {
|
||||
if f.Kind == "source" {
|
||||
return true
|
||||
}
|
||||
return primaryPorts[f.OS+"/"+f.Arch]
|
||||
}
|
||||
|
||||
func (f File) Highlight() bool {
|
||||
switch {
|
||||
case f.Kind == "source":
|
||||
return true
|
||||
case f.Arch == "amd64" && f.OS == "linux":
|
||||
return true
|
||||
case f.Arch == "amd64" && f.Kind == "installer":
|
||||
switch f.OS {
|
||||
case "windows":
|
||||
return true
|
||||
case "darwin":
|
||||
if !strings.Contains(f.Filename, "osx10.6") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (f File) URL() string {
|
||||
return downloadBaseURL + f.Filename
|
||||
}
|
||||
|
||||
type Release struct {
|
||||
Version string `json:"version"`
|
||||
Stable bool `json:"stable"`
|
||||
Files []File `json:"files"`
|
||||
Visible bool `json:"-"` // show files on page load
|
||||
SplitPortTable bool `json:"-"` // whether files should be split by primary/other ports.
|
||||
}
|
||||
|
||||
type Feature struct {
|
||||
// The File field will be filled in by the first stable File
|
||||
// whose name matches the given fileRE.
|
||||
File
|
||||
fileRE *regexp.Regexp
|
||||
|
||||
Platform string // "Microsoft Windows", "Apple macOS", "Linux"
|
||||
Requirements string // "Windows XP and above, 64-bit Intel Processor"
|
||||
}
|
||||
|
||||
// featuredFiles lists the platforms and files to be featured
|
||||
// at the top of the downloads page.
|
||||
var featuredFiles = []Feature{
|
||||
{
|
||||
Platform: "Microsoft Windows",
|
||||
Requirements: "Windows 7 or later, Intel 64-bit processor",
|
||||
fileRE: regexp.MustCompile(`\.windows-amd64\.msi$`),
|
||||
},
|
||||
{
|
||||
Platform: "Apple macOS",
|
||||
Requirements: "macOS 10.10 or later, Intel 64-bit processor",
|
||||
fileRE: regexp.MustCompile(`\.darwin-amd64(-osx10\.8)?\.pkg$`),
|
||||
},
|
||||
{
|
||||
Platform: "Linux",
|
||||
Requirements: "Linux 2.6.23 or later, Intel 64-bit processor",
|
||||
fileRE: regexp.MustCompile(`\.linux-amd64\.tar\.gz$`),
|
||||
},
|
||||
{
|
||||
Platform: "Source",
|
||||
fileRE: regexp.MustCompile(`\.src\.tar\.gz$`),
|
||||
},
|
||||
}
|
||||
|
||||
// data to send to the template; increment cacheKey if you change this.
|
||||
type listTemplateData struct {
|
||||
Featured []Feature
|
||||
Stable, Unstable, Archive []Release
|
||||
}
|
||||
|
||||
var (
|
||||
listTemplate = template.Must(template.New("").Funcs(templateFuncs).Parse(templateHTML))
|
||||
templateFuncs = template.FuncMap{"pretty": pretty}
|
||||
)
|
||||
|
||||
func filesToFeatured(fs []File) (featured []Feature) {
|
||||
for _, feature := range featuredFiles {
|
||||
for _, file := range fs {
|
||||
if feature.fileRE.MatchString(file.Filename) {
|
||||
feature.File = file
|
||||
featured = append(featured, feature)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func filesToReleases(fs []File) (stable, unstable, archive []Release) {
|
||||
sort.Sort(fileOrder(fs))
|
||||
|
||||
var r *Release
|
||||
var stableMaj, stableMin int
|
||||
add := func() {
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
if !r.Stable {
|
||||
if len(unstable) != 0 {
|
||||
// Only show one (latest) unstable version.
|
||||
return
|
||||
}
|
||||
maj, min, _ := parseVersion(r.Version)
|
||||
if maj < stableMaj || maj == stableMaj && min <= stableMin {
|
||||
// Display unstable version only if newer than the
|
||||
// latest stable release.
|
||||
return
|
||||
}
|
||||
unstable = append(unstable, *r)
|
||||
return
|
||||
}
|
||||
|
||||
// Reports whether the release is the most recent minor version of the
|
||||
// two most recent major versions.
|
||||
shouldAddStable := func() bool {
|
||||
if len(stable) >= 2 {
|
||||
// Show up to two stable versions.
|
||||
return false
|
||||
}
|
||||
if len(stable) == 0 {
|
||||
// Most recent stable version.
|
||||
stableMaj, stableMin, _ = parseVersion(r.Version)
|
||||
return true
|
||||
}
|
||||
if maj, _, _ := parseVersion(r.Version); maj == stableMaj {
|
||||
// Older minor version of most recent major version.
|
||||
return false
|
||||
}
|
||||
// Second most recent stable version.
|
||||
return true
|
||||
}
|
||||
if !shouldAddStable() {
|
||||
archive = append(archive, *r)
|
||||
return
|
||||
}
|
||||
|
||||
// Split the file list into primary/other ports for the stable releases.
|
||||
// NOTE(cbro): This is only done for stable releases because maintaining the historical
|
||||
// nature of primary/other ports for older versions is infeasible.
|
||||
// If freebsd is considered primary some time in the future, we'd not want to
|
||||
// mark all of the older freebsd binaries as "primary".
|
||||
// It might be better if we set that as a flag when uploading.
|
||||
r.SplitPortTable = true
|
||||
r.Visible = true // Toggle open all stable releases.
|
||||
stable = append(stable, *r)
|
||||
}
|
||||
for _, f := range fs {
|
||||
if r == nil || f.Version != r.Version {
|
||||
add()
|
||||
r = &Release{
|
||||
Version: f.Version,
|
||||
Stable: isStable(f.Version),
|
||||
}
|
||||
}
|
||||
r.Files = append(r.Files, f)
|
||||
}
|
||||
add()
|
||||
return
|
||||
}
|
||||
|
||||
// isStable reports whether the version string v is a stable version.
|
||||
func isStable(v string) bool {
|
||||
return !strings.Contains(v, "beta") && !strings.Contains(v, "rc")
|
||||
}
|
||||
|
||||
type fileOrder []File
|
||||
|
||||
func (s fileOrder) Len() int { return len(s) }
|
||||
func (s fileOrder) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
func (s fileOrder) Less(i, j int) bool {
|
||||
a, b := s[i], s[j]
|
||||
if av, bv := a.Version, b.Version; av != bv {
|
||||
return versionLess(av, bv)
|
||||
}
|
||||
if a.OS != b.OS {
|
||||
return a.OS < b.OS
|
||||
}
|
||||
if a.Arch != b.Arch {
|
||||
return a.Arch < b.Arch
|
||||
}
|
||||
if a.Kind != b.Kind {
|
||||
return a.Kind < b.Kind
|
||||
}
|
||||
return a.Filename < b.Filename
|
||||
}
|
||||
|
||||
func versionLess(a, b string) bool {
|
||||
// Put stable releases first.
|
||||
if isStable(a) != isStable(b) {
|
||||
return isStable(a)
|
||||
}
|
||||
maja, mina, ta := parseVersion(a)
|
||||
majb, minb, tb := parseVersion(b)
|
||||
if maja == majb {
|
||||
if mina == minb {
|
||||
return ta >= tb
|
||||
}
|
||||
return mina >= minb
|
||||
}
|
||||
return maja >= majb
|
||||
}
|
||||
|
||||
func parseVersion(v string) (maj, min int, tail string) {
|
||||
if i := strings.Index(v, "beta"); i > 0 {
|
||||
tail = v[i:]
|
||||
v = v[:i]
|
||||
}
|
||||
if i := strings.Index(v, "rc"); i > 0 {
|
||||
tail = v[i:]
|
||||
v = v[:i]
|
||||
}
|
||||
p := strings.Split(strings.TrimPrefix(v, "go1."), ".")
|
||||
maj, _ = strconv.Atoi(p[0])
|
||||
if len(p) < 2 {
|
||||
return
|
||||
}
|
||||
min, _ = strconv.Atoi(p[1])
|
||||
return
|
||||
}
|
||||
|
||||
func validUser(user string) bool {
|
||||
switch user {
|
||||
case "adg", "bradfitz", "cbro", "andybons", "valsorda", "dmitshur", "katiehockman", "julieqiu":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var (
|
||||
fileRe = regexp.MustCompile(`^go[0-9a-z.]+\.[0-9a-z.-]+\.(tar\.gz|pkg|msi|zip)$`)
|
||||
goGetRe = regexp.MustCompile(`^go[0-9a-z.]+\.[0-9a-z.-]+$`)
|
||||
)
|
||||
|
||||
// pretty returns a human-readable version of the given OS, Arch, or Kind.
|
||||
func pretty(s string) string {
|
||||
t, ok := prettyStrings[s]
|
||||
if !ok {
|
||||
return s
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
var prettyStrings = map[string]string{
|
||||
"darwin": "macOS",
|
||||
"freebsd": "FreeBSD",
|
||||
"linux": "Linux",
|
||||
"windows": "Windows",
|
||||
|
||||
"386": "x86",
|
||||
"amd64": "x86-64",
|
||||
"armv6l": "ARMv6",
|
||||
"arm64": "ARMv8",
|
||||
|
||||
"archive": "Archive",
|
||||
"installer": "Installer",
|
||||
"source": "Source",
|
||||
}
|
@ -1,159 +0,0 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package dl
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseVersion(t *testing.T) {
|
||||
for _, c := range []struct {
|
||||
in string
|
||||
maj, min int
|
||||
tail string
|
||||
}{
|
||||
{"go1.5", 5, 0, ""},
|
||||
{"go1.5beta1", 5, 0, "beta1"},
|
||||
{"go1.5.1", 5, 1, ""},
|
||||
{"go1.5.1rc1", 5, 1, "rc1"},
|
||||
} {
|
||||
maj, min, tail := parseVersion(c.in)
|
||||
if maj != c.maj || min != c.min || tail != c.tail {
|
||||
t.Errorf("parseVersion(%q) = %v, %v, %q; want %v, %v, %q",
|
||||
c.in, maj, min, tail, c.maj, c.min, c.tail)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileOrder(t *testing.T) {
|
||||
fs := []File{
|
||||
{Filename: "go1.3.src.tar.gz", Version: "go1.3", OS: "", Arch: "", Kind: "source"},
|
||||
{Filename: "go1.3.1.src.tar.gz", Version: "go1.3.1", OS: "", Arch: "", Kind: "source"},
|
||||
{Filename: "go1.3.linux-amd64.tar.gz", Version: "go1.3", OS: "linux", Arch: "amd64", Kind: "archive"},
|
||||
{Filename: "go1.3.1.linux-amd64.tar.gz", Version: "go1.3.1", OS: "linux", Arch: "amd64", Kind: "archive"},
|
||||
{Filename: "go1.3.darwin-amd64.tar.gz", Version: "go1.3", OS: "darwin", Arch: "amd64", Kind: "archive"},
|
||||
{Filename: "go1.3.darwin-amd64.pkg", Version: "go1.3", OS: "darwin", Arch: "amd64", Kind: "installer"},
|
||||
{Filename: "go1.3.darwin-386.tar.gz", Version: "go1.3", OS: "darwin", Arch: "386", Kind: "archive"},
|
||||
{Filename: "go1.3beta1.linux-amd64.tar.gz", Version: "go1.3beta1", OS: "linux", Arch: "amd64", Kind: "archive"},
|
||||
{Filename: "go1.3beta2.linux-amd64.tar.gz", Version: "go1.3beta2", OS: "linux", Arch: "amd64", Kind: "archive"},
|
||||
{Filename: "go1.3rc1.linux-amd64.tar.gz", Version: "go1.3rc1", OS: "linux", Arch: "amd64", Kind: "archive"},
|
||||
{Filename: "go1.2.linux-amd64.tar.gz", Version: "go1.2", OS: "linux", Arch: "amd64", Kind: "archive"},
|
||||
{Filename: "go1.2.2.linux-amd64.tar.gz", Version: "go1.2.2", OS: "linux", Arch: "amd64", Kind: "archive"},
|
||||
}
|
||||
sort.Sort(fileOrder(fs))
|
||||
var s []string
|
||||
for _, f := range fs {
|
||||
s = append(s, f.Filename)
|
||||
}
|
||||
got := strings.Join(s, "\n")
|
||||
want := strings.Join([]string{
|
||||
"go1.3.1.src.tar.gz",
|
||||
"go1.3.1.linux-amd64.tar.gz",
|
||||
"go1.3.src.tar.gz",
|
||||
"go1.3.darwin-386.tar.gz",
|
||||
"go1.3.darwin-amd64.tar.gz",
|
||||
"go1.3.darwin-amd64.pkg",
|
||||
"go1.3.linux-amd64.tar.gz",
|
||||
"go1.2.2.linux-amd64.tar.gz",
|
||||
"go1.2.linux-amd64.tar.gz",
|
||||
"go1.3rc1.linux-amd64.tar.gz",
|
||||
"go1.3beta2.linux-amd64.tar.gz",
|
||||
"go1.3beta1.linux-amd64.tar.gz",
|
||||
}, "\n")
|
||||
if got != want {
|
||||
t.Errorf("sort order is\n%s\nwant:\n%s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilesToReleases(t *testing.T) {
|
||||
fs := []File{
|
||||
{Version: "go1.7.4", OS: "darwin"},
|
||||
{Version: "go1.7.4", OS: "windows"},
|
||||
{Version: "go1.7", OS: "darwin"},
|
||||
{Version: "go1.7", OS: "windows"},
|
||||
{Version: "go1.6.2", OS: "darwin"},
|
||||
{Version: "go1.6.2", OS: "windows"},
|
||||
{Version: "go1.6", OS: "darwin"},
|
||||
{Version: "go1.6", OS: "windows"},
|
||||
{Version: "go1.5.2", OS: "darwin"},
|
||||
{Version: "go1.5.2", OS: "windows"},
|
||||
{Version: "go1.5", OS: "darwin"},
|
||||
{Version: "go1.5", OS: "windows"},
|
||||
{Version: "go1.5beta1", OS: "windows"},
|
||||
}
|
||||
stable, unstable, archive := filesToReleases(fs)
|
||||
if got, want := list(stable), "go1.7.4, go1.6.2"; got != want {
|
||||
t.Errorf("stable = %q; want %q", got, want)
|
||||
}
|
||||
if got, want := list(unstable), ""; got != want {
|
||||
t.Errorf("unstable = %q; want %q", got, want)
|
||||
}
|
||||
if got, want := list(archive), "go1.7, go1.6, go1.5.2, go1.5"; got != want {
|
||||
t.Errorf("archive = %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOldUnstableNotShown(t *testing.T) {
|
||||
fs := []File{
|
||||
{Version: "go1.7.4"},
|
||||
{Version: "go1.7"},
|
||||
{Version: "go1.7beta1"},
|
||||
}
|
||||
_, unstable, _ := filesToReleases(fs)
|
||||
if len(unstable) != 0 {
|
||||
t.Errorf("got unstable, want none")
|
||||
}
|
||||
}
|
||||
|
||||
// A new beta should show up under unstable, but not show up under archive. See golang.org/issue/29669.
|
||||
func TestNewUnstableShownOnce(t *testing.T) {
|
||||
fs := []File{
|
||||
{Version: "go1.12beta2"},
|
||||
{Version: "go1.11.4"},
|
||||
{Version: "go1.11"},
|
||||
{Version: "go1.10.7"},
|
||||
{Version: "go1.10"},
|
||||
{Version: "go1.9"},
|
||||
}
|
||||
stable, unstable, archive := filesToReleases(fs)
|
||||
if got, want := list(stable), "go1.11.4, go1.10.7"; got != want {
|
||||
t.Errorf("stable = %q; want %q", got, want)
|
||||
}
|
||||
if got, want := list(unstable), "go1.12beta2"; got != want {
|
||||
t.Errorf("unstable = %q; want %q", got, want)
|
||||
}
|
||||
if got, want := list(archive), "go1.11, go1.10, go1.9"; got != want {
|
||||
t.Errorf("archive = %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnstableShown(t *testing.T) {
|
||||
fs := []File{
|
||||
{Version: "go1.8beta2"},
|
||||
{Version: "go1.8rc1"},
|
||||
{Version: "go1.7.4"},
|
||||
{Version: "go1.7"},
|
||||
{Version: "go1.7beta1"},
|
||||
}
|
||||
_, unstable, _ := filesToReleases(fs)
|
||||
// Show RCs ahead of betas.
|
||||
if got, want := list(unstable), "go1.8rc1"; got != want {
|
||||
t.Errorf("unstable = %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// list returns a version list string for the given releases.
|
||||
func list(rs []Release) string {
|
||||
var s string
|
||||
for i, r := range rs {
|
||||
if i > 0 {
|
||||
s += ", "
|
||||
}
|
||||
s += r.Version
|
||||
}
|
||||
return s
|
||||
}
|
@ -1,266 +0,0 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build golangorg
|
||||
|
||||
package dl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"cloud.google.com/go/datastore"
|
||||
"golang.org/x/tools/godoc/env"
|
||||
"golang.org/x/tools/internal/memcache"
|
||||
)
|
||||
|
||||
type server struct {
|
||||
datastore *datastore.Client
|
||||
memcache *memcache.CodecClient
|
||||
}
|
||||
|
||||
func RegisterHandlers(mux *http.ServeMux, dc *datastore.Client, mc *memcache.Client) {
|
||||
s := server{dc, mc.WithCodec(memcache.Gob)}
|
||||
mux.HandleFunc("/dl", s.getHandler)
|
||||
mux.HandleFunc("/dl/", s.getHandler) // also serves listHandler
|
||||
mux.HandleFunc("/dl/upload", s.uploadHandler)
|
||||
|
||||
// NOTE(cbro): this only needs to be run once per project,
|
||||
// and should be behind an admin login.
|
||||
// TODO(cbro): move into a locally-run program? or remove?
|
||||
// mux.HandleFunc("/dl/init", initHandler)
|
||||
}
|
||||
|
||||
// rootKey is the ancestor of all File entities.
|
||||
var rootKey = datastore.NameKey("FileRoot", "root", nil)
|
||||
|
||||
func (h server) listHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "GET" {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
ctx := r.Context()
|
||||
var d listTemplateData
|
||||
|
||||
if err := h.memcache.Get(ctx, cacheKey, &d); err != nil {
|
||||
if err != memcache.ErrCacheMiss {
|
||||
log.Printf("ERROR cache get error: %v", err)
|
||||
// NOTE(cbro): continue to hit datastore if the memcache is down.
|
||||
}
|
||||
|
||||
var fs []File
|
||||
q := datastore.NewQuery("File").Ancestor(rootKey)
|
||||
if _, err := h.datastore.GetAll(ctx, q, &fs); err != nil {
|
||||
log.Printf("ERROR error listing: %v", err)
|
||||
http.Error(w, "Could not get download page. Try again in a few minutes.", 500)
|
||||
return
|
||||
}
|
||||
d.Stable, d.Unstable, d.Archive = filesToReleases(fs)
|
||||
if len(d.Stable) > 0 {
|
||||
d.Featured = filesToFeatured(d.Stable[0].Files)
|
||||
}
|
||||
|
||||
item := &memcache.Item{Key: cacheKey, Object: &d, Expiration: cacheDuration}
|
||||
if err := h.memcache.Set(ctx, item); err != nil {
|
||||
log.Printf("ERROR cache set error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if r.URL.Query().Get("mode") == "json" {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
enc := json.NewEncoder(w)
|
||||
enc.SetIndent("", " ")
|
||||
if err := enc.Encode(d.Stable); err != nil {
|
||||
log.Printf("ERROR rendering JSON for releases: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err := listTemplate.ExecuteTemplate(w, "root", d); err != nil {
|
||||
log.Printf("ERROR executing template: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h server) uploadHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
ctx := r.Context()
|
||||
|
||||
// Authenticate using a user token (same as gomote).
|
||||
user := r.FormValue("user")
|
||||
if !validUser(user) {
|
||||
http.Error(w, "bad user", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
if r.FormValue("key") != h.userKey(ctx, user) {
|
||||
http.Error(w, "bad key", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
var f File
|
||||
defer r.Body.Close()
|
||||
if err := json.NewDecoder(r.Body).Decode(&f); err != nil {
|
||||
log.Printf("ERROR decoding upload JSON: %v", err)
|
||||
http.Error(w, "Something broke", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if f.Filename == "" {
|
||||
http.Error(w, "Must provide Filename", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if f.Uploaded.IsZero() {
|
||||
f.Uploaded = time.Now()
|
||||
}
|
||||
k := datastore.NameKey("File", f.Filename, rootKey)
|
||||
if _, err := h.datastore.Put(ctx, k, &f); err != nil {
|
||||
log.Printf("ERROR File entity: %v", err)
|
||||
http.Error(w, "could not put File entity", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if err := h.memcache.Delete(ctx, cacheKey); err != nil {
|
||||
log.Printf("ERROR delete error: %v", err)
|
||||
}
|
||||
io.WriteString(w, "OK")
|
||||
}
|
||||
|
||||
func (h server) getHandler(w http.ResponseWriter, r *http.Request) {
|
||||
isGoGet := (r.Method == "GET" || r.Method == "HEAD") && r.FormValue("go-get") == "1"
|
||||
// For go get, we need to serve the same meta tags at /dl for cmd/go to
|
||||
// validate against the import path.
|
||||
if r.URL.Path == "/dl" && isGoGet {
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
fmt.Fprintf(w, `<!DOCTYPE html><html><head>
|
||||
<meta name="go-import" content="golang.org/dl git https://go.googlesource.com/dl">
|
||||
</head></html>`)
|
||||
return
|
||||
}
|
||||
if r.URL.Path == "/dl" {
|
||||
http.Redirect(w, r, "/dl/", http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
name := strings.TrimPrefix(r.URL.Path, "/dl/")
|
||||
var redirectURL string
|
||||
switch {
|
||||
case name == "":
|
||||
h.listHandler(w, r)
|
||||
return
|
||||
case fileRe.MatchString(name):
|
||||
http.Redirect(w, r, downloadBaseURL+name, http.StatusFound)
|
||||
return
|
||||
case name == "gotip":
|
||||
redirectURL = "https://godoc.org/golang.org/dl/gotip"
|
||||
case goGetRe.MatchString(name):
|
||||
redirectURL = "https://golang.org/dl/#" + name
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
if !isGoGet {
|
||||
w.Header().Set("Location", redirectURL)
|
||||
}
|
||||
fmt.Fprintf(w, `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="go-import" content="golang.org/dl git https://go.googlesource.com/dl">
|
||||
<meta http-equiv="refresh" content="0; url=%s">
|
||||
</head>
|
||||
<body>
|
||||
Nothing to see here; <a href="%s">move along</a>.
|
||||
</body>
|
||||
</html>
|
||||
`, html.EscapeString(redirectURL), html.EscapeString(redirectURL))
|
||||
}
|
||||
|
||||
func (h server) initHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var fileRoot struct {
|
||||
Root string
|
||||
}
|
||||
ctx := r.Context()
|
||||
k := rootKey
|
||||
_, err := h.datastore.RunInTransaction(ctx, func(tx *datastore.Transaction) error {
|
||||
err := tx.Get(k, &fileRoot)
|
||||
if err != nil && err != datastore.ErrNoSuchEntity {
|
||||
return err
|
||||
}
|
||||
_, err = tx.Put(k, &fileRoot)
|
||||
return err
|
||||
}, nil)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
io.WriteString(w, "OK")
|
||||
}
|
||||
|
||||
func (h server) userKey(c context.Context, user string) string {
|
||||
hash := hmac.New(md5.New, []byte(h.secret(c)))
|
||||
hash.Write([]byte("user-" + user))
|
||||
return fmt.Sprintf("%x", hash.Sum(nil))
|
||||
}
|
||||
|
||||
// Code below copied from x/build/app/key
|
||||
|
||||
var theKey struct {
|
||||
sync.RWMutex
|
||||
builderKey
|
||||
}
|
||||
|
||||
type builderKey struct {
|
||||
Secret string
|
||||
}
|
||||
|
||||
func (k *builderKey) Key() *datastore.Key {
|
||||
return datastore.NameKey("BuilderKey", "root", nil)
|
||||
}
|
||||
|
||||
func (h server) secret(ctx context.Context) string {
|
||||
// check with rlock
|
||||
theKey.RLock()
|
||||
k := theKey.Secret
|
||||
theKey.RUnlock()
|
||||
if k != "" {
|
||||
return k
|
||||
}
|
||||
|
||||
// prepare to fill; check with lock and keep lock
|
||||
theKey.Lock()
|
||||
defer theKey.Unlock()
|
||||
if theKey.Secret != "" {
|
||||
return theKey.Secret
|
||||
}
|
||||
|
||||
// fill
|
||||
if err := h.datastore.Get(ctx, theKey.Key(), &theKey.builderKey); err != nil {
|
||||
if err == datastore.ErrNoSuchEntity {
|
||||
// If the key is not stored in datastore, write it.
|
||||
// This only happens at the beginning of a new deployment.
|
||||
// The code is left here for SDK use and in case a fresh
|
||||
// deployment is ever needed. "gophers rule" is not the
|
||||
// real key.
|
||||
if env.IsProd() {
|
||||
panic("lost key from datastore")
|
||||
}
|
||||
theKey.Secret = "gophers rule"
|
||||
h.datastore.Put(ctx, theKey.Key(), &theKey.builderKey)
|
||||
return theKey.Secret
|
||||
}
|
||||
panic("cannot load builder key: " + err.Error())
|
||||
}
|
||||
|
||||
return theKey.Secret
|
||||
}
|
277
godoc/dl/tmpl.go
277
godoc/dl/tmpl.go
@ -1,277 +0,0 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package dl
|
||||
|
||||
// TODO(adg): refactor this to use the tools/godoc/static template.
|
||||
|
||||
const templateHTML = `
|
||||
{{define "root"}}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Downloads - The Go Programming Language</title>
|
||||
<link type="text/css" rel="stylesheet" href="/lib/godoc/style.css">
|
||||
<script type="text/javascript">window.initFuncs = [];</script>
|
||||
<style>
|
||||
table.codetable {
|
||||
margin-left: 20px; margin-right: 20px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.codetable tr {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
table.codetable tr:nth-child(2n), table.codetable tr.first {
|
||||
background-color: white;
|
||||
}
|
||||
table.codetable td, table.codetable th {
|
||||
white-space: nowrap;
|
||||
padding: 6px 10px;
|
||||
}
|
||||
table.codetable tt {
|
||||
font-size: xx-small;
|
||||
}
|
||||
table.codetable tr.highlight td {
|
||||
font-weight: bold;
|
||||
}
|
||||
a.downloadBox {
|
||||
display: block;
|
||||
color: #222;
|
||||
border: 1px solid #375EAB;
|
||||
border-radius: 5px;
|
||||
background: #E0EBF5;
|
||||
width: 280px;
|
||||
float: left;
|
||||
margin-left: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
a.downloadBox:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.downloadBox .platform {
|
||||
font-size: large;
|
||||
}
|
||||
.downloadBox .filename {
|
||||
color: #375EAB;
|
||||
font-weight: bold;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
a.downloadBox:hover .filename {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.downloadBox .size {
|
||||
font-size: small;
|
||||
font-weight: normal;
|
||||
}
|
||||
.downloadBox .reqs {
|
||||
font-size: small;
|
||||
font-style: italic;
|
||||
}
|
||||
.downloadBox .checksum {
|
||||
font-size: 5pt;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="topbar"><div class="container">
|
||||
|
||||
<div class="top-heading"><a href="/">The Go Programming Language</a></div>
|
||||
<form method="GET" action="/search">
|
||||
<div id="menu">
|
||||
<a href="/doc/">Documents</a>
|
||||
<a href="/pkg/">Packages</a>
|
||||
<a href="/project/">The Project</a>
|
||||
<a href="/help/">Help</a>
|
||||
<a href="/blog/">Blog</a>
|
||||
<span class="search-box"><input type="search" id="search" name="q" placeholder="Search" aria-label="Search" required><button type="submit"><span><!-- magnifying glass: --><svg width="24" height="24" viewBox="0 0 24 24"><title>submit search</title><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/><path d="M0 0h24v24H0z" fill="none"/></svg></span></button></span>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div></div>
|
||||
|
||||
<div id="page">
|
||||
<div class="container">
|
||||
|
||||
<h1>Downloads</h1>
|
||||
|
||||
<p>
|
||||
After downloading a binary release suitable for your system,
|
||||
please follow the <a href="/doc/install">installation instructions</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you are building from source,
|
||||
follow the <a href="/doc/install/source">source installation instructions</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
See the <a href="/doc/devel/release.html">release history</a> for more
|
||||
information about Go releases.
|
||||
</p>
|
||||
|
||||
{{with .Featured}}
|
||||
<h3 id="featured">Featured downloads</h3>
|
||||
{{range .}}
|
||||
{{template "download" .}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
<div style="clear: both;"></div>
|
||||
|
||||
{{with .Stable}}
|
||||
<h3 id="stable">Stable versions</h3>
|
||||
{{template "releases" .}}
|
||||
{{end}}
|
||||
|
||||
{{with .Unstable}}
|
||||
<h3 id="unstable">Unstable version</h3>
|
||||
{{template "releases" .}}
|
||||
{{end}}
|
||||
|
||||
{{with .Archive}}
|
||||
<div class="toggle" id="archive">
|
||||
<div class="collapsed">
|
||||
<h3 class="toggleButton" title="Click to show versions">Archived versions▹</h3>
|
||||
</div>
|
||||
<div class="expanded">
|
||||
<h3 class="toggleButton" title="Click to hide versions">Archived versions▾</h3>
|
||||
{{template "releases" .}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<div id="footer">
|
||||
<p>
|
||||
Except as
|
||||
<a href="https://developers.google.com/site-policies#restrictions">noted</a>,
|
||||
the content of this page is licensed under the Creative Commons
|
||||
Attribution 3.0 License,<br>
|
||||
and code is licensed under a <a href="http://golang.org/LICENSE">BSD license</a>.<br>
|
||||
<a href="http://golang.org/doc/tos.html">Terms of Service</a> |
|
||||
<a href="http://www.google.com/intl/en/policies/privacy/">Privacy Policy</a>
|
||||
</p>
|
||||
</div><!-- #footer -->
|
||||
|
||||
</div><!-- .container -->
|
||||
</div><!-- #page -->
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-11222381-2', 'auto');
|
||||
ga('send', 'pageview');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
<script src="/lib/godoc/jquery.js"></script>
|
||||
<script src="/lib/godoc/godocs.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('a.download').click(function(e) {
|
||||
// Try using the link text as the file name,
|
||||
// unless there's a child element of class 'filename'.
|
||||
var filename = $(this).text();
|
||||
var child = $(this).find('.filename');
|
||||
if (child.length > 0) {
|
||||
filename = child.text();
|
||||
}
|
||||
|
||||
// This must be kept in sync with the filenameRE in godocs.js.
|
||||
var filenameRE = /^go1\.\d+(\.\d+)?([a-z0-9]+)?\.([a-z0-9]+)(-[a-z0-9]+)?(-osx10\.[68])?\.([a-z.]+)$/;
|
||||
var m = filenameRE.exec(filename);
|
||||
if (!m) {
|
||||
// Don't redirect to the download page if it won't recognize this file.
|
||||
// (Should not happen.)
|
||||
return;
|
||||
}
|
||||
|
||||
var dest = "/doc/install";
|
||||
if (filename.indexOf(".src.") != -1) {
|
||||
dest += "/source";
|
||||
}
|
||||
dest += "?download=" + filename;
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
window.location = dest;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
{{end}}
|
||||
|
||||
{{define "releases"}}
|
||||
{{range .}}
|
||||
<div class="toggle{{if .Visible}}Visible{{end}}" id="{{.Version}}">
|
||||
<div class="collapsed">
|
||||
<h2 class="toggleButton" title="Click to show downloads for this version">{{.Version}} ▹</h2>
|
||||
</div>
|
||||
<div class="expanded">
|
||||
<h2 class="toggleButton" title="Click to hide downloads for this version">{{.Version}} ▾</h2>
|
||||
{{if .Stable}}{{else}}
|
||||
<p>This is an <b>unstable</b> version of Go. Use with caution.</p>
|
||||
<p>If you already have Go installed, you can install this version by running:</p>
|
||||
<pre>
|
||||
go get golang.org/dl/{{.Version}}
|
||||
</pre>
|
||||
<p>Then, use the <code>{{.Version}}</code> command instead of the <code>go</code> command to use {{.Version}}.</p>
|
||||
{{end}}
|
||||
{{template "files" .}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{define "files"}}
|
||||
<table class="codetable">
|
||||
<thead>
|
||||
<tr class="first">
|
||||
<th>File name</th>
|
||||
<th>Kind</th>
|
||||
<th>OS</th>
|
||||
<th>Arch</th>
|
||||
<th>Size</th>
|
||||
{{/* Use the checksum type of the first file for the column heading. */}}
|
||||
<th>{{(index .Files 0).ChecksumType}} Checksum</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{{if .SplitPortTable}}
|
||||
{{range .Files}}{{if .PrimaryPort}}{{template "file" .}}{{end}}{{end}}
|
||||
|
||||
{{/* TODO(cbro): add a link to an explanatory doc page */}}
|
||||
<tr class="first"><th colspan="6" class="first">Other Ports</th></tr>
|
||||
{{range .Files}}{{if not .PrimaryPort}}{{template "file" .}}{{end}}{{end}}
|
||||
{{else}}
|
||||
{{range .Files}}{{template "file" .}}{{end}}
|
||||
{{end}}
|
||||
</table>
|
||||
{{end}}
|
||||
|
||||
{{define "file"}}
|
||||
<tr{{if .Highlight}} class="highlight"{{end}}>
|
||||
<td class="filename"><a class="download" href="{{.URL}}">{{.Filename}}</a></td>
|
||||
<td>{{pretty .Kind}}</td>
|
||||
<td>{{.PrettyOS}}</td>
|
||||
<td>{{pretty .Arch}}</td>
|
||||
<td>{{.PrettySize}}</td>
|
||||
<td><tt>{{.PrettyChecksum}}</tt></td>
|
||||
</tr>
|
||||
{{end}}
|
||||
|
||||
{{define "download"}}
|
||||
<a class="download downloadBox" href="{{.URL}}">
|
||||
<div class="platform">{{.Platform}}</div>
|
||||
{{with .Requirements}}<div class="reqs">{{.}}</div>{{end}}
|
||||
<div>
|
||||
<span class="filename">{{.Filename}}</span>
|
||||
{{if .Size}}<span class="size">({{.PrettySize}})</span>{{end}}
|
||||
</div>
|
||||
</a>
|
||||
{{end}}
|
||||
`
|
@ -1,170 +0,0 @@
|
||||
// Copyright 2015 The Go 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 proxy proxies requests to the playground's compile and share handlers.
|
||||
// It is designed to run only on the instance of godoc that serves golang.org.
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/tools/godoc/env"
|
||||
)
|
||||
|
||||
const playgroundURL = "https://play.golang.org"
|
||||
|
||||
type Request struct {
|
||||
Body string
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Errors string
|
||||
Events []Event
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
Message string
|
||||
Kind string // "stdout" or "stderr"
|
||||
Delay time.Duration // time to wait before printing Message
|
||||
}
|
||||
|
||||
const expires = 7 * 24 * time.Hour // 1 week
|
||||
var cacheControlHeader = fmt.Sprintf("public, max-age=%d", int(expires.Seconds()))
|
||||
|
||||
func RegisterHandlers(mux *http.ServeMux) {
|
||||
mux.HandleFunc("/compile", compile)
|
||||
mux.HandleFunc("/share", share)
|
||||
}
|
||||
|
||||
func compile(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
http.Error(w, "I only answer to POST requests.", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
|
||||
body := r.FormValue("body")
|
||||
res := &Response{}
|
||||
req := &Request{Body: body}
|
||||
if err := makeCompileRequest(ctx, req, res); err != nil {
|
||||
log.Printf("ERROR compile error: %v", err)
|
||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
var out interface{}
|
||||
switch r.FormValue("version") {
|
||||
case "2":
|
||||
out = res
|
||||
default: // "1"
|
||||
out = struct {
|
||||
CompileErrors string `json:"compile_errors"`
|
||||
Output string `json:"output"`
|
||||
}{res.Errors, flatten(res.Events)}
|
||||
}
|
||||
b, err := json.Marshal(out)
|
||||
if err != nil {
|
||||
log.Printf("ERROR encoding response: %v", err)
|
||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
expiresTime := time.Now().Add(expires).UTC()
|
||||
w.Header().Set("Expires", expiresTime.Format(time.RFC1123))
|
||||
w.Header().Set("Cache-Control", cacheControlHeader)
|
||||
w.Write(b)
|
||||
}
|
||||
|
||||
// makePlaygroundRequest sends the given Request to the playground compile
|
||||
// endpoint and stores the response in the given Response.
|
||||
func makeCompileRequest(ctx context.Context, req *Request, res *Response) error {
|
||||
reqJ, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshalling request: %v", err)
|
||||
}
|
||||
hReq, _ := http.NewRequest("POST", playgroundURL+"/compile", bytes.NewReader(reqJ))
|
||||
hReq.Header.Set("Content-Type", "application/json")
|
||||
hReq = hReq.WithContext(ctx)
|
||||
|
||||
r, err := http.DefaultClient.Do(hReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("making request: %v", err)
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
if r.StatusCode != http.StatusOK {
|
||||
b, _ := ioutil.ReadAll(r.Body)
|
||||
return fmt.Errorf("bad status: %v body:\n%s", r.Status, b)
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(res); err != nil {
|
||||
return fmt.Errorf("unmarshalling response: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// flatten takes a sequence of Events and returns their contents, concatenated.
|
||||
func flatten(seq []Event) string {
|
||||
var buf bytes.Buffer
|
||||
for _, e := range seq {
|
||||
buf.WriteString(e.Message)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func share(w http.ResponseWriter, r *http.Request) {
|
||||
if googleCN(r) {
|
||||
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
// HACK(cbro): use a simple proxy rather than httputil.ReverseProxy because of Issue #28168.
|
||||
// TODO: investigate using ReverseProxy with a Director, unsetting whatever's necessary to make that work.
|
||||
req, _ := http.NewRequest("POST", playgroundURL+"/share", r.Body)
|
||||
req.Header.Set("Content-Type", r.Header.Get("Content-Type"))
|
||||
req = req.WithContext(r.Context())
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
log.Printf("ERROR share error: %v", err)
|
||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
copyHeader := func(k string) {
|
||||
if v := resp.Header.Get(k); v != "" {
|
||||
w.Header().Set(k, v)
|
||||
}
|
||||
}
|
||||
copyHeader("Content-Type")
|
||||
copyHeader("Content-Length")
|
||||
defer resp.Body.Close()
|
||||
w.WriteHeader(resp.StatusCode)
|
||||
io.Copy(w, resp.Body)
|
||||
}
|
||||
|
||||
func googleCN(r *http.Request) bool {
|
||||
if r.FormValue("googlecn") != "" {
|
||||
return true
|
||||
}
|
||||
if !env.IsProd() {
|
||||
return false
|
||||
}
|
||||
if strings.HasSuffix(r.Host, ".cn") {
|
||||
return true
|
||||
}
|
||||
switch r.Header.Get("X-AppEngine-Country") {
|
||||
case "", "ZZ", "CN":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
@ -1,186 +0,0 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build golangorg
|
||||
|
||||
// Package short implements a simple URL shortener, serving an administrative
|
||||
// interface at /s and shortened urls from /s/key.
|
||||
// It is designed to run only on the instance of godoc that serves golang.org.
|
||||
package short
|
||||
|
||||
// TODO(adg): collect statistics on URL visits
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
|
||||
"cloud.google.com/go/datastore"
|
||||
"golang.org/x/tools/internal/memcache"
|
||||
"google.golang.org/appengine/user"
|
||||
)
|
||||
|
||||
const (
|
||||
prefix = "/s"
|
||||
kind = "Link"
|
||||
baseURL = "https://golang.org" + prefix
|
||||
)
|
||||
|
||||
// Link represents a short link.
|
||||
type Link struct {
|
||||
Key, Target string
|
||||
}
|
||||
|
||||
var validKey = regexp.MustCompile(`^[a-zA-Z0-9-_.]+$`)
|
||||
|
||||
type server struct {
|
||||
datastore *datastore.Client
|
||||
memcache *memcache.CodecClient
|
||||
}
|
||||
|
||||
func RegisterHandlers(mux *http.ServeMux, dc *datastore.Client, mc *memcache.Client) {
|
||||
s := server{dc, mc.WithCodec(memcache.JSON)}
|
||||
mux.HandleFunc(prefix+"/", s.linkHandler)
|
||||
|
||||
// TODO(cbro): move storage of the links to a text file in Gerrit.
|
||||
// Disable the admin handler until that happens, since GAE Flex doesn't support
|
||||
// the "google.golang.org/appengine/user" package.
|
||||
// See golang.org/issue/29988 and golang.org/issue/27205#issuecomment-418673218.
|
||||
// mux.HandleFunc(prefix, adminHandler)
|
||||
mux.HandleFunc(prefix, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
io.WriteString(w, "Link creation temporarily unavailable. See golang.org/issue/29988.")
|
||||
})
|
||||
}
|
||||
|
||||
// linkHandler services requests to short URLs.
|
||||
// http://golang.org/s/key
|
||||
// It consults memcache and datastore for the Link for key.
|
||||
// It then sends a redirects or an error message.
|
||||
func (h server) linkHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
key := r.URL.Path[len(prefix)+1:]
|
||||
if !validKey.MatchString(key) {
|
||||
http.Error(w, "not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
var link Link
|
||||
if err := h.memcache.Get(ctx, cacheKey(key), &link); err != nil {
|
||||
k := datastore.NameKey(kind, key, nil)
|
||||
err = h.datastore.Get(ctx, k, &link)
|
||||
switch err {
|
||||
case datastore.ErrNoSuchEntity:
|
||||
http.Error(w, "not found", http.StatusNotFound)
|
||||
return
|
||||
default: // != nil
|
||||
log.Printf("ERROR %q: %v", key, err)
|
||||
http.Error(w, "internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
case nil:
|
||||
item := &memcache.Item{
|
||||
Key: cacheKey(key),
|
||||
Object: &link,
|
||||
}
|
||||
if err := h.memcache.Set(ctx, item); err != nil {
|
||||
log.Printf("WARNING %q: %v", key, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
http.Redirect(w, r, link.Target, http.StatusFound)
|
||||
}
|
||||
|
||||
var adminTemplate = template.Must(template.New("admin").Parse(templateHTML))
|
||||
|
||||
// adminHandler serves an administrative interface.
|
||||
func (h server) adminHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
if !user.IsAdmin(ctx) {
|
||||
http.Error(w, "forbidden", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
var newLink *Link
|
||||
var doErr error
|
||||
if r.Method == "POST" {
|
||||
key := r.FormValue("key")
|
||||
switch r.FormValue("do") {
|
||||
case "Add":
|
||||
newLink = &Link{key, r.FormValue("target")}
|
||||
doErr = h.putLink(ctx, newLink)
|
||||
case "Delete":
|
||||
k := datastore.NameKey(kind, key, nil)
|
||||
doErr = h.datastore.Delete(ctx, k)
|
||||
default:
|
||||
http.Error(w, "unknown action", http.StatusBadRequest)
|
||||
}
|
||||
err := h.memcache.Delete(ctx, cacheKey(key))
|
||||
if err != nil && err != memcache.ErrCacheMiss {
|
||||
log.Printf("WARNING %q: %v", key, err)
|
||||
}
|
||||
}
|
||||
|
||||
var links []*Link
|
||||
q := datastore.NewQuery(kind).Order("Key")
|
||||
if _, err := h.datastore.GetAll(ctx, q, &links); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
log.Printf("ERROR %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Put the new link in the list if it's not there already.
|
||||
// (Eventual consistency means that it might not show up
|
||||
// immediately, which might be confusing for the user.)
|
||||
if newLink != nil && doErr == nil {
|
||||
found := false
|
||||
for i := range links {
|
||||
if links[i].Key == newLink.Key {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
links = append([]*Link{newLink}, links...)
|
||||
}
|
||||
newLink = nil
|
||||
}
|
||||
|
||||
var data = struct {
|
||||
BaseURL string
|
||||
Prefix string
|
||||
Links []*Link
|
||||
New *Link
|
||||
Error error
|
||||
}{baseURL, prefix, links, newLink, doErr}
|
||||
if err := adminTemplate.Execute(w, &data); err != nil {
|
||||
log.Printf("ERROR adminTemplate: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// putLink validates the provided link and puts it into the datastore.
|
||||
func (h server) putLink(ctx context.Context, link *Link) error {
|
||||
if !validKey.MatchString(link.Key) {
|
||||
return errors.New("invalid key; must match " + validKey.String())
|
||||
}
|
||||
if _, err := url.Parse(link.Target); err != nil {
|
||||
return fmt.Errorf("bad target: %v", err)
|
||||
}
|
||||
k := datastore.NameKey(kind, link.Key, nil)
|
||||
_, err := h.datastore.Put(ctx, k, link)
|
||||
return err
|
||||
}
|
||||
|
||||
// cacheKey returns a short URL key as a memcache key.
|
||||
func cacheKey(key string) string {
|
||||
return "link-" + key
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package short
|
||||
|
||||
const templateHTML = `
|
||||
<!doctype HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>golang.org URL shortener</title>
|
||||
<style>
|
||||
body {
|
||||
background: white;
|
||||
}
|
||||
input {
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
input[type=text] {
|
||||
width: 400px;
|
||||
}
|
||||
input, td, th {
|
||||
color: #333;
|
||||
font-family: Georgia, Times New Roman, serif;
|
||||
}
|
||||
input, td {
|
||||
font-size: 14pt;
|
||||
}
|
||||
th {
|
||||
font-size: 16pt;
|
||||
text-align: left;
|
||||
padding-top: 10px;
|
||||
}
|
||||
.autoselect {
|
||||
border: none;
|
||||
}
|
||||
.error {
|
||||
color: #900;
|
||||
}
|
||||
table {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<table>
|
||||
|
||||
{{with .Error}}
|
||||
<tr>
|
||||
<th colspan="3">Error</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="error" colspan="3">{{.}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
|
||||
<tr>
|
||||
<th>Key</th>
|
||||
<th>Target</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
|
||||
<form method="POST" action="{{.Prefix}}">
|
||||
<tr>
|
||||
<td><input type="text" name="key"{{with .New}} value="{{.Key}}"{{end}}></td>
|
||||
<td><input type="text" name="target"{{with .New}} value="{{.Target}}"{{end}}></td>
|
||||
<td><input type="submit" name="do" value="Add">
|
||||
</tr>
|
||||
</form>
|
||||
|
||||
{{with .Links}}
|
||||
<tr>
|
||||
<th>Short Link</th>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
{{range .}}
|
||||
<tr>
|
||||
<td><input class="autoselect" type="text" orig="{{$.BaseURL}}/{{.Key}}" value="{{$.BaseURL}}/{{.Key}}"></td>
|
||||
<td><input class="autoselect" type="text" orig="{{.Target}}" value="{{.Target}}"></td>
|
||||
<td>
|
||||
<form method="POST" action="{{$.Prefix}}">
|
||||
<input type="hidden" name="key" value="{{.Key}}">
|
||||
<input type="submit" name="do" value="Delete" class="delete">
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
</table>
|
||||
|
||||
</body>
|
||||
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
|
||||
<script type="text/javascript">window.jQuery || document.write(unescape("%3Cscript src='/doc/jquery.js' type='text/javascript'%3E%3C/script%3E"));</script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('.autoselect').each(function() {
|
||||
$(this).click(function() {
|
||||
$(this).select();
|
||||
});
|
||||
$(this).change(function() {
|
||||
$(this).val($(this).attr('orig'));
|
||||
});
|
||||
});
|
||||
$('.delete').click(function(e) {
|
||||
var link = $(this).closest('tr').find('input').first().val();
|
||||
var ok = confirm('Delete this link?\n' + link);
|
||||
if (!ok) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
`
|
@ -1,159 +0,0 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build golangorg
|
||||
|
||||
// Package memcache provides a minimally compatible interface for
|
||||
// google.golang.org/appengine/memcache
|
||||
// and stores the data in Redis (e.g., via Cloud Memorystore).
|
||||
package memcache
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/gob"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/gomodule/redigo/redis"
|
||||
)
|
||||
|
||||
var ErrCacheMiss = errors.New("memcache: cache miss")
|
||||
|
||||
func New(addr string) *Client {
|
||||
const maxConns = 20
|
||||
|
||||
pool := redis.NewPool(func() (redis.Conn, error) {
|
||||
return redis.Dial("tcp", addr)
|
||||
}, maxConns)
|
||||
|
||||
return &Client{
|
||||
pool: pool,
|
||||
}
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
pool *redis.Pool
|
||||
}
|
||||
|
||||
type CodecClient struct {
|
||||
client *Client
|
||||
codec Codec
|
||||
}
|
||||
|
||||
type Item struct {
|
||||
Key string
|
||||
Value []byte
|
||||
Object interface{} // Used with Codec.
|
||||
Expiration time.Duration // Read-only.
|
||||
}
|
||||
|
||||
func (c *Client) WithCodec(codec Codec) *CodecClient {
|
||||
return &CodecClient{
|
||||
c, codec,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Delete(ctx context.Context, key string) error {
|
||||
conn, err := c.pool.GetContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
_, err = conn.Do("DEL", key)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *CodecClient) Delete(ctx context.Context, key string) error {
|
||||
return c.client.Delete(ctx, key)
|
||||
}
|
||||
|
||||
func (c *Client) Set(ctx context.Context, item *Item) error {
|
||||
if item.Value == nil {
|
||||
return errors.New("nil item value")
|
||||
}
|
||||
return c.set(ctx, item.Key, item.Value, item.Expiration)
|
||||
}
|
||||
|
||||
func (c *CodecClient) Set(ctx context.Context, item *Item) error {
|
||||
if item.Object == nil {
|
||||
return errors.New("nil object value")
|
||||
}
|
||||
b, err := c.codec.Marshal(item.Object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.client.set(ctx, item.Key, b, item.Expiration)
|
||||
}
|
||||
|
||||
func (c *Client) set(ctx context.Context, key string, value []byte, expiration time.Duration) error {
|
||||
conn, err := c.pool.GetContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
if expiration == 0 {
|
||||
_, err := conn.Do("SET", key, value)
|
||||
return err
|
||||
}
|
||||
|
||||
// NOTE(cbro): redis does not support expiry in units more granular than a second.
|
||||
exp := int64(expiration.Seconds())
|
||||
if exp == 0 {
|
||||
// Redis doesn't allow a zero expiration, delete the key instead.
|
||||
_, err := conn.Do("DEL", key)
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = conn.Do("SETEX", key, exp, value)
|
||||
return err
|
||||
}
|
||||
|
||||
// Get gets the item.
|
||||
func (c *Client) Get(ctx context.Context, key string) ([]byte, error) {
|
||||
conn, err := c.pool.GetContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
b, err := redis.Bytes(conn.Do("GET", key))
|
||||
if err == redis.ErrNil {
|
||||
err = ErrCacheMiss
|
||||
}
|
||||
return b, err
|
||||
}
|
||||
|
||||
func (c *CodecClient) Get(ctx context.Context, key string, v interface{}) error {
|
||||
b, err := c.client.Get(ctx, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.codec.Unmarshal(b, v)
|
||||
}
|
||||
|
||||
var (
|
||||
Gob = Codec{gobMarshal, gobUnmarshal}
|
||||
JSON = Codec{json.Marshal, json.Unmarshal}
|
||||
)
|
||||
|
||||
type Codec struct {
|
||||
Marshal func(interface{}) ([]byte, error)
|
||||
Unmarshal func([]byte, interface{}) error
|
||||
}
|
||||
|
||||
func gobMarshal(v interface{}) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
if err := gob.NewEncoder(&buf).Encode(v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func gobUnmarshal(data []byte, v interface{}) error {
|
||||
return gob.NewDecoder(bytes.NewBuffer(data)).Decode(v)
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build golangorg
|
||||
|
||||
package memcache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func getClient(t *testing.T) *Client {
|
||||
t.Helper()
|
||||
|
||||
addr := os.Getenv("GOLANG_REDIS_ADDR")
|
||||
if addr == "" {
|
||||
t.Skip("skipping because GOLANG_REDIS_ADDR is unset")
|
||||
}
|
||||
|
||||
return New(addr)
|
||||
}
|
||||
|
||||
func TestCacheMiss(t *testing.T) {
|
||||
c := getClient(t)
|
||||
ctx := context.Background()
|
||||
|
||||
if _, err := c.Get(ctx, "doesnotexist"); err != ErrCacheMiss {
|
||||
t.Errorf("got %v; want ErrCacheMiss", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpiry(t *testing.T) {
|
||||
c := getClient(t).WithCodec(Gob)
|
||||
ctx := context.Background()
|
||||
|
||||
key := "testexpiry"
|
||||
|
||||
firstTime := time.Now()
|
||||
err := c.Set(ctx, &Item{
|
||||
Key: key,
|
||||
Object: firstTime,
|
||||
Expiration: 3500 * time.Millisecond, // NOTE: check that non-rounded expiries work.
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Set: %v", err)
|
||||
}
|
||||
|
||||
var newTime time.Time
|
||||
if err := c.Get(ctx, key, &newTime); err != nil {
|
||||
t.Fatalf("Get: %v", err)
|
||||
}
|
||||
if !firstTime.Equal(newTime) {
|
||||
t.Errorf("Get: got value %v, want %v", newTime, firstTime)
|
||||
}
|
||||
|
||||
time.Sleep(4 * time.Second)
|
||||
|
||||
if err := c.Get(ctx, key, &newTime); err != ErrCacheMiss {
|
||||
t.Errorf("Get: got %v, want ErrCacheMiss", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShortExpiry(t *testing.T) {
|
||||
c := getClient(t).WithCodec(Gob)
|
||||
ctx := context.Background()
|
||||
|
||||
key := "testshortexpiry"
|
||||
|
||||
err := c.Set(ctx, &Item{
|
||||
Key: key,
|
||||
Value: []byte("ok"),
|
||||
Expiration: time.Millisecond,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Set: %v", err)
|
||||
}
|
||||
|
||||
if err := c.Get(ctx, key, nil); err != ErrCacheMiss {
|
||||
t.Errorf("GetBytes: got %v, want ErrCacheMiss", err)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user