10
10
package dl
11
11
12
12
import (
13
- "crypto/hmac"
14
- "crypto/md5"
15
- "encoding/json"
16
13
"fmt"
17
- "html"
18
14
"html/template"
19
- "io"
20
- "log"
21
- "net/http"
22
15
"regexp"
23
16
"sort"
24
17
"strconv"
25
18
"strings"
26
- "sync"
27
19
"time"
28
-
29
- "cloud.google.com/go/datastore"
30
- "golang.org/x/net/context"
31
- "golang.org/x/tools/godoc/env"
32
- "golang.org/x/tools/internal/memcache"
33
20
)
34
21
35
22
const (
@@ -38,23 +25,6 @@ const (
38
25
cacheDuration = time .Hour
39
26
)
40
27
41
- type server struct {
42
- datastore * datastore.Client
43
- memcache * memcache.CodecClient
44
- }
45
-
46
- func RegisterHandlers (mux * http.ServeMux , dc * datastore.Client , mc * memcache.Client ) {
47
- s := server {dc , mc .WithCodec (memcache .Gob )}
48
- mux .HandleFunc ("/dl" , s .getHandler )
49
- mux .HandleFunc ("/dl/" , s .getHandler ) // also serves listHandler
50
- mux .HandleFunc ("/dl/upload" , s .uploadHandler )
51
-
52
- // NOTE(cbro): this only needs to be run once per project,
53
- // and should be behind an admin login.
54
- // TODO(cbro): move into a locally-run program? or remove?
55
- // mux.HandleFunc("/dl/init", initHandler)
56
- }
57
-
58
28
// File represents a file on the golang.org downloads page.
59
29
// It should be kept in sync with the upload code in x/build/cmd/release.
60
30
type File struct {
@@ -199,53 +169,6 @@ var (
199
169
templateFuncs = template.FuncMap {"pretty" : pretty }
200
170
)
201
171
202
- func (h server ) listHandler (w http.ResponseWriter , r * http.Request ) {
203
- if r .Method != "GET" {
204
- http .Error (w , "method not allowed" , http .StatusMethodNotAllowed )
205
- return
206
- }
207
- ctx := r .Context ()
208
- var d listTemplateData
209
-
210
- if err := h .memcache .Get (ctx , cacheKey , & d ); err != nil {
211
- if err != memcache .ErrCacheMiss {
212
- log .Printf ("ERROR cache get error: %v" , err )
213
- // NOTE(cbro): continue to hit datastore if the memcache is down.
214
- }
215
-
216
- var fs []File
217
- q := datastore .NewQuery ("File" ).Ancestor (rootKey )
218
- if _ , err := h .datastore .GetAll (ctx , q , & fs ); err != nil {
219
- log .Printf ("ERROR error listing: %v" , err )
220
- http .Error (w , "Could not get download page. Try again in a few minutes." , 500 )
221
- return
222
- }
223
- d .Stable , d .Unstable , d .Archive = filesToReleases (fs )
224
- if len (d .Stable ) > 0 {
225
- d .Featured = filesToFeatured (d .Stable [0 ].Files )
226
- }
227
-
228
- item := & memcache.Item {Key : cacheKey , Object : & d , Expiration : cacheDuration }
229
- if err := h .memcache .Set (ctx , item ); err != nil {
230
- log .Printf ("ERROR cache set error: %v" , err )
231
- }
232
- }
233
-
234
- if r .URL .Query ().Get ("mode" ) == "json" {
235
- w .Header ().Set ("Content-Type" , "application/json" )
236
- enc := json .NewEncoder (w )
237
- enc .SetIndent ("" , " " )
238
- if err := enc .Encode (d .Stable ); err != nil {
239
- log .Printf ("ERROR rendering JSON for releases: %v" , err )
240
- }
241
- return
242
- }
243
-
244
- if err := listTemplate .ExecuteTemplate (w , "root" , d ); err != nil {
245
- log .Printf ("ERROR executing template: %v" , err )
246
- }
247
- }
248
-
249
172
func filesToFeatured (fs []File ) (featured []Feature ) {
250
173
for _ , feature := range featuredFiles {
251
174
for _ , file := range fs {
@@ -390,101 +313,6 @@ func parseVersion(v string) (maj, min int, tail string) {
390
313
return
391
314
}
392
315
393
- func (h server ) uploadHandler (w http.ResponseWriter , r * http.Request ) {
394
- if r .Method != "POST" {
395
- http .Error (w , "method not allowed" , http .StatusMethodNotAllowed )
396
- return
397
- }
398
- ctx := r .Context ()
399
-
400
- // Authenticate using a user token (same as gomote).
401
- user := r .FormValue ("user" )
402
- if ! validUser (user ) {
403
- http .Error (w , "bad user" , http .StatusForbidden )
404
- return
405
- }
406
- if r .FormValue ("key" ) != h .userKey (ctx , user ) {
407
- http .Error (w , "bad key" , http .StatusForbidden )
408
- return
409
- }
410
-
411
- var f File
412
- defer r .Body .Close ()
413
- if err := json .NewDecoder (r .Body ).Decode (& f ); err != nil {
414
- log .Printf ("ERROR decoding upload JSON: %v" , err )
415
- http .Error (w , "Something broke" , http .StatusInternalServerError )
416
- return
417
- }
418
- if f .Filename == "" {
419
- http .Error (w , "Must provide Filename" , http .StatusBadRequest )
420
- return
421
- }
422
- if f .Uploaded .IsZero () {
423
- f .Uploaded = time .Now ()
424
- }
425
- k := datastore .NameKey ("File" , f .Filename , rootKey )
426
- if _ , err := h .datastore .Put (ctx , k , & f ); err != nil {
427
- log .Printf ("ERROR File entity: %v" , err )
428
- http .Error (w , "could not put File entity" , http .StatusInternalServerError )
429
- return
430
- }
431
- if err := h .memcache .Delete (ctx , cacheKey ); err != nil {
432
- log .Printf ("ERROR delete error: %v" , err )
433
- }
434
- io .WriteString (w , "OK" )
435
- }
436
-
437
- func (h server ) getHandler (w http.ResponseWriter , r * http.Request ) {
438
- // For go get golang.org/dl/go1.x.y, we need to serve the
439
- // same meta tags at /dl for cmd/go to validate against /dl/go1.x.y:
440
- if r .URL .Path == "/dl" && (r .Method == "GET" || r .Method == "HEAD" ) && r .FormValue ("go-get" ) == "1" {
441
- w .Header ().Set ("Content-Type" , "text/html; charset=utf-8" )
442
- fmt .Fprintf (w , `<!DOCTYPE html><html><head>
443
- <meta name="go-import" content="golang.org/dl git https://go.googlesource.com/dl">
444
- </head></html>` )
445
- return
446
- }
447
- if r .URL .Path == "/dl" {
448
- http .Redirect (w , r , "/dl/" , http .StatusFound )
449
- return
450
- }
451
-
452
- name := strings .TrimPrefix (r .URL .Path , "/dl/" )
453
- if name == "" {
454
- h .listHandler (w , r )
455
- return
456
- }
457
- if fileRe .MatchString (name ) {
458
- http .Redirect (w , r , downloadBaseURL + name , http .StatusFound )
459
- return
460
- }
461
- if goGetRe .MatchString (name ) {
462
- var isGoGet bool
463
- if r .Method == "GET" || r .Method == "HEAD" {
464
- w .Header ().Set ("Content-Type" , "text/html; charset=utf-8" )
465
- isGoGet = r .FormValue ("go-get" ) == "1"
466
- }
467
- if ! isGoGet {
468
- w .Header ().Set ("Location" , "https://golang.org/dl/#" + name )
469
- w .WriteHeader (http .StatusFound )
470
- }
471
- fmt .Fprintf (w , `<!DOCTYPE html>
472
- <html>
473
- <head>
474
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
475
- <meta name="go-import" content="golang.org/dl git https://go.googlesource.com/dl">
476
- <meta http-equiv="refresh" content="0; url=https://golang.org/dl/#%s">
477
- </head>
478
- <body>
479
- Nothing to see here; <a href="https://golang.org/dl/#%s">move along</a>.
480
- </body>
481
- </html>
482
- ` , html .EscapeString (name ), html .EscapeString (name ))
483
- return
484
- }
485
- http .NotFound (w , r )
486
- }
487
-
488
316
func validUser (user string ) bool {
489
317
switch user {
490
318
case "adg" , "bradfitz" , "cbro" , "andybons" , "valsorda" , "dmitshur" , "katiehockman" :
@@ -493,41 +321,11 @@ func validUser(user string) bool {
493
321
return false
494
322
}
495
323
496
- func (h server ) userKey (c context.Context , user string ) string {
497
- hash := hmac .New (md5 .New , []byte (h .secret (c )))
498
- hash .Write ([]byte ("user-" + user ))
499
- return fmt .Sprintf ("%x" , hash .Sum (nil ))
500
- }
501
-
502
324
var (
503
325
fileRe = regexp .MustCompile (`^go[0-9a-z.]+\.[0-9a-z.-]+\.(tar\.gz|pkg|msi|zip)$` )
504
326
goGetRe = regexp .MustCompile (`^go[0-9a-z.]+\.[0-9a-z.-]+$` )
505
327
)
506
328
507
- func (h server ) initHandler (w http.ResponseWriter , r * http.Request ) {
508
- var fileRoot struct {
509
- Root string
510
- }
511
- ctx := r .Context ()
512
- k := rootKey
513
- _ , err := h .datastore .RunInTransaction (ctx , func (tx * datastore.Transaction ) error {
514
- err := tx .Get (k , & fileRoot )
515
- if err != nil && err != datastore .ErrNoSuchEntity {
516
- return err
517
- }
518
- _ , err = tx .Put (k , & fileRoot )
519
- return err
520
- }, nil )
521
- if err != nil {
522
- http .Error (w , err .Error (), 500 )
523
- return
524
- }
525
- io .WriteString (w , "OK" )
526
- }
527
-
528
- // rootKey is the ancestor of all File entities.
529
- var rootKey = datastore .NameKey ("FileRoot" , "root" , nil )
530
-
531
329
// pretty returns a human-readable version of the given OS, Arch, or Kind.
532
330
func pretty (s string ) string {
533
331
t , ok := prettyStrings [s ]
@@ -552,55 +350,3 @@ var prettyStrings = map[string]string{
552
350
"installer" : "Installer" ,
553
351
"source" : "Source" ,
554
352
}
555
-
556
- // Code below copied from x/build/app/key
557
-
558
- var theKey struct {
559
- sync.RWMutex
560
- builderKey
561
- }
562
-
563
- type builderKey struct {
564
- Secret string
565
- }
566
-
567
- func (k * builderKey ) Key () * datastore.Key {
568
- return datastore .NameKey ("BuilderKey" , "root" , nil )
569
- }
570
-
571
- func (h server ) secret (ctx context.Context ) string {
572
- // check with rlock
573
- theKey .RLock ()
574
- k := theKey .Secret
575
- theKey .RUnlock ()
576
- if k != "" {
577
- return k
578
- }
579
-
580
- // prepare to fill; check with lock and keep lock
581
- theKey .Lock ()
582
- defer theKey .Unlock ()
583
- if theKey .Secret != "" {
584
- return theKey .Secret
585
- }
586
-
587
- // fill
588
- if err := h .datastore .Get (ctx , theKey .Key (), & theKey .builderKey ); err != nil {
589
- if err == datastore .ErrNoSuchEntity {
590
- // If the key is not stored in datastore, write it.
591
- // This only happens at the beginning of a new deployment.
592
- // The code is left here for SDK use and in case a fresh
593
- // deployment is ever needed. "gophers rule" is not the
594
- // real key.
595
- if env .IsProd () {
596
- panic ("lost key from datastore" )
597
- }
598
- theKey .Secret = "gophers rule"
599
- h .datastore .Put (ctx , theKey .Key (), & theKey .builderKey )
600
- return theKey .Secret
601
- }
602
- panic ("cannot load builder key: " + err .Error ())
603
- }
604
-
605
- return theKey .Secret
606
- }
0 commit comments