1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16
17
18 package client
19
20 import (
21 "bytes"
22 "context"
23 "crypto/tls"
24 "encoding/json"
25 "errors"
26 "fmt"
27 "io"
28 "io/ioutil"
29 "log"
30 "net"
31 "net/http"
32 "net/url"
33 "os"
34 "regexp"
35 "strings"
36 "sync"
37 "time"
38
39 "perkeep.org/internal/hashutil"
40 "perkeep.org/internal/httputil"
41 "perkeep.org/internal/osutil"
42 "perkeep.org/pkg/auth"
43 "perkeep.org/pkg/blob"
44 "perkeep.org/pkg/blobserver"
45 "perkeep.org/pkg/buildinfo"
46 "perkeep.org/pkg/client/android"
47 "perkeep.org/pkg/schema"
48 "perkeep.org/pkg/search"
49 "perkeep.org/pkg/types/camtypes"
50
51 "go4.org/syncutil"
52 "golang.org/x/net/http2"
53 )
54
55
56
57
58
59 type Client struct {
60
61
62
63
64
65
66 server string
67
68 prefixOnce syncutil.Once
69 prefixv string
70 isSharePrefix bool
71
72 discoOnce syncutil.Once
73 searchRoot string
74 downloadHelper string
75 storageGen string
76 hasLegacySHA1 bool
77 syncHandlers []*SyncInfo
78 serverKeyID string
79 helpRoot string
80 shareRoot string
81 serverPublicKeyBlobRef blob.Ref
82
83 signerOnce sync.Once
84 signer *schema.Signer
85 signerErr error
86 signHandler string
87
88 authMode auth.AuthMode
89
90
91 authErr error
92
93 httpClient *http.Client
94 haveCache HaveCache
95
96
97 sto blobserver.Storage
98
99 initTrustedCertsOnce sync.Once
100
101
102
103
104
105
106
107
108
109
110 trustedCerts []string
111
112
113
114 insecureAnyTLSCert bool
115
116 initIgnoredFilesOnce sync.Once
117
118
119
120
121 ignoredFiles []string
122 ignoreChecker func(path string) bool
123
124 initSignerPublicKeyBlobrefOnce sync.Once
125 signerPublicKeyRef blob.Ref
126 publicKeyArmored string
127
128 statsMutex sync.Mutex
129 stats Stats
130
131
132
133
134 viaMu sync.RWMutex
135 via map[blob.Ref]blob.Ref
136
137
138
139
140 Verbose bool
141
142
143
144
145 Logger *log.Logger
146
147 httpGate *syncutil.Gate
148 transportConfig *TransportConfig
149
150 noExtConfig bool
151
152
153
154
155
156 sameOrigin bool
157 }
158
159 const maxParallelHTTP_h1 = 5
160 const maxParallelHTTP_h2 = 50
161
162
163
164 var inGopherJS bool
165
166
167
168
169
170 func New(opts ...ClientOption) (*Client, error) {
171 c := &Client{
172 haveCache: noHaveCache{},
173 Logger: log.New(os.Stderr, "", log.Ldate|log.Ltime),
174 authMode: auth.None{},
175 }
176 for _, v := range opts {
177 v.modifyClient(c)
178 }
179 if c.sto != nil && len(opts) > 1 {
180 return nil, errors.New("use of OptionUseStorageClient precludes use of any other options")
181 }
182
183 if inGopherJS {
184 c.noExtConfig = true
185 c.sameOrigin = true
186 }
187 if c.noExtConfig {
188 c.setDefaultHTTPClient()
189 return c, nil
190 }
191
192 if c.server != "" {
193 if !isURLOrHostPort(c.server) {
194 configOnce.Do(parseConfig)
195 serverConf, ok := config.Servers[c.server]
196 if !ok {
197 log.Fatalf("%q looks like a server alias, but no such alias found in config at %v", c.server, osutil.UserClientConfigPath())
198 }
199 c.server = serverConf.Server
200 }
201 c.setDefaultHTTPClient()
202 return c, nil
203 }
204
205 var err error
206 c.server, err = getServer()
207 if err != nil {
208 return nil, err
209 }
210 err = c.SetupAuth()
211 if err != nil {
212 return nil, err
213 }
214 c.setDefaultHTTPClient()
215 return c, nil
216 }
217
218 func (c *Client) setDefaultHTTPClient() {
219 if c.httpClient == nil {
220 c.httpClient = &http.Client{
221 Transport: c.transportForConfig(c.transportConfig),
222 }
223 }
224 c.httpGate = syncutil.NewGate(httpGateSize(c.httpClient.Transport))
225 }
226
227
228 func NewOrFail(opts ...ClientOption) *Client {
229 c, err := New(opts...)
230 if err != nil {
231 log.Fatalf("error creating client: %v", err)
232 }
233 return c
234 }
235
236
237 func (c *Client) NewPathClient(path string) (*Client, error) {
238 u, err := url.Parse(c.server)
239 if err != nil {
240 return nil, fmt.Errorf("bogus server %q for NewPathClient receiver: %v", c.server, err)
241 }
242 u.Path = path
243 pc, err := New(OptionServer(u.String()))
244 if err != nil {
245 return nil, err
246 }
247 pc.authMode = c.authMode
248 pc.discoOnce.Do(noop)
249 return pc, nil
250 }
251
252
253 type TransportConfig struct {
254
255
256 Proxy func(*http.Request) (*url.URL, error)
257 Verbose bool
258 }
259
260 func (c *Client) useHTTP2(tc *TransportConfig) bool {
261 if !c.useTLS() {
262 return false
263 }
264 if android.IsChild() {
265
266 return false
267 }
268 if os.Getenv("HTTPS_PROXY") != "" || os.Getenv("https_proxy") != "" ||
269 (tc != nil && tc.Proxy != nil) {
270
271 return false
272 }
273 return true
274 }
275
276
277
278
279
280 func (c *Client) transportForConfig(tc *TransportConfig) http.RoundTripper {
281 if inGopherJS {
282
283
284
285
286 return nil
287 }
288 if c == nil {
289 return nil
290 }
291 var transport http.RoundTripper
292 proxy := http.ProxyFromEnvironment
293 if tc != nil && tc.Proxy != nil {
294 proxy = tc.Proxy
295 }
296
297 if c.useHTTP2(tc) {
298 transport = &http2.Transport{
299 DialTLS: c.http2DialTLSFunc(),
300 }
301 } else {
302 transport = &http.Transport{
303 DialTLS: c.DialTLSFunc(),
304 Dial: c.DialFunc(),
305 Proxy: proxy,
306 MaxIdleConnsPerHost: maxParallelHTTP_h1,
307 }
308 }
309 httpStats := &httputil.StatsTransport{
310 Transport: transport,
311 }
312 if tc != nil {
313 httpStats.VerboseLog = tc.Verbose
314 }
315 transport = httpStats
316 if android.IsChild() {
317 transport = &android.StatsTransport{Rt: transport}
318 }
319 return transport
320 }
321
322
323
324 func (c *Client) HTTPStats() *httputil.StatsTransport {
325 st, _ := c.httpClient.Transport.(*httputil.StatsTransport)
326 return st
327 }
328
329 type ClientOption interface {
330 modifyClient(*Client)
331 }
332
333
334
335
336
337
338
339
340 func OptionServer(server string) ClientOption {
341 return optionServer(server)
342 }
343
344 type optionServer string
345
346 func (s optionServer) modifyClient(c *Client) { c.server = string(s) }
347
348
349
350
351
352
353
354
355
356
357
358 func OptionUseStorageClient(s blobserver.Storage) ClientOption {
359 return optionStorage{s}
360 }
361
362 type optionStorage struct {
363 sto blobserver.Storage
364 }
365
366 func (o optionStorage) modifyClient(c *Client) {
367 c.sto = o.sto
368 }
369
370
371
372 func OptionTransportConfig(tc *TransportConfig) ClientOption {
373 return optionTransportConfig{tc}
374 }
375
376 type optionTransportConfig struct {
377 tc *TransportConfig
378 }
379
380 func (o optionTransportConfig) modifyClient(c *Client) { c.transportConfig = o.tc }
381
382
383
384
385
386 func OptionInsecure(v bool) ClientOption {
387 return optionInsecure(v)
388 }
389
390 type optionInsecure bool
391
392 func (o optionInsecure) modifyClient(c *Client) { c.insecureAnyTLSCert = bool(o) }
393
394
395
396
397
398
399
400 func OptionTrustedCert(cert string) ClientOption {
401
402 return optionTrustedCert(cert)
403 }
404
405 type optionTrustedCert string
406
407 func (o optionTrustedCert) modifyClient(c *Client) {
408 cert := string(o)
409 if cert != "" {
410 c.initTrustedCertsOnce.Do(func() {})
411 c.trustedCerts = []string{string(o)}
412 }
413 }
414
415 type optionNoExtConfig bool
416
417 func (o optionNoExtConfig) modifyClient(c *Client) { c.noExtConfig = bool(o) }
418
419
420
421
422 func OptionNoExternalConfig() ClientOption {
423 return optionNoExtConfig(true)
424 }
425
426 type optionAuthMode struct {
427 m auth.AuthMode
428 }
429
430 func (o optionAuthMode) modifyClient(c *Client) { c.authMode = o.m }
431
432
433
434 func OptionAuthMode(m auth.AuthMode) ClientOption {
435 return optionAuthMode{m}
436 }
437
438
439 func noop() error { return nil }
440
441 var shareURLRx = regexp.MustCompile(`^(.+)/(` + blob.Pattern + ")$")
442
443
444
445 func NewFromShareRoot(ctx context.Context, shareBlobURL string, opts ...ClientOption) (c *Client, target blob.Ref, err error) {
446 var root string
447 m := shareURLRx.FindStringSubmatch(shareBlobURL)
448 if m == nil {
449 return nil, blob.Ref{}, fmt.Errorf("Unknown share URL base")
450 }
451 c, err = New(append(opts[:len(opts):cap(opts)], OptionServer(m[1]))...)
452 if err != nil {
453 return nil, blob.Ref{}, err
454 }
455 c.discoOnce.Do(noop)
456 c.prefixOnce.Do(noop)
457 c.prefixv = m[1]
458 c.isSharePrefix = true
459 c.authMode = auth.None{}
460 c.via = make(map[blob.Ref]blob.Ref)
461 root = m[2]
462
463 req := c.newRequest(ctx, "GET", shareBlobURL, nil)
464 res, err := c.expect2XX(req)
465 if err != nil {
466 return nil, blob.Ref{}, fmt.Errorf("error fetching %s: %v", shareBlobURL, err)
467 }
468 defer res.Body.Close()
469 var buf bytes.Buffer
470 rootbr, ok := blob.Parse(root)
471 if !ok {
472 return nil, blob.Ref{}, fmt.Errorf("invalid root blob ref for sharing: %q", root)
473 }
474 b, err := schema.BlobFromReader(rootbr, io.TeeReader(res.Body, &buf))
475 if err != nil {
476 return nil, blob.Ref{}, fmt.Errorf("error parsing JSON from %s: %v , with response: %q", shareBlobURL, err, buf.Bytes())
477 }
478 if b.ShareAuthType() != schema.ShareHaveRef {
479 return nil, blob.Ref{}, fmt.Errorf("unknown share authType of %q", b.ShareAuthType())
480 }
481 target = b.ShareTarget()
482 if !target.Valid() {
483 return nil, blob.Ref{}, fmt.Errorf("no target")
484 }
485 c.via[target] = rootbr
486 return c, target, nil
487 }
488
489
490
491 func (c *Client) SetHTTPClient(client *http.Client) {
492 if client == nil {
493 client = http.DefaultClient
494 }
495 c.httpClient = client
496 }
497
498
499 func (c *Client) HTTPClient() *http.Client {
500 return c.httpClient
501 }
502
503
504 type HaveCache interface {
505 StatBlobCache(br blob.Ref) (size uint32, ok bool)
506 NoteBlobExists(br blob.Ref, size uint32)
507 }
508
509 type noHaveCache struct{}
510
511 func (noHaveCache) StatBlobCache(blob.Ref) (uint32, bool) { return 0, false }
512 func (noHaveCache) NoteBlobExists(blob.Ref, uint32) {}
513
514 func (c *Client) SetHaveCache(cache HaveCache) {
515 if cache == nil {
516 cache = noHaveCache{}
517 }
518 c.haveCache = cache
519 }
520
521 func (c *Client) printf(format string, v ...interface{}) {
522 if c.Verbose && c.Logger != nil {
523 c.Logger.Printf(format, v...)
524 }
525 }
526
527 func (c *Client) Stats() Stats {
528 c.statsMutex.Lock()
529 defer c.statsMutex.Unlock()
530 return c.stats
531 }
532
533
534 var ErrNoSearchRoot = errors.New("client: server doesn't support search")
535
536
537 var ErrNoHelpRoot = errors.New("client: server does not have a help handler")
538
539
540 var ErrNoShareRoot = errors.New("client: server does not have a share handler")
541
542
543 var ErrNoSigning = fmt.Errorf("client: server doesn't support signing")
544
545
546
547 var ErrNoStorageGeneration = errors.New("client: server doesn't report a storage generation")
548
549
550 var ErrNoSync = errors.New("client: server has no sync handlers")
551
552
553
554
555
556 func (c *Client) BlobRoot() (string, error) {
557 prefix, err := c.prefix()
558 if err != nil {
559 return "", err
560 }
561 return prefix + "/", nil
562 }
563
564
565
566
567 func (c *Client) ServerKeyID() (string, error) {
568 if err := c.condDiscovery(); err != nil {
569 return "", err
570 }
571 if c.serverKeyID == "" {
572 return "", ErrNoSigning
573 }
574 return c.serverKeyID, nil
575 }
576
577
578
579 func (c *Client) ServerPublicKeyBlobRef() (blob.Ref, error) {
580 if err := c.condDiscovery(); err != nil {
581 return blob.Ref{}, err
582 }
583
584 if !c.serverPublicKeyBlobRef.Valid() {
585 return blob.Ref{}, ErrNoSigning
586 }
587 return c.serverPublicKeyBlobRef, nil
588 }
589
590
591
592
593 func (c *Client) SearchRoot() (string, error) {
594 if err := c.condDiscovery(); err != nil {
595 return "", err
596 }
597 if c.searchRoot == "" {
598 return "", ErrNoSearchRoot
599 }
600 return c.searchRoot, nil
601 }
602
603
604
605
606 func (c *Client) HelpRoot() (string, error) {
607 if err := c.condDiscovery(); err != nil {
608 return "", err
609 }
610 if c.helpRoot == "" {
611 return "", ErrNoHelpRoot
612 }
613 return c.helpRoot, nil
614 }
615
616
617
618
619 func (c *Client) ShareRoot() (string, error) {
620 if err := c.condDiscovery(); err != nil {
621 return "", err
622 }
623 if c.shareRoot == "" {
624 return "", ErrNoShareRoot
625 }
626 return c.shareRoot, nil
627 }
628
629
630
631
632 func (c *Client) SignHandler() (string, error) {
633 if err := c.condDiscovery(); err != nil {
634 return "", err
635 }
636 if c.signHandler == "" {
637 return "", ErrNoSigning
638 }
639 return c.signHandler, nil
640 }
641
642
643
644
645
646
647
648
649
650
651 func (c *Client) StorageGeneration() (string, error) {
652 if err := c.condDiscovery(); err != nil {
653 return "", err
654 }
655 if c.storageGen == "" {
656 return "", ErrNoStorageGeneration
657 }
658 return c.storageGen, nil
659 }
660
661
662 func (c *Client) HasLegacySHA1() (bool, error) {
663 if err := c.condDiscovery(); err != nil {
664 return false, err
665 }
666 return c.hasLegacySHA1, nil
667 }
668
669
670
671 type SyncInfo struct {
672 From string
673 To string
674 ToIndex bool
675 }
676
677
678
679
680
681 func (c *Client) SyncHandlers() ([]*SyncInfo, error) {
682 if err := c.condDiscovery(); err != nil {
683 return nil, err
684 }
685 if c.syncHandlers == nil {
686 return nil, ErrNoSync
687 }
688 return c.syncHandlers, nil
689 }
690
691 var _ search.GetRecentPermanoder = (*Client)(nil)
692
693
694 func (c *Client) GetRecentPermanodes(ctx context.Context, req *search.RecentRequest) (*search.RecentResponse, error) {
695 sr, err := c.SearchRoot()
696 if err != nil {
697 return nil, err
698 }
699 url := sr + req.URLSuffix()
700 hreq := c.newRequest(ctx, "GET", url)
701 hres, err := c.expect2XX(hreq)
702 if err != nil {
703 return nil, err
704 }
705 res := new(search.RecentResponse)
706 if err := httputil.DecodeJSON(hres, res); err != nil {
707 return nil, err
708 }
709 if err := res.Err(); err != nil {
710 return nil, err
711 }
712 return res, nil
713 }
714
715
716
717
718
719 func (c *Client) GetPermanodesWithAttr(ctx context.Context, req *search.WithAttrRequest) (*search.WithAttrResponse, error) {
720 sr, err := c.SearchRoot()
721 if err != nil {
722 return nil, err
723 }
724 url := sr + req.URLSuffix()
725 hreq := c.newRequest(ctx, "GET", url)
726 hres, err := c.expect2XX(hreq)
727 if err != nil {
728 return nil, err
729 }
730 res := new(search.WithAttrResponse)
731 if err := httputil.DecodeJSON(hres, res); err != nil {
732 return nil, err
733 }
734 if err := res.Err(); err != nil {
735 return nil, err
736 }
737 return res, nil
738 }
739
740 func (c *Client) Describe(ctx context.Context, req *search.DescribeRequest) (*search.DescribeResponse, error) {
741 sr, err := c.SearchRoot()
742 if err != nil {
743 return nil, err
744 }
745 url := sr + req.URLSuffixPost()
746 body, err := json.MarshalIndent(req, "", "\t")
747 if err != nil {
748 return nil, err
749 }
750 hreq := c.newRequest(ctx, "POST", url, bytes.NewReader(body))
751 hres, err := c.expect2XX(hreq)
752 if err != nil {
753 return nil, err
754 }
755 res := new(search.DescribeResponse)
756 if err := httputil.DecodeJSON(hres, res); err != nil {
757 return nil, err
758 }
759 return res, nil
760 }
761
762 func (c *Client) GetClaims(ctx context.Context, req *search.ClaimsRequest) (*search.ClaimsResponse, error) {
763 sr, err := c.SearchRoot()
764 if err != nil {
765 return nil, err
766 }
767 url := sr + req.URLSuffix()
768 hreq := c.newRequest(ctx, "GET", url)
769 hres, err := c.expect2XX(hreq)
770 if err != nil {
771 return nil, err
772 }
773 res := new(search.ClaimsResponse)
774 if err := httputil.DecodeJSON(hres, res); err != nil {
775 return nil, err
776 }
777 return res, nil
778 }
779
780 func (c *Client) query(ctx context.Context, req *search.SearchQuery) (*http.Response, error) {
781 sr, err := c.SearchRoot()
782 if err != nil {
783 return nil, err
784 }
785 url := sr + req.URLSuffix()
786 body, err := json.Marshal(req)
787 if err != nil {
788 return nil, err
789 }
790 hreq := c.newRequest(ctx, "POST", url, bytes.NewReader(body))
791 return c.expect2XX(hreq)
792 }
793
794 func (c *Client) Query(ctx context.Context, req *search.SearchQuery) (*search.SearchResult, error) {
795 hres, err := c.query(ctx, req)
796 if err != nil {
797 return nil, err
798 }
799 res := new(search.SearchResult)
800 if err := httputil.DecodeJSON(hres, res); err != nil {
801 return nil, err
802 }
803 return res, nil
804 }
805
806
807
808 func (c *Client) QueryRaw(ctx context.Context, req *search.SearchQuery) ([]byte, error) {
809 hres, err := c.query(ctx, req)
810 if err != nil {
811 return nil, err
812 }
813 defer hres.Body.Close()
814 return ioutil.ReadAll(hres.Body)
815 }
816
817
818
819
820
821
822
823
824
825
826
827
828 func (c *Client) SearchExistingFileSchema(ctx context.Context, wholeRef ...blob.Ref) (blob.Ref, error) {
829 sr, err := c.SearchRoot()
830 if err != nil {
831 return blob.Ref{}, err
832 }
833 if len(wholeRef) == 0 {
834 return blob.Ref{}, nil
835 }
836 url := sr + "camli/search/files"
837 for i, ref := range wholeRef {
838 if i == 0 {
839 url += "?wholedigest=" + ref.String()
840 } else {
841 url += "&wholedigest=" + ref.String()
842 }
843 }
844 req := c.newRequest(ctx, "GET", url)
845 res, err := c.doReqGated(req)
846 if err != nil {
847 return blob.Ref{}, err
848 }
849 if res.StatusCode != 200 {
850 body, _ := ioutil.ReadAll(io.LimitReader(res.Body, 1<<20))
851 res.Body.Close()
852 return blob.Ref{}, fmt.Errorf("client: got status code %d from URL %s; body %s", res.StatusCode, url, body)
853 }
854 var ress camtypes.FileSearchResponse
855 if err := httputil.DecodeJSON(res, &ress); err != nil {
856
857 mismatch, err := c.versionMismatch(ctx)
858 if err != nil {
859 log.Printf("Could not verify whether client is too recent or server is too old: %v", err)
860 } else if mismatch {
861 return blob.Ref{}, fmt.Errorf("Client is too recent for this server. Use a client built before 2018-01-13-6e8a5930c9, or upgrade the server to after that revision.")
862 }
863 return blob.Ref{}, fmt.Errorf("client: error parsing JSON from URL %s: %v", url, err)
864 }
865 if len(ress.Files) == 0 {
866 return blob.Ref{}, nil
867 }
868 for wholeRef, files := range ress.Files {
869 for _, f := range files {
870 if c.FileHasContents(ctx, f, blob.MustParse(wholeRef)) {
871 return f, nil
872 }
873 }
874 }
875 return blob.Ref{}, nil
876 }
877
878
879
880 func (c *Client) versionMismatch(ctx context.Context) (bool, error) {
881 const shortRFC3339 = "2006-01-02"
882 version := buildinfo.GitInfo
883 if version == "" {
884 return false, errors.New("unknown client version")
885 }
886 version = version[:10]
887 clientDate, err := time.Parse(shortRFC3339, version)
888 if err != nil {
889 return false, fmt.Errorf("could not parse date from version %q: %v", version, err)
890 }
891 apiChangeDate, _ := time.Parse(shortRFC3339, "2018-01-13")
892 if !clientDate.After(apiChangeDate) {
893
894 return false, nil
895 }
896 url := c.discoRoot() + "/status/status.json"
897 req := c.newRequest(ctx, "GET", url)
898 res, err := c.doReqGated(req)
899 if err != nil {
900 return false, err
901 }
902 if res.StatusCode != 200 {
903 body, _ := ioutil.ReadAll(io.LimitReader(res.Body, 1<<20))
904 res.Body.Close()
905 return false, fmt.Errorf("got status code %d from URL %s; body %s", res.StatusCode, url, body)
906 }
907 var status struct {
908 Version string `json:"version"`
909 }
910 if err := httputil.DecodeJSON(res, &status); err != nil {
911 return false, fmt.Errorf("error parsing JSON from URL %s: %v", url, err)
912 }
913 serverVersion := status.Version[:10]
914 serverDate, err := time.Parse(shortRFC3339, serverVersion)
915 if err != nil {
916 return false, fmt.Errorf("could not parse date from server version %q: %v", status.Version, err)
917 }
918 if serverDate.After(apiChangeDate) {
919
920 return false, nil
921 }
922 return true, nil
923 }
924
925
926
927
928 func (c *Client) FileHasContents(ctx context.Context, f, wholeRef blob.Ref) bool {
929 if err := c.condDiscovery(); err != nil {
930 return false
931 }
932 if c.downloadHelper == "" {
933 return false
934 }
935 req := c.newRequest(ctx, "HEAD", c.downloadHelper+f.String()+"/?verifycontents="+wholeRef.String())
936 res, err := c.expect2XX(req)
937 if err != nil {
938 log.Printf("download helper HEAD error: %v", err)
939 return false
940 }
941 defer res.Body.Close()
942 return res.Header.Get("X-Camli-Contents") == wholeRef.String()
943 }
944
945
946
947
948 func (c *Client) prefix() (string, error) {
949 if err := c.prefixOnce.Do(c.initPrefix); err != nil {
950 return "", err
951 }
952 return c.prefixv, nil
953 }
954
955
956
957 func (c *Client) blobPrefix() (string, error) {
958 pfx, err := c.prefix()
959 if err != nil {
960 return "", err
961 }
962 if !c.isSharePrefix {
963 pfx += "/camli"
964 }
965 return pfx, nil
966 }
967
968
969 func (c *Client) discoRoot() string {
970 s := c.server
971 if c.sameOrigin {
972 s = strings.TrimPrefix(s, "http://")
973 s = strings.TrimPrefix(s, "https://")
974 parts := strings.SplitN(s, "/", 1)
975 if len(parts) < 2 {
976 return "/"
977 }
978 return "/" + parts[1]
979 }
980 if !strings.HasPrefix(s, "http") {
981 s = "https://" + s
982 }
983 return s
984 }
985
986
987
988
989
990 func (c *Client) initPrefix() error {
991 c.isSharePrefix = false
992 root := c.discoRoot()
993 u, err := url.Parse(root)
994 if err != nil {
995 return err
996 }
997 if len(u.Path) > 1 {
998 c.prefixv = strings.TrimRight(root, "/")
999 return nil
1000 }
1001 return c.condDiscovery()
1002 }
1003
1004 func (c *Client) condDiscovery() error {
1005 if c.sto != nil {
1006 return errors.New("client not using HTTP")
1007 }
1008 return c.discoOnce.Do(c.doDiscovery)
1009 }
1010
1011
1012
1013
1014 func (c *Client) DiscoveryDoc(ctx context.Context) (io.Reader, error) {
1015 res, err := c.discoveryResp(ctx)
1016 if err != nil {
1017 return nil, err
1018 }
1019 defer res.Body.Close()
1020 const maxSize = 1 << 20
1021 all, err := ioutil.ReadAll(io.LimitReader(res.Body, maxSize+1))
1022 if err != nil {
1023 return nil, err
1024 }
1025 if len(all) > maxSize {
1026 return nil, errors.New("discovery document oddly large")
1027 }
1028 if len(all) > 0 && all[len(all)-1] != '\n' {
1029 all = append(all, '\n')
1030 }
1031 return bytes.NewReader(all), err
1032 }
1033
1034
1035 func (c *Client) HTTPVersion(ctx context.Context) (string, error) {
1036 req := c.newRequest(ctx, "HEAD", c.discoRoot(), nil)
1037 res, err := c.doReqGated(req)
1038 if err != nil {
1039 return "", err
1040 }
1041 return res.Proto, err
1042 }
1043
1044 func (c *Client) discoveryResp(ctx context.Context) (*http.Response, error) {
1045
1046
1047 req := c.newRequest(ctx, "GET", c.discoRoot(), nil)
1048 req.Header.Set("Accept", "text/x-camli-configuration")
1049 res, err := c.doReqGated(req)
1050 if err != nil {
1051 return nil, err
1052 }
1053 if res.StatusCode != 200 {
1054 res.Body.Close()
1055 errMsg := fmt.Sprintf("got status %q from blobserver URL %q during configuration discovery", res.Status, c.discoRoot())
1056 if res.StatusCode == 401 && c.authErr != nil {
1057 errMsg = fmt.Sprintf("%v. %v", c.authErr, errMsg)
1058 }
1059 return nil, errors.New(errMsg)
1060 }
1061
1062
1063
1064 if ct := res.Header.Get("Content-Type"); ct != "text/javascript" {
1065 res.Body.Close()
1066 return nil, fmt.Errorf("Blobserver returned unexpected type %q from discovery", ct)
1067 }
1068 return res, nil
1069 }
1070
1071 func (c *Client) doDiscovery() error {
1072 ctx := context.TODO()
1073 root, err := url.Parse(c.discoRoot())
1074 if err != nil {
1075 return err
1076 }
1077
1078 res, err := c.discoveryResp(ctx)
1079 if err != nil {
1080 return err
1081 }
1082
1083 var disco camtypes.Discovery
1084 if err := httputil.DecodeJSON(res, &disco); err != nil {
1085 return err
1086 }
1087
1088 if disco.SearchRoot == "" {
1089 c.searchRoot = ""
1090 } else {
1091 u, err := root.Parse(disco.SearchRoot)
1092 if err != nil {
1093 return fmt.Errorf("client: invalid searchRoot %q; failed to resolve", disco.SearchRoot)
1094 }
1095 c.searchRoot = u.String()
1096 }
1097
1098 u, err := root.Parse(disco.HelpRoot)
1099 if err != nil {
1100 return fmt.Errorf("client: invalid helpRoot %q; failed to resolve", disco.HelpRoot)
1101 }
1102 c.helpRoot = u.String()
1103
1104 u, err = root.Parse(disco.ShareRoot)
1105 if err != nil {
1106 return fmt.Errorf("client: invalid shareRoot %q; failed to resolve", disco.ShareRoot)
1107 }
1108 c.shareRoot = u.String()
1109
1110 c.storageGen = disco.StorageGeneration
1111 c.hasLegacySHA1 = disco.HasLegacySHA1Index
1112
1113 u, err = root.Parse(disco.BlobRoot)
1114 if err != nil {
1115 return fmt.Errorf("client: error resolving blobRoot: %v", err)
1116 }
1117 c.prefixv = strings.TrimRight(u.String(), "/")
1118
1119 if disco.UIDiscovery != nil {
1120 u, err = root.Parse(disco.DownloadHelper)
1121 if err != nil {
1122 return fmt.Errorf("client: invalid downloadHelper %q; failed to resolve", disco.DownloadHelper)
1123 }
1124 c.downloadHelper = u.String()
1125 }
1126
1127 if disco.SyncHandlers != nil {
1128 for _, v := range disco.SyncHandlers {
1129 ufrom, err := root.Parse(v.From)
1130 if err != nil {
1131 return fmt.Errorf("client: invalid %q \"from\" sync; failed to resolve", v.From)
1132 }
1133 uto, err := root.Parse(v.To)
1134 if err != nil {
1135 return fmt.Errorf("client: invalid %q \"to\" sync; failed to resolve", v.To)
1136 }
1137 c.syncHandlers = append(c.syncHandlers, &SyncInfo{
1138 From: ufrom.String(),
1139 To: uto.String(),
1140 ToIndex: v.ToIndex,
1141 })
1142 }
1143 }
1144
1145 if disco.Signing != nil {
1146 c.serverKeyID = disco.Signing.PublicKeyID
1147 c.serverPublicKeyBlobRef = disco.Signing.PublicKeyBlobRef
1148 c.signHandler = disco.Signing.SignHandler
1149 }
1150 return nil
1151 }
1152
1153
1154
1155
1156 func (c *Client) GetJSON(ctx context.Context, url string, data interface{}) error {
1157 if !strings.HasPrefix(url, c.discoRoot()) {
1158 return fmt.Errorf("wrong URL (%q) for this server", url)
1159 }
1160 hreq := c.newRequest(ctx, "GET", url)
1161 resp, err := c.expect2XX(hreq)
1162 if err != nil {
1163 return err
1164 }
1165 return httputil.DecodeJSON(resp, data)
1166 }
1167
1168
1169
1170
1171 func (c *Client) Post(ctx context.Context, url string, bodyType string, body io.Reader) error {
1172 resp, err := c.post(ctx, url, bodyType, body)
1173 if err != nil {
1174 return err
1175 }
1176 return resp.Body.Close()
1177 }
1178
1179
1180
1181
1182 func (c *Client) Sign(ctx context.Context, server string, r io.Reader) (signed []byte, err error) {
1183 signHandler, err := c.SignHandler()
1184 if err != nil {
1185 return nil, err
1186 }
1187 signServer := strings.TrimSuffix(server, "/") + signHandler
1188 resp, err := c.post(ctx, signServer, "application/x-www-form-urlencoded", r)
1189 if err != nil {
1190 return nil, err
1191 }
1192 defer resp.Body.Close()
1193 return ioutil.ReadAll(resp.Body)
1194 }
1195
1196 func (c *Client) post(ctx context.Context, url string, bodyType string, body io.Reader) (*http.Response, error) {
1197 if !c.sameOrigin && !strings.HasPrefix(url, c.discoRoot()) {
1198 return nil, fmt.Errorf("wrong URL (%q) for this server", url)
1199 }
1200 req := c.newRequest(ctx, "POST", url, body)
1201 req.Header.Set("Content-Type", bodyType)
1202 res, err := c.expect2XX(req)
1203 if err != nil {
1204 return nil, err
1205 }
1206 return res, nil
1207 }
1208
1209
1210
1211 func (c *Client) newRequest(ctx context.Context, method, url string, body ...io.Reader) *http.Request {
1212 var bodyR io.Reader
1213 if len(body) > 0 {
1214 bodyR = body[0]
1215 }
1216 if len(body) > 1 {
1217 panic("too many body arguments")
1218 }
1219 req, err := http.NewRequest(method, url, bodyR)
1220 if err != nil {
1221 panic(err.Error())
1222 }
1223
1224 if br, ok := bodyR.(*bytes.Reader); ok {
1225 req.ContentLength = int64(br.Len())
1226 }
1227 c.authMode.AddAuthHeader(req)
1228 return req.WithContext(ctx)
1229 }
1230
1231
1232
1233 func (c *Client) expect2XX(req *http.Request) (*http.Response, error) {
1234 res, err := c.doReqGated(req)
1235 if err == nil && (res.StatusCode < 200 || res.StatusCode > 299) {
1236 buf := new(bytes.Buffer)
1237 io.CopyN(buf, res.Body, 1<<20)
1238 res.Body.Close()
1239 return res, fmt.Errorf("client: got status code %d from URL %s; body %s", res.StatusCode, req.URL.String(), buf.String())
1240 }
1241 return res, err
1242 }
1243
1244 func (c *Client) doReqGated(req *http.Request) (*http.Response, error) {
1245 c.httpGate.Start()
1246 defer c.httpGate.Done()
1247 return c.httpClient.Do(req)
1248 }
1249
1250
1251 func (c *Client) DialFunc() func(network, addr string) (net.Conn, error) {
1252 if c.useTLS() {
1253 return nil
1254 }
1255 if android.IsChild() {
1256 return func(network, addr string) (net.Conn, error) {
1257 return android.Dial(network, addr)
1258 }
1259 }
1260 return nil
1261 }
1262
1263 func (c *Client) http2DialTLSFunc() func(network, addr string, cfg *tls.Config) (net.Conn, error) {
1264 trustedCerts := c.getTrustedCerts()
1265 if !c.insecureAnyTLSCert && len(trustedCerts) == 0 {
1266
1267
1268 return nil
1269 }
1270 return func(network, addr string, cfg *tls.Config) (net.Conn, error) {
1271
1272 cfg.InsecureSkipVerify = true
1273 conn, err := tls.Dial(network, addr, cfg)
1274 if err != nil {
1275 return nil, err
1276 }
1277 if c.insecureAnyTLSCert {
1278 return conn, err
1279 }
1280 state := conn.ConnectionState()
1281 if p := state.NegotiatedProtocol; p != http2.NextProtoTLS {
1282 return nil, fmt.Errorf("http2: unexpected ALPN protocol %q; want %q", p, http2.NextProtoTLS)
1283 }
1284 if !state.NegotiatedProtocolIsMutual {
1285 return nil, errors.New("http2: could not negotiate protocol mutually")
1286 }
1287 certs := state.PeerCertificates
1288 if len(certs) < 1 {
1289 return nil, fmt.Errorf("no TLS peer certificates from %s", addr)
1290 }
1291 sig := hashutil.SHA256Prefix(certs[0].Raw)
1292 for _, v := range trustedCerts {
1293 if v == sig {
1294 return conn, nil
1295 }
1296 }
1297 return nil, fmt.Errorf("TLS server at %v presented untrusted certificate (signature %q)", addr, sig)
1298 }
1299 }
1300
1301
1302
1303
1304
1305
1306 func (c *Client) DialTLSFunc() func(network, addr string) (net.Conn, error) {
1307 if !c.useTLS() {
1308 return nil
1309 }
1310 trustedCerts := c.getTrustedCerts()
1311 var stdTLS bool
1312 if !c.insecureAnyTLSCert && len(trustedCerts) == 0 {
1313
1314 stdTLS = true
1315 if !android.IsChild() {
1316
1317 return nil
1318 }
1319 }
1320
1321 return func(network, addr string) (net.Conn, error) {
1322 var conn *tls.Conn
1323 var err error
1324 if android.IsChild() {
1325 ac, err := android.Dial(network, addr)
1326 if err != nil {
1327 return nil, err
1328 }
1329 var tlsConfig *tls.Config
1330 if stdTLS {
1331 tlsConfig, err = android.TLSConfig()
1332 if err != nil {
1333 return nil, err
1334 }
1335 } else {
1336 tlsConfig = &tls.Config{InsecureSkipVerify: true}
1337 }
1338
1339
1340
1341 tlsConfig.ServerName = c.serverNameOfAddr(addr)
1342 conn = tls.Client(ac, tlsConfig)
1343 if err := conn.Handshake(); err != nil {
1344 return nil, err
1345 }
1346 if stdTLS {
1347
1348
1349 return conn, nil
1350 }
1351 } else {
1352 conn, err = tls.Dial(network, addr, &tls.Config{InsecureSkipVerify: true})
1353 if err != nil {
1354 return nil, err
1355 }
1356 }
1357 if c.insecureAnyTLSCert {
1358 return conn, nil
1359 }
1360 certs := conn.ConnectionState().PeerCertificates
1361 if len(certs) < 1 {
1362 return nil, fmt.Errorf("no TLS peer certificates from %s", addr)
1363 }
1364 sig := hashutil.SHA256Prefix(certs[0].Raw)
1365 for _, v := range trustedCerts {
1366 if v == sig {
1367 return conn, nil
1368 }
1369 }
1370 return nil, fmt.Errorf("TLS server at %v presented untrusted certificate (signature %q)", addr, sig)
1371 }
1372 }
1373
1374
1375
1376
1377 func (c *Client) serverNameOfAddr(addr string) string {
1378 serverName, _, err := net.SplitHostPort(addr)
1379 if err != nil {
1380 c.printf("could not get server name from address %q: %v", addr, err)
1381 return ""
1382 }
1383 if ip := net.ParseIP(serverName); ip != nil {
1384 return ""
1385 }
1386 return serverName
1387 }
1388
1389
1390
1391 func (c *Client) Signer() (*schema.Signer, error) {
1392 c.signerOnce.Do(c.signerInit)
1393 return c.signer, c.signerErr
1394 }
1395
1396 func (c *Client) signerInit() {
1397 c.signer, c.signerErr = c.buildSigner()
1398 }
1399
1400 func (c *Client) buildSigner() (*schema.Signer, error) {
1401 c.initSignerPublicKeyBlobrefOnce.Do(c.initSignerPublicKeyBlobref)
1402 if !c.signerPublicKeyRef.Valid() {
1403 return nil, camtypes.ErrClientNoPublicKey
1404 }
1405 return schema.NewSigner(c.signerPublicKeyRef, strings.NewReader(c.publicKeyArmored), c.SecretRingFile())
1406 }
1407
1408
1409
1410 func (c *Client) signBlob(ctx context.Context, bb schema.Buildable, sigTime time.Time) (string, error) {
1411 signer, err := c.Signer()
1412 if err != nil {
1413 return "", err
1414 }
1415 return bb.Builder().SignAt(ctx, signer, sigTime)
1416 }
1417
1418
1419
1420
1421
1422 func (c *Client) uploadPublicKey(ctx context.Context) error {
1423 sigRef := c.SignerPublicKeyBlobref()
1424 if !sigRef.Valid() {
1425 return nil
1426 }
1427 var err error
1428 if _, keyUploaded := c.haveCache.StatBlobCache(sigRef); !keyUploaded {
1429 _, err = c.uploadString(ctx, c.publicKeyArmored, false)
1430 }
1431 return err
1432 }
1433
1434
1435 func (c *Client) checkMatchingKeys() {
1436 serverKey, err := c.ServerKeyID()
1437 if err != nil {
1438 log.Printf("Warning: Could not obtain the server's key id: %v", err)
1439 return
1440 }
1441 if serverKey != c.signer.KeyIDLong() {
1442 log.Printf("Warning: client (%s) and server (%s) keys differ.", c.signer.KeyIDLong(), serverKey)
1443 }
1444 }
1445
1446 func (c *Client) UploadAndSignBlob(ctx context.Context, b schema.AnyBlob) (*PutResult, error) {
1447 signed, err := c.signBlob(ctx, b.Blob(), time.Time{})
1448 if err != nil {
1449 return nil, err
1450 }
1451 c.checkMatchingKeys()
1452 if err := c.uploadPublicKey(ctx); err != nil {
1453 return nil, err
1454 }
1455 return c.uploadString(ctx, signed, false)
1456 }
1457
1458 func (c *Client) UploadBlob(ctx context.Context, b schema.AnyBlob) (*PutResult, error) {
1459
1460
1461 return c.uploadString(ctx, b.Blob().JSON(), true)
1462 }
1463
1464 func (c *Client) uploadString(ctx context.Context, s string, stat bool) (*PutResult, error) {
1465 uh := NewUploadHandleFromString(s)
1466 uh.SkipStat = !stat
1467 return c.Upload(ctx, uh)
1468 }
1469
1470 func (c *Client) UploadNewPermanode(ctx context.Context) (*PutResult, error) {
1471 unsigned := schema.NewUnsignedPermanode()
1472 return c.UploadAndSignBlob(ctx, unsigned)
1473 }
1474
1475 func (c *Client) UploadPlannedPermanode(ctx context.Context, key string, sigTime time.Time) (*PutResult, error) {
1476 unsigned := schema.NewPlannedPermanode(key)
1477 signed, err := c.signBlob(ctx, unsigned, sigTime)
1478 if err != nil {
1479 return nil, err
1480 }
1481 c.checkMatchingKeys()
1482 if err := c.uploadPublicKey(ctx); err != nil {
1483 return nil, err
1484 }
1485 return c.uploadString(ctx, signed, true)
1486 }
1487
1488
1489
1490
1491
1492
1493
1494 func (c *Client) IsIgnoredFile(fullpath string) bool {
1495 c.initIgnoredFilesOnce.Do(c.initIgnoredFiles)
1496 return c.ignoreChecker(fullpath)
1497 }
1498
1499
1500 func (c *Client) Close() error {
1501 if cl, ok := c.sto.(io.Closer); ok {
1502 return cl.Close()
1503 }
1504 if c := c.HTTPClient(); c != nil {
1505 switch t := c.Transport.(type) {
1506 case *http.Transport:
1507 t.CloseIdleConnections()
1508 case *http2.Transport:
1509 t.CloseIdleConnections()
1510 }
1511 }
1512 return nil
1513 }
1514
1515 func httpGateSize(rt http.RoundTripper) int {
1516 switch v := rt.(type) {
1517 case *httputil.StatsTransport:
1518 return httpGateSize(v.Transport)
1519 case *http.Transport:
1520 return maxParallelHTTP_h1
1521 case *http2.Transport:
1522 return maxParallelHTTP_h2
1523 default:
1524 return maxParallelHTTP_h1
1525 }
1526 }