This file is indexed.

/usr/share/gocode/src/github.com/jacobsa/gcloud/httputil/do.go is in golang-github-jacobsa-gcloud-dev 0.0~git20150709-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package httputil

import (
	"fmt"
	"io"
	"net/http"
	"reflect"
	"time"

	"golang.org/x/net/context"
)

// Wait for the allDone channel to be closed. If the context is cancelled
// before then, cancel the HTTP request via the canceller repeatedly until
// allDone is closed.
//
// We must cancel repeatedly because the http package's design for cancellation
// is flawed: the canceller must naturally race with the transport receiving
// the request (CancelRequest may be called before RoundTrip, and in this case
// the cancellation will be lost). This is especially a problem with wrapper
// transports like the oauth2 one, which may do extra work before calling
// through to http.Transport.
func propagateCancellation(
	ctx context.Context,
	allDone chan struct{},
	c CancellableRoundTripper,
	req *http.Request) {
	// If the context is not cancellable, there is nothing interesting we can do.
	cancelChan := ctx.Done()
	if cancelChan == nil {
		return
	}

	// If the user closes allDone before the context is cancelled, we can return
	// immediately.
	select {
	case <-allDone:
		return

	case <-cancelChan:
	}

	// The context has been cancelled. Repeatedly cancel the HTTP request as
	// described above until the user closes allDone.
	for {
		c.CancelRequest(req)

		select {
		case <-allDone:
			return

		case <-time.After(10 * time.Millisecond):
		}
	}
}

// An io.ReadCloser that closes a channel when it is closed. Used to clean up
// the propagateCancellation helper.
type closeChannelReadCloser struct {
	wrapped io.ReadCloser
	c       chan struct{}
}

func (rc *closeChannelReadCloser) Read(p []byte) (n int, err error) {
	n, err = rc.wrapped.Read(p)
	return
}

func (rc *closeChannelReadCloser) Close() (err error) {
	if rc.c != nil {
		close(rc.c)
		rc.c = nil
	}

	err = rc.wrapped.Close()
	return
}

// Call client.Do with the supplied request, cancelling the request if the
// context is cancelled. Return an error if the client does not support
// cancellation.
func Do(
	ctx context.Context,
	client *http.Client,
	req *http.Request) (resp *http.Response, err error) {
	// Make sure the transport supports cancellation.
	c, ok := client.Transport.(CancellableRoundTripper)
	if !ok {
		err = fmt.Errorf(
			"Transport of type %v doesn't support cancellation",
			reflect.TypeOf(client.Transport))
		return
	}

	// Set up a goroutine that will watch the context for cancellation, and a
	// channel that tells it that it can return.
	allDone := make(chan struct{})
	go propagateCancellation(ctx, allDone, c, req)

	// Call through. On error, clean up the helper function.
	resp, err = client.Do(req)
	if err != nil {
		close(allDone)
		return
	}

	// Otherwise, wrap the body in a layer that will tell the helper function
	// that it can return when it is closed.
	resp.Body = &closeChannelReadCloser{
		wrapped: resp.Body,
		c:       allDone,
	}

	return
}