Ejemplo n.º 1
0
func (self *Routes) signUrl(res http.ResponseWriter, req *http.Request) {
	// Using ReadAll could be sketchy here since we are reading unbounded data
	// into memory...
	body, err := ioutil.ReadAll(req.Body)

	if err != nil {
		res.WriteHeader(500)
		fmt.Fprintf(res, "Error reading body")
		return
	}

	urlString := strings.TrimSpace(string(body))
	cd := tcclient.ConnectionData(self.ConnectionData)
	bewitUrl, err := (&cd).SignedURL(urlString, nil, time.Hour*1)

	if err != nil {
		res.WriteHeader(500)
		fmt.Fprintf(res, "Error creating bewit url")
		return
	}

	headers := res.Header()
	headers.Set("Location", bewitUrl.String())
	res.WriteHeader(303)
	fmt.Fprintf(res, bewitUrl.String())
}
Ejemplo n.º 2
0
// Routes implements the `http.Handler` interface
func (self *Routes) ServeHTTP(res http.ResponseWriter, req *http.Request) {
	if req.URL.Path == "/credentials" {
		log.Printf("Update credentials request %s\n", req.URL.String())
		self.updateCredentials(res, req)
		return
	}

	self.lock.RLock()
	defer self.lock.RUnlock()

	headersToSend := res.Header()
	headersToSend.Set("X-Taskcluster-Proxy-Version", version)
	cert, err := self.Credentials.Cert()
	if cert != nil {
		if err != nil {
			res.WriteHeader(500)
			// Note, self.Credentials does not expose secrets when rendered as a string
			fmt.Fprintf(res, "TaskCluster Proxy has invalid certificate: %v\n%v", self.Credentials, err)
			return
		} else {
			headersToSend.Set("X-Taskcluster-Proxy-Temp-Scopes", fmt.Sprintf("%s", cert.Scopes))
		}
	} else {
		headersToSend.Set("X-Taskcluster-Proxy-Perm-ClientId", fmt.Sprintf("%s", self.Credentials.ClientId))
	}
	if authScopes := self.Credentials.AuthorizedScopes; authScopes != nil {
		headersToSend.Set("X-Taskcluster-Authorized-Scopes", fmt.Sprintf("%s", authScopes))
	}

	// A special case for the proxy is returning a bewit signed url.
	if req.URL.Path[0:6] == "/bewit" {
		self.signUrl(res, req)
		return
	}

	targetPath, err := tcServices.ConvertPath(req.URL)

	// Unkown service which we are trying to hit...
	if err != nil {
		res.WriteHeader(404)
		log.Printf("Attempting to use unkown service %s", req.URL.String())
		fmt.Fprintf(res, "Unkown taskcluster service: %s", err)
		return
	}
	headersToSend.Set("X-Taskcluster-Endpoint", targetPath.String())

	log.Printf("Proxying %s | %s | %s", req.URL, req.Method, targetPath)

	payload := (*json.RawMessage)(nil)
	// In theory, req.Body should never be nil when running as a server, but
	// during testing, with a direct call to the method rather than a real http
	// request coming in from outside, it could be. For example see:
	// https://github.com/taskcluster/taskcluster-proxy/blob/6744fb1d3eaa791394fe651ff3a3f99f606828d5/authorization_test.go#L111
	// Furthermore, it is correct to create an http (client) request with a nil
	// body. See https://golang.org/pkg/net/http/#Request.
	//
	// Technically a client request should not be passed to a server method,
	// but in reality there are not separate types (e.g. HttpClientRequest,
	// HttpServerRequest) and so it can easily happen and is usually done.  For
	// this reason, and to avoid confusion around this, let's keep the nil
	// check in here.
	if req.Body != nil {
		// Note: we cannot use:
		// `err := json.NewDecoder(req.Body).Decode(payload)` since the body
		// might be empty and we'd get a json decoding error. Therefore we read
		// into memory and test the length upfront.
		body, err := ioutil.ReadAll(req.Body)
		// If we fail to create a request notify the client.
		if err != nil {
			res.WriteHeader(500)
			fmt.Fprintf(res, "Failed to generate proxy request (could not read http body) - %s", err)
			return
		}
		if len(body) > 0 {
			payload = new(json.RawMessage)
			err = json.Unmarshal(body, payload)
			if err != nil {
				res.WriteHeader(400)
				fmt.Fprintf(res, "Malformed payload - http request body is not valid json - %s", err)
				return
			}
		}
	}

	cd := tcclient.ConnectionData(self.ConnectionData)
	_, cs, err := (&cd).APICall(payload, req.Method, targetPath.String(), new(json.RawMessage), nil)
	// If we fail to create a request notify the client.
	if err != nil {
		switch err.(type) {
		case httpbackoff.BadHttpResponseCode:
			// nothing extra to do - header and body will be proxied back
		default:
			res.WriteHeader(500)
			fmt.Fprintf(res, "Failed during proxy request: %s", err)
			return
		}
	}

	// Map the headers from the proxy back into our proxyResponse
	for key, _ := range cs.HttpResponse.Header {
		headersToSend.Set(key, cs.HttpResponse.Header.Get(key))
	}

	// Write the proxyResponse headers and status.
	res.WriteHeader(cs.HttpResponse.StatusCode)

	// Proxy the proxyResponse body from the endpoint to our response.
	res.Write([]byte(cs.HttpResponseBody))
}