github.com/nya3jp/tast@v0.0.0-20230601000426-85c8e4d83a9b/src/go.chromium.org/tast/core/internal/devserver/pseudo.go (about)

     1  // Copyright 2018 The ChromiumOS Authors
     2  // Use of this source code is governed by a BSD-style license that can be
     3  // found in the LICENSE file.
     4  
     5  package devserver
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"io"
    11  	"net/http"
    12  	"net/url"
    13  	"os"
    14  )
    15  
    16  const gsDownloadURL = "https://storage.googleapis.com"
    17  
    18  // PseudoClient is an implementation of Client to simulate devservers without credentials.
    19  type PseudoClient struct {
    20  	cl      *http.Client
    21  	baseURL string
    22  }
    23  
    24  var _ Client = &PseudoClient{}
    25  
    26  type pseudoClientOptions struct {
    27  	cl      *http.Client
    28  	baseURL string
    29  }
    30  
    31  // PseudoClientOption is an option accepted by NewPseudoClient to configure
    32  // PseudoClient initialization.
    33  type PseudoClientOption func(o *pseudoClientOptions)
    34  
    35  // WithHTTPClient returns an option that specifies http.Client used by
    36  // PseudoClient.
    37  func WithHTTPClient(cl *http.Client) PseudoClientOption {
    38  	return func(o *pseudoClientOptions) { o.cl = cl }
    39  }
    40  
    41  // WithBaseURL returns an option that specifies the base URL of Google Cloud
    42  // Storage HTTP API.
    43  func WithBaseURL(baseURL string) PseudoClientOption {
    44  	return func(o *pseudoClientOptions) { o.baseURL = baseURL }
    45  }
    46  
    47  // NewPseudoClient creates a PseudoClient.
    48  func NewPseudoClient(opts ...PseudoClientOption) *PseudoClient {
    49  	o := &pseudoClientOptions{
    50  		cl:      defaultHTTPClient,
    51  		baseURL: gsDownloadURL,
    52  	}
    53  	for _, opts := range opts {
    54  		opts(o)
    55  	}
    56  	return &PseudoClient{cl: o.cl, baseURL: o.baseURL}
    57  }
    58  
    59  // TearDown does nothing.
    60  func (c *PseudoClient) TearDown() error {
    61  	return nil
    62  }
    63  
    64  // Stage returns a url to GCS directly from storage.googleapis.com.
    65  func (c *PseudoClient) Stage(ctx context.Context, gsURL string) (*url.URL, error) {
    66  	bucket, path, err := ParseGSURL(gsURL)
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	dlURL, _ := url.Parse(c.baseURL)
    72  	dlURL.Path = fmt.Sprintf("/%s/%s", bucket, path)
    73  	return dlURL, nil
    74  }
    75  
    76  // Open downloads a file on GCS directly from storage.googleapis.com.
    77  func (c *PseudoClient) Open(ctx context.Context, gsURL string) (io.ReadCloser, error) {
    78  	dlURL, err := c.Stage(ctx, gsURL)
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  	req, err := http.NewRequest("GET", dlURL.String(), nil)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	req = req.WithContext(ctx)
    87  
    88  	res, err := c.cl.Do(req)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	switch res.StatusCode {
    94  	case http.StatusOK:
    95  		return res.Body, nil
    96  	case http.StatusNotFound:
    97  		res.Body.Close()
    98  		return nil, os.ErrNotExist
    99  	default:
   100  		res.Body.Close()
   101  		return nil, fmt.Errorf("got status %d", res.StatusCode)
   102  	}
   103  }