...

Source file src/cmd/go/internal/web/api.go

     1	// Copyright 2017 The Go Authors. All rights reserved.
     2	// Use of this source code is governed by a BSD-style
     3	// license that can be found in the LICENSE file.
     4	
     5	// Package web defines minimal helper routines for accessing HTTP/HTTPS
     6	// resources without requiring external dependenicies on the net package.
     7	//
     8	// If the cmd_go_bootstrap build tag is present, web avoids the use of the net
     9	// package and returns errors for all network operations.
    10	package web
    11	
    12	import (
    13		"fmt"
    14		"io"
    15		"io/ioutil"
    16		"net/url"
    17		"os"
    18		"strings"
    19	)
    20	
    21	// SecurityMode specifies whether a function should make network
    22	// calls using insecure transports (eg, plain text HTTP).
    23	// The zero value is "secure".
    24	type SecurityMode int
    25	
    26	const (
    27		SecureOnly      SecurityMode = iota // Reject plain HTTP; validate HTTPS.
    28		DefaultSecurity                     // Allow plain HTTP if explicit; validate HTTPS.
    29		Insecure                            // Allow plain HTTP if not explicitly HTTPS; skip HTTPS validation.
    30	)
    31	
    32	// An HTTPError describes an HTTP error response (non-200 result).
    33	type HTTPError struct {
    34		URL        string // redacted
    35		Status     string
    36		StatusCode int
    37	}
    38	
    39	func (e *HTTPError) Error() string {
    40		return fmt.Sprintf("reading %s: %v", e.URL, e.Status)
    41	}
    42	
    43	func (e *HTTPError) Is(target error) bool {
    44		return target == os.ErrNotExist && (e.StatusCode == 404 || e.StatusCode == 410)
    45	}
    46	
    47	// GetBytes returns the body of the requested resource, or an error if the
    48	// response status was not http.StatusOK.
    49	//
    50	// GetBytes is a convenience wrapper around Get and Response.Err.
    51	func GetBytes(u *url.URL) ([]byte, error) {
    52		resp, err := Get(DefaultSecurity, u)
    53		if err != nil {
    54			return nil, err
    55		}
    56		defer resp.Body.Close()
    57		if err := resp.Err(); err != nil {
    58			return nil, err
    59		}
    60		b, err := ioutil.ReadAll(resp.Body)
    61		if err != nil {
    62			return nil, fmt.Errorf("reading %s: %v", Redacted(u), err)
    63		}
    64		return b, nil
    65	}
    66	
    67	type Response struct {
    68		URL        string // redacted
    69		Status     string
    70		StatusCode int
    71		Header     map[string][]string
    72		Body       io.ReadCloser
    73	}
    74	
    75	// Err returns an *HTTPError corresponding to the response r.
    76	// It returns nil if the response r has StatusCode 200 or 0 (unset).
    77	func (r *Response) Err() error {
    78		if r.StatusCode == 200 || r.StatusCode == 0 {
    79			return nil
    80		}
    81		return &HTTPError{URL: r.URL, Status: r.Status, StatusCode: r.StatusCode}
    82	}
    83	
    84	// Get returns the body of the HTTP or HTTPS resource specified at the given URL.
    85	//
    86	// If the URL does not include an explicit scheme, Get first tries "https".
    87	// If the server does not respond under that scheme and the security mode is
    88	// Insecure, Get then tries "http".
    89	// The URL included in the response indicates which scheme was actually used,
    90	// and it is a redacted URL suitable for use in error messages.
    91	//
    92	// For the "https" scheme only, credentials are attached using the
    93	// cmd/go/internal/auth package. If the URL itself includes a username and
    94	// password, it will not be attempted under the "http" scheme unless the
    95	// security mode is Insecure.
    96	//
    97	// Get returns a non-nil error only if the request did not receive a response
    98	// under any applicable scheme. (A non-2xx response does not cause an error.)
    99	func Get(security SecurityMode, u *url.URL) (*Response, error) {
   100		return get(security, u)
   101	}
   102	
   103	// Redacted returns a redacted string form of the URL,
   104	// suitable for printing in error messages.
   105	// The string form replaces any non-empty password
   106	// in the original URL with "[redacted]".
   107	func Redacted(u *url.URL) string {
   108		if u.User != nil {
   109			if _, ok := u.User.Password(); ok {
   110				redacted := *u
   111				redacted.User = url.UserPassword(u.User.Username(), "[redacted]")
   112				u = &redacted
   113			}
   114		}
   115		return u.String()
   116	}
   117	
   118	// OpenBrowser attempts to open the requested URL in a web browser.
   119	func OpenBrowser(url string) (opened bool) {
   120		return openBrowser(url)
   121	}
   122	
   123	// Join returns the result of adding the slash-separated
   124	// path elements to the end of u's path.
   125	func Join(u *url.URL, path string) *url.URL {
   126		j := *u
   127		if path == "" {
   128			return &j
   129		}
   130		j.Path = strings.TrimSuffix(u.Path, "/") + "/" + strings.TrimPrefix(path, "/")
   131		j.RawPath = strings.TrimSuffix(u.RawPath, "/") + "/" + strings.TrimPrefix(path, "/")
   132		return &j
   133	}
   134	

View as plain text