blob: a71469dd94d9fef779429029852ec6e5054ef376 [file] [log] [blame]
// Copyright 2021 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 frontend
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"golang.org/x/pkgsite/internal/auth"
"golang.org/x/pkgsite/internal/derrors"
)
// A Client for interacting with the frontend. This is only used for tests.
type Client struct {
// URL of the frontend server host.
url string
// Client used for HTTP requests.
httpClient *http.Client
}
// NewClient creates a new frontend client. This is only used for tests.
func NewClient(url string) *Client {
tok, ok := os.LookupEnv("GO_DISCOVERY_FRONTEND_AUTHORIZATION")
c := &Client{
url: url,
httpClient: http.DefaultClient,
}
if ok {
c.httpClient = auth.NewClientBearer(tok)
}
return c
}
// GetVersions returns a VersionsDetails for the specified pkgPath.
// This is only used for tests.
func (c *Client) GetVersions(pkgPath string) (_ *VersionsDetails, err error) {
defer derrors.Wrap(&err, "GetVersions(%q)", pkgPath)
u := fmt.Sprintf("%s/%s?tab=versions&content=json", c.url, pkgPath)
body, err := c.fetchJSONPage(u)
if err != nil {
return nil, err
}
var vd VersionsDetails
if err := json.Unmarshal(body, &vd); err != nil {
return nil, fmt.Errorf("json.Unmarshal: %v:\nDoes GO_DISCOVERY_SERVE_STATS=true on the frontend?", err)
}
return &vd, nil
}
// Search returns a SearchPage for a search query and mode.
func (c *Client) Search(q, mode string) (_ *SearchPage, err error) {
defer derrors.Wrap(&err, "Search(%q, %q)", q, mode)
u := fmt.Sprintf("%s/search?q=%s&content=json&m=%s", c.url, url.QueryEscape(q), mode)
body, err := c.fetchJSONPage(u)
if err != nil {
return nil, err
}
var sp SearchPage
if err := json.Unmarshal(body, &sp); err != nil {
return nil, fmt.Errorf("json.Unmarshal: %v:\nDoes GO_DISCOVERY_SERVE_STATS=true on the frontend?", err)
}
return &sp, nil
}
func (c *Client) fetchJSONPage(url string) (_ []byte, err error) {
defer derrors.Wrap(&err, "fetchJSONPage(%q)", url)
r, err := c.httpClient.Get(url)
if err != nil {
return nil, err
}
defer r.Body.Close()
if r.StatusCode != http.StatusOK {
return nil, fmt.Errorf(r.Status)
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
return nil, err
}
return body, nil
}
func (s *Server) shouldServeJSON(r *http.Request) bool {
return s.serveStats && r.FormValue("content") == "json"
}
func (s *Server) serveJSONPage(w http.ResponseWriter, r *http.Request, d interface{}) (err error) {
defer derrors.Wrap(&err, "serveJSONPage(ctx, w, r)")
if !s.shouldServeJSON(r) {
return derrors.NotFound
}
data, err := json.Marshal(d)
if err != nil {
return fmt.Errorf("json.Marshal: %v", err)
}
if _, err := w.Write(data); err != nil {
return fmt.Errorf("w.Write: %v", err)
}
return nil
}