<commit_before>/*
 * Minio Cloud Storage, (C) 2016, 2017 Minio, Inc.
 *
 * 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 cmd

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
)

// Signature and API related constants.
const (
	signV2Algorithm = "AWS"
)

// AWS S3 Signature V2 calculation rule is give here:
// http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html#RESTAuthenticationStringToSign

// Whitelist resource list that will be used in query string for signature-V2 calculation.
// The list should be alphabetically sorted
var resourceList = []string{
	"acl",
	"delete",
	"lifecycle",
	"location",
	"logging",
	"notification",
	"partNumber",
	"policy",
	"requestPayment",
	"response-cache-control",
	"response-content-disposition",
	"response-content-encoding",
	"response-content-language",
	"response-content-type",
	"response-expires",
	"torrent",
	"uploadId",
	"uploads",
	"versionId",
	"versioning",
	"versions",
	"website",
}

func doesPolicySignatureV2Match(formValues http.Header) APIErrorCode {
	cred := serverConfig.GetCredential()
	accessKey := formValues.Get("AWSAccessKeyId")
	if accessKey != cred.AccessKey {
		return ErrInvalidAccessKeyID
	}
	policy := formValues.Get("Policy")
	signature := formValues.Get("Signature")
	if signature != calculateSignatureV2(policy, cred.SecretKey) {
		return ErrSignatureDoesNotMatch
	}
	return ErrNone
}

// doesPresignV2SignatureMatch - Verify query headers with presigned signature
//     - http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html#RESTAuthenticationQueryStringAuth
// returns ErrNone if matches. S3 errors otherwise.
func doesPresignV2SignatureMatch(r *http.Request) APIErrorCode {
	// Access credentials.
	cred := serverConfig.GetCredential()

	// r.RequestURI will have raw encoded URI as sent by the client.
	splits := splitStr(r.RequestURI, "?", 2)
	encodedResource, encodedQuery := splits[0], splits[1]

	queries := strings.Split(encodedQuery, "&")
	var filteredQueries []string
	var gotSignature string
	var expires string
	var accessKey string
	var err error
	for _, query := range queries {
		keyval := strings.Split(query, "=")
		switch keyval[0] {
		case "AWSAccessKeyId":
			accessKey, err = url.QueryUnescape(keyval[1])
		case "Signature":
			gotSignature, err = url.QueryUnescape(keyval[1])
		case "Expires":
			expires, err = url.QueryUnescape(keyval[1])
		default:
			unescapedQuery, qerr := url.QueryUnescape(query)
			if qerr == nil {
				filteredQueries = append(filteredQueries, unescapedQuery)
			} else {
				err = qerr
			}
		}
		// Check if the query unescaped properly.
		if err != nil {
			errorIf(err, "Unable to unescape query values", queries)
			return ErrInvalidQueryParams
		}
	}

	// Invalid access key.
	if accessKey == "" {
		return ErrInvalidQueryParams
	}

	// Validate if access key id same.
	if accessKey != cred.AccessKey {
		return ErrInvalidAccessKeyID
	}

	// Make sure the request has not expired.
	expiresInt, err := strconv.ParseInt(expires, 10, 64)
	if err != nil {
		return ErrMalformedExpires
	}

	// Check if the presigned URL has expired.
	if expiresInt < UTCNow().Unix() {
		return ErrExpiredPresignRequest
	}

	expectedSignature := preSignatureV2(r.Method, encodedResource, strings.Join(filteredQueries, "&"), r.Header, expires)
	if gotSignature != expectedSignature {
		return ErrSignatureDoesNotMatch
	}

	return ErrNone
}

// Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature;
// Signature = Base64( HMAC-SHA1( YourSecretKey, UTF-8-Encoding-Of( StringToSign ) ) );
//
// StringToSign = HTTP-Verb + "\n" +
//  	Content-Md5 + "\n" +
//  	Content-Type + "\n" +
//  	Date + "\n" +
//  	CanonicalizedProtocolHeaders +
//  	CanonicalizedResource;
//
// CanonicalizedResource = [ "/" + Bucket ] +
//  	<HTTP-Request-URI, from the protocol name up to the query string> +
//  	[ subresource, if present. For example "?acl", "?location", "?logging", or "?torrent"];
//
// CanonicalizedProtocolHeaders = <described below>

// doesSignV2Match - Verify authorization header with calculated header in accordance with
//     - http://docs.aws.amazon.com/AmazonS3/latest/dev/auth-request-sig-v2.html
// returns true if matches, false otherwise. if error is not nil then it is always false

func validateV2AuthHeader(v2Auth string) APIErrorCode {
	if v2Auth == "" {
		return ErrAuthHeaderEmpty
	}
	// Verify if the header algorithm is supported or not.
	if !strings.HasPrefix(v2Auth, signV2Algorithm) {
		return ErrSignatureVersionNotSupported
	}

	// below is V2 Signed Auth header format, splitting on `space` (after the `AWS` string).
	// Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature
	authFields := strings.Split(v2Auth, " ")
	if len(authFields) != 2 {
		return ErrMissingFields
	}

	// Then will be splitting on ":", this will seprate `AWSAccessKeyId` and `Signature` string.
	keySignFields := strings.Split(strings.TrimSpace(authFields[1]), ":")
	if len(keySignFields) != 2 {
		return ErrMissingFields
	}

	// Access credentials.
	cred := serverConfig.GetCredential()
	if keySignFields[0] != cred.AccessKey {
		return ErrInvalidAccessKeyID
	}

	return ErrNone
}

func doesSignV2Match(r *http.Request) APIErrorCode {
	v2Auth := r.Header.Get("Authorization")

	if apiError := validateV2AuthHeader(v2Auth); apiError != ErrNone {
		return apiError
	}

	// r.RequestURI will have raw encoded URI as sent by the client.
	splits := splitStr(r.RequestURI, "?", 2)
	encodedResource, encodedQuery := splits[0], splits[1]

	expectedAuth := signatureV2(r.Method, encodedResource, encodedQuery, r.Header)
	if v2Auth != expectedAuth {
		return ErrSignatureDoesNotMatch
	}

	return ErrNone
}

func calculateSignatureV2(stringToSign string, secret string) string {
	hm := hmac.New(sha1.New, []byte(secret))
	hm.Write([]byte(stringToSign))
	return base64.StdEncoding.EncodeToString(hm.Sum(nil))
}

// Return signature-v2 for the presigned request.
func preSignatureV2(method string, encodedResource string, encodedQuery string, headers http.Header, expires string) string {
	cred := serverConfig.GetCredential()
	stringToSign := presignV2STS(method, encodedResource, encodedQuery, headers, expires)
	return calculateSignatureV2(stringToSign, cred.SecretKey)
}

// Return signature-v2 authrization header.
func signatureV2(method string, encodedResource string, encodedQuery string, headers http.Header) string {
	cred := serverConfig.GetCredential()
	stringToSign := signV2STS(method, encodedResource, encodedQuery, headers)
	signature := calculateSignatureV2(stringToSign, cred.SecretKey)
	return fmt.Sprintf("%s %s:%s", signV2Algorithm, cred.AccessKey, signature)
}

// Return canonical headers.
func canonicalizedAmzHeadersV2(headers http.Header) string {
	var keys []string
	keyval := make(map[string]string)
	for key := range headers {
		lkey := strings.ToLower(key)
		if !strings.HasPrefix(lkey, "x-amz-") {
			continue
		}
		keys = append(keys, lkey)
		keyval[lkey] = strings.Join(headers[key], ",")
	}
	sort.Strings(keys)
	var canonicalHeaders []string
	for _, key := range keys {
		canonicalHeaders = append(canonicalHeaders, key+":"+keyval[key])
	}
	return strings.Join(canonicalHeaders, "\n")
}

// Return canonical resource string.
func canonicalizedResourceV2(encodedPath string, encodedQuery string) string {
	queries := strings.Split(encodedQuery, "&")
	keyval := make(map[string]string)
	for _, query := range queries {
		key := query
		val := ""
		index := strings.Index(query, "=")
		if index != -1 {
			key = query[:index]
			val = query[index+1:]
		}
		keyval[key] = val
	}
	var canonicalQueries []string
	for _, key := range resourceList {
		val, ok := keyval[key]
		if !ok {
			continue
		}
		if val == "" {
			canonicalQueries = append(canonicalQueries, key)
			continue
		}
		canonicalQueries = append(canonicalQueries, key+"="+val)
	}
	if len(canonicalQueries) == 0 {
		return encodedPath
	}
	// the queries will be already sorted as resourceList is sorted.
	return encodedPath + "?" + strings.Join(canonicalQueries, "&")
}

// Return string to sign for authz header calculation.
func signV2STS(method string, encodedResource string, encodedQuery string, headers http.Header) string {
	canonicalHeaders := canonicalizedAmzHeadersV2(headers)
	if len(canonicalHeaders) > 0 {
		canonicalHeaders += "\n"
	}

	// From the Amazon docs:
	//
	// StringToSign = HTTP-Verb + "\n" +
	// 	 Content-Md5 + "\n" +
	//	 Content-Type + "\n" +
	//	 Date + "\n" +
	//	 CanonicalizedProtocolHeaders +
	//	 CanonicalizedResource;
	stringToSign := strings.Join([]string{
		method,
		headers.Get("Content-MD5"),
		headers.Get("Content-Type"),
		headers.Get("Date"),
		canonicalHeaders,
	}, "\n") + canonicalizedResourceV2(encodedResource, encodedQuery)

	return stringToSign
}

// Return string to sign for pre-sign signature calculation.
func presignV2STS(method string, encodedResource string, encodedQuery string, headers http.Header, expires string) string {
	canonicalHeaders := canonicalizedAmzHeadersV2(headers)
	if len(canonicalHeaders) > 0 {
		canonicalHeaders += "\n"
	}

	// From the Amazon docs:
	//
	// StringToSign = HTTP-Verb + "\n" +
	// 	 Content-Md5 + "\n" +
	//	 Content-Type + "\n" +
	//	 Expires + "\n" +
	//	 CanonicalizedProtocolHeaders +
	//	 CanonicalizedResource;
	stringToSign := strings.Join([]string{
		method,
		headers.Get("Content-MD5"),
		headers.Get("Content-Type"),
		expires,
		canonicalHeaders,
	}, "\n") + canonicalizedResourceV2(encodedResource, encodedQuery)
	return stringToSign
}
<commit_msg>sigv2: Unespace canonicalized resources values (#4034)<commit_after>/*
 * Minio Cloud Storage, (C) 2016, 2017 Minio, Inc.
 *
 * 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 cmd

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
)

// Signature and API related constants.
const (
	signV2Algorithm = "AWS"
)

// AWS S3 Signature V2 calculation rule is give here:
// http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html#RESTAuthenticationStringToSign

// Whitelist resource list that will be used in query string for signature-V2 calculation.
// The list should be alphabetically sorted
var resourceList = []string{
	"acl",
	"delete",
	"lifecycle",
	"location",
	"logging",
	"notification",
	"partNumber",
	"policy",
	"requestPayment",
	"response-cache-control",
	"response-content-disposition",
	"response-content-encoding",
	"response-content-language",
	"response-content-type",
	"response-expires",
	"torrent",
	"uploadId",
	"uploads",
	"versionId",
	"versioning",
	"versions",
	"website",
}

func doesPolicySignatureV2Match(formValues http.Header) APIErrorCode {
	cred := serverConfig.GetCredential()
	accessKey := formValues.Get("AWSAccessKeyId")
	if accessKey != cred.AccessKey {
		return ErrInvalidAccessKeyID
	}
	policy := formValues.Get("Policy")
	signature := formValues.Get("Signature")
	if signature != calculateSignatureV2(policy, cred.SecretKey) {
		return ErrSignatureDoesNotMatch
	}
	return ErrNone
}

// doesPresignV2SignatureMatch - Verify query headers with presigned signature
//     - http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html#RESTAuthenticationQueryStringAuth
// returns ErrNone if matches. S3 errors otherwise.
func doesPresignV2SignatureMatch(r *http.Request) APIErrorCode {
	// Access credentials.
	cred := serverConfig.GetCredential()

	// r.RequestURI will have raw encoded URI as sent by the client.
	splits := splitStr(r.RequestURI, "?", 2)
	encodedResource, encodedQuery := splits[0], splits[1]

	queries := strings.Split(encodedQuery, "&")
	var filteredQueries []string
	var gotSignature string
	var expires string
	var accessKey string
	var err error
	for _, query := range queries {
		keyval := strings.Split(query, "=")
		switch keyval[0] {
		case "AWSAccessKeyId":
			accessKey, err = url.QueryUnescape(keyval[1])
		case "Signature":
			gotSignature, err = url.QueryUnescape(keyval[1])
		case "Expires":
			expires, err = url.QueryUnescape(keyval[1])
		default:
			unescapedQuery, qerr := url.QueryUnescape(query)
			if qerr == nil {
				filteredQueries = append(filteredQueries, unescapedQuery)
			} else {
				err = qerr
			}
		}
		// Check if the query unescaped properly.
		if err != nil {
			errorIf(err, "Unable to unescape query values", queries)
			return ErrInvalidQueryParams
		}
	}

	// Invalid access key.
	if accessKey == "" {
		return ErrInvalidQueryParams
	}

	// Validate if access key id same.
	if accessKey != cred.AccessKey {
		return ErrInvalidAccessKeyID
	}

	// Make sure the request has not expired.
	expiresInt, err := strconv.ParseInt(expires, 10, 64)
	if err != nil {
		return ErrMalformedExpires
	}

	// Check if the presigned URL has expired.
	if expiresInt < UTCNow().Unix() {
		return ErrExpiredPresignRequest
	}

	expectedSignature := preSignatureV2(r.Method, encodedResource, strings.Join(filteredQueries, "&"), r.Header, expires)
	if gotSignature != expectedSignature {
		return ErrSignatureDoesNotMatch
	}

	return ErrNone
}

// Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature;
// Signature = Base64( HMAC-SHA1( YourSecretKey, UTF-8-Encoding-Of( StringToSign ) ) );
//
// StringToSign = HTTP-Verb + "\n" +
//  	Content-Md5 + "\n" +
//  	Content-Type + "\n" +
//  	Date + "\n" +
//  	CanonicalizedProtocolHeaders +
//  	CanonicalizedResource;
//
// CanonicalizedResource = [ "/" + Bucket ] +
//  	<HTTP-Request-URI, from the protocol name up to the query string> +
//  	[ subresource, if present. For example "?acl", "?location", "?logging", or "?torrent"];
//
// CanonicalizedProtocolHeaders = <described below>

// doesSignV2Match - Verify authorization header with calculated header in accordance with
//     - http://docs.aws.amazon.com/AmazonS3/latest/dev/auth-request-sig-v2.html
// returns true if matches, false otherwise. if error is not nil then it is always false

func validateV2AuthHeader(v2Auth string) APIErrorCode {
	if v2Auth == "" {
		return ErrAuthHeaderEmpty
	}
	// Verify if the header algorithm is supported or not.
	if !strings.HasPrefix(v2Auth, signV2Algorithm) {
		return ErrSignatureVersionNotSupported
	}

	// below is V2 Signed Auth header format, splitting on `space` (after the `AWS` string).
	// Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature
	authFields := strings.Split(v2Auth, " ")
	if len(authFields) != 2 {
		return ErrMissingFields
	}

	// Then will be splitting on ":", this will seprate `AWSAccessKeyId` and `Signature` string.
	keySignFields := strings.Split(strings.TrimSpace(authFields[1]), ":")
	if len(keySignFields) != 2 {
		return ErrMissingFields
	}

	// Access credentials.
	cred := serverConfig.GetCredential()
	if keySignFields[0] != cred.AccessKey {
		return ErrInvalidAccessKeyID
	}

	return ErrNone
}

func doesSignV2Match(r *http.Request) APIErrorCode {
	v2Auth := r.Header.Get("Authorization")

	if apiError := validateV2AuthHeader(v2Auth); apiError != ErrNone {
		return apiError
	}

	// r.RequestURI will have raw encoded URI as sent by the client.
	splits := splitStr(r.RequestURI, "?", 2)
	encodedResource, encodedQuery := splits[0], splits[1]

	expectedAuth := signatureV2(r.Method, encodedResource, encodedQuery, r.Header)
	if v2Auth != expectedAuth {
		return ErrSignatureDoesNotMatch
	}

	return ErrNone
}

func calculateSignatureV2(stringToSign string, secret string) string {
	hm := hmac.New(sha1.New, []byte(secret))
	hm.Write([]byte(stringToSign))
	return base64.StdEncoding.EncodeToString(hm.Sum(nil))
}

// Return signature-v2 for the presigned request.
func preSignatureV2(method string, encodedResource string, encodedQuery string, headers http.Header, expires string) string {
	cred := serverConfig.GetCredential()
	stringToSign := presignV2STS(method, encodedResource, encodedQuery, headers, expires)
	return calculateSignatureV2(stringToSign, cred.SecretKey)
}

// Return signature-v2 authrization header.
func signatureV2(method string, encodedResource string, encodedQuery string, headers http.Header) string {
	cred := serverConfig.GetCredential()
	stringToSign := signV2STS(method, encodedResource, encodedQuery, headers)
	signature := calculateSignatureV2(stringToSign, cred.SecretKey)
	return fmt.Sprintf("%s %s:%s", signV2Algorithm, cred.AccessKey, signature)
}

// Return canonical headers.
func canonicalizedAmzHeadersV2(headers http.Header) string {
	var keys []string
	keyval := make(map[string]string)
	for key := range headers {
		lkey := strings.ToLower(key)
		if !strings.HasPrefix(lkey, "x-amz-") {
			continue
		}
		keys = append(keys, lkey)
		keyval[lkey] = strings.Join(headers[key], ",")
	}
	sort.Strings(keys)
	var canonicalHeaders []string
	for _, key := range keys {
		canonicalHeaders = append(canonicalHeaders, key+":"+keyval[key])
	}
	return strings.Join(canonicalHeaders, "\n")
}

// Return canonical resource string.
func canonicalizedResourceV2(encodedPath string, encodedQuery string) string {
	queries := strings.Split(encodedQuery, "&")
	keyval := make(map[string]string)
	for _, query := range queries {
		key := query
		val := ""
		index := strings.Index(query, "=")
		if index != -1 {
			key = query[:index]
			val = query[index+1:]
		}
		keyval[key] = val
	}
	var canonicalQueries []string
	for _, key := range resourceList {
		val, ok := keyval[key]
		if !ok {
			continue
		}
		if val == "" {
			canonicalQueries = append(canonicalQueries, key)
			continue
		}
		// Resources values should be unescaped
		unescapedVal, err := url.QueryUnescape(val)
		if err != nil {
			errorIf(err, "Unable to unescape query value (query = `%s`, value = `%s`)", key, val)
			continue
		}
		canonicalQueries = append(canonicalQueries, key+"="+unescapedVal)
	}
	if len(canonicalQueries) == 0 {
		return encodedPath
	}
	// the queries will be already sorted as resourceList is sorted.
	return encodedPath + "?" + strings.Join(canonicalQueries, "&")
}

// Return string to sign for authz header calculation.
func signV2STS(method string, encodedResource string, encodedQuery string, headers http.Header) string {
	canonicalHeaders := canonicalizedAmzHeadersV2(headers)
	if len(canonicalHeaders) > 0 {
		canonicalHeaders += "\n"
	}

	// From the Amazon docs:
	//
	// StringToSign = HTTP-Verb + "\n" +
	// 	 Content-Md5 + "\n" +
	//	 Content-Type + "\n" +
	//	 Date + "\n" +
	//	 CanonicalizedProtocolHeaders +
	//	 CanonicalizedResource;
	stringToSign := strings.Join([]string{
		method,
		headers.Get("Content-MD5"),
		headers.Get("Content-Type"),
		headers.Get("Date"),
		canonicalHeaders,
	}, "\n") + canonicalizedResourceV2(encodedResource, encodedQuery)

	return stringToSign
}

// Return string to sign for pre-sign signature calculation.
func presignV2STS(method string, encodedResource string, encodedQuery string, headers http.Header, expires string) string {
	canonicalHeaders := canonicalizedAmzHeadersV2(headers)
	if len(canonicalHeaders) > 0 {
		canonicalHeaders += "\n"
	}

	// From the Amazon docs:
	//
	// StringToSign = HTTP-Verb + "\n" +
	// 	 Content-Md5 + "\n" +
	//	 Content-Type + "\n" +
	//	 Expires + "\n" +
	//	 CanonicalizedProtocolHeaders +
	//	 CanonicalizedResource;
	stringToSign := strings.Join([]string{
		method,
		headers.Get("Content-MD5"),
		headers.Get("Content-Type"),
		expires,
		canonicalHeaders,
	}, "\n") + canonicalizedResourceV2(encodedResource, encodedQuery)
	return stringToSign
}
<|endoftext|>


<commit_before>// Copyright 2014 The Go AUTHORS. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

// Command tipgodoc is the beginning of the new tip.golang.org server,
// serving the latest HEAD straight from the Git oven.
package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"sync"
	"time"
)

const (
	repoURL      = "https://go.googlesource.com/"
	metaURL      = "https://go.googlesource.com/?b=master&format=JSON"
	startTimeout = 5 * time.Minute
)

var indexingMsg = []byte("Indexing in progress: result may be inaccurate")

func main() {
	p := new(Proxy)
	go p.run()
	http.Handle("/", p)
	log.Fatal(http.ListenAndServe(":8080", nil))
}

// Proxy implements the tip.golang.org server: a reverse-proxy
// that builds and runs godoc instances showing the latest docs.
type Proxy struct {
	mu    sync.Mutex // protects the followin'
	proxy http.Handler
	cur   string    // signature of gorepo+toolsrepo
	cmd   *exec.Cmd // live godoc instance, or nil for none
	side  string
	err   error
}

func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/_tipstatus" {
		p.serveStatus(w, r)
		return
	}
	p.mu.Lock()
	proxy := p.proxy
	err := p.err
	p.mu.Unlock()
	if proxy == nil {
		s := "tip.golang.org is starting up"
		if err != nil {
			s = err.Error()
		}
		http.Error(w, s, http.StatusInternalServerError)
		return
	}
	proxy.ServeHTTP(w, r)
}

func (p *Proxy) serveStatus(w http.ResponseWriter, r *http.Request) {
	p.mu.Lock()
	defer p.mu.Unlock()
	fmt.Fprintf(w, "side=%v\ncurrent=%v\nerror=%v\n", p.side, p.cur, p.err)
}

// run runs in its own goroutine.
func (p *Proxy) run() {
	p.side = "a"
	for {
		p.poll()
		time.Sleep(30 * time.Second)
	}
}

// poll runs from the run loop goroutine.
func (p *Proxy) poll() {
	heads := gerritMetaMap()
	if heads == nil {
		return
	}

	sig := heads["go"] + "-" + heads["tools"]

	p.mu.Lock()
	changes := sig != p.cur
	curSide := p.side
	p.cur = sig
	p.mu.Unlock()

	if !changes {
		return
	}

	newSide := "b"
	if curSide == "b" {
		newSide = "a"
	}

	cmd, hostport, err := initSide(newSide, heads["go"], heads["tools"])

	p.mu.Lock()
	defer p.mu.Unlock()
	if err != nil {
		log.Println(err)
		p.err = err
		return
	}

	u, err := url.Parse(fmt.Sprintf("http://%v/", hostport))
	if err != nil {
		log.Println(err)
		p.err = err
		return
	}
	p.proxy = httputil.NewSingleHostReverseProxy(u)
	p.side = newSide
	if p.cmd != nil {
		p.cmd.Process.Kill()
	}
	p.cmd = cmd
}

func initSide(side, goHash, toolsHash string) (godoc *exec.Cmd, hostport string, err error) {
	dir := filepath.Join(os.TempDir(), "tipgodoc", side)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return nil, "", err
	}

	goDir := filepath.Join(dir, "go")
	toolsDir := filepath.Join(dir, "gopath/src/golang.org/x/tools")
	if err := checkout(repoURL+"go", goHash, goDir); err != nil {
		return nil, "", err
	}
	if err := checkout(repoURL+"tools", toolsHash, toolsDir); err != nil {
		return nil, "", err
	}

	make := exec.Command(filepath.Join(goDir, "src/make.bash"))
	make.Dir = filepath.Join(goDir, "src")
	if err := runErr(make); err != nil {
		return nil, "", err
	}
	goBin := filepath.Join(goDir, "bin/go")
	install := exec.Command(goBin, "install", "golang.org/x/tools/cmd/godoc")
	install.Env = []string{
		"GOROOT=" + goDir,
		"GOPATH=" + filepath.Join(dir, "gopath"),
		"GOROOT_BOOTSTRAP=" + os.Getenv("GOROOT_BOOTSTRAP"),
	}
	if err := runErr(install); err != nil {
		return nil, "", err
	}

	godocBin := filepath.Join(goDir, "bin/godoc")
	hostport = "localhost:8081"
	if side == "b" {
		hostport = "localhost:8082"
	}
	godoc = exec.Command(godocBin, "-http="+hostport, "-index", "-index_interval=-1s")
	godoc.Env = []string{"GOROOT=" + goDir}
	// TODO(adg): log this somewhere useful
	godoc.Stdout = os.Stdout
	godoc.Stderr = os.Stderr
	if err := godoc.Start(); err != nil {
		return nil, "", err
	}
	go func() {
		// TODO(bradfitz): tell the proxy that this side is dead
		if err := godoc.Wait(); err != nil {
			log.Printf("side %v exited: %v", side, err)
		}
	}()

	deadline := time.Now().Add(startTimeout)
	for time.Now().Before(deadline) {
		time.Sleep(time.Second)
		var res *http.Response
		res, err = http.Get(fmt.Sprintf("http://%v/search?q=FALLTHROUGH", hostport))
		if err != nil {
			continue
		}
		rbody, err := ioutil.ReadAll(res.Body)
		res.Body.Close()
		if err == nil && res.StatusCode == http.StatusOK &&
			!bytes.Contains(rbody, indexingMsg) {
			return godoc, hostport, nil
		}
	}
	godoc.Process.Kill()
	return nil, "", fmt.Errorf("timed out waiting for side %v at %v (%v)", side, hostport, err)
}

func runErr(cmd *exec.Cmd) error {
	out, err := cmd.CombinedOutput()
	if err != nil {
		if len(out) == 0 {
			return err
		}
		return fmt.Errorf("%s\n%v", out, err)
	}
	return nil
}

func checkout(repo, hash, path string) error {
	// Clone git repo if it doesn't exist.
	if _, err := os.Stat(filepath.Join(path, ".git")); os.IsNotExist(err) {
		if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
			return err
		}
		if err := runErr(exec.Command("git", "clone", repo, path)); err != nil {
			return err
		}
	} else if err != nil {
		return err
	}

	// Pull down changes and update to hash.
	cmd := exec.Command("git", "fetch")
	cmd.Dir = path
	if err := runErr(cmd); err != nil {
		return err
	}
	cmd = exec.Command("git", "reset", "--hard", hash)
	cmd.Dir = path
	if err := runErr(cmd); err != nil {
		return err
	}
	cmd = exec.Command("git", "clean", "-d", "-f", "-x")
	cmd.Dir = path
	return runErr(cmd)
}

// gerritMetaMap returns the map from repo name (e.g. "go") to its
// latest master hash.
// The returned map is nil on any transient error.
func gerritMetaMap() map[string]string {
	res, err := http.Get(metaURL)
	if err != nil {
		return nil
	}
	defer res.Body.Close()
	defer io.Copy(ioutil.Discard, res.Body) // ensure EOF for keep-alive
	if res.StatusCode != 200 {
		return nil
	}
	var meta map[string]struct {
		Branches map[string]string
	}
	br := bufio.NewReader(res.Body)
	// For security reasons or something, this URL starts with ")]}'\n" before
	// the JSON object. So ignore that.
	// Shawn Pearce says it's guaranteed to always be just one line, ending in '\n'.
	for {
		b, err := br.ReadByte()
		if err != nil {
			return nil
		}
		if b == '\n' {
			break
		}
	}
	if err := json.NewDecoder(br).Decode(&meta); err != nil {
		log.Printf("JSON decoding error from %v: %s", metaURL, err)
		return nil
	}
	m := map[string]string{}
	for repo, v := range meta {
		if master, ok := v.Branches["master"]; ok {
			m[repo] = master
		}
	}
	return m
}
<commit_msg>cmd/tipgodoc: Kill godoc process if http.ListenAndServe fails<commit_after>// Copyright 2014 The Go AUTHORS. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

// Command tipgodoc is the beginning of the new tip.golang.org server,
// serving the latest HEAD straight from the Git oven.
package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"sync"
	"time"
)

const (
	repoURL      = "https://go.googlesource.com/"
	metaURL      = "https://go.googlesource.com/?b=master&format=JSON"
	startTimeout = 5 * time.Minute
)

var indexingMsg = []byte("Indexing in progress: result may be inaccurate")

func main() {
	p := new(Proxy)
	go p.run()
	http.Handle("/", p)

	if err := http.ListenAndServe(":8080", nil); err != nil {
		p.stop()
		log.Fatal(err)
	}
}

// Proxy implements the tip.golang.org server: a reverse-proxy
// that builds and runs godoc instances showing the latest docs.
type Proxy struct {
	mu    sync.Mutex // protects the followin'
	proxy http.Handler
	cur   string    // signature of gorepo+toolsrepo
	cmd   *exec.Cmd // live godoc instance, or nil for none
	side  string
	err   error
}

func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/_tipstatus" {
		p.serveStatus(w, r)
		return
	}
	p.mu.Lock()
	proxy := p.proxy
	err := p.err
	p.mu.Unlock()
	if proxy == nil {
		s := "tip.golang.org is starting up"
		if err != nil {
			s = err.Error()
		}
		http.Error(w, s, http.StatusInternalServerError)
		return
	}
	proxy.ServeHTTP(w, r)
}

func (p *Proxy) serveStatus(w http.ResponseWriter, r *http.Request) {
	p.mu.Lock()
	defer p.mu.Unlock()
	fmt.Fprintf(w, "side=%v\ncurrent=%v\nerror=%v\n", p.side, p.cur, p.err)
}

// run runs in its own goroutine.
func (p *Proxy) run() {
	p.side = "a"
	for {
		p.poll()
		time.Sleep(30 * time.Second)
	}
}

func (p *Proxy) stop() {
	p.mu.Lock()
	defer p.mu.Unlock()
	if p.cmd != nil {
		p.cmd.Process.Kill()
	}
}

// poll runs from the run loop goroutine.
func (p *Proxy) poll() {
	heads := gerritMetaMap()
	if heads == nil {
		return
	}

	sig := heads["go"] + "-" + heads["tools"]

	p.mu.Lock()
	changes := sig != p.cur
	curSide := p.side
	p.cur = sig
	p.mu.Unlock()

	if !changes {
		return
	}

	newSide := "b"
	if curSide == "b" {
		newSide = "a"
	}

	cmd, hostport, err := initSide(newSide, heads["go"], heads["tools"])

	p.mu.Lock()
	defer p.mu.Unlock()
	if err != nil {
		log.Println(err)
		p.err = err
		return
	}

	u, err := url.Parse(fmt.Sprintf("http://%v/", hostport))
	if err != nil {
		log.Println(err)
		p.err = err
		return
	}
	p.proxy = httputil.NewSingleHostReverseProxy(u)
	p.side = newSide
	if p.cmd != nil {
		p.cmd.Process.Kill()
	}
	p.cmd = cmd
}

func initSide(side, goHash, toolsHash string) (godoc *exec.Cmd, hostport string, err error) {
	dir := filepath.Join(os.TempDir(), "tipgodoc", side)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return nil, "", err
	}

	goDir := filepath.Join(dir, "go")
	toolsDir := filepath.Join(dir, "gopath/src/golang.org/x/tools")
	if err := checkout(repoURL+"go", goHash, goDir); err != nil {
		return nil, "", err
	}
	if err := checkout(repoURL+"tools", toolsHash, toolsDir); err != nil {
		return nil, "", err
	}

	make := exec.Command(filepath.Join(goDir, "src/make.bash"))
	make.Dir = filepath.Join(goDir, "src")
	if err := runErr(make); err != nil {
		return nil, "", err
	}
	goBin := filepath.Join(goDir, "bin/go")
	install := exec.Command(goBin, "install", "golang.org/x/tools/cmd/godoc")
	install.Env = []string{
		"GOROOT=" + goDir,
		"GOPATH=" + filepath.Join(dir, "gopath"),
		"GOROOT_BOOTSTRAP=" + os.Getenv("GOROOT_BOOTSTRAP"),
	}
	if err := runErr(install); err != nil {
		return nil, "", err
	}

	godocBin := filepath.Join(goDir, "bin/godoc")
	hostport = "localhost:8081"
	if side == "b" {
		hostport = "localhost:8082"
	}
	godoc = exec.Command(godocBin, "-http="+hostport, "-index", "-index_interval=-1s")
	godoc.Env = []string{"GOROOT=" + goDir}
	// TODO(adg): log this somewhere useful
	godoc.Stdout = os.Stdout
	godoc.Stderr = os.Stderr
	if err := godoc.Start(); err != nil {
		return nil, "", err
	}
	go func() {
		// TODO(bradfitz): tell the proxy that this side is dead
		if err := godoc.Wait(); err != nil {
			log.Printf("side %v exited: %v", side, err)
		}
	}()

	deadline := time.Now().Add(startTimeout)
	for time.Now().Before(deadline) {
		time.Sleep(time.Second)
		var res *http.Response
		res, err = http.Get(fmt.Sprintf("http://%v/search?q=FALLTHROUGH", hostport))
		if err != nil {
			continue
		}
		rbody, err := ioutil.ReadAll(res.Body)
		res.Body.Close()
		if err == nil && res.StatusCode == http.StatusOK &&
			!bytes.Contains(rbody, indexingMsg) {
			return godoc, hostport, nil
		}
	}
	godoc.Process.Kill()
	return nil, "", fmt.Errorf("timed out waiting for side %v at %v (%v)", side, hostport, err)
}

func runErr(cmd *exec.Cmd) error {
	out, err := cmd.CombinedOutput()
	if err != nil {
		if len(out) == 0 {
			return err
		}
		return fmt.Errorf("%s\n%v", out, err)
	}
	return nil
}

func checkout(repo, hash, path string) error {
	// Clone git repo if it doesn't exist.
	if _, err := os.Stat(filepath.Join(path, ".git")); os.IsNotExist(err) {
		if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
			return err
		}
		if err := runErr(exec.Command("git", "clone", repo, path)); err != nil {
			return err
		}
	} else if err != nil {
		return err
	}

	// Pull down changes and update to hash.
	cmd := exec.Command("git", "fetch")
	cmd.Dir = path
	if err := runErr(cmd); err != nil {
		return err
	}
	cmd = exec.Command("git", "reset", "--hard", hash)
	cmd.Dir = path
	if err := runErr(cmd); err != nil {
		return err
	}
	cmd = exec.Command("git", "clean", "-d", "-f", "-x")
	cmd.Dir = path
	return runErr(cmd)
}

// gerritMetaMap returns the map from repo name (e.g. "go") to its
// latest master hash.
// The returned map is nil on any transient error.
func gerritMetaMap() map[string]string {
	res, err := http.Get(metaURL)
	if err != nil {
		return nil
	}
	defer res.Body.Close()
	defer io.Copy(ioutil.Discard, res.Body) // ensure EOF for keep-alive
	if res.StatusCode != 200 {
		return nil
	}
	var meta map[string]struct {
		Branches map[string]string
	}
	br := bufio.NewReader(res.Body)
	// For security reasons or something, this URL starts with ")]}'\n" before
	// the JSON object. So ignore that.
	// Shawn Pearce says it's guaranteed to always be just one line, ending in '\n'.
	for {
		b, err := br.ReadByte()
		if err != nil {
			return nil
		}
		if b == '\n' {
			break
		}
	}
	if err := json.NewDecoder(br).Decode(&meta); err != nil {
		log.Printf("JSON decoding error from %v: %s", metaURL, err)
		return nil
	}
	m := map[string]string{}
	for repo, v := range meta {
		if master, ok := v.Branches["master"]; ok {
			m[repo] = master
		}
	}
	return m
}
<|endoftext|>


<commit_before>// Downloads torrents from the command-line.
package main

import (
	"expvar"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/anacrolix/envpprof"
	"github.com/anacrolix/tagflag"
	"github.com/dustin/go-humanize"
	"github.com/gosuri/uiprogress"
	"golang.org/x/time/rate"

	"github.com/anacrolix/torrent"
	"github.com/anacrolix/torrent/iplist"
	"github.com/anacrolix/torrent/metainfo"
	"github.com/anacrolix/torrent/storage"
)

var progress = uiprogress.New()

func torrentBar(t *torrent.Torrent) {
	bar := progress.AddBar(1)
	bar.AppendCompleted()
	bar.AppendFunc(func(*uiprogress.Bar) (ret string) {
		select {
		case <-t.GotInfo():
		default:
			return "getting info"
		}
		if t.Seeding() {
			return "seeding"
		} else if t.BytesCompleted() == t.Info().TotalLength() {
			return "completed"
		} else {
			return fmt.Sprintf("downloading (%s/%s)", humanize.Bytes(uint64(t.BytesCompleted())), humanize.Bytes(uint64(t.Info().TotalLength())))
		}
	})
	bar.PrependFunc(func(*uiprogress.Bar) string {
		return t.Name()
	})
	go func() {
		<-t.GotInfo()
		tl := int(t.Info().TotalLength())
		if tl == 0 {
			bar.Set(1)
			return
		}
		bar.Total = tl
		for {
			bc := t.BytesCompleted()
			bar.Set(int(bc))
			time.Sleep(time.Second)
		}
	}()
}

func addTorrents(client *torrent.Client) {
	for _, arg := range flags.Torrent {
		t := func() *torrent.Torrent {
			if strings.HasPrefix(arg, "magnet:") {
				t, err := client.AddMagnet(arg)
				if err != nil {
					log.Fatalf("error adding magnet: %s", err)
				}
				return t
			} else if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") {
				response, err := http.Get(arg)
				if err != nil {
					log.Fatalf("Error downloading torrent file: %s", err)
				}

				metaInfo, err := metainfo.Load(response.Body)
				defer response.Body.Close()
				if err != nil {
					fmt.Fprintf(os.Stderr, "error loading torrent file %q: %s\n", arg, err)
					os.Exit(1)
				}
				t, err := client.AddTorrent(metaInfo)
				if err != nil {
					log.Fatal(err)
				}
				return t
			} else if strings.HasPrefix(arg, "infohash:") {
				t, _ := client.AddTorrentInfoHash(metainfo.NewHashFromHex(strings.TrimPrefix(arg, "infohash:")))
				return t
			} else {
				metaInfo, err := metainfo.LoadFromFile(arg)
				if err != nil {
					fmt.Fprintf(os.Stderr, "error loading torrent file %q: %s\n", arg, err)
					os.Exit(1)
				}
				t, err := client.AddTorrent(metaInfo)
				if err != nil {
					log.Fatal(err)
				}
				return t
			}
		}()
		torrentBar(t)
		t.AddPeers(func() (ret []torrent.Peer) {
			for _, ta := range flags.TestPeer {
				ret = append(ret, torrent.Peer{
					IP:   ta.IP,
					Port: ta.Port,
				})
			}
			return
		}())
		go func() {
			<-t.GotInfo()
			t.DownloadAll()
		}()
	}
}

var flags = struct {
	Mmap            bool           `help:"memory-map torrent data"`
	TestPeer        []*net.TCPAddr `help:"addresses of some starting peers"`
	Seed            bool           `help:"seed after download is complete"`
	Addr            *net.TCPAddr   `help:"network listen addr"`
	UploadRate      tagflag.Bytes  `help:"max piece bytes to send per second"`
	DownloadRate    tagflag.Bytes  `help:"max bytes per second down from peers"`
	Debug           bool
	PackedBlocklist string
	Stats           *bool
	tagflag.StartPos
	Torrent []string `arity:"+" help:"torrent file path or magnet uri"`
}{
	UploadRate:   -1,
	DownloadRate: -1,
}

func stdoutAndStderrAreSameFile() bool {
	fi1, _ := os.Stdout.Stat()
	fi2, _ := os.Stderr.Stat()
	return os.SameFile(fi1, fi2)
}

func statsEnabled() bool {
	if flags.Stats == nil {
		return flags.Debug
	}
	return *flags.Stats
}

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	tagflag.Parse(&flags)
	defer envpprof.Stop()
	clientConfig := torrent.NewDefaultClientConfig()
	clientConfig.Debug = flags.Debug
	clientConfig.Seed = flags.Seed
	if flags.PackedBlocklist != "" {
		blocklist, err := iplist.MMapPackedFile(flags.PackedBlocklist)
		if err != nil {
			log.Fatalf("error loading blocklist: %s", err)
		}
		defer blocklist.Close()
		clientConfig.IPBlocklist = blocklist
	}
	if flags.Mmap {
		clientConfig.DefaultStorage = storage.NewMMap("")
	}
	if flags.Addr != nil {
		clientConfig.SetListenAddr(flags.Addr.String())
	}
	if flags.UploadRate != -1 {
		clientConfig.UploadRateLimiter = rate.NewLimiter(rate.Limit(flags.UploadRate), 256<<10)
	}
	if flags.DownloadRate != -1 {
		clientConfig.DownloadRateLimiter = rate.NewLimiter(rate.Limit(flags.DownloadRate), 1<<20)
	}

	client, err := torrent.NewClient(clientConfig)
	if err != nil {
		log.Fatalf("error creating client: %s", err)
	}
	defer client.Close()
	// Write status on the root path on the default HTTP muxer. This will be
	// bound to localhost somewhere if GOPPROF is set, thanks to the envpprof
	// import.
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		client.WriteStatus(w)
	})
	if stdoutAndStderrAreSameFile() {
		log.SetOutput(progress.Bypass())
	}
	progress.Start()
	addTorrents(client)
	if client.WaitAll() {
		log.Print("downloaded ALL the torrents")
	} else {
		log.Fatal("y u no complete torrents?!")
	}
	if flags.Seed {
		outputStats(client)
		select {}
	}
	outputStats(client)
}

func outputStats(cl *torrent.Client) {
	if !statsEnabled() {
		return
	}
	expvar.Do(func(kv expvar.KeyValue) {
		fmt.Printf("%s: %s\n", kv.Key, kv.Value)
	})
	cl.WriteStatus(os.Stdout)
}
<commit_msg>attempt to close the client on signal<commit_after>// Downloads torrents from the command-line.
package main

import (
	"expvar"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/anacrolix/envpprof"
	"github.com/anacrolix/tagflag"
	"github.com/dustin/go-humanize"
	"github.com/gosuri/uiprogress"
	"golang.org/x/time/rate"

	"github.com/anacrolix/torrent"
	"github.com/anacrolix/torrent/iplist"
	"github.com/anacrolix/torrent/metainfo"
	"github.com/anacrolix/torrent/storage"
)

var progress = uiprogress.New()

func torrentBar(t *torrent.Torrent) {
	bar := progress.AddBar(1)
	bar.AppendCompleted()
	bar.AppendFunc(func(*uiprogress.Bar) (ret string) {
		select {
		case <-t.GotInfo():
		default:
			return "getting info"
		}
		if t.Seeding() {
			return "seeding"
		} else if t.BytesCompleted() == t.Info().TotalLength() {
			return "completed"
		} else {
			return fmt.Sprintf("downloading (%s/%s)", humanize.Bytes(uint64(t.BytesCompleted())), humanize.Bytes(uint64(t.Info().TotalLength())))
		}
	})
	bar.PrependFunc(func(*uiprogress.Bar) string {
		return t.Name()
	})
	go func() {
		<-t.GotInfo()
		tl := int(t.Info().TotalLength())
		if tl == 0 {
			bar.Set(1)
			return
		}
		bar.Total = tl
		for {
			bc := t.BytesCompleted()
			bar.Set(int(bc))
			time.Sleep(time.Second)
		}
	}()
}

func addTorrents(client *torrent.Client) {
	for _, arg := range flags.Torrent {
		t := func() *torrent.Torrent {
			if strings.HasPrefix(arg, "magnet:") {
				t, err := client.AddMagnet(arg)
				if err != nil {
					log.Fatalf("error adding magnet: %s", err)
				}
				return t
			} else if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") {
				response, err := http.Get(arg)
				if err != nil {
					log.Fatalf("Error downloading torrent file: %s", err)
				}

				metaInfo, err := metainfo.Load(response.Body)
				defer response.Body.Close()
				if err != nil {
					fmt.Fprintf(os.Stderr, "error loading torrent file %q: %s\n", arg, err)
					os.Exit(1)
				}
				t, err := client.AddTorrent(metaInfo)
				if err != nil {
					log.Fatal(err)
				}
				return t
			} else if strings.HasPrefix(arg, "infohash:") {
				t, _ := client.AddTorrentInfoHash(metainfo.NewHashFromHex(strings.TrimPrefix(arg, "infohash:")))
				return t
			} else {
				metaInfo, err := metainfo.LoadFromFile(arg)
				if err != nil {
					fmt.Fprintf(os.Stderr, "error loading torrent file %q: %s\n", arg, err)
					os.Exit(1)
				}
				t, err := client.AddTorrent(metaInfo)
				if err != nil {
					log.Fatal(err)
				}
				return t
			}
		}()
		torrentBar(t)
		t.AddPeers(func() (ret []torrent.Peer) {
			for _, ta := range flags.TestPeer {
				ret = append(ret, torrent.Peer{
					IP:   ta.IP,
					Port: ta.Port,
				})
			}
			return
		}())
		go func() {
			<-t.GotInfo()
			t.DownloadAll()
		}()
	}
}

var flags = struct {
	Mmap            bool           `help:"memory-map torrent data"`
	TestPeer        []*net.TCPAddr `help:"addresses of some starting peers"`
	Seed            bool           `help:"seed after download is complete"`
	Addr            *net.TCPAddr   `help:"network listen addr"`
	UploadRate      tagflag.Bytes  `help:"max piece bytes to send per second"`
	DownloadRate    tagflag.Bytes  `help:"max bytes per second down from peers"`
	Debug           bool
	PackedBlocklist string
	Stats           *bool
	tagflag.StartPos
	Torrent []string `arity:"+" help:"torrent file path or magnet uri"`
}{
	UploadRate:   -1,
	DownloadRate: -1,
}

func stdoutAndStderrAreSameFile() bool {
	fi1, _ := os.Stdout.Stat()
	fi2, _ := os.Stderr.Stat()
	return os.SameFile(fi1, fi2)
}

func statsEnabled() bool {
	if flags.Stats == nil {
		return flags.Debug
	}
	return *flags.Stats
}

func exitSignalHandlers(client *torrent.Client) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
	for {
		log.Printf("close signal received: %+v", <-c)
		client.Close()
	}
}

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	tagflag.Parse(&flags)
	defer envpprof.Stop()
	clientConfig := torrent.NewDefaultClientConfig()
	clientConfig.Debug = flags.Debug
	clientConfig.Seed = flags.Seed
	if flags.PackedBlocklist != "" {
		blocklist, err := iplist.MMapPackedFile(flags.PackedBlocklist)
		if err != nil {
			log.Fatalf("error loading blocklist: %s", err)
		}
		defer blocklist.Close()
		clientConfig.IPBlocklist = blocklist
	}
	if flags.Mmap {
		clientConfig.DefaultStorage = storage.NewMMap("")
	}
	if flags.Addr != nil {
		clientConfig.SetListenAddr(flags.Addr.String())
	}
	if flags.UploadRate != -1 {
		clientConfig.UploadRateLimiter = rate.NewLimiter(rate.Limit(flags.UploadRate), 256<<10)
	}
	if flags.DownloadRate != -1 {
		clientConfig.DownloadRateLimiter = rate.NewLimiter(rate.Limit(flags.DownloadRate), 1<<20)
	}

	client, err := torrent.NewClient(clientConfig)
	if err != nil {
		log.Fatalf("error creating client: %s", err)
	}
	defer client.Close()
	go exitSignalHandlers(client)

	// Write status on the root path on the default HTTP muxer. This will be
	// bound to localhost somewhere if GOPPROF is set, thanks to the envpprof
	// import.
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		client.WriteStatus(w)
	})
	if stdoutAndStderrAreSameFile() {
		log.SetOutput(progress.Bypass())
	}
	progress.Start()
	addTorrents(client)
	if client.WaitAll() {
		log.Print("downloaded ALL the torrents")
	} else {
		log.Fatal("y u no complete torrents?!")
	}
	if flags.Seed {
		outputStats(client)
		select {}
	}
	outputStats(client)
}

func outputStats(cl *torrent.Client) {
	if !statsEnabled() {
		return
	}
	expvar.Do(func(kv expvar.KeyValue) {
		fmt.Printf("%s: %s\n", kv.Key, kv.Value)
	})
	cl.WriteStatus(os.Stdout)
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Knative Authors

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 main

import (
	"flag"
	"log"

	"go.uber.org/zap"

	"github.com/knative/pkg/configmap"
	"github.com/knative/pkg/logging/logkey"
	"github.com/knative/pkg/signals"
	"github.com/knative/pkg/webhook"
	"github.com/knative/serving/pkg/apis/serving/v1alpha1"
	"github.com/knative/serving/pkg/logging"
	"github.com/knative/serving/pkg/system"

	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
)

const (
	logLevelKey = "webhook"
)

func main() {
	flag.Parse()
	cm, err := configmap.Load("/etc/config-logging")
	if err != nil {
		log.Fatalf("Error loading logging configuration: %v", err)
	}
	config, err := logging.NewConfigFromMap(cm)
	if err != nil {
		log.Fatalf("Error parsing logging configuration: %v", err)
	}
	logger, atomicLevel := logging.NewLoggerFromConfig(config, logLevelKey)
	defer logger.Sync()
	logger = logger.With(zap.String(logkey.ControllerType, "webhook"))

	logger.Info("Starting the Configuration Webhook")

	// set up signals so we handle the first shutdown signal gracefully
	stopCh := signals.SetupSignalHandler()

	clusterConfig, err := rest.InClusterConfig()
	if err != nil {
		logger.Fatal("Failed to get in cluster config", zap.Error(err))
	}

	kubeClient, err := kubernetes.NewForConfig(clusterConfig)
	if err != nil {
		logger.Fatal("Failed to get the client set", zap.Error(err))
	}

	// Watch the logging config map and dynamically update logging levels.
	configMapWatcher := configmap.NewDefaultWatcher(kubeClient, system.Namespace)
	configMapWatcher.Watch(logging.ConfigName, logging.UpdateLevelFromConfigMap(logger, atomicLevel, logLevelKey))
	if err = configMapWatcher.Start(stopCh); err != nil {
		logger.Fatalf("failed to start configuration manager: %v", err)
	}

	options := webhook.ControllerOptions{
		ServiceName:    "webhook",
		DeploymentName: "webhook",
		Namespace:      system.Namespace,
		Port:           443,
		SecretName:     "webhook-certs",
		WebhookName:    "webhook.serving.knative.dev",
	}
	controller := webhook.AdmissionController{
		Client:  kubeClient,
		Options: options,
		Handlers: map[schema.GroupVersionKind]runtime.Object{
			v1alpha1.SchemeGroupVersion.WithKind("Revision"):      &v1alpha1.Revision{},
			v1alpha1.SchemeGroupVersion.WithKind("Configuration"): &v1alpha1.Configuration{},
			v1alpha1.SchemeGroupVersion.WithKind("Route"):         &v1alpha1.Route{},
			v1alpha1.SchemeGroupVersion.WithKind("Service"):       &v1alpha1.Service{},
		},
		Logger: logger,
	}
	if err != nil {
		logger.Fatal("Failed to create the admission controller", zap.Error(err))
	}
	controller.Run(stopCh)
}
<commit_msg>This adds the KPA to our webhook validation and defaulting. (#1836)<commit_after>/*
Copyright 2017 The Knative Authors

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 main

import (
	"flag"
	"log"

	"go.uber.org/zap"

	"github.com/knative/pkg/configmap"
	"github.com/knative/pkg/logging/logkey"
	"github.com/knative/pkg/signals"
	"github.com/knative/pkg/webhook"
	kpa "github.com/knative/serving/pkg/apis/autoscaling/v1alpha1"
	"github.com/knative/serving/pkg/apis/serving/v1alpha1"
	"github.com/knative/serving/pkg/logging"
	"github.com/knative/serving/pkg/system"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
)

const (
	logLevelKey = "webhook"
)

func main() {
	flag.Parse()
	cm, err := configmap.Load("/etc/config-logging")
	if err != nil {
		log.Fatalf("Error loading logging configuration: %v", err)
	}
	config, err := logging.NewConfigFromMap(cm)
	if err != nil {
		log.Fatalf("Error parsing logging configuration: %v", err)
	}
	logger, atomicLevel := logging.NewLoggerFromConfig(config, logLevelKey)
	defer logger.Sync()
	logger = logger.With(zap.String(logkey.ControllerType, "webhook"))

	logger.Info("Starting the Configuration Webhook")

	// set up signals so we handle the first shutdown signal gracefully
	stopCh := signals.SetupSignalHandler()

	clusterConfig, err := rest.InClusterConfig()
	if err != nil {
		logger.Fatal("Failed to get in cluster config", zap.Error(err))
	}

	kubeClient, err := kubernetes.NewForConfig(clusterConfig)
	if err != nil {
		logger.Fatal("Failed to get the client set", zap.Error(err))
	}

	// Watch the logging config map and dynamically update logging levels.
	configMapWatcher := configmap.NewDefaultWatcher(kubeClient, system.Namespace)
	configMapWatcher.Watch(logging.ConfigName, logging.UpdateLevelFromConfigMap(logger, atomicLevel, logLevelKey))
	if err = configMapWatcher.Start(stopCh); err != nil {
		logger.Fatalf("failed to start configuration manager: %v", err)
	}

	options := webhook.ControllerOptions{
		ServiceName:    "webhook",
		DeploymentName: "webhook",
		Namespace:      system.Namespace,
		Port:           443,
		SecretName:     "webhook-certs",
		WebhookName:    "webhook.serving.knative.dev",
	}
	controller := webhook.AdmissionController{
		Client:  kubeClient,
		Options: options,
		Handlers: map[schema.GroupVersionKind]runtime.Object{
			v1alpha1.SchemeGroupVersion.WithKind("Revision"):      &v1alpha1.Revision{},
			v1alpha1.SchemeGroupVersion.WithKind("Configuration"): &v1alpha1.Configuration{},
			v1alpha1.SchemeGroupVersion.WithKind("Route"):         &v1alpha1.Route{},
			v1alpha1.SchemeGroupVersion.WithKind("Service"):       &v1alpha1.Service{},
			kpa.SchemeGroupVersion.WithKind("PodAutoscaler"):      &kpa.PodAutoscaler{},
		},
		Logger: logger,
	}
	if err != nil {
		logger.Fatal("Failed to create the admission controller", zap.Error(err))
	}
	controller.Run(stopCh)
}
<|endoftext|>


<commit_before>package routes

import (
	"github.com/justinas/alice"
    "github.com/gorilla/handlers"
    "github.com/gorilla/mux"
    "app/middleware"
    "app/socket"
)

// Router setups all the API routes and middleware
func Router() *mux.Router  {
    common := alice.New(middleware.Authentication, middleware.RecoverHandler)

	socketHandlers := handlers.MethodHandler{
		"GET": common.ThenFunc(socket.Handler),
	}

    router := mux.NewRouter()
	router.Handle("/ws", socketHandlers)

    return router
}
<commit_msg>Rename route<commit_after>package routes

import (
	"github.com/justinas/alice"
    "github.com/gorilla/handlers"
    "github.com/gorilla/mux"
    "app/middleware"
    "app/socket"
)

// Router setups all the API routes and middleware
func Router() *mux.Router  {
    common := alice.New(middleware.Authentication, middleware.RecoverHandler)

	socketHandlers := handlers.MethodHandler{
		"GET": common.ThenFunc(socket.Handler),
	}

    router := mux.NewRouter()
	router.Handle("/connect", socketHandlers)

    return router
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/go-netrc/netrc"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"sort"
)

const (
	Version = "0.0.1"
)

func getCreds(machine string) (user, pass string) {
	m, err := netrc.FindMachine(os.Getenv("HOME")+"/.netrc", machine)
	if err != nil {
		panic(err)
	}

	return m.Login, m.Password
}

// generic api requests
func apiReq(v interface{}, meth string, url string) {
	req, err := http.NewRequest(meth, url, nil)
	if err != nil {
		panic(err)
	}

	req.SetBasicAuth(getCreds(req.Host))
	req.Header.Add("User-Agent", fmt.Sprintf("hk/%s", Version))
	req.Header.Add("Accept", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	if res.StatusCode == 401 {
		error("Unauthorized")
	}
	if res.StatusCode == 403 {
		error("Unauthorized")
	}
	if res.StatusCode != 200 {
		fmt.Printf("%v\n", res)
		error("Unexpected error")
	}

	err = json.NewDecoder(res.Body).Decode(v)
	if err != nil {
		panic(err)
	}
}

// error formatting
func error(msg string) {
	fmt.Fprintf(os.Stderr, "Error: %s.\n", msg)
	os.Exit(1)
}

func unrecArg(arg, cmd string) {
	error(fmt.Sprintf("Unrecognized argument '%s'. See 'hk help %s'", arg, cmd))
}

func unrecCmd(cmd string) {
	error(fmt.Sprintf("'%s' is not an hk command. See 'hk help'", cmd))
}

// info formatting
func cmdHelp(usage string, desc string) {
	fmt.Printf("Usage: %s\n\n", usage)
	fmt.Printf("%s.\n", desc)
}

// commands
func envHelp() {
	cmdHelp("hk env -a <app>", "Show all config vars")
}

func env() {
	if (len(os.Args) != 4) || (os.Args[2] != "-a") {
		error("Invalid usage. See 'hk help env'")
	}
	appName := os.Args[3]
	var config map[string]string
	apiReq(&config, "GET", fmt.Sprintf("https://api.heroku.com/apps/%s/config_vars", appName))
	for k, v := range config {
		fmt.Printf("%s=%s\n", k, v)
	}
}

func getHelp() {
	cmdHelp("hk get -a <app> <key>", "Get the value of a config var")
}

func get() {
	if (len(os.Args) != 5) || (os.Args[2] != "-a") {
		error("Invalid usage. See 'hk help get'")
	}
	appName := os.Args[3]
	key := os.Args[4]
	var config map[string]string
	apiReq(&config, "GET", fmt.Sprintf("https://api.heroku.com/apps/%s/config_vars", appName))
	value, found := config[key]
	if !found {
		error(fmt.Sprintf("No such key as '%s'", key))
	}
	fmt.Println(value)
}

func infoHelp() {
	cmdHelp("hk info -a <app>", "Show app info")
}

func info() {
	if (len(os.Args) != 4) || (os.Args[2] != "-a") {
		error("Invalid usage. See 'hk help info'")
	}
	appName := os.Args[3]
	var info struct {
		Name   string
		Owner  string `json:"owner_email"`
		Stack  string
		GitURL string `json:"git_url"`
		WebURL string `json:"web_url"`
	}
	apiReq(&info, "GET", fmt.Sprintf("https://api.heroku.com/apps/%s", appName))
	fmt.Printf("Name:     %s\n", info.Name)
	fmt.Printf("Owner:    %s\n", info.Owner)
	fmt.Printf("Stack:    %s\n", info.Stack)
	fmt.Printf("Git URL:  %s\n", info.GitURL)
	fmt.Printf("Web URL:  %s\n", info.WebURL)
}

func credsHelp() {
	cmdHelp("hk creds", "Show API credentials")
}

func creds() {
	fmt.Println(getCreds("api.heroku.com"))
}

func listHelp() {
	cmdHelp("hk list", "List accessible apps")
}

func list() {
	if len(os.Args) != 2 {
		unrecArg(os.Args[2], "list")
	}
	var apps []struct{ Name string }
	apiReq(&apps, "GET", "https://api.heroku.com/apps")
	for _, app := range apps {
		fmt.Printf("%s\n", app.Name)
	}
}

func psHelp() {
	cmdHelp("hk ps -a <app>", "List app processes")
}

type Proc struct {
	Name    string `json:"process"`
	State   string
	Command string
}

type Procs []*Proc

func (p Procs) Len() int           { return len(p) }
func (p Procs) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
func (p Procs) Less(i, j int) bool { return p[i].Name < p[j].Name }

func ps() {
	if (len(os.Args) != 4) || (os.Args[2] != "-a") {
		error("Invalid usage. See 'hk help ps'")
	}
	appName := os.Args[3]
	var procs Procs
	apiReq(&procs, "GET", fmt.Sprintf("https://api.heroku.com/apps/%s/ps", appName))
	sort.Sort(procs)
	fmt.Printf("Process           State       Command\n")
	fmt.Printf("----------------  ----------  ------------------------\n")
	for _, proc := range procs {
		fmt.Printf("%-16s  %-10s  %s\n", proc.Name, proc.State, proc.Command)
	}
}

func versionHelp() {
	cmdHelp("hk version", "Show hk client version")
}

func version() {
	if len(os.Args) != 2 {
		unrecArg(os.Args[2], "version")
	}
	fmt.Printf("%s\n", Version)
}

func help() {
	if len(os.Args) <= 2 {
		usage()
	} else {
		cmd := os.Args[2]
		switch cmd {
		case "env":
			envHelp()
		case "get":
			getHelp()
		case "info":
			infoHelp()
		case "creds":
			credsHelp()
		case "list":
			listHelp()
		case "ps":
			psHelp()
		case "version":
			versionHelp()
		default:
			unrecCmd(cmd)
		}
	}
}

// top-level usage
func usage() {
	fmt.Printf("Usage: hk <command> [-a <app>] [command-specific-options]\n\n")
	fmt.Printf("Supported hk commands are:\n")
	fmt.Printf("  addons          List add-ons\n")
	fmt.Printf("  addons-add      Add an add-on\n")
	fmt.Printf("  addons-open     Open an add-on page\n")
	fmt.Printf("  addons-remove   Remove an add-on \n")
	fmt.Printf("  create          Create an app\n")
	fmt.Printf("  destroy         Destroy an app\n")
	fmt.Printf("  env             List config vars\n")
	fmt.Printf("  get             Get config var\n")
	fmt.Printf("  help            Show this help\n")
	fmt.Printf("  info            Show app info\n")
	fmt.Printf("  list            List apps\n")
	fmt.Printf("  login           Log in\n")
	fmt.Printf("  logout          Log out\n")
	fmt.Printf("  logs            Show logs\n")
	fmt.Printf("  open            Open app\n")
	fmt.Printf("  pg              List databases\n")
	fmt.Printf("  pg-info         Show database info\n")
	fmt.Printf("  pg-promote      Promote a database\n")
	fmt.Printf("  ps-psql         Open a psql database shell\n")
	fmt.Printf("  pg-wait         Await a database\n")
	fmt.Printf("  ps              List processes\n")
	fmt.Printf("  release         Show release info\n")
	fmt.Printf("  releases        List releases\n")
	fmt.Printf("  rename          Rename an app\n")
	fmt.Printf("  restart         Restart processes\n")
	fmt.Printf("  rollback        Rollback to a previous release\n")
	fmt.Printf("  run             Run a process\n")
	fmt.Printf("  set             Set config var\n")
	fmt.Printf("  scale           Scale processes\n")
	fmt.Printf("  stop            Stop a process\n")
	fmt.Printf("  creds           Show auth creds\n")
	fmt.Printf("  unset           Unset config vars\n")
	fmt.Printf("  version         Display version\n\n")
	fmt.Printf("See 'hk help <command>' for more information on a specific command.\n")
}

// entry point
func main() {
	if len(os.Args) <= 1 {
		usage()
	} else {
		cmd := os.Args[1]
		switch cmd {
		case "env":
			env()
		case "get":
			get()
		case "help":
			help()
		case "info":
			info()
		case "creds":
			creds()
		case "list":
			list()
		case "ps":
			ps()
		case "version":
			version()
		default:
			unrecCmd(cmd)
		}
	}
}
<commit_msg>obey HEROKU_API_URL<commit_after>package main

import (
	"code.google.com/p/go-netrc/netrc"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"sort"
	"strings"
)

const (
	Version = "0.0.1"
)

var apiURL = "https://api.heroku.com"

func getCreds(u *url.URL) (user, pass string) {
	if u.User != nil {
		pw, _ := u.User.Password()
		return u.User.Username(), pw
	}

	m, err := netrc.FindMachine(os.Getenv("HOME")+"/.netrc", u.Host)
	if err != nil {
		panic(err)
	}

	return m.Login, m.Password
}

// generic api requests
func apiReq(v interface{}, meth string, url string) {
	req, err := http.NewRequest(meth, url, nil)
	if err != nil {
		panic(err)
	}

	req.SetBasicAuth(getCreds(req.URL))
	req.Header.Add("User-Agent", fmt.Sprintf("hk/%s", Version))
	req.Header.Add("Accept", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	if res.StatusCode == 401 {
		error("Unauthorized")
	}
	if res.StatusCode == 403 {
		error("Unauthorized")
	}
	if res.StatusCode != 200 {
		fmt.Printf("%v\n", res)
		error("Unexpected error")
	}

	err = json.NewDecoder(res.Body).Decode(v)
	if err != nil {
		panic(err)
	}
}

// error formatting
func error(msg string) {
	fmt.Fprintf(os.Stderr, "Error: %s.\n", msg)
	os.Exit(1)
}

func unrecArg(arg, cmd string) {
	error(fmt.Sprintf("Unrecognized argument '%s'. See 'hk help %s'", arg, cmd))
}

func unrecCmd(cmd string) {
	error(fmt.Sprintf("'%s' is not an hk command. See 'hk help'", cmd))
}

// info formatting
func cmdHelp(usage string, desc string) {
	fmt.Printf("Usage: %s\n\n", usage)
	fmt.Printf("%s.\n", desc)
}

// commands
func envHelp() {
	cmdHelp("hk env -a <app>", "Show all config vars")
}

func env() {
	if (len(os.Args) != 4) || (os.Args[2] != "-a") {
		error("Invalid usage. See 'hk help env'")
	}
	appName := os.Args[3]
	var config map[string]string
	apiReq(&config, "GET", fmt.Sprintf(apiURL+"/apps/%s/config_vars", appName))
	for k, v := range config {
		fmt.Printf("%s=%s\n", k, v)
	}
}

func getHelp() {
	cmdHelp("hk get -a <app> <key>", "Get the value of a config var")
}

func get() {
	if (len(os.Args) != 5) || (os.Args[2] != "-a") {
		error("Invalid usage. See 'hk help get'")
	}
	appName := os.Args[3]
	key := os.Args[4]
	var config map[string]string
	apiReq(&config, "GET", fmt.Sprintf(apiURL+"/apps/%s/config_vars", appName))
	value, found := config[key]
	if !found {
		error(fmt.Sprintf("No such key as '%s'", key))
	}
	fmt.Println(value)
}

func infoHelp() {
	cmdHelp("hk info -a <app>", "Show app info")
}

func info() {
	if (len(os.Args) != 4) || (os.Args[2] != "-a") {
		error("Invalid usage. See 'hk help info'")
	}
	appName := os.Args[3]
	var info struct {
		Name   string
		Owner  string `json:"owner_email"`
		Stack  string
		GitURL string `json:"git_url"`
		WebURL string `json:"web_url"`
	}
	apiReq(&info, "GET", fmt.Sprintf(apiURL+"/apps/%s", appName))
	fmt.Printf("Name:     %s\n", info.Name)
	fmt.Printf("Owner:    %s\n", info.Owner)
	fmt.Printf("Stack:    %s\n", info.Stack)
	fmt.Printf("Git URL:  %s\n", info.GitURL)
	fmt.Printf("Web URL:  %s\n", info.WebURL)
}

func credsHelp() {
	cmdHelp("hk creds", "Show API credentials")
}

func creds() {
	u, err := url.Parse(apiURL)
	if err != nil {
		error(err.Error())
	}
	fmt.Println(getCreds(u))
}

func listHelp() {
	cmdHelp("hk list", "List accessible apps")
}

func list() {
	if len(os.Args) != 2 {
		unrecArg(os.Args[2], "list")
	}
	var apps []struct{ Name string }
	apiReq(&apps, "GET", apiURL+"/apps")
	for _, app := range apps {
		fmt.Printf("%s\n", app.Name)
	}
}

func psHelp() {
	cmdHelp("hk ps -a <app>", "List app processes")
}

type Proc struct {
	Name    string `json:"process"`
	State   string
	Command string
}

type Procs []*Proc

func (p Procs) Len() int           { return len(p) }
func (p Procs) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
func (p Procs) Less(i, j int) bool { return p[i].Name < p[j].Name }

func ps() {
	if (len(os.Args) != 4) || (os.Args[2] != "-a") {
		error("Invalid usage. See 'hk help ps'")
	}
	appName := os.Args[3]
	var procs Procs
	apiReq(&procs, "GET", fmt.Sprintf(apiURL+"/apps/%s/ps", appName))
	sort.Sort(procs)
	fmt.Printf("Process           State       Command\n")
	fmt.Printf("----------------  ----------  ------------------------\n")
	for _, proc := range procs {
		fmt.Printf("%-16s  %-10s  %s\n", proc.Name, proc.State, proc.Command)
	}
}

func versionHelp() {
	cmdHelp("hk version", "Show hk client version")
}

func version() {
	if len(os.Args) != 2 {
		unrecArg(os.Args[2], "version")
	}
	fmt.Printf("%s\n", Version)
}

func help() {
	if len(os.Args) <= 2 {
		usage()
	} else {
		cmd := os.Args[2]
		switch cmd {
		case "env":
			envHelp()
		case "get":
			getHelp()
		case "info":
			infoHelp()
		case "creds":
			credsHelp()
		case "list":
			listHelp()
		case "ps":
			psHelp()
		case "version":
			versionHelp()
		default:
			unrecCmd(cmd)
		}
	}
}

// top-level usage
func usage() {
	fmt.Printf("Usage: hk <command> [-a <app>] [command-specific-options]\n\n")
	fmt.Printf("Supported hk commands are:\n")
	fmt.Printf("  addons          List add-ons\n")
	fmt.Printf("  addons-add      Add an add-on\n")
	fmt.Printf("  addons-open     Open an add-on page\n")
	fmt.Printf("  addons-remove   Remove an add-on \n")
	fmt.Printf("  create          Create an app\n")
	fmt.Printf("  destroy         Destroy an app\n")
	fmt.Printf("  env             List config vars\n")
	fmt.Printf("  get             Get config var\n")
	fmt.Printf("  help            Show this help\n")
	fmt.Printf("  info            Show app info\n")
	fmt.Printf("  list            List apps\n")
	fmt.Printf("  login           Log in\n")
	fmt.Printf("  logout          Log out\n")
	fmt.Printf("  logs            Show logs\n")
	fmt.Printf("  open            Open app\n")
	fmt.Printf("  pg              List databases\n")
	fmt.Printf("  pg-info         Show database info\n")
	fmt.Printf("  pg-promote      Promote a database\n")
	fmt.Printf("  ps-psql         Open a psql database shell\n")
	fmt.Printf("  pg-wait         Await a database\n")
	fmt.Printf("  ps              List processes\n")
	fmt.Printf("  release         Show release info\n")
	fmt.Printf("  releases        List releases\n")
	fmt.Printf("  rename          Rename an app\n")
	fmt.Printf("  restart         Restart processes\n")
	fmt.Printf("  rollback        Rollback to a previous release\n")
	fmt.Printf("  run             Run a process\n")
	fmt.Printf("  set             Set config var\n")
	fmt.Printf("  scale           Scale processes\n")
	fmt.Printf("  stop            Stop a process\n")
	fmt.Printf("  creds           Show auth creds\n")
	fmt.Printf("  unset           Unset config vars\n")
	fmt.Printf("  version         Display version\n\n")
	fmt.Printf("See 'hk help <command>' for more information on a specific command.\n")
}

// entry point
func main() {
	if s := os.Getenv("HEROKU_API_URL"); s != "" {
		apiURL = strings.TrimRight(s, "/")
	}

	if len(os.Args) <= 1 {
		usage()
	} else {
		cmd := os.Args[1]
		switch cmd {
		case "env":
			env()
		case "get":
			get()
		case "help":
			help()
		case "info":
			info()
		case "creds":
			creds()
		case "list":
			list()
		case "ps":
			ps()
		case "version":
			version()
		default:
			unrecCmd(cmd)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	//	"os"
	"github.com/tzaffi/go-bitbucket"
	"golang.org/x/crypto/ssh/terminal"
	"reflect"
	"syscall"
)

func getMyRepos(client *bitbucket.Client, owner string, team string, options ...string) interface{} {
	opt := &bitbucket.RepositoriesOptions{
		Owner: owner,
		Team:  team,
	}
	/*
	if options != nil {
		fmt.Println("something:")
	} else {
		fmt.Println("nada:")
	}
  */
	fmt.Printf("options = %v\tTtype = %T\n", options, options)
	getAllPages := options != nil && options[0] == "ALL_PAGES"
	fmt.Println("getting all pages ?", getAllPages)
	var pages []uint;
	if(!getAllPages) {
		pages = []uint{1}
	} else {
		pages = []uint{1, 11}
	}

	res := client.Repositories.ListForTeam(opt, pages...)

	return res

	//res := c.Repositories.ListForAccount(opt)
	//var result interface{}
	//return result
}

func getPretty(res *interface{}) string {
	resJson, _ := json.MarshalIndent(res, "", "  ")
	return string(resJson)
}

func printPretty(res *interface{}) {
	fmt.Println(getPretty(res))
}

func reflectionLength(res *interface{}) int {
	resVal := *res
	fmt.Printf("reflect.TypeOf(resVal) = %v\nreflect.TypeOf(resVal).Kind() = %v\n",
		reflect.TypeOf(resVal), reflect.TypeOf(resVal).Kind())
	switch reflect.TypeOf(resVal).Kind() {
	case reflect.Slice:
		s := reflect.ValueOf(resVal)
		return s.Len()
	default:
		return -1
	}
}

// cf. https://blog.golang.org/json-and-go#TOC_5.
func reflectionParse(res *interface{}) {
	resVal := *res
	switch t0 := resVal.(type) {
	case []interface{}:
		fmt.Println("array")
	case map[string]interface{}:
		fmt.Println("map")
	default:
		fmt.Printf("Surprise, surprise. Is %v\n", t0)
	}
}

//find all values that have the given key and a string value
func filterByKey(res *interface{}, key string) []string {
  var result []string
	resVal := *res
	switch t0 := resVal.(type) {
	case []interface{}:
		for _, v := range resVal.([]interface{}) {
			result = append(result, filterByKey(&v, key)...)
		}
	case map[string]interface{}:
		for k, v := range resVal.(map[string]interface{}) {
			if k == key && reflect.TypeOf(v).Kind() == reflect.String {
				result = append(result, v.(string))
			} else {
				result = append(result, filterByKey(&v, key)...)
			}
		}
	default:
		fmt.Printf("Surprise, surprise. %v is type %T\n", t0, t0)
		return result
	}
	return result
}

func main() {
	var username string
	fmt.Print("Bitbucket Email: ")
	fmt.Scanln(&username)

	fmt.Print("Bitbucket Password: ")
	bytePassword, _ := terminal.ReadPassword(int(syscall.Stdin))
	password := string(bytePassword)
	fmt.Print("Thanks [" + username + "] !!!!\n")

	c := bitbucket.NewBasicAuth(username, password)
	res := getMyRepos(c, "edlabtc", "edlabtc", "ALL_PAGES")
	fmt.Println("reflectionLength(&res) == ", reflectionLength(&res))
	fmt.Println("len(getPretty(&res)) == ", len(getPretty(&res)))
	reflectionParse(&res)
	repos := filterByKey(&res, "full_name")
	reposM, _ := json.MarshalIndent(repos, "", " ")
	fmt.Println("repos:", string(reposM))

	//printPretty(&res)
}
<commit_msg>sort repository response<commit_after>package main

import (
	"encoding/json"
	"fmt"
	//	"os"
	"github.com/tzaffi/go-bitbucket"
	"golang.org/x/crypto/ssh/terminal"
	"reflect"
	"sort"
	"syscall"
)

func getMyRepos(client *bitbucket.Client, owner string, team string, options ...string) interface{} {
	opt := &bitbucket.RepositoriesOptions{
		Owner: owner,
		Team:  team,
	}
	/*
	if options != nil {
		fmt.Println("something:")
	} else {
		fmt.Println("nada:")
	}
  */
	fmt.Printf("options = %v\tTtype = %T\n", options, options)
	getAllPages := options != nil && options[0] == "ALL_PAGES"
	fmt.Println("getting all pages ?", getAllPages)
	var pages []uint;
	if(!getAllPages) {
		pages = []uint{1}
	} else {
		pages = []uint{1, 11}
	}

	res := client.Repositories.ListForTeam(opt, pages...)

	return res

	//res := c.Repositories.ListForAccount(opt)
	//var result interface{}
	//return result
}

func getPretty(res *interface{}) string {
	resJson, _ := json.MarshalIndent(res, "", "  ")
	return string(resJson)
}

func printPretty(res *interface{}) {
	fmt.Println(getPretty(res))
}

func reflectionLength(res *interface{}) int {
	resVal := *res
	fmt.Printf("reflect.TypeOf(resVal) = %v\nreflect.TypeOf(resVal).Kind() = %v\n",
		reflect.TypeOf(resVal), reflect.TypeOf(resVal).Kind())
	switch reflect.TypeOf(resVal).Kind() {
	case reflect.Slice:
		s := reflect.ValueOf(resVal)
		return s.Len()
	default:
		return -1
	}
}

// cf. https://blog.golang.org/json-and-go#TOC_5.
func reflectionParse(res *interface{}) {
	resVal := *res
	switch t0 := resVal.(type) {
	case []interface{}:
		fmt.Println("array")
	case map[string]interface{}:
		fmt.Println("map")
	default:
		fmt.Printf("Surprise, surprise. Is %v\n", t0)
	}
}

//find all values that have the given key and a string value
func filterByKey(res *interface{}, key string) []string {
  var result []string
	resVal := *res
	switch t0 := resVal.(type) {
	case []interface{}:
		for _, v := range resVal.([]interface{}) {
			result = append(result, filterByKey(&v, key)...)
		}
	case map[string]interface{}:
		for k, v := range resVal.(map[string]interface{}) {
			if k == key && reflect.TypeOf(v).Kind() == reflect.String {
				result = append(result, v.(string))
			} else {
				result = append(result, filterByKey(&v, key)...)
			}
		}
	default:
		fmt.Printf("Surprise, surprise. %v is type %T\n", t0, t0)
		return result
	}
	return result
}

func main() {
	var username string
	fmt.Print("Bitbucket Email: ")
	fmt.Scanln(&username)

	fmt.Print("Bitbucket Password: ")
	bytePassword, _ := terminal.ReadPassword(int(syscall.Stdin))
	password := string(bytePassword)
	fmt.Print("Thanks [" + username + "] !!!!\n")

	c := bitbucket.NewBasicAuth(username, password)
	res := getMyRepos(c, "edlabtc", "edlabtc", "ALL_PAGES")
	fmt.Println("reflectionLength(&res) == ", reflectionLength(&res))
	fmt.Println("len(getPretty(&res)) == ", len(getPretty(&res)))
	reflectionParse(&res)
	repos := filterByKey(&res, "full_name")
	sort.Strings(repos)
	reposM, _ := json.MarshalIndent(repos, "", " ")
	fmt.Println("repos:", string(reposM))

	//printPretty(&res)
}
<|endoftext|>


<commit_before>package guber

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
)

type Request struct {
	client    *Client
	method    string
	baseurl   string
	query     string
	resource  string
	namespace string
	name      string
	body      []byte

	// NOTE this is used distinct from err, because a 404 is not technically an
	// error, except to the end-user who expects a resource to be there.
	// Without this, we don't have a way to determine if an err was a 404 or
	// something lower-level without inspecting the error message.
	found bool

	err      error
	response *http.Response
}

func (r *Request) error(err error) {
	if err != nil && r.err == nil {
		fmt.Println("REQUEST ERROR", err)
		r.err = err
	}
}

func (r *Request) url() string {
	path := ""
	if r.namespace != "" {
		path = fmt.Sprintf("namespaces/%s/", r.namespace)
	}
	path = path + r.resource
	if r.name != "" {
		path = path + "/" + r.name
	}
	if r.query != "" {
		path = path + "?" + r.query
	}
	return r.baseurl + "/" + path
}

func (r *Request) Resource(res Resource) *Request {
	baseurl := fmt.Sprintf("https://%s", r.client.Host)
	if res.DomainName() != "" {
		baseurl = fmt.Sprintf("%s/%s", baseurl, res.DomainName())
	}
	r.baseurl = fmt.Sprintf("%s/%s/%s", baseurl, res.ApiGroup(), res.ApiVersion())
	r.resource = res.ApiName()
	return r
}

func (r *Request) Namespace(namespace string) *Request {
	r.namespace = namespace
	return r
}

func (r *Request) Name(name string) *Request {
	r.name = name
	return r
}

func (r *Request) Entity(e Entity) *Request {
	body, err := json.Marshal(e)

	// TODO
	fmt.Println("Req body: ", string(body))

	r.body = body
	r.error(err)
	return r
}

func (r *Request) Query(q *QueryParams) *Request {
	if q == nil {
		return r
	}

	// v, err := query.Values(q)
	// if err != nil {
	// 	panic(err) // TODO should use r.error() here probably
	// }
	// queryStr := v.Encode()

	// TODO  -- we went with this terribly rigid strategy because of how query pkg encodes the = chars
	if ls := q.LabelSelector; ls != "" {
		r.query = "labelSelector=" + ls
	}

	return r
}

func (r *Request) Do() *Request {
	req, err := http.NewRequest(r.method, r.url(), bytes.NewBuffer(r.body))
	if err != nil {
		panic(err) // TODO
	}

	req.SetBasicAuth(r.client.Username, r.client.Password)
	r.error(err)

	// TODO
	// fmt.Println(r.url())
	fmt.Println(*req)

	resp, err := r.client.http.Do(req)
	r.error(err)

	// TODO
	if resp != nil {
		r.response = resp

		r.readBody()

		if resp.StatusCode == 404 {
			r.found = false
		} else if status := resp.Status; status[:2] != "20" {
			errMsg := fmt.Sprintf("Status: %s, Body: %s", status, string(r.body))
			r.error(errors.New(errMsg))
		} else {
			r.found = true // NOTE this only really matters for lookups, but we set it true here anyhow
		}
	}
	return r
}

func (r *Request) readBody() {
	if r.response == nil {
		r.error(errors.New("Response is nil"))
		return
	}
	defer r.response.Body.Close()
	body, err := ioutil.ReadAll(r.response.Body)
	r.body = body
	r.error(err)
}

// The exit point for a Request (where error is pooped out)
func (r *Request) Into(e Entity) error {
	if r.body != nil {
		json.Unmarshal(r.body, e)
	}
	return r.err
}
<commit_msg>Remove all Println calls from request.go<commit_after>package guber

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
)

type Request struct {
	client    *Client
	method    string
	baseurl   string
	query     string
	resource  string
	namespace string
	name      string
	body      []byte

	// NOTE this is used distinct from err, because a 404 is not technically an
	// error, except to the end-user who expects a resource to be there.
	// Without this, we don't have a way to determine if an err was a 404 or
	// something lower-level without inspecting the error message.
	found bool

	err      error
	response *http.Response
}

func (r *Request) error(err error) {
	if err != nil && r.err == nil {
		r.err = err
	}
}

func (r *Request) url() string {
	path := ""
	if r.namespace != "" {
		path = fmt.Sprintf("namespaces/%s/", r.namespace)
	}
	path = path + r.resource
	if r.name != "" {
		path = path + "/" + r.name
	}
	if r.query != "" {
		path = path + "?" + r.query
	}
	return r.baseurl + "/" + path
}

func (r *Request) Resource(res Resource) *Request {
	baseurl := fmt.Sprintf("https://%s", r.client.Host)
	if res.DomainName() != "" {
		baseurl = fmt.Sprintf("%s/%s", baseurl, res.DomainName())
	}
	r.baseurl = fmt.Sprintf("%s/%s/%s", baseurl, res.ApiGroup(), res.ApiVersion())
	r.resource = res.ApiName()
	return r
}

func (r *Request) Namespace(namespace string) *Request {
	r.namespace = namespace
	return r
}

func (r *Request) Name(name string) *Request {
	r.name = name
	return r
}

func (r *Request) Entity(e Entity) *Request {
	body, err := json.Marshal(e)
	r.body = body
	r.error(err)
	return r
}

func (r *Request) Query(q *QueryParams) *Request {
	if q == nil {
		return r
	}

	// v, err := query.Values(q)
	// if err != nil {
	// 	panic(err) // TODO should use r.error() here probably
	// }
	// queryStr := v.Encode()

	// TODO  -- we went with this terribly rigid strategy because of how query pkg encodes the = chars
	if ls := q.LabelSelector; ls != "" {
		r.query = "labelSelector=" + ls
	}

	return r
}

func (r *Request) Do() *Request {
	req, err := http.NewRequest(r.method, r.url(), bytes.NewBuffer(r.body))
	if err != nil {
		panic(err) // TODO
	}

	req.SetBasicAuth(r.client.Username, r.client.Password)
	r.error(err)

	resp, err := r.client.http.Do(req)
	r.error(err)

	// TODO
	if resp != nil {
		r.response = resp

		r.readBody()

		if resp.StatusCode == 404 {
			r.found = false
		} else if status := resp.Status; status[:2] != "20" {
			errMsg := fmt.Sprintf("Status: %s, Body: %s", status, string(r.body))
			r.error(errors.New(errMsg))
		} else {
			r.found = true // NOTE this only really matters for lookups, but we set it true here anyhow
		}
	}
	return r
}

func (r *Request) readBody() {
	if r.response == nil {
		r.error(errors.New("Response is nil"))
		return
	}
	defer r.response.Body.Close()
	body, err := ioutil.ReadAll(r.response.Body)
	r.body = body
	r.error(err)
}

// The exit point for a Request (where error is pooped out)
func (r *Request) Into(e Entity) error {
	if r.body != nil {
		json.Unmarshal(r.body, e)
	}
	return r.err
}
<|endoftext|>


<commit_before>package gotumblr

import (
	"net/url"
	"net/http"
	"io/ioutil"
	"encoding/json"
	"github.com/kurrik/oauth1a"
)

//Make queries to the Tumblr API through TumblrRequest
type TumblrRequest struct {
	service *oauth1a.Service
	userConfig *oauth1a.UserConfig
	host string
}

//Initializes the TumblrRequest.
//consumerKey is the consumer key of your Tumblr Application
//consumerSecret is the consumer secret of your Tumblr Application
//callbackUrl is the callback URL of your Tumblr Application
//oauthToken is the user specific token, received from the /access_token endpoint
//oauthSecret is the user specific secret, received from the /access_token endpoint
//host is the host that you are tryng to send information to (e.g. http://api.tumblr.com)
func NewTumblrRequest(consumerKey, consumerSecret, oauthToken, oauthSecret, callbackUrl, host string) *TumblrRequest {
	service := &oauth1a.Service{
		RequestURL:   "http://www.tumblr.com/oauth/request_token",
		AuthorizeURL: "http://www.rumblr.com/oauth/authorize",
		AccessURL:    "http://www.tumblr.com/oauth/access_token",
		ClientConfig: &oauth1a.ClientConfig{
			ConsumerKey:    consumerKey,
			ConsumerSecret: consumerSecret,
			CallbackURL:    callbackUrl,
	    },
		Signer: new(oauth1a.HmacSha1Signer),
	}
	userConfig := oauth1a.NewAuthorizedConfig(oauthToken, oauthSecret)
	return &TumblrRequest{service, userConfig, host}
}

//Make a GET request to the API with properly formatted parameters
//url: the url you are making the request to
//params: the parameters needed for the request
func (tr *TumblrRequest) Get(url string, params map[string]string) map[string]interface{} {
	full_url := tr.host + url
	if len(params) != 0 {
		values := url.Values{}
		for key, value := range params {
			values.Set(key, value)
			full_url = full_url + "?" + values.Encode()
		}
	}
	httpRequest, err := http.NewRequest("GET", full_url, nil)
	if err != nil {
		fmt.Println(err)
	}
	tr.service.Sign(httpRequest, tr.userConfig)
	var httpResponse *http.Response
	httpClient := new(http.Client)
	httpResponse, err2 := httpClient.Do(httpRequest)
	if err2 != nil {
		fmt.Println(err2)
	}
	defer httpResponse.Body.Close()
	body, err3 := ioutil.ReadAll(httpResponse.Body)
	if err3 != nil {
		fmt.Println(err3)
	}
	return tr.JsonParse(body)
}

//Makes a POST request to the API, allows for multipart data uploads
//url: the url you are making the request to
//params: all the parameters needed for the request
//files: list of files
func (tr *TumblrRequest) Post(url string, params map[string]string, files []string) map[string]interface{} {

}

//Parse JSON response.
//content: the content returned from the web request to be parsed as JSON
func (tr *TumblrRequest) JsonParse(content []byte) map[string]interface{} {
	data := map[string]interface{}{}
	err := json.Unmarshal(content, &data)
	if err != nil {
		fmt.Println(err)
	}
	ok_statuses := []int{200, 201, 301}
	status := data["meta"].(map[string]interface{})["status"]
	for _, ok_status := range ok_statuses {
		if status == ok_status {
			return data["response"].(map[string]interface{})
		}
	}
	return data
}

//Generates and makes a multipart request for data files
//url: the url you are making the request to
//params: all parameters needed for the request
//files: a list of files
func (tr *TumblrRequest) PostMultipart(url string, params map[string]string, files []string) map[string]interface{} {

}

//Properly encodes the multipart body of the request
//fields: the parameters used in the request
//files: a list of lists containing information about the files
func (tr *TumblrRequest) EncodeMultipartFormdata(fields map[string]string, files []string) (string, string) {

}
<commit_msg>Add POST method<commit_after>package gotumblr

import (
	"net/url"
	"net/http"
	"io/ioutil"
	"encoding/json"
	"github.com/kurrik/oauth1a"
)

//Make queries to the Tumblr API through TumblrRequest
type TumblrRequest struct {
	service *oauth1a.Service
	userConfig *oauth1a.UserConfig
	host string
}

//Initializes the TumblrRequest.
//consumerKey is the consumer key of your Tumblr Application
//consumerSecret is the consumer secret of your Tumblr Application
//callbackUrl is the callback URL of your Tumblr Application
//oauthToken is the user specific token, received from the /access_token endpoint
//oauthSecret is the user specific secret, received from the /access_token endpoint
//host is the host that you are tryng to send information to (e.g. http://api.tumblr.com)
func NewTumblrRequest(consumerKey, consumerSecret, oauthToken, oauthSecret, callbackUrl, host string) *TumblrRequest {
	service := &oauth1a.Service{
		RequestURL:   "http://www.tumblr.com/oauth/request_token",
		AuthorizeURL: "http://www.rumblr.com/oauth/authorize",
		AccessURL:    "http://www.tumblr.com/oauth/access_token",
		ClientConfig: &oauth1a.ClientConfig{
			ConsumerKey:    consumerKey,
			ConsumerSecret: consumerSecret,
			CallbackURL:    callbackUrl,
	    },
		Signer: new(oauth1a.HmacSha1Signer),
	}
	userConfig := oauth1a.NewAuthorizedConfig(oauthToken, oauthSecret)
	return &TumblrRequest{service, userConfig, host}
}

//Make a GET request to the API with properly formatted parameters
//url: the url you are making the request to
//params: the parameters needed for the request
func (tr *TumblrRequest) Get(url string, params map[string]string) map[string]interface{} {
	full_url := tr.host + url
	if len(params) != 0 {
		values := url.Values{}
		for key, value := range params {
			values.Set(key, value)
			full_url = full_url + "?" + values.Encode()
		}
	}
	httpRequest, err := http.NewRequest("GET", full_url, nil)
	if err != nil {
		fmt.Println(err)
	}
	tr.service.Sign(httpRequest, tr.userConfig)
	var httpResponse *http.Response
	httpClient := new(http.Client)
	httpResponse, err2 := httpClient.Do(httpRequest)
	if err2 != nil {
		fmt.Println(err2)
	}
	defer httpResponse.Body.Close()
	body, err3 := ioutil.ReadAll(httpResponse.Body)
	if err3 != nil {
		fmt.Println(err3)
	}
	return tr.JsonParse(body)
}

//Makes a POST request to the API, allows for multipart data uploads
//url: the url you are making the request to
//params: all the parameters needed for the request
//files: list of files
func (tr *TumblrRequest) Post(url string, params map[string]string, files []string) map[string]interface{} {
	full_url := tr.host + url
	if len(files) != 0 {
		return tr.PostMultipart(url, params, files)
	} else {
		values := url.Values{}
		for key, value := range params {
			value.Set(key, value)
		}
		httpRequest, err := http.NewRequest("POST", full_url, strings.NewReader(values.Encode()))
		if err != nil {
			fmt.Println(err)
		}
		httpRequest.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		tr.service.Sign(httpRequest, tr.userConfig)
		var httpResponse *http.Response
		httpClient := new(http.Client)
		httpResponse, err2 := httpClient.Do(httpRequest)
		if err2 != nil {
			fmt.Println(err2)
		}
		defer httpResponse.Body.Close()
		body, err3 := ioutil.ReadAll(httpResponse.Body)
		if err3 != nil {
			fmt.Println(err3)
		}
		return tr.JsonParse(body)
	}
}

//Parse JSON response.
//content: the content returned from the web request to be parsed as JSON
func (tr *TumblrRequest) JsonParse(content []byte) map[string]interface{} {
	data := map[string]interface{}{}
	err := json.Unmarshal(content, &data)
	if err != nil {
		fmt.Println(err)
	}
	ok_statuses := []int{200, 201, 301}
	status := data["meta"].(map[string]interface{})["status"]
	for _, ok_status := range ok_statuses {
		if status == ok_status {
			return data["response"].(map[string]interface{})
		}
	}
	return data
}

//Generates and makes a multipart request for data files
//url: the url you are making the request to
//params: all parameters needed for the request
//files: a list of files
func (tr *TumblrRequest) PostMultipart(url string, params map[string]string, files []string) map[string]interface{} {

}

//Properly encodes the multipart body of the request
//fields: the parameters used in the request
//files: a list of lists containing information about the files
func (tr *TumblrRequest) EncodeMultipartFormdata(fields map[string]string, files []string) (string, string) {

}
<|endoftext|>