<commit_before>// Package pastebin is a simple modern and powerful pastebin service
package pastebin

import (
	"crypto/sha1"
	"database/sql"
	"encoding/base64"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"html"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"time"

	duration "github.com/channelmeter/iso8601duration"
	// uniuri is used for easy random string generation
	"github.com/dchest/uniuri"
	// pygments is used for syntax highlighting
	"github.com/ewhal/pygments"
	// mysql driver
	_ "github.com/go-sql-driver/mysql"
	// mux is used for url routing
	"github.com/gorilla/mux"
)

const (
	// ADDRESS that pastebin will return links for
	ADDRESS = "http://localhost:9900"
	// LENGTH of paste id
	LENGTH = 6
	// PORT that pastebin will listen on
	PORT = ":9900"
	// USERNAME for database
	USERNAME = ""
	// PASS database password
	PASS = ""
	// NAME database name
	NAME = ""
	// DATABASE connection String
	DATABASE = USERNAME + ":" + PASS + "@/" + NAME + "?charset=utf8"
)

// Template pages
var templates = template.Must(template.ParseFiles("assets/paste.html", "assets/index.html", "assets/clone.html"))
var syntax, _ = ioutil.ReadFile("assets/syntax.html")

// Response API struct
type Response struct {
	ID     string `json:"id"`
	TITLE  string `json:"title"`
	HASH   string `json:"hash"`
	URL    string `json:"url"`
	SIZE   int    `json:"size"`
	DELKEY string `json:"delkey"`
}

// Page generation struct
type Page struct {
	Title    string
	Body     []byte
	Raw      string
	Home     string
	Download string
	Clone    string
}

// check error handling function
func Check(err error) {
	if err != nil {
		log.Println(err)
	}
}

// GenerateName uses uniuri to generate a random string that isn't in the
// database
func GenerateName() string {
	// use uniuri to generate random string
	id := uniuri.NewLen(LENGTH)

	db, err := sql.Open("mysql", DATABASE)
	Check(err)
	defer db.Close()
	// query database if id exists and if it does call generateName again
	query, err := db.Query("select id from pastebin where id=?", id)
	if err != sql.ErrNoRows {
		for query.Next() {
			GenerateName()
		}
	}

	return id

}

// Sha1 hashes paste into a sha1 hash
func Sha1(paste string) string {
	hasher := sha1.New()

	hasher.Write([]byte(paste))
	sha := base64.URLEncoding.EncodeToString(hasher.Sum(nil))
	return sha
}

// Save function handles the saving of each paste.
// raw string is the raw paste input
// lang string is the user specified language for syntax highlighting
// title string user customized title
// expiry string duration that the paste will exist for
// Returns Response struct
func Save(raw string, lang string, title string, expiry string) Response {

	db, err := sql.Open("mysql", DATABASE)
	Check(err)
	defer db.Close()

	// hash paste data and query database to see if paste exists
	sha := Sha1(raw)
	query, err := db.Query("select id, title, hash, data, delkey from pastebin where hash=?", sha)

	if err != sql.ErrNoRows {
		for query.Next() {
			var id, title, hash, paste, delkey string
			err := query.Scan(&id, &title, &hash, &paste, &delkey)
			Check(err)
			url := ADDRESS + "/p/" + id
			return Response{id, title, hash, url, len(paste), delkey}
		}
	}
	id := GenerateName()
	url := ADDRESS + "/p/" + id
	if lang != "" {
		url += "/" + lang
	}

	const timeFormat = "2006-01-02 15:04:05"

	dura, err := duration.FromString(expiry) // dura is time.Duration type
	Check(err)

	duration := dura.ToDuration()
	expiryTime := time.Now().Add(duration).Format(timeFormat)

	delKey := uniuri.NewLen(40)
	dataEscaped := html.EscapeString(raw)

	stmt, err := db.Prepare("INSERT INTO pastebin(id, title, hash, data, delkey, expiry) values(?,?,?,?,?,?)")
	Check(err)
	if title == "" {
		title = id
	}
	_, err = stmt.Exec(id, html.EscapeString(title), sha, dataEscaped, delKey, expiryTime)
	Check(err)

	return Response{id, title, sha, url, len(dataEscaped), delKey}
}

// DelHandler checks to see if delkey and pasteid exist in the database.
// if both exist and are correct the paste will be removed.
func DelHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["pasteId"]
	delkey := vars["delKey"]

	db, err := sql.Open("mysql", DATABASE)
	Check(err)
	defer db.Close()

	stmt, err := db.Prepare("delete from pastebin where delkey=? and id=?")
	Check(err)

	res, err := stmt.Exec(html.EscapeString(delkey), html.EscapeString(id))
	Check(err)

	_, err = res.RowsAffected()
	if err != sql.ErrNoRows {
		io.WriteString(w, id+" deleted")
	}
}

// SaveHandler Handles saving pastes and outputing responses
func SaveHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	output := vars["output"]
	switch r.Method {
	case "POST":
		paste := r.FormValue("p")
		lang := r.FormValue("lang")
		title := r.FormValue("title")
		expiry := r.FormValue("expiry")
		if paste == "" {
			http.Error(w, "Empty paste", 500)
			return
		}
		b := Save(paste, lang, title, expiry)

		switch output {
		case "json":
			w.Header().Set("Content-Type", "application/json")
			err := json.NewEncoder(w).Encode(b)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

		case "xml":
			x, err := xml.MarshalIndent(b, "", "  ")
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			w.Header().Set("Content-Type", "application/xml")
			w.Write(x)

		case "html":
			w.Header().Set("Content-Type", "text/html")
			io.WriteString(w, "
URL: "+b.URL+"
")
			io.WriteString(w, "
Delete Key: "+b.DELKEY+"
")

		case "redirect":
			http.Redirect(w, r, b.URL, 301)

		default:
			w.Header().Set("Content-Type", "text/plain; charset=UTF-8; imeanit=yes")
			io.WriteString(w, b.URL+"\n")
			io.WriteString(w, "delete key: "+b.DELKEY+"\n")
		}
	}

}

// Highlight uses user specified input to call pygments library to highlight the
// paste
func Highlight(s string, lang string) (string, error) {

	highlight, err := pygments.Highlight(html.UnescapeString(s), html.EscapeString(lang), "html", "style=autumn,linenos=True, lineanchors=True,anchorlinenos=True,noclasses=True,", "utf-8")
	if err != nil {
		return "", err
	}
	return highlight, nil

}

// GetPaste takes pasteid and language
// queries the database and returns paste data
func GetPaste(paste string, lang string) (string, string) {
	param1 := html.EscapeString(paste)
	db, err := sql.Open("mysql", DATABASE)
	Check(err)
	defer db.Close()
	var title, s string
	var expiry string
	err = db.QueryRow("select title, data, expiry from pastebin where id=?", param1).Scan(&title, &s, &expiry)
	Check(err)
	if time.Now().Format("2006-01-02 15:04:05") > expiry {
		stmt, err := db.Prepare("delete from pastebin where id=?")
		Check(err)
		_, err = stmt.Exec(param1)
		Check(err)
		return "Error invalid paste", ""
	}

	if err == sql.ErrNoRows {
		return "Error invalid paste", ""
	}
	if lang != "" {
		high, err := Highlight(s, lang)
		Check(err)
		return high, html.UnescapeString(title)
	}
	return html.UnescapeString(s), html.UnescapeString(title)
}

// PasteHandler handles the generation of paste pages with the links
func PasteHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	lang := vars["lang"]

	s, title := GetPaste(paste, lang)

	// button links
	link := ADDRESS + "/raw/" + paste
	download := ADDRESS + "/download/" + paste
	clone := ADDRESS + "/clone/" + paste
	// Page struct
	p := &Page{
		Title:    title,
		Body:     []byte(s),
		Raw:      link,
		Home:     ADDRESS,
		Download: download,
		Clone:    clone,
	}
	if lang == "" {

		err := templates.ExecuteTemplate(w, "paste.html", p)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}

	} else {
		fmt.Fprintf(w, string(syntax), p.Title, p.Title, s, p.Home, p.Download, p.Raw, p.Clone)

	}
}

// CloneHandler handles generating the clone pages
func CloneHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]

	s, title := GetPaste(paste, "")

	// Page links
	link := ADDRESS + "/raw/" + paste
	download := ADDRESS + "/download/" + paste
	clone := ADDRESS + "/clone/" + paste

	// Clone page struct
	p := &Page{
		Title:    title,
		Body:     []byte(s),
		Raw:      link,
		Home:     ADDRESS,
		Download: download,
		Clone:    clone,
	}
	err := templates.ExecuteTemplate(w, "clone.html", p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

}

// DownloadHandler forces downloads of selected pastes
func DownloadHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	s, _ := GetPaste(paste, "")

	// Set header to an attachment so browser will automatically download it
	w.Header().Set("Content-Disposition", "attachment; filename="+paste)
	w.Header().Set("Content-Type", r.Header.Get("Content-Type"))
	io.WriteString(w, s)

}

// RawHandler displays the pastes in text/plain format
func RawHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	s, _ := GetPaste(paste, "")

	w.Header().Set("Content-Type", "text/plain; charset=UTF-8; imeanit=yes")
	// simply write string to browser
	io.WriteString(w, s)

}

// RootHandler handles generating the root page
func RootHandler(w http.ResponseWriter, r *http.Request) {
	err := templates.ExecuteTemplate(w, "index.html", &Page{})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func main() {
	router := mux.NewRouter()
	router.HandleFunc("/p/{pasteId}", PasteHandler).Methods("GET")
	router.HandleFunc("/raw/{pasteId}", RawHandler).Methods("GET")
	router.HandleFunc("/p/{pasteId}/{lang}", PasteHandler).Methods("GET")
	router.HandleFunc("/clone/{pasteId}", CloneHandler).Methods("GET")
	router.HandleFunc("/download/{pasteId}", DownloadHandler).Methods("GET")
	router.HandleFunc("/p", SaveHandler).Methods("POST")
	router.HandleFunc("/p/{output}", SaveHandler).Methods("POST")
	router.HandleFunc("/p/{pasteId}/{delKey}", DelHandler).Methods("DELETE")
	router.HandleFunc("/", RootHandler)
	err := http.ListenAndServe(PORT, router)
	if err != nil {
		log.Fatal(err)
	}

}
<commit_msg>Reintroduce duration function<commit_after>// Package pastebin is a simple modern and powerful pastebin service
package pastebin

import (
	"crypto/sha1"
	"database/sql"
	"encoding/base64"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"html"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"time"

	duration "github.com/channelmeter/iso8601duration"
	// uniuri is used for easy random string generation
	"github.com/dchest/uniuri"
	// pygments is used for syntax highlighting
	"github.com/ewhal/pygments"
	// mysql driver
	_ "github.com/go-sql-driver/mysql"
	// mux is used for url routing
	"github.com/gorilla/mux"
)

const (
	// ADDRESS that pastebin will return links for
	ADDRESS = "http://localhost:9900"
	// LENGTH of paste id
	LENGTH = 6
	// PORT that pastebin will listen on
	PORT = ":9900"
	// USERNAME for database
	USERNAME = ""
	// PASS database password
	PASS = ""
	// NAME database name
	NAME = ""
	// DATABASE connection String
	DATABASE = USERNAME + ":" + PASS + "@/" + NAME + "?charset=utf8"
)

// Template pages
var templates = template.Must(template.ParseFiles("assets/paste.html", "assets/index.html", "assets/clone.html"))
var syntax, _ = ioutil.ReadFile("assets/syntax.html")

// Response API struct
type Response struct {
	ID     string `json:"id"`
	TITLE  string `json:"title"`
	HASH   string `json:"hash"`
	URL    string `json:"url"`
	SIZE   int    `json:"size"`
	DELKEY string `json:"delkey"`
}

// Page generation struct
type Page struct {
	Title    string
	Body     []byte
	Raw      string
	Home     string
	Download string
	Clone    string
}

// check error handling function
func Check(err error) {
	if err != nil {
		log.Println(err)
	}
}

// GenerateName uses uniuri to generate a random string that isn't in the
// database
func GenerateName() string {
	// use uniuri to generate random string
	id := uniuri.NewLen(LENGTH)

	db, err := sql.Open("mysql", DATABASE)
	Check(err)
	defer db.Close()
	// query database if id exists and if it does call generateName again
	query, err := db.Query("select id from pastebin where id=?", id)
	if err != sql.ErrNoRows {
		for query.Next() {
			GenerateName()
		}
	}

	return id

}

// Sha1 hashes paste into a sha1 hash
func Sha1(paste string) string {
	hasher := sha1.New()

	hasher.Write([]byte(paste))
	sha := base64.URLEncoding.EncodeToString(hasher.Sum(nil))
	return sha
}

// DurationFromExpiry takes the expiry in string format and returns the duration
// that the paste will exist for
func DurationFromExpiry(expiry string) time.Duration {
	dura, err := duration.FromString(expiry) // dura is time.Duration type
	Check(err)

	duration := dura.ToDuration()

	return duration
}

// Save function handles the saving of each paste.
// raw string is the raw paste input
// lang string is the user specified language for syntax highlighting
// title string user customized title
// expiry string duration that the paste will exist for
// Returns Response struct
func Save(raw string, lang string, title string, expiry string) Response {

	db, err := sql.Open("mysql", DATABASE)
	Check(err)
	defer db.Close()

	// hash paste data and query database to see if paste exists
	sha := Sha1(raw)
	query, err := db.Query("select id, title, hash, data, delkey from pastebin where hash=?", sha)

	if err != sql.ErrNoRows {
		for query.Next() {
			var id, title, hash, paste, delkey string
			err := query.Scan(&id, &title, &hash, &paste, &delkey)
			Check(err)
			url := ADDRESS + "/p/" + id
			return Response{id, title, hash, url, len(paste), delkey}
		}
	}
	id := GenerateName()
	url := ADDRESS + "/p/" + id
	if lang != "" {
		url += "/" + lang
	}

	const timeFormat = "2006-01-02 15:04:05"
	expiryTime := time.Now().Add(DurationFromExpiry(expiry)).Format(timeFormat)

	delKey := uniuri.NewLen(40)
	dataEscaped := html.EscapeString(raw)

	stmt, err := db.Prepare("INSERT INTO pastebin(id, title, hash, data, delkey, expiry) values(?,?,?,?,?,?)")
	Check(err)
	if title == "" {
		title = id
	}
	_, err = stmt.Exec(id, html.EscapeString(title), sha, dataEscaped, delKey, expiryTime)
	Check(err)

	return Response{id, title, sha, url, len(dataEscaped), delKey}
}

// DelHandler checks to see if delkey and pasteid exist in the database.
// if both exist and are correct the paste will be removed.
func DelHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["pasteId"]
	delkey := vars["delKey"]

	db, err := sql.Open("mysql", DATABASE)
	Check(err)
	defer db.Close()

	stmt, err := db.Prepare("delete from pastebin where delkey=? and id=?")
	Check(err)

	res, err := stmt.Exec(html.EscapeString(delkey), html.EscapeString(id))
	Check(err)

	_, err = res.RowsAffected()
	if err != sql.ErrNoRows {
		io.WriteString(w, id+" deleted")
	}
}

// SaveHandler Handles saving pastes and outputing responses
func SaveHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	output := vars["output"]
	switch r.Method {
	case "POST":
		paste := r.FormValue("p")
		lang := r.FormValue("lang")
		title := r.FormValue("title")
		expiry := r.FormValue("expiry")
		if paste == "" {
			http.Error(w, "Empty paste", 500)
			return
		}
		b := Save(paste, lang, title, expiry)

		switch output {
		case "json":
			w.Header().Set("Content-Type", "application/json")
			err := json.NewEncoder(w).Encode(b)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

		case "xml":
			x, err := xml.MarshalIndent(b, "", "  ")
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			w.Header().Set("Content-Type", "application/xml")
			w.Write(x)

		case "html":
			w.Header().Set("Content-Type", "text/html")
			io.WriteString(w, "
URL: "+b.URL+"
")
			io.WriteString(w, "
Delete Key: "+b.DELKEY+"
")

		case "redirect":
			http.Redirect(w, r, b.URL, 301)

		default:
			w.Header().Set("Content-Type", "text/plain; charset=UTF-8; imeanit=yes")
			io.WriteString(w, b.URL+"\n")
			io.WriteString(w, "delete key: "+b.DELKEY+"\n")
		}
	}

}

// Highlight uses user specified input to call pygments library to highlight the
// paste
func Highlight(s string, lang string) (string, error) {

	highlight, err := pygments.Highlight(html.UnescapeString(s), html.EscapeString(lang), "html", "style=autumn,linenos=True, lineanchors=True,anchorlinenos=True,noclasses=True,", "utf-8")
	if err != nil {
		return "", err
	}
	return highlight, nil

}

// GetPaste takes pasteid and language
// queries the database and returns paste data
func GetPaste(paste string, lang string) (string, string) {
	param1 := html.EscapeString(paste)
	db, err := sql.Open("mysql", DATABASE)
	Check(err)
	defer db.Close()
	var title, s string
	var expiry string
	err = db.QueryRow("select title, data, expiry from pastebin where id=?", param1).Scan(&title, &s, &expiry)
	Check(err)
	if time.Now().Format("2006-01-02 15:04:05") > expiry {
		stmt, err := db.Prepare("delete from pastebin where id=?")
		Check(err)
		_, err = stmt.Exec(param1)
		Check(err)
		return "Error invalid paste", ""
	}

	if err == sql.ErrNoRows {
		return "Error invalid paste", ""
	}
	if lang != "" {
		high, err := Highlight(s, lang)
		Check(err)
		return high, html.UnescapeString(title)
	}
	return html.UnescapeString(s), html.UnescapeString(title)
}

// PasteHandler handles the generation of paste pages with the links
func PasteHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	lang := vars["lang"]

	s, title := GetPaste(paste, lang)

	// button links
	link := ADDRESS + "/raw/" + paste
	download := ADDRESS + "/download/" + paste
	clone := ADDRESS + "/clone/" + paste
	// Page struct
	p := &Page{
		Title:    title,
		Body:     []byte(s),
		Raw:      link,
		Home:     ADDRESS,
		Download: download,
		Clone:    clone,
	}
	if lang == "" {

		err := templates.ExecuteTemplate(w, "paste.html", p)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}

	} else {
		fmt.Fprintf(w, string(syntax), p.Title, p.Title, s, p.Home, p.Download, p.Raw, p.Clone)

	}
}

// CloneHandler handles generating the clone pages
func CloneHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]

	s, title := GetPaste(paste, "")

	// Page links
	link := ADDRESS + "/raw/" + paste
	download := ADDRESS + "/download/" + paste
	clone := ADDRESS + "/clone/" + paste

	// Clone page struct
	p := &Page{
		Title:    title,
		Body:     []byte(s),
		Raw:      link,
		Home:     ADDRESS,
		Download: download,
		Clone:    clone,
	}
	err := templates.ExecuteTemplate(w, "clone.html", p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

}

// DownloadHandler forces downloads of selected pastes
func DownloadHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	s, _ := GetPaste(paste, "")

	// Set header to an attachment so browser will automatically download it
	w.Header().Set("Content-Disposition", "attachment; filename="+paste)
	w.Header().Set("Content-Type", r.Header.Get("Content-Type"))
	io.WriteString(w, s)

}

// RawHandler displays the pastes in text/plain format
func RawHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	s, _ := GetPaste(paste, "")

	w.Header().Set("Content-Type", "text/plain; charset=UTF-8; imeanit=yes")
	// simply write string to browser
	io.WriteString(w, s)

}

// RootHandler handles generating the root page
func RootHandler(w http.ResponseWriter, r *http.Request) {
	err := templates.ExecuteTemplate(w, "index.html", &Page{})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func main() {
	router := mux.NewRouter()
	router.HandleFunc("/p/{pasteId}", PasteHandler).Methods("GET")
	router.HandleFunc("/raw/{pasteId}", RawHandler).Methods("GET")
	router.HandleFunc("/p/{pasteId}/{lang}", PasteHandler).Methods("GET")
	router.HandleFunc("/clone/{pasteId}", CloneHandler).Methods("GET")
	router.HandleFunc("/download/{pasteId}", DownloadHandler).Methods("GET")
	router.HandleFunc("/p", SaveHandler).Methods("POST")
	router.HandleFunc("/p/{output}", SaveHandler).Methods("POST")
	router.HandleFunc("/p/{pasteId}/{delKey}", DelHandler).Methods("DELETE")
	router.HandleFunc("/", RootHandler)
	err := http.ListenAndServe(PORT, router)
	if err != nil {
		log.Fatal(err)
	}

}
<|endoftext|>


<commit_before>package apptesting

import (
	"fmt"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"runtime"

	"github.com/gin-gonic/gin"
)

// Server - структура тестового сервера.
// Это сервер gin, работающий в сторонней горутине и принимающий
// подключения только по одному пути и работающий только с одной HandlerFunc.
// Используется для тестирования серверных компонентов путем имитации входящих
// подключений.
type Server struct {
	// URL, по которому принимает подключения сервер.
	URL *url.URL
}

var lastPort = 8080

// NewServer запускает тестовый сервер на случайном порте, который направляет
// входящие подключения на функцию fn.
func NewServer(fn gin.HandlerFunc) *Server {
	// Чтобы тестирования не началось случайно до того, как запустится сервер Gin.
	runtime.GOMAXPROCS(1)

	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.GET("/test/url", fn)
	r.POST("/test/url", fn)

	addr := fmt.Sprintf(":%v", lastPort)
	urlString := fmt.Sprintf("http://localhost:%v/test/url", lastPort)
	lastPort++

	// Сервер запускается в отдельной горутине.
	go r.Run(addr)
	// Передаем очередь выполнения горутине сервера.
	runtime.Gosched()
	// Контроль текущей горутине вернется, как только сервер заблокируется
	// в ожидании подключений - то есть когда он будет полностью инициализирован.
	// Довольно костыльное решение, но gin не предоставляет
	// нормального функционала для тестирования.

	serverURL, err := url.Parse(urlString)
	if err != nil {
		panic(err)
	}
	return &Server{URL: serverURL}
}

// Client - тестовый клиент, основанный на http.Client, но автоматически
// сохраняющий все куки, полученные с сервера.
type Client struct {
	*http.Client
}

// NewClient инициализует клиент с пустым CookieJar
func NewClient() *Client {
	jar, _ := cookiejar.New(nil)
	return &Client{&http.Client{Jar: jar}}
}

// Get ведет себя как http.Client.Get, но в случае успешного запроса
// сохраняет все полученные куки.
func (c *Client) Get(rawURL string) (resp *http.Response, err error) {
	resp, err = c.Client.Get(rawURL)
	if err != nil {
		return
	}
	parsed, err := url.Parse(rawURL)
	if err != nil {
		return
	}
	c.Client.Jar.SetCookies(parsed, resp.Cookies())
	return
}

// PostForm ведет себя как http.Client.PostForm, но в случае успешного запроса
// сохраняет все полученные куки.
func (c *Client) PostForm(rawURL string, data url.Values) (resp *http.Response, err error) {
	resp, err = c.Client.PostForm(rawURL, data)
	if err != nil {
		return
	}
	parsed, err := url.Parse(rawURL)
	if err != nil {
		return
	}
	c.Client.Jar.SetCookies(parsed, resp.Cookies())
	return
}

// ClearCookie стирает все куки, хранящиеся в клиенте.
func (c *Client) ClearCookie() {
	c.Client.Jar, _ = cookiejar.New(nil)
}
<commit_msg>Fix template loading on test server<commit_after>package apptesting

import (
	"fmt"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"runtime"

	"github.com/gin-gonic/gin"
)

// Server - структура тестового сервера.
// Это сервер gin, работающий в сторонней горутине и принимающий
// подключения только по одному пути и работающий только с одной HandlerFunc.
// Используется для тестирования серверных компонентов путем имитации входящих
// подключений.
type Server struct {
	// URL, по которому принимает подключения сервер.
	URL *url.URL
}

var lastPort = 8080

// NewServer запускает тестовый сервер на случайном порте, который направляет
// входящие подключения на функцию fn.
func NewServer(fn gin.HandlerFunc) *Server {
	// Чтобы тестирования не началось случайно до того, как запустится сервер Gin.
	runtime.GOMAXPROCS(1)

	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.LoadHTMLGlob("../../templates/*")
	r.GET("/test/url", fn)
	r.POST("/test/url", fn)

	addr := fmt.Sprintf(":%v", lastPort)
	urlString := fmt.Sprintf("http://localhost:%v/test/url", lastPort)
	lastPort++

	// Сервер запускается в отдельной горутине.
	go r.Run(addr)
	// Передаем очередь выполнения горутине сервера.
	runtime.Gosched()
	// Контроль текущей горутине вернется, как только сервер заблокируется
	// в ожидании подключений - то есть когда он будет полностью инициализирован.
	// Довольно костыльное решение, но gin не предоставляет
	// нормального функционала для тестирования.

	serverURL, err := url.Parse(urlString)
	if err != nil {
		panic(err)
	}
	return &Server{URL: serverURL}
}

// Client - тестовый клиент, основанный на http.Client, но автоматически
// сохраняющий все куки, полученные с сервера.
type Client struct {
	*http.Client
}

// NewClient инициализует клиент с пустым CookieJar
func NewClient() *Client {
	jar, _ := cookiejar.New(nil)
	return &Client{&http.Client{Jar: jar}}
}

// Get ведет себя как http.Client.Get, но в случае успешного запроса
// сохраняет все полученные куки.
func (c *Client) Get(rawURL string) (resp *http.Response, err error) {
	resp, err = c.Client.Get(rawURL)
	if err != nil {
		return
	}
	parsed, err := url.Parse(rawURL)
	if err != nil {
		return
	}
	c.Client.Jar.SetCookies(parsed, resp.Cookies())
	return
}

// PostForm ведет себя как http.Client.PostForm, но в случае успешного запроса
// сохраняет все полученные куки.
func (c *Client) PostForm(rawURL string, data url.Values) (resp *http.Response, err error) {
	resp, err = c.Client.PostForm(rawURL, data)
	if err != nil {
		return
	}
	parsed, err := url.Parse(rawURL)
	if err != nil {
		return
	}
	c.Client.Jar.SetCookies(parsed, resp.Cookies())
	return
}

// ClearCookie стирает все куки, хранящиеся в клиенте.
func (c *Client) ClearCookie() {
	c.Client.Jar, _ = cookiejar.New(nil)
}
<|endoftext|>


<commit_before>package htm

import (
	//"math"
	"bytes"
)

//Entries are positions of non-zero values
type SparseEntry struct {
	Row int
	Col int
}

//Sparse binary matrix stores indexes of non-zero entries in matrix
//to conserve space
type SparseBinaryMatrix struct {
	Width             int
	Height            int
	TotalNonZeroCount int
	Entries           []SparseEntry
}

//Create new sparse binary matrix of specified size
func NewSparseBinaryMatrix(height, width int) *SparseBinaryMatrix {
	m := &SparseBinaryMatrix{}
	m.Height = height
	m.Width = width
	//Intialize with 70% sparsity
	//m.Entries = make([]SparseEntry, int(math.Ceil(width*height*0.3)))
	return m
}

//Create sparse binary matrix from specified dense matrix
func NewSparseBinaryMatrixFromDense(values [][]bool) *SparseBinaryMatrix {
	if len(values) < 1 {
		panic("No values specified.")
	}
	m := &SparseBinaryMatrix{}
	m.Height = len(values)
	m.Width = len(values[0])

	for r := 0; r < m.Height; r++ {
		m.SetRowFromDense(r, values[r])
	}

	return m
}

// Creates a sparse binary matrix from specified integer array
// (any values greater than 0 are true)
func NewSparseBinaryMatrixFromInts(values [][]int) *SparseBinaryMatrix {
	if len(values) < 1 {
		panic("No values specified.")
	}

	m := &SparseBinaryMatrix{}
	m.Height = len(values)
	m.Width = len(values[0])

	for r := 0; r < m.Height; r++ {
		for c := 0; c < m.Width; c++ {
			if values[r][c] > 0 {
				m.Set(r, c, true)
			}
		}
	}

	return m
}

// func NewRandSparseBinaryMatrix() *SparseBinaryMatrix {
// }

// func (sm *SparseBinaryMatrix) Resize(width int, height int) {
// }

//Returns flattend dense represenation
func (sm *SparseBinaryMatrix) Flatten() []bool {
	result := make([]bool, sm.Height*sm.Width)
	for _, val := range sm.Entries {
		result[(val.Row*sm.Width)+val.Col] = true
	}
	return result
}

//Get value at col,row position
func (sm *SparseBinaryMatrix) Get(row int, col int) bool {
	for _, val := range sm.Entries {
		if val.Row == row && val.Col == col {
			return true
		}
	}
	return false
}

func (sm *SparseBinaryMatrix) delete(row int, col int) {
	for idx, val := range sm.Entries {
		if val.Row == row && val.Col == col {
			sm.Entries = append(sm.Entries[:idx], sm.Entries[idx+1:]...)
			break
		}
	}

}

//Set value at row,col position
func (sm *SparseBinaryMatrix) Set(row int, col int, value bool) {
	if !value {
		sm.delete(row, col)
		return
	}

	if sm.Get(row, col) {
		return
	}

	newEntry := SparseEntry{}
	newEntry.Col = col
	newEntry.Row = row
	sm.Entries = append(sm.Entries, newEntry)

}

//Replaces specified row with values, assumes values is ordered
//correctly
func (sm *SparseBinaryMatrix) ReplaceRow(row int, values []bool) {
	sm.validateRowCol(row, len(values))

	for i := 0; i < sm.Width; i++ {
		sm.Set(row, i, values[i])
	}
}

//Replaces row with true values at specified indices
func (sm *SparseBinaryMatrix) ReplaceRowByIndices(row int, indices []int) {
	sm.validateRow(row)

	for i := 0; i < sm.Width; i++ {
		val := false
		for x := 0; x < len(indices); x++ {
			if i == indices[x] {
				val = true
				break
			}
		}
		sm.Set(row, i, val)
	}
}

//Returns dense row
func (sm *SparseBinaryMatrix) GetDenseRow(row int) []bool {
	sm.validateRow(row)
	result := make([]bool, sm.Width)

	for i := 0; i < len(sm.Entries); i++ {
		if sm.Entries[i].Row == row {
			result[sm.Entries[i].Col] = true
		}
	}

	return result
}

//Returns a rows "on" indices
func (sm *SparseBinaryMatrix) GetRowIndices(row int) []int {
	result := []int{}
	for i := 0; i < len(sm.Entries); i++ {
		if sm.Entries[i].Row == row {
			result = append(result, sm.Entries[i].Col)
		}
	}
	return result
}

//Sets a sparse row from dense representation
func (sm *SparseBinaryMatrix) SetRowFromDense(row int, denseRow []bool) {
	sm.validateRowCol(row, len(denseRow))
	for i := 0; i < sm.Width; i++ {
		sm.Set(row, i, denseRow[i])
	}
}

//In a normal matrix this would be multiplication in binary terms
//we just and then sum the true entries
func (sm *SparseBinaryMatrix) RowAndSum(row []bool) []int {
	sm.validateCol(len(row))
	result := make([]int, sm.Height)

	for _, val := range sm.Entries {
		if row[val.Col] {
			result[val.Row]++
		}
	}

	return result
}

//Returns # of rows with at least 1 true value
func (sm *SparseBinaryMatrix) TotatTrueRows() int {
	var hitRows []int
	for _, val := range sm.Entries {
		if !ContainsInt(val.Row, hitRows) {
			hitRows = append(hitRows, val.Row)
		}
	}
	return len(hitRows)
}

//Returns # of cols with at least 1 true value
func (sm *SparseBinaryMatrix) TotalTrueCols() int {
	var hitCols []int
	for _, val := range sm.Entries {
		if !ContainsInt(val.Col, hitCols) {
			hitCols = append(hitCols, val.Col)
		}
	}
	return len(hitCols)
}

//Clears  all entries
func (sm *SparseBinaryMatrix) Clear() {
	sm.Entries = nil
}

//Copys a matrix
func (sm *SparseBinaryMatrix) Copy() *SparseBinaryMatrix {
	result := new(SparseBinaryMatrix)
	result.Width = sm.Width
	result.Height = sm.Height
	result.Entries = make([]SparseEntry, len(sm.Entries))
	for idx, val := range sm.Entries {
		result.Entries[idx] = val
	}

	return result
}

func (sm *SparseBinaryMatrix) ToString() string {
	var buffer bytes.Buffer

	for r := 0; r < sm.Height; r++ {
		for c := 0; c < sm.Width; c++ {
			if sm.Get(r, c) {
				buffer.WriteByte('1')
			} else {
				buffer.WriteByte('0')
			}
		}
		buffer.WriteByte('\n')
	}

	return buffer.String()
}

func (sm *SparseBinaryMatrix) validateCol(col int) {
	if col > sm.Width {
		panic("Specified row is wider than matrix.")
	}
}

func (sm *SparseBinaryMatrix) validateRow(row int) {
	if row > sm.Height {
		panic("Specified row is out of bounds.")
	}
}

func (sm *SparseBinaryMatrix) validateRowCol(row int, col int) {
	sm.validateCol(col)
	sm.validateRow(row)
}
<commit_msg>implement sbm logical or function<commit_after>package htm

import (
	//"math"
	"bytes"
)

//Entries are positions of non-zero values
type SparseEntry struct {
	Row int
	Col int
}

//Sparse binary matrix stores indexes of non-zero entries in matrix
//to conserve space
type SparseBinaryMatrix struct {
	Width             int
	Height            int
	TotalNonZeroCount int
	Entries           []SparseEntry
}

//Create new sparse binary matrix of specified size
func NewSparseBinaryMatrix(height, width int) *SparseBinaryMatrix {
	m := &SparseBinaryMatrix{}
	m.Height = height
	m.Width = width
	//Intialize with 70% sparsity
	//m.Entries = make([]SparseEntry, int(math.Ceil(width*height*0.3)))
	return m
}

//Create sparse binary matrix from specified dense matrix
func NewSparseBinaryMatrixFromDense(values [][]bool) *SparseBinaryMatrix {
	if len(values) < 1 {
		panic("No values specified.")
	}
	m := &SparseBinaryMatrix{}
	m.Height = len(values)
	m.Width = len(values[0])

	for r := 0; r < m.Height; r++ {
		m.SetRowFromDense(r, values[r])
	}

	return m
}

// Creates a sparse binary matrix from specified integer array
// (any values greater than 0 are true)
func NewSparseBinaryMatrixFromInts(values [][]int) *SparseBinaryMatrix {
	if len(values) < 1 {
		panic("No values specified.")
	}

	m := &SparseBinaryMatrix{}
	m.Height = len(values)
	m.Width = len(values[0])

	for r := 0; r < m.Height; r++ {
		for c := 0; c < m.Width; c++ {
			if values[r][c] > 0 {
				m.Set(r, c, true)
			}
		}
	}

	return m
}

// func NewRandSparseBinaryMatrix() *SparseBinaryMatrix {
// }

// func (sm *SparseBinaryMatrix) Resize(width int, height int) {
// }

//Returns flattend dense represenation
func (sm *SparseBinaryMatrix) Flatten() []bool {
	result := make([]bool, sm.Height*sm.Width)
	for _, val := range sm.Entries {
		result[(val.Row*sm.Width)+val.Col] = true
	}
	return result
}

//Get value at col,row position
func (sm *SparseBinaryMatrix) Get(row int, col int) bool {
	for _, val := range sm.Entries {
		if val.Row == row && val.Col == col {
			return true
		}
	}
	return false
}

func (sm *SparseBinaryMatrix) delete(row int, col int) {
	for idx, val := range sm.Entries {
		if val.Row == row && val.Col == col {
			sm.Entries = append(sm.Entries[:idx], sm.Entries[idx+1:]...)
			break
		}
	}

}

//Set value at row,col position
func (sm *SparseBinaryMatrix) Set(row int, col int, value bool) {
	if !value {
		sm.delete(row, col)
		return
	}

	if sm.Get(row, col) {
		return
	}

	newEntry := SparseEntry{}
	newEntry.Col = col
	newEntry.Row = row
	sm.Entries = append(sm.Entries, newEntry)

}

//Replaces specified row with values, assumes values is ordered
//correctly
func (sm *SparseBinaryMatrix) ReplaceRow(row int, values []bool) {
	sm.validateRowCol(row, len(values))

	for i := 0; i < sm.Width; i++ {
		sm.Set(row, i, values[i])
	}
}

//Replaces row with true values at specified indices
func (sm *SparseBinaryMatrix) ReplaceRowByIndices(row int, indices []int) {
	sm.validateRow(row)

	for i := 0; i < sm.Width; i++ {
		val := false
		for x := 0; x < len(indices); x++ {
			if i == indices[x] {
				val = true
				break
			}
		}
		sm.Set(row, i, val)
	}
}

//Returns dense row
func (sm *SparseBinaryMatrix) GetDenseRow(row int) []bool {
	sm.validateRow(row)
	result := make([]bool, sm.Width)

	for i := 0; i < len(sm.Entries); i++ {
		if sm.Entries[i].Row == row {
			result[sm.Entries[i].Col] = true
		}
	}

	return result
}

//Returns a rows "on" indices
func (sm *SparseBinaryMatrix) GetRowIndices(row int) []int {
	result := []int{}
	for i := 0; i < len(sm.Entries); i++ {
		if sm.Entries[i].Row == row {
			result = append(result, sm.Entries[i].Col)
		}
	}
	return result
}

//Sets a sparse row from dense representation
func (sm *SparseBinaryMatrix) SetRowFromDense(row int, denseRow []bool) {
	sm.validateRowCol(row, len(denseRow))
	for i := 0; i < sm.Width; i++ {
		sm.Set(row, i, denseRow[i])
	}
}

//In a normal matrix this would be multiplication in binary terms
//we just and then sum the true entries
func (sm *SparseBinaryMatrix) RowAndSum(row []bool) []int {
	sm.validateCol(len(row))
	result := make([]int, sm.Height)

	for _, val := range sm.Entries {
		if row[val.Col] {
			result[val.Row]++
		}
	}

	return result
}

//Returns # of rows with at least 1 true value
func (sm *SparseBinaryMatrix) TotatTrueRows() int {
	var hitRows []int
	for _, val := range sm.Entries {
		if !ContainsInt(val.Row, hitRows) {
			hitRows = append(hitRows, val.Row)
		}
	}
	return len(hitRows)
}

//Returns # of cols with at least 1 true value
func (sm *SparseBinaryMatrix) TotalTrueCols() int {
	var hitCols []int
	for _, val := range sm.Entries {
		if !ContainsInt(val.Col, hitCols) {
			hitCols = append(hitCols, val.Col)
		}
	}
	return len(hitCols)
}

// Ors 2 matrices
func (sm *SparseBinaryMatrix) Or(sm2 *SparseBinaryMatrix) *SparseBinaryMatrix {
	result := NewSparseBinaryMatrix(sm.Height, sm.Width)

	for _, val := range sm.Entries {
		result.Set(val.Row, val.Col, true)
	}

	for _, val := range sm2.Entries {
		result.Set(val.Row, val.Col, true)
	}

	return result
}

//Clears  all entries
func (sm *SparseBinaryMatrix) Clear() {
	sm.Entries = nil
}

//Copys a matrix
func (sm *SparseBinaryMatrix) Copy() *SparseBinaryMatrix {
	result := new(SparseBinaryMatrix)
	result.Width = sm.Width
	result.Height = sm.Height
	result.Entries = make([]SparseEntry, len(sm.Entries))
	for idx, val := range sm.Entries {
		result.Entries[idx] = val
	}

	return result
}

func (sm *SparseBinaryMatrix) ToString() string {
	var buffer bytes.Buffer

	for r := 0; r < sm.Height; r++ {
		for c := 0; c < sm.Width; c++ {
			if sm.Get(r, c) {
				buffer.WriteByte('1')
			} else {
				buffer.WriteByte('0')
			}
		}
		buffer.WriteByte('\n')
	}

	return buffer.String()
}

func (sm *SparseBinaryMatrix) validateCol(col int) {
	if col > sm.Width {
		panic("Specified row is wider than matrix.")
	}
}

func (sm *SparseBinaryMatrix) validateRow(row int) {
	if row > sm.Height {
		panic("Specified row is out of bounds.")
	}
}

func (sm *SparseBinaryMatrix) validateRowCol(row int, col int) {
	sm.validateCol(col)
	sm.validateRow(row)
}
<|endoftext|>


<commit_before>package nagiosplugin

import (
	"fmt"
	"math"
	"strconv"
	"strings"
)

// PerfDatum represents one metric to be reported as part of a check
// result.
type PerfDatum struct {
	label string
	value float64
	unit  string
	min   *float64
	max   *float64
	warn  *float64
	crit  *float64
}

// fmtPerfFloat returns a string representation of n formatted in the
// typical /\d+(\.\d+)/ pattern. The difference from %f is that it
// removes any trailing zeroes (like %g except it never returns
// values in scientific notation).
func fmtPerfFloat(n float64) string {
	return strconv.FormatFloat(n, 'f', -1, 64)
}

// validUnit returns true if the string is a valid UOM; otherwise false.
// It is case-insensitive.
func validUnit(unit string) bool {
	switch strings.ToLower(unit) {
	case "", "us", "ms", "s", "%", "b", "kb", "mb", "gb", "tb", "c":
		return true
	}
	return false
}

// NewPerfDatum returns a PerfDatum object suitable to use in a check
// result. unit must a valid Nagios unit, i.e., one of "us", "ms", "s",
// "%", "b", "kb", "mb", "gb", "tb", "c", or the empty string.
//
// Zero to four thresholds may be supplied: min, max, warn and crit.
// Thresholds may be positive infinity, negative infinity, or NaN in
// which case they will be omitted in check output.
func NewPerfDatum(label string, unit string, value float64, thresholds ...float64) (*PerfDatum, error) {
	datum := new(PerfDatum)
	datum.label = label
	datum.value = value
	datum.unit = unit
	if !validUnit(unit) {
		return nil, fmt.Errorf("Invalid unit %v", unit)
	}
	if math.IsInf(value, 0) || math.IsNaN(value) {
		return nil, fmt.Errorf("Perfdata value may not be infinity or NaN: %v.", value)
	}
	if len(thresholds) >= 1 {
		datum.min = &thresholds[0]
	}
	if len(thresholds) >= 2 {
		datum.max = &thresholds[1]
	}
	if len(thresholds) >= 3 {
		datum.warn = &thresholds[2]
	}
	if len(thresholds) >= 4 {
		datum.crit = &thresholds[3]
	}
	return datum, nil
}

// isThresholdSet returns true if one of min, max, warn or crit are set
// and false otherwise. They are determined to be 'set' if they are not
// a) the nil pointer, b) (either) infinity or c) NaN.
func isThresholdSet(t *float64) bool {
	switch {
	case t == nil:
		return false
	case math.IsInf(*t, 0):
		return false
	case math.IsNaN(*t):
		return false
	}
	return true
}

// fmtThreshold returns a string representation of min, max, warn or
// crit (whether or not they are set).
func fmtThreshold(t *float64) string {
	if !isThresholdSet(t) {
		return ""
	}
	return fmtPerfFloat(*t)
}

// String returns the string representation of a PerfDatum, suitable for
// check output.
func (p PerfDatum) String() string {
	val := fmtPerfFloat(p.value)
	value := fmt.Sprintf("%s=%s%s", p.label, val, p.unit)
	value += fmt.Sprintf(";%s;%s", fmtThreshold(p.warn), fmtThreshold(p.crit))
	value += fmt.Sprintf(";%s;%s", fmtThreshold(p.min), fmtThreshold(p.max))
	return value
}

// RenderPerfdata accepts a slice of PerfDatum objects and returns their
// concatenated string representations in a form suitable to append to
// the first line of check output.
func RenderPerfdata(perfdata []PerfDatum) string {
	value := ""
	if len(perfdata) == 0 {
		return value
	}
	// Demarcate start of perfdata in check output.
	value += " |"
	for _, datum := range perfdata {
		value += fmt.Sprintf(" %v", datum)
	}
	return value
}
<commit_msg>Make perfdata docs a little more clear<commit_after>package nagiosplugin

import (
	"fmt"
	"math"
	"strconv"
	"strings"
)

// PerfDatum represents one metric to be reported as part of a check
// result.
type PerfDatum struct {
	label string
	value float64
	unit  string
	min   *float64
	max   *float64
	warn  *float64
	crit  *float64
}

// fmtPerfFloat returns a string representation of n formatted in the
// typical /\d+(\.\d+)/ pattern. The difference from %f is that it
// removes any trailing zeroes (like %g except it never returns
// values in scientific notation).
func fmtPerfFloat(n float64) string {
	return strconv.FormatFloat(n, 'f', -1, 64)
}

// validUnit returns true if the string is a valid UOM; otherwise false.
// It is case-insensitive.
func validUnit(unit string) bool {
	switch strings.ToLower(unit) {
	case "", "us", "ms", "s", "%", "b", "kb", "mb", "gb", "tb", "c":
		return true
	}
	return false
}

// NewPerfDatum returns a PerfDatum object suitable to use in a check
// result. unit must a valid Nagios unit, i.e., one of "us", "ms", "s",
// "%", "b", "kb", "mb", "gb", "tb", "c", or the empty string.
//
// Zero to four thresholds may be supplied: min, max, warn and crit.
// Thresholds may be positive infinity, negative infinity, or NaN, in
// which case they will be omitted in check output.
func NewPerfDatum(label string, unit string, value float64, thresholds ...float64) (*PerfDatum, error) {
	datum := new(PerfDatum)
	datum.label = label
	datum.value = value
	datum.unit = unit
	if !validUnit(unit) {
		return nil, fmt.Errorf("Invalid unit %v", unit)
	}
	if math.IsInf(value, 0) || math.IsNaN(value) {
		return nil, fmt.Errorf("Perfdata value may not be infinity or NaN: %v.", value)
	}
	if len(thresholds) >= 1 {
		datum.min = &thresholds[0]
	}
	if len(thresholds) >= 2 {
		datum.max = &thresholds[1]
	}
	if len(thresholds) >= 3 {
		datum.warn = &thresholds[2]
	}
	if len(thresholds) >= 4 {
		datum.crit = &thresholds[3]
	}
	return datum, nil
}

// isThresholdSet returns true if one of min, max, warn or crit are set
// and false otherwise. They are determined to be 'set' if they are not
// a) the nil pointer, b) infinity (positive or negative) or c) NaN.
func isThresholdSet(t *float64) bool {
	switch {
	case t == nil:
		return false
	case math.IsInf(*t, 0):
		return false
	case math.IsNaN(*t):
		return false
	}
	return true
}

// fmtThreshold returns a string representation of min, max, warn or
// crit (whether or not they are set).
func fmtThreshold(t *float64) string {
	if !isThresholdSet(t) {
		return ""
	}
	return fmtPerfFloat(*t)
}

// String returns the string representation of a PerfDatum, suitable for
// check output.
func (p PerfDatum) String() string {
	val := fmtPerfFloat(p.value)
	value := fmt.Sprintf("%s=%s%s", p.label, val, p.unit)
	value += fmt.Sprintf(";%s;%s", fmtThreshold(p.warn), fmtThreshold(p.crit))
	value += fmt.Sprintf(";%s;%s", fmtThreshold(p.min), fmtThreshold(p.max))
	return value
}

// RenderPerfdata accepts a slice of PerfDatum objects and returns their
// concatenated string representations in a form suitable to append to
// the first line of check output.
func RenderPerfdata(perfdata []PerfDatum) string {
	value := ""
	if len(perfdata) == 0 {
		return value
	}
	// Demarcate start of perfdata in check output.
	value += " |"
	for _, datum := range perfdata {
		value += fmt.Sprintf(" %v", datum)
	}
	return value
}
<|endoftext|>


<commit_before>/***** BEGIN LICENSE BLOCK *****

# Author: David Birdsong (david@imgix.com)
# Copyright (c) 2014, Zebrafish Labs Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 	Redistributions of source code must retain the above copyright notice,
# 	this list of conditions and the following disclaimer.
#
# 	Redistributions in binary form must reproduce the above copyright notice,
# 	this list of conditions and the following disclaimer in the documentation
# 	and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ***** END LICENSE BLOCK *****/

/*
Package hekametrics adds an output for https://github.com/rcrowley/go-metrics/

hekametrics encodes all metrics from a registry into a Heka protobuf message
and sends to a Heka server on it's native listener port.
*/
package hekametrics

import (
	"code.google.com/p/go-uuid/uuid"
	"fmt"
	"github.com/mozilla-services/heka/client"
	"github.com/mozilla-services/heka/message"
	"github.com/rcrowley/go-metrics"
	"log"
	"net/url"
	"os"
	"time"
)

var logger = log.New(os.Stderr, "[hekametrics]", log.LstdFlags)

type HekaClient struct {
	pid               int32
	hostname, msgtype string

	client    client.Client
	encoder   client.StreamEncoder
	sender    client.Sender
	connect_s *url.URL
}

//NewHekaClient creates and returns a HekaClient
//
//connect string like 'tcp://127.0.0.1:5564' and 'udp://127.0.0.1:5564'
//
//msgtype sets the 'Type' field on a Heka message
func NewHekaClient(connect, msgtype string) (hc *HekaClient, err error) {
	hc = &HekaClient{}
	hc.connect_s, err = url.ParseRequestURI(connect)
	if err != nil {
		return nil, err
	}
	switch hc.connect_s.Scheme {
	case "tcp", "udp":
	default:
		return nil, fmt.Errorf("scheme: '%s' not supported, try 'tcp://<host>:<port>' or 'udp://<host>:<port>'", hc.connect_s.Scheme)
	}
	hc.msgtype = msgtype
	hc.encoder = client.NewProtobufEncoder(nil)
	hc.pid = int32(os.Getpid())
	hc.hostname, err = os.Hostname()
	if err != nil {
		hc.hostname = "<no hostname>"
	}
	return
}

func (hc *HekaClient) write(b []byte) error {
	var err error
	reconnect := func() (e error) {
		if hc.sender != nil {
			hc.sender.Close()
			hc.sender = nil
		}

		logger.Printf("Connecting: %s\n", hc.connect_s)
		hc.sender, e = client.NewNetworkSender(hc.connect_s.Scheme, hc.connect_s.Host)
		if e != nil {
			hc.sender = nil
			logger.Printf("Err Connecting: %s %v\n", hc.connect_s, e)
		}
		return e
	}

	if hc.sender == nil {
		err = reconnect()
		if err != nil {
			return err
		}

	}

	err = hc.sender.SendMessage(b)
	if err != nil {
		logger.Printf("Inject: [error] send message: %s\n", err)
		err = reconnect()
		if err != nil {
			return err
		}
		err = hc.sender.SendMessage(b)
		if err != nil {
			return err
		}

	}

	return err

}

// LogHeka is a blocking exporter function which encodes and sends metrics to a Heka server
//
// all metrics in metrics.Registry r are stored on message.Message.Fields
//
// flushing them every Duration d
func (hc *HekaClient) LogHeka(r metrics.Registry, d time.Duration) {

	var (
		stream []byte
		err    error
	)

	for {
		msg := make_message(r)
		msg.SetTimestamp(time.Now().UnixNano())
		msg.SetUuid(uuid.NewRandom())
		msg.SetLogger("go-metrics")
		msg.SetType(hc.msgtype)
		msg.SetPid(hc.pid)
		msg.SetSeverity(100)
		msg.SetHostname(hc.hostname)
		msg.SetPayload("")

		err = hc.encoder.EncodeMessageStream(msg, &stream)
		if err != nil {
			logger.Printf("Inject: [error] encode message: %s\n", err)
		}
		err = hc.write(stream)
		if err != nil {
			logger.Printf("Inject: [error] send message: %s\n", err)
		}
		time.Sleep(d)

	}

}

func make_message(r metrics.Registry) *message.Message {

	msg := &message.Message{}
	add_float_mapping := func(pref string, names []string, vals []float64) {
		for i, n := range names {

			n = fmt.Sprintf("%s.%s", pref, n)

			if i+1 > len(vals) {
				logger.Println("skipping: %s no value\n", n)
				continue
			}
			f, e := message.NewField(n, vals[i], "")
			if e == nil {
				msg.AddField(f)
			} else {
				logger.Println("skipping: %s %v: %v\n", n, vals[i], e)
			}

		}

	}

	r.Each(func(name string, i interface{}) {

		switch metric := i.(type) {
		case metrics.Counter:
			message.NewInt64Field(msg, name, metric.Count(), "")
		case metrics.Gauge:
			message.NewInt64Field(msg, name, metric.Value(), "")

		case metrics.GaugeFloat64:
			f, e := message.NewField(name, metric.Value(), "")
			if e == nil {
				msg.AddField(f)
			} else {
				logger.Println("skipping: %s %v: %v\n", name, metric.Value(), e)
			}

		case metrics.Histogram:
			h := metric.Snapshot()
			vals_fl := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
			vals_fl = append(vals_fl, h.Mean(), h.StdDev())
			names := []string{"50-percentile", "75-percentile", "95-percentile",
				"99-percentile", "999-percentile", "mean", "std-dev"}
			add_float_mapping(fmt.Sprintf("%s.histogram", name), names, vals_fl)

			names = []string{"count", "min", "max"}
			vals_i := []int64{h.Count(), h.Min(), h.Max()}

			for i, n := range names {
				n = fmt.Sprintf("%s.histogram.%s", name, n)
				message.NewInt64Field(msg, n, vals_i[i], n)
			}

		case metrics.Meter:
			m := metric.Snapshot()
			message.NewInt64Field(msg, fmt.Sprintf("%s.count", name),
				m.Count(), "")
			names := []string{"one-minute", "five-minute", "fifteen-minute", "mean"}
			vals_fl := []float64{m.Rate1(), m.Rate5(), m.Rate15(), m.RateMean()}

			add_float_mapping(name, names, vals_fl)
		case metrics.Timer:
			h := metric.Snapshot()
			vals_fl := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
			vals_fl = append(vals_fl, h.Mean(), h.StdDev(), h.Rate1(),
				h.Rate5(), h.Rate15(), h.RateMean())
			names := []string{"50-percentile", "75-percentile", "95-percentile",
				"99-percentile", "999-percentile", "mean", "std-dev", "one-minute",
				"five-minute", "fifteen-minute", "mean-rate"}

			add_float_mapping(fmt.Sprintf("%s.timer", name), names, vals_fl)
			names = []string{"count", "min", "max"}
			vals_i := []int64{h.Count(), h.Min(), h.Max()}
			for i, n := range names {
				n = fmt.Sprintf("%s.timer.%s", name, n)
				message.NewInt64Field(msg, n, vals_i[i], "")
			}

		}
	})
	return msg

}
<commit_msg>stop method from another goroutine<commit_after>/***** BEGIN LICENSE BLOCK *****

# Author: David Birdsong (david@imgix.com)
# Copyright (c) 2014, Zebrafish Labs Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 	Redistributions of source code must retain the above copyright notice,
# 	this list of conditions and the following disclaimer.
#
# 	Redistributions in binary form must reproduce the above copyright notice,
# 	this list of conditions and the following disclaimer in the documentation
# 	and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ***** END LICENSE BLOCK *****/

/*
Package hekametrics adds an output for https://github.com/rcrowley/go-metrics/

hekametrics encodes all metrics from a registry into a Heka protobuf message
and sends to a Heka server on it's native listener port.
*/
package hekametrics

import (
	"code.google.com/p/go-uuid/uuid"
	"fmt"
	"github.com/mozilla-services/heka/client"
	"github.com/mozilla-services/heka/message"
	"github.com/rcrowley/go-metrics"
	"log"
	"net/url"
	"os"
	"time"
)

var logger = log.New(os.Stderr, "[hekametrics]", log.LstdFlags)

type HekaClient struct {
	pid               int32
	hostname, msgtype string

	client    client.Client
	encoder   client.StreamEncoder
	sender    client.Sender
	connect_s *url.URL
	stop      chan struct{}
}

//NewHekaClient creates and returns a HekaClient
//
//connect string like 'tcp://127.0.0.1:5564' and 'udp://127.0.0.1:5564'
//
//msgtype sets the 'Type' field on a Heka message
func NewHekaClient(connect, msgtype string) (hc *HekaClient, err error) {
	hc = &HekaClient{}
	hc.connect_s, err = url.ParseRequestURI(connect)
	if err != nil {
		return nil, err
	}
	switch hc.connect_s.Scheme {
	case "tcp", "udp":
	default:
		return nil, fmt.Errorf("scheme: '%s' not supported, try 'tcp://<host>:<port>' or 'udp://<host>:<port>'", hc.connect_s.Scheme)
	}
	hc.msgtype = msgtype
	hc.encoder = client.NewProtobufEncoder(nil)
	hc.pid = int32(os.Getpid())
	hc.hostname, err = os.Hostname()
	if err != nil {
		hc.hostname = "<no hostname>"
	}
	hc.stop = make(chan struct{})
	return
}

func (hc *HekaClient) write(b []byte) error {
	var err error
	reconnect := func() (e error) {
		if hc.sender != nil {
			hc.sender.Close()
			hc.sender = nil
		}

		logger.Printf("Connecting: %s\n", hc.connect_s)
		hc.sender, e = client.NewNetworkSender(hc.connect_s.Scheme, hc.connect_s.Host)
		if e != nil {
			hc.sender = nil
			logger.Printf("Err Connecting: %s %v\n", hc.connect_s, e)
		}
		return e
	}

	if hc.sender == nil {
		err = reconnect()
		if err != nil {
			return err
		}

	}

	err = hc.sender.SendMessage(b)
	if err != nil {
		logger.Printf("Inject: [error] send message: %s\n", err)
		err = reconnect()
		if err != nil {
			return err
		}
		err = hc.sender.SendMessage(b)
		if err != nil {
			return err
		}

	}
	return err
}

// Stops LogHeka from another goroutine
func (hc *HekaClient) Stop() {
	close(hc.stop)
}

// LogHeka is a blocking exporter function which encodes and sends metrics to a Heka server
//
// all metrics in metrics.Registry r are stored on message.Message.Fields
//
// flushing them every Duration d
func (hc *HekaClient) LogHeka(r metrics.Registry, d time.Duration) {

	var (
		stream  []byte
		err     error
		running bool = true
	)

	for running {
		select {
		case _, running = <-hc.stop:
		default:
			msg := make_message(r)
			msg.SetTimestamp(time.Now().UnixNano())
			msg.SetUuid(uuid.NewRandom())
			msg.SetLogger("go-metrics")
			msg.SetType(hc.msgtype)
			msg.SetPid(hc.pid)
			msg.SetSeverity(100)
			msg.SetHostname(hc.hostname)
			msg.SetPayload("")

			err = hc.encoder.EncodeMessageStream(msg, &stream)
			if err != nil {
				logger.Printf("Inject: [error] encode message: %s\n", err)
			}
			err = hc.write(stream)
			if err != nil {
				logger.Printf("Inject: [error] send message: %s\n", err)
			}
			time.Sleep(d)
		}

	}

}

func make_message(r metrics.Registry) *message.Message {

	msg := &message.Message{}
	add_float_mapping := func(pref string, names []string, vals []float64) {
		for i, n := range names {

			n = fmt.Sprintf("%s.%s", pref, n)

			if i+1 > len(vals) {
				logger.Println("skipping: %s no value\n", n)
				continue
			}
			f, e := message.NewField(n, vals[i], "")
			if e == nil {
				msg.AddField(f)
			} else {
				logger.Println("skipping: %s %v: %v\n", n, vals[i], e)
			}

		}

	}

	r.Each(func(name string, i interface{}) {

		switch metric := i.(type) {
		case metrics.Counter:
			message.NewInt64Field(msg, name, metric.Count(), "")
		case metrics.Gauge:
			message.NewInt64Field(msg, name, metric.Value(), "")

		case metrics.GaugeFloat64:
			f, e := message.NewField(name, metric.Value(), "")
			if e == nil {
				msg.AddField(f)
			} else {
				logger.Println("skipping: %s %v: %v\n", name, metric.Value(), e)
			}

		case metrics.Histogram:
			h := metric.Snapshot()
			vals_fl := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
			vals_fl = append(vals_fl, h.Mean(), h.StdDev())
			names := []string{"50-percentile", "75-percentile", "95-percentile",
				"99-percentile", "999-percentile", "mean", "std-dev"}
			add_float_mapping(fmt.Sprintf("%s.histogram", name), names, vals_fl)

			names = []string{"count", "min", "max"}
			vals_i := []int64{h.Count(), h.Min(), h.Max()}

			for i, n := range names {
				n = fmt.Sprintf("%s.histogram.%s", name, n)
				message.NewInt64Field(msg, n, vals_i[i], n)
			}

		case metrics.Meter:
			m := metric.Snapshot()
			message.NewInt64Field(msg, fmt.Sprintf("%s.count", name),
				m.Count(), "")
			names := []string{"one-minute", "five-minute", "fifteen-minute", "mean"}
			vals_fl := []float64{m.Rate1(), m.Rate5(), m.Rate15(), m.RateMean()}

			add_float_mapping(name, names, vals_fl)
		case metrics.Timer:
			h := metric.Snapshot()
			vals_fl := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
			vals_fl = append(vals_fl, h.Mean(), h.StdDev(), h.Rate1(),
				h.Rate5(), h.Rate15(), h.RateMean())
			names := []string{"50-percentile", "75-percentile", "95-percentile",
				"99-percentile", "999-percentile", "mean", "std-dev", "one-minute",
				"five-minute", "fifteen-minute", "mean-rate"}

			add_float_mapping(fmt.Sprintf("%s.timer", name), names, vals_fl)
			names = []string{"count", "min", "max"}
			vals_i := []int64{h.Count(), h.Min(), h.Max()}
			for i, n := range names {
				n = fmt.Sprintf("%s.timer.%s", name, n)
				message.NewInt64Field(msg, n, vals_i[i], "")
			}

		}
	})
	return msg

}
<|endoftext|>


<commit_before>package identico

import (
	"image"
	"image/color"
)

func ReplaceMask(src image.Image, col color.Color) image.Image {
	bounds := src.Bounds()
	dst := image.NewRGBA(bounds)
	w, h := bounds.Max.X, bounds.Max.Y

	for x := 0; x < w; x++ {
		for y := 0; y < h; y++ {
			rgba := col.(color.NRGBA)
			pixel := src.At(x, y).(color.NRGBA)
			if pixel.A != 0 {
				rgba.A = pixel.A
				dst.Set(x, y, rgba)
			} else {
				dst.Set(x, y, pixel)
			}
		}
	}
	return dst
}
<commit_msg>Add background and foreground drawing<commit_after>package identico

import (
	"image"
	"image/color"
	"image/draw"
)

func Classic(mask image.Image, bg, fg color.Color) image.Image {
	bounds := mask.Bounds()
	w, h := bounds.Max.X, bounds.Max.Y
	bgimg := FillBackground(w, h, bg)
	fgimg := ReplaceMask(mask, fg)

	dst := image.NewNRGBA(bounds)
	draw.Draw(dst, bounds, bgimg, image.ZP, draw.Src)
	draw.Draw(dst, bounds, fgimg, image.ZP, draw.Over)
	return dst
}

func FillBackground(width, height int, col color.Color) image.Image {
	img := image.NewNRGBA(image.Rect(0, 0, width, height))
	draw.Draw(img, img.Bounds(), &image.Uniform{col}, image.ZP, draw.Src)
	return img
}

func ReplaceMask(mask image.Image, col color.Color) image.Image {
	bounds := mask.Bounds()
	dst := image.NewNRGBA(bounds)
	w, h := bounds.Max.X, bounds.Max.Y
	r, g, b, _ := col.RGBA()

	for x := 0; x < w; x++ {
		for y := 0; y < h; y++ {
			pixel := mask.At(x, y)
			_, _, _, alpha := pixel.RGBA()
			if alpha != 0 {
				rgba := color.NRGBA{shift(r), shift(g), shift(b), shift(alpha)}
				dst.Set(x, y, rgba)
			} else {
				dst.Set(x, y, pixel)
			}
		}
	}
	return dst
}

func shift(v uint32) uint8 {
	return uint8(v >> 8)
}
<|endoftext|>


<commit_before>package service

import (
	"bufio"
	"context"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/davecgh/go-spew/spew"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/mount"
	"github.com/docker/docker/api/types/swarm"
	"github.com/docker/docker/client"
	"github.com/dokkur/swanager/config"
	"github.com/dokkur/swanager/core/entities"
	"github.com/dokkur/swanager/core/swarm/task"
)

// StatusStruct represents service state
type StatusStruct struct {
	TaskID    string
	Node      string
	Status    string
	Timestamp time.Time
	Error     string
}

// SpecOptions service create params
type SpecOptions struct {
	Service     *entities.Service
	NetworkName string
	Index       uint64
}

// Create creates swarm service form Service entity
func Create(opts SpecOptions) (string, error) {
	cli, err := client.NewEnvClient()
	if err != nil {
		panic(err)
	}
	defer cli.Close()

	serviceSpec := getServiceSpec(opts)
	serviceCreateOptions := types.ServiceCreateOptions{}

	log().WithField("spec", fmt.Sprintf("%+v", serviceSpec)).Debug("Creating swarm service")

	responce, err := cli.ServiceCreate(context.Background(), serviceSpec, serviceCreateOptions)
	if err != nil {
		panic(err)
	}

	if len(responce.Warnings) > 0 {
		log().Debug("Warnings:")
		log().Debugf("%+v", responce.Warnings)
	}

	return responce.ID, nil
}

// Update - updates existing service
func Update(opts SpecOptions) error {
	cli, err := client.NewEnvClient()
	if err != nil {
		panic(err)
	}
	defer cli.Close()

	serviceSpec := getServiceSpec(opts)
	serviceUpdateOptions := types.ServiceUpdateOptions{}

	responce, err := cli.ServiceUpdate(context.Background(), opts.Service.NSName, swarm.Version{Index: opts.Index}, serviceSpec, serviceUpdateOptions)
	if err != nil {
		panic(err)
	}

	if len(responce.Warnings) > 0 {
		log().Debug("Warnings:")
		log().Debugf("%+v", responce.Warnings)
	}

	return nil
}

// Remove removes service
func Remove(service *entities.Service) error {
	log().Debugf("Removing service. %s", service.NSName)
	cli, err := client.NewEnvClient()
	if err != nil {
		panic(err)
	}

	defer cli.Close()

	err = cli.ServiceRemove(context.Background(), service.NSName)
	if err != nil {
		fmt.Println(err)
		return err
	}

	return err
}

// Inspect return service status
func Inspect(service *entities.Service) (*swarm.Service, error) {
	cli, err := client.NewEnvClient()
	if err != nil {
		panic(err)
	}
	defer cli.Close()

	serviceInspection, _, err := cli.ServiceInspectWithRaw(context.Background(), service.NSName, types.ServiceInspectOptions{InsertDefaults: true})
	if err != nil {
		return nil, err
	}

	return &serviceInspection, nil
}

// Status returns service status
func Status(service *entities.Service) ([]StatusStruct, error) {
	tasks, err := task.ListFor(service.NSName)
	if err != nil {
		return nil, err
	}

	result := make([]StatusStruct, 0)
	for _, task := range *tasks {
		result = append(result, StatusStruct{
			TaskID:    task.ID,
			Node:      task.NodeID,
			Status:    string(task.Status.State),
			Timestamp: task.Status.Timestamp,
			Error:     task.Status.Err,
		})
	}
	return result, nil
}

// Logs return service logs
func Logs(service *entities.Service) ([]string, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	cli, err := client.NewEnvClient()
	if err != nil {
		return nil, err
	}
	defer cli.Close()

	reader, err := cli.ServiceLogs(ctx, service.NSName, types.ContainerLogsOptions{
		ShowStderr: true,
		ShowStdout: true,
		Follow:     false,
		Tail:       "20",
	})
	if err != nil {
		return nil, err
	}

	scanner := bufio.NewScanner(reader)
	result := make([]string, 0)

	for scanner.Scan() {
		result = append(result, scanner.Text())
	}
	return result, nil
}

func getServiceSpec(opts SpecOptions) swarm.ServiceSpec {
	opts.Service.LoadApplication()

	mounts := getServiceVolumes(opts.Service)

	containerSpec := swarm.ContainerSpec{
		Image:   opts.Service.Image,
		Mounts:  mounts,
		Env:     prepareEnvVars(opts.Service),
		Command: prepareCommand(opts.Service),
	}

	updateConfig := swarm.UpdateConfig{
		Parallelism:     opts.Service.Parallelism,
		FailureAction:   "pause",
		MaxFailureRatio: 0.5,
	}

	return swarm.ServiceSpec{
		Annotations: swarm.Annotations{
			Name: opts.Service.NSName,
			Labels: map[string]string{
				"swanager_id":    opts.Service.ID,
				"application_id": opts.Service.Application.ID,
			},
		},
		TaskTemplate: swarm.TaskSpec{
			ContainerSpec: containerSpec,
			Resources: &swarm.ResourceRequirements{
				Limits: &swarm.Resources{
					NanoCPUs:    0, // CPU ratio * 10^9 :)
					MemoryBytes: 0, // in bytes
				},
			},
			Networks: []swarm.NetworkAttachmentConfig{
				swarm.NetworkAttachmentConfig{Target: opts.NetworkName},
			},
		},
		Mode: swarm.ServiceMode{
			Replicated: &swarm.ReplicatedService{Replicas: opts.Service.Replicas},
		},
		UpdateConfig: &updateConfig,
		EndpointSpec: &swarm.EndpointSpec{
			Mode:  swarm.ResolutionModeVIP,
			Ports: preparePorts(opts.Service),
		},
	}
}

// Update service in db with currently running service params
// e.g. Autoassigned published ports
func updateWithRunningSpec(service *entities.Service) error {
	running, err := Inspect(service)
	if err != nil {
		return err
	}

	// runningPorts := running.
	spew.Dump(running)

	return nil
}

func getServiceVolumes(service *entities.Service) []mount.Mount {
	service.LoadApplication()

	result := make([]mount.Mount, 0)
	for _, vol := range service.Volumes {
		sourcePath := getMountPath(service, vol)
		os.MkdirAll(sourcePath, 0777)

		result = append(result, mount.Mount{
			Type:     mount.TypeBind,
			Source:   sourcePath,
			Target:   vol.Service,
			ReadOnly: false,
		})
	}
	return result
}

func prepareCommand(service *entities.Service) []string {
	if service.Command == "" {
		return make([]string, 0)
	}
	return regexp.MustCompile("\\s+").Split(service.Command, -1)
}

func prepareEnvVars(service *entities.Service) (vars []string) {
	for _, envVar := range service.EnvVariables {
		vars = append(vars, fmt.Sprintf("%s=%s", envVar.Name, envVar.Value))
	}
	return
}

func preparePorts(service *entities.Service) (ports []swarm.PortConfig) {

	for _, port := range service.PublishedPorts {
		// Don't publish port if disabled
		if port.Disabled {
			continue
		}

		ports = append(ports, swarm.PortConfig{
			Name:          "swanager_port",
			Protocol:      stringToProtocol(port.Protocol),
			TargetPort:    port.Internal,
			PublishedPort: port.External,
			PublishMode:   swarm.PortConfigPublishModeIngress,
		})
	}
	return
}

func stringToProtocol(protocol string) swarm.PortConfigProtocol {
	switch strings.ToLower(protocol) {
	case "udp":
		return swarm.PortConfigProtocolUDP
	}
	return swarm.PortConfigProtocolTCP
}

func getMountPathPrefix(service *entities.Service, appWide bool) string {
	path := service.NSName
	if appWide {
		path = "app_wide"
	}

	return filepath.Join(config.MountPathPrefix, service.ApplicationID, path)
}

func getMountPath(service *entities.Service, vol entities.ServiceVolume) string {
	path := vol.Service
	if vol.Backend != "" {
		path = vol.Backend
	}

	return filepath.Join(getMountPathPrefix(service, vol.AppWide), path)
}

func log() *logrus.Entry {
	return logrus.WithField("module", "swarm.service")
}

// LoadVolumeSizes loads volume sized into struct
func LoadVolumeSizes(service *entities.Service) {
	var wg sync.WaitGroup
	for index := range service.Volumes {
		wg.Add(1)

		go dirSize(service, &service.Volumes[index], &wg)
	}
	wg.Wait()
}

func dirSize(service *entities.Service, vol *entities.ServiceVolume, wg *sync.WaitGroup) {
	defer wg.Done()
	var size int64

	root := getMountPath(service, *vol)

	filepath.Walk(root, func(_ string, info os.FileInfo, err error) error {
		if err == nil && !info.IsDir() {
			size += info.Size()
		}
		return err
	})
	vol.Size = size
}
<commit_msg>Commented service volume sizes for now<commit_after>package service

import (
	"bufio"
	"context"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/davecgh/go-spew/spew"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/mount"
	"github.com/docker/docker/api/types/swarm"
	"github.com/docker/docker/client"
	"github.com/dokkur/swanager/config"
	"github.com/dokkur/swanager/core/entities"
	"github.com/dokkur/swanager/core/swarm/task"
)

// StatusStruct represents service state
type StatusStruct struct {
	TaskID    string
	Node      string
	Status    string
	Timestamp time.Time
	Error     string
}

// SpecOptions service create params
type SpecOptions struct {
	Service     *entities.Service
	NetworkName string
	Index       uint64
}

// Create creates swarm service form Service entity
func Create(opts SpecOptions) (string, error) {
	cli, err := client.NewEnvClient()
	if err != nil {
		panic(err)
	}
	defer cli.Close()

	serviceSpec := getServiceSpec(opts)
	serviceCreateOptions := types.ServiceCreateOptions{}

	log().WithField("spec", fmt.Sprintf("%+v", serviceSpec)).Debug("Creating swarm service")

	responce, err := cli.ServiceCreate(context.Background(), serviceSpec, serviceCreateOptions)
	if err != nil {
		panic(err)
	}

	if len(responce.Warnings) > 0 {
		log().Debug("Warnings:")
		log().Debugf("%+v", responce.Warnings)
	}

	return responce.ID, nil
}

// Update - updates existing service
func Update(opts SpecOptions) error {
	cli, err := client.NewEnvClient()
	if err != nil {
		panic(err)
	}
	defer cli.Close()

	serviceSpec := getServiceSpec(opts)
	serviceUpdateOptions := types.ServiceUpdateOptions{}

	responce, err := cli.ServiceUpdate(context.Background(), opts.Service.NSName, swarm.Version{Index: opts.Index}, serviceSpec, serviceUpdateOptions)
	if err != nil {
		panic(err)
	}

	if len(responce.Warnings) > 0 {
		log().Debug("Warnings:")
		log().Debugf("%+v", responce.Warnings)
	}

	return nil
}

// Remove removes service
func Remove(service *entities.Service) error {
	log().Debugf("Removing service. %s", service.NSName)
	cli, err := client.NewEnvClient()
	if err != nil {
		panic(err)
	}

	defer cli.Close()

	err = cli.ServiceRemove(context.Background(), service.NSName)
	if err != nil {
		fmt.Println(err)
		return err
	}

	return err
}

// Inspect return service status
func Inspect(service *entities.Service) (*swarm.Service, error) {
	cli, err := client.NewEnvClient()
	if err != nil {
		panic(err)
	}
	defer cli.Close()

	serviceInspection, _, err := cli.ServiceInspectWithRaw(context.Background(), service.NSName, types.ServiceInspectOptions{InsertDefaults: true})
	if err != nil {
		return nil, err
	}

	return &serviceInspection, nil
}

// Status returns service status
func Status(service *entities.Service) ([]StatusStruct, error) {
	tasks, err := task.ListFor(service.NSName)
	if err != nil {
		return nil, err
	}

	result := make([]StatusStruct, 0)
	for _, task := range *tasks {
		result = append(result, StatusStruct{
			TaskID:    task.ID,
			Node:      task.NodeID,
			Status:    string(task.Status.State),
			Timestamp: task.Status.Timestamp,
			Error:     task.Status.Err,
		})
	}
	return result, nil
}

// Logs return service logs
func Logs(service *entities.Service) ([]string, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	cli, err := client.NewEnvClient()
	if err != nil {
		return nil, err
	}
	defer cli.Close()

	reader, err := cli.ServiceLogs(ctx, service.NSName, types.ContainerLogsOptions{
		ShowStderr: true,
		ShowStdout: true,
		Follow:     false,
		Tail:       "20",
	})
	if err != nil {
		return nil, err
	}

	scanner := bufio.NewScanner(reader)
	result := make([]string, 0)

	for scanner.Scan() {
		result = append(result, scanner.Text())
	}
	return result, nil
}

func getServiceSpec(opts SpecOptions) swarm.ServiceSpec {
	opts.Service.LoadApplication()

	mounts := getServiceVolumes(opts.Service)

	containerSpec := swarm.ContainerSpec{
		Image:   opts.Service.Image,
		Mounts:  mounts,
		Env:     prepareEnvVars(opts.Service),
		Command: prepareCommand(opts.Service),
	}

	updateConfig := swarm.UpdateConfig{
		Parallelism:     opts.Service.Parallelism,
		FailureAction:   "pause",
		MaxFailureRatio: 0.5,
	}

	return swarm.ServiceSpec{
		Annotations: swarm.Annotations{
			Name: opts.Service.NSName,
			Labels: map[string]string{
				"swanager_id":    opts.Service.ID,
				"application_id": opts.Service.Application.ID,
			},
		},
		TaskTemplate: swarm.TaskSpec{
			ContainerSpec: containerSpec,
			Resources: &swarm.ResourceRequirements{
				Limits: &swarm.Resources{
					NanoCPUs:    0, // CPU ratio * 10^9 :)
					MemoryBytes: 0, // in bytes
				},
			},
			Networks: []swarm.NetworkAttachmentConfig{
				swarm.NetworkAttachmentConfig{Target: opts.NetworkName},
			},
		},
		Mode: swarm.ServiceMode{
			Replicated: &swarm.ReplicatedService{Replicas: opts.Service.Replicas},
		},
		UpdateConfig: &updateConfig,
		EndpointSpec: &swarm.EndpointSpec{
			Mode:  swarm.ResolutionModeVIP,
			Ports: preparePorts(opts.Service),
		},
	}
}

// Update service in db with currently running service params
// e.g. Autoassigned published ports
func updateWithRunningSpec(service *entities.Service) error {
	running, err := Inspect(service)
	if err != nil {
		return err
	}

	// runningPorts := running.
	spew.Dump(running)

	return nil
}

func getServiceVolumes(service *entities.Service) []mount.Mount {
	service.LoadApplication()

	result := make([]mount.Mount, 0)
	for _, vol := range service.Volumes {
		sourcePath := getMountPath(service, vol)
		os.MkdirAll(sourcePath, 0777)

		result = append(result, mount.Mount{
			Type:     mount.TypeBind,
			Source:   sourcePath,
			Target:   vol.Service,
			ReadOnly: false,
		})
	}
	return result
}

func prepareCommand(service *entities.Service) []string {
	if service.Command == "" {
		return make([]string, 0)
	}
	return regexp.MustCompile("\\s+").Split(service.Command, -1)
}

func prepareEnvVars(service *entities.Service) (vars []string) {
	for _, envVar := range service.EnvVariables {
		vars = append(vars, fmt.Sprintf("%s=%s", envVar.Name, envVar.Value))
	}
	return
}

func preparePorts(service *entities.Service) (ports []swarm.PortConfig) {

	for _, port := range service.PublishedPorts {
		// Don't publish port if disabled
		if port.Disabled {
			continue
		}

		ports = append(ports, swarm.PortConfig{
			Name:          "swanager_port",
			Protocol:      stringToProtocol(port.Protocol),
			TargetPort:    port.Internal,
			PublishedPort: port.External,
			PublishMode:   swarm.PortConfigPublishModeIngress,
		})
	}
	return
}

func stringToProtocol(protocol string) swarm.PortConfigProtocol {
	switch strings.ToLower(protocol) {
	case "udp":
		return swarm.PortConfigProtocolUDP
	}
	return swarm.PortConfigProtocolTCP
}

func getMountPathPrefix(service *entities.Service, appWide bool) string {
	path := service.NSName
	if appWide {
		path = "app_wide"
	}

	return filepath.Join(config.MountPathPrefix, service.ApplicationID, path)
}

func getMountPath(service *entities.Service, vol entities.ServiceVolume) string {
	path := vol.Service
	if vol.Backend != "" {
		path = vol.Backend
	}

	return filepath.Join(getMountPathPrefix(service, vol.AppWide), path)
}

func log() *logrus.Entry {
	return logrus.WithField("module", "swarm.service")
}

// LoadVolumeSizes loads volume sized into struct
func LoadVolumeSizes(service *entities.Service) {
	var wg sync.WaitGroup
	for index := range service.Volumes {
		wg.Add(1)

		go dirSize(service, &service.Volumes[index], &wg)
	}
	wg.Wait()
}

func dirSize(service *entities.Service, vol *entities.ServiceVolume, wg *sync.WaitGroup) {
	defer wg.Done()
//	var size int64

//	root := getMountPath(service, *vol)

//	filepath.Walk(root, func(_ string, info os.FileInfo, err error) error {
//		if err == nil && !info.IsDir() {
//			size += info.Size()
//		}
//		return err
//	})
//	vol.Size = size
	vol.Size = 0
}
<|endoftext|>


<commit_before>package hoard

import (
	"testing"

	"github.com/go-kit/kit/log"
	"github.com/monax/hoard/v8/config"
	"github.com/monax/hoard/v8/reference"
	"github.com/monax/hoard/v8/stores"
	"github.com/stretchr/testify/assert"
)

func TestDeterministicEncryptedStore(t *testing.T) {
	hrd := NewHoard(stores.NewMemoryStore(), config.NoopSecretManager, log.NewNopLogger())
	bunsIn := bs("hot buns")

	ref, err := hrd.Put(bunsIn, make([]byte, 32))
	assert.NoError(t, err)

	bunsOut, err := hrd.Get(ref)
	assert.Equal(t, bunsIn, bunsOut)

	_, err = hrd.Get(reference.New(ref.Address, pad("wrong secret", 32), nil))
	assert.Error(t, err)

	statInfo, err := hrd.Store().Stat(ref.Address)
	assert.NoError(t, err)
	assert.True(t, statInfo.Exists)
	// Our GCM cipher should be running an overhead of 16 bytes
	// (no IV, but 16-byte authentication tag)
	assert.Equal(t, uint64(len(bunsIn))+16+32, statInfo.Size_)

	loc := hrd.Store().Location(ref.Address)
	assert.Equal(t, "memfs://a0abd6a3e5d8f343b3e71b2e97af05f38652dd6345021ca34655aa27e7aa94ae", loc)

	// flip LSB of first byte of address to get an non-existent address
	ref.Address[0] = ref.Address[0] ^ 1
	statInfo, err = hrd.Store().Stat(ref.Address)
	assert.NoError(t, err)
	assert.False(t, statInfo.Exists)
}

func bs(s string) []byte {
	return ([]byte)(s)
}

func pad(s string, n int) []byte {
	b := make([]byte, n)
	copy(b, bs(s))
	return b
}
<commit_msg>fix test for new nonce setup<commit_after>package hoard

import (
	"testing"

	"github.com/go-kit/kit/log"
	"github.com/monax/hoard/v8/config"
	"github.com/monax/hoard/v8/reference"
	"github.com/monax/hoard/v8/stores"
	"github.com/stretchr/testify/assert"
)

func TestDeterministicEncryptedStore(t *testing.T) {
	hrd := NewHoard(stores.NewMemoryStore(), config.NoopSecretManager, log.NewNopLogger())
	bunsIn := bs("hot buns")

	ref, err := hrd.Put(bunsIn, make([]byte, 32))
	assert.NoError(t, err)

	bunsOut, err := hrd.Get(ref)
	assert.Equal(t, bunsIn, bunsOut)

	_, err = hrd.Get(reference.New(ref.Address, pad("wrong secret", 32), nil))
	assert.Error(t, err)

	statInfo, err := hrd.Store().Stat(ref.Address)
	assert.NoError(t, err)
	assert.True(t, statInfo.Exists)
	// Our GCM cipher should be running an overhead of 16 bytes
	// (no IV, but 16-byte authentication tag)
	assert.Equal(t, uint64(len(bunsIn))+16+32, statInfo.Size_)

	loc := hrd.Store().Location(ref.Address)
	assert.Equal(t, "memfs://75b382c29b0d8382a09b856f7a0f00300548c9f369574f68cfc9c62fcab2d1dc", loc)

	// flip LSB of first byte of address to get an non-existent address
	ref.Address[0] = ref.Address[0] ^ 1
	statInfo, err = hrd.Store().Stat(ref.Address)
	assert.NoError(t, err)
	assert.False(t, statInfo.Exists)
}

func bs(s string) []byte {
	return ([]byte)(s)
}

func pad(s string, n int) []byte {
	b := make([]byte, n)
	copy(b, bs(s))
	return b
}
<|endoftext|>


<commit_before>//An easier way to use http.Client
package com

import (
	"bytes"
	"errors"
	"fmt"
	//	"github.com/alecthomas/log4go"
	"github.com/axgle/mahonia"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/http/cookiejar"
	//	"strings"
	"log"
)

type HttpClient struct {
	c       *http.Client
	cookies []*http.Cookie
	jar     *cookiejar.Jar

	Header http.Header

	//编码转换相关处理
	conv    bool //conv between utf-8 and charset
	charset string
	enc     mahonia.Encoder
	dec     mahonia.Decoder

	//链接转向相关处理
	redirect    bool   //是否转向了。每次Get之前置为false
	redirectUrl string //转向后的链接
}

func NewHttpClient() (this *HttpClient) {

	this = &HttpClient{}

	this.cookies = nil
	this.jar, _ = cookiejar.New(nil)

	this.c = &http.Client{Jar: this.jar, CheckRedirect: func(req *http.Request, via []*http.Request) error {
		this.redirect = true
		this.redirectUrl = req.URL.String()
		//return errors.New("Redirected!")
		return nil
	}}

	this.Header = make(http.Header)
	this.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
	//this.Header.Add("Accept-Encoding", "gzip,deflate,sdch")
	this.Header.Add("Accept-Language", "zh-CN,zh;q=0.8")
	this.Header.Add("Connection", "keep-alive")
	this.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36")

	this.conv = false

	return this
}

//you should set it only once!
func (this *HttpClient) SetCharSet(charset string) {
	this.conv = true
	this.charset = charset
	this.enc = mahonia.NewEncoder(charset)
	this.dec = mahonia.NewDecoder(charset)
}

func (this *HttpClient) SetUa(ua string) {
	this.Header.Set("User-Agent", ua)
}

func (this *HttpClient) Enc(in string) string {
	if this.conv {
		return this.enc.ConvertString(in)
	}
	return in
}

func (this *HttpClient) Dec(in string) string {
	if this.conv {
		return this.dec.ConvertString(in)
	}
	return in
}

func (this *HttpClient) Get(url string) (page string, err error) {
	this.clearRedirect()
	//自动转化为GB2312编码
	gbkUrl := this.Enc(url)

	req, err := http.NewRequest("GET", gbkUrl, nil)
	if err != nil {
		err = errors.New(fmt.Sprintf("HttpClient.Get(%s),NewRequest error:%s", url, err.Error()))
		return
	}

	for k, v := range this.Header {
		req.Header.Add(k, v[0])
	}

	resp, err := this.c.Do(req)

	if err != nil {
		err = errors.New(fmt.Sprintf("HttpClient.Get(%s),Response error:%s", url, err.Error()))
		return
	}

	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	log.Println(string(body))
	if err != nil {
		err = errors.New(fmt.Sprintf("HttpClient.Get(%s),Read body error:%s", url, err.Error()))
		return
	}

	this.cookies = this.jar.Cookies(req.URL)

	page = this.Dec(string(body))
	return
}

func (this *HttpClient) Post(url, postdata string) (page string, err error) {
	this.clearRedirect()
	//自动转化为GB2312编码
	gbkUrl := this.Enc(url)
	gbkPostdata := this.Enc(postdata)

	req, _ := http.NewRequest("POST", gbkUrl, bytes.NewReader([]byte(gbkPostdata)))
	for k, v := range this.Header {
		req.Header.Add(k, v[0])
	}
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	resp, err := this.c.Do(req)

	if err != nil {
		err = errors.New(fmt.Sprintf("HttpClient.Post(%s,%s),Response error:%s", url, postdata, err.Error()))
		return
	}
	//	log4go.Debug("Response code:%d status:%s", resp.StatusCode, resp.Status)

	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		err = errors.New(fmt.Sprintf("HttpClient.Post(%s,%s),Read body error:%s", url, postdata, err.Error()))
		return
	}

	this.cookies = this.jar.Cookies(req.URL)

	page = this.Dec(string(body))

	//log4go.Finest("HttpClient.Post(%s,%s) returns:", url, postdata)
	//log4go.Finest(page)
	return
}

/*
///尚未测试
func (this *HttpClient) PostValues(surl string, postDict map[string]string) (page string, err error) {
	log4go.Debug("HttpClient.PostValues(%s,%T)", surl, postDict)
	postValues := url.Values{}
	for postKey, PostValue := range postDict {
		postValues.Set(this.Enc(postKey), this.Enc(PostValue))
	}
	postDataStr := postValues.Encode()

	return this.Post(surl, postDataStr)
}
*/
func (this *HttpClient) PostMultipart(u string, w *multipart.Writer, b *bytes.Buffer) (page string, err error) {
	this.clearRedirect()
	//log4go.Debug("HttpClient.PostMultipart(%s,w)", u)
	//自动转化为GB2312编码
	gbkUrl := this.Enc(u)

	req, _ := http.NewRequest("POST", gbkUrl, b)
	for k, v := range this.Header {
		req.Header.Add(k, v[0])
	}
	req.Header.Add("Content-Type", w.FormDataContentType())
	//log4go.Finest("PostMultipart Content-Type: %s", w.FormDataContentType())
	resp, err := this.c.Do(req)

	if err != nil {
		err = errors.New(fmt.Sprintf("HttpClient.PostMultipart ,Response error:%s", err.Error()))
		return
	}
	//log4go.Debug("Response code:%d status:%s", resp.StatusCode, resp.Status)

	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		err = errors.New(fmt.Sprintf("HttpClient.PostMultipart ,Read body error:%s", err.Error()))
		return
	}

	this.cookies = this.jar.Cookies(req.URL)

	page = this.Dec(string(body))

	//log4go.Finest("HttpClient.PostMultipart to url :%s returns :", u)
	//log4go.Finest(page)
	return

}

func (p *HttpClient) clearRedirect() {
	p.redirect = false
	p.redirectUrl = ""
}
func (p *HttpClient) CheckRedirect() (b bool, url string) {
	return p.redirect, p.redirectUrl
}
<commit_msg>改进支持gzip，get<commit_after>//An easier way to use http.Client
package com

import (
	"bytes"
	"errors"
	"fmt"
	//	"github.com/alecthomas/log4go"
	"github.com/axgle/mahonia"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/http/cookiejar"
	//	"strings"
	"compress/gzip"
	"io"
	"log"
)

type HttpClient struct {
	c       *http.Client
	cookies []*http.Cookie
	jar     *cookiejar.Jar

	Header http.Header

	//编码转换相关处理
	conv    bool //conv between utf-8 and charset
	charset string
	enc     mahonia.Encoder
	dec     mahonia.Decoder

	//链接转向相关处理
	redirect    bool   //是否转向了。每次Get之前置为false
	redirectUrl string //转向后的链接
}

func NewHttpClient() (this *HttpClient) {

	this = &HttpClient{}

	this.cookies = nil
	this.jar, _ = cookiejar.New(nil)

	this.c = &http.Client{Jar: this.jar, CheckRedirect: func(req *http.Request, via []*http.Request) error {
		this.redirect = true
		this.redirectUrl = req.URL.String()
		//return errors.New("Redirected!")
		return nil
	}}

	this.Header = make(http.Header)
	this.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
	this.Header.Add("Accept-Encoding", "gzip,deflate,sdch")
	this.Header.Add("Accept-Language", "zh-CN,zh;q=0.8")
	this.Header.Add("Connection", "keep-alive")
	this.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36")

	this.conv = false

	return this
}

//you should set it only once!
func (this *HttpClient) SetCharSet(charset string) {
	this.conv = true
	this.charset = charset
	this.enc = mahonia.NewEncoder(charset)
	this.dec = mahonia.NewDecoder(charset)
}

func (this *HttpClient) SetUa(ua string) {
	this.Header.Set("User-Agent", ua)
}

func (this *HttpClient) Enc(in string) string {
	if this.conv {
		return this.enc.ConvertString(in)
	}
	return in
}

func (this *HttpClient) Dec(in string) string {
	if this.conv {
		return this.dec.ConvertString(in)
	}
	return in
}

func (this *HttpClient) Get(url string) (page string, err error) {
	this.clearRedirect()
	//自动转化为GB2312编码
	gbkUrl := this.Enc(url)

	req, err := http.NewRequest("GET", gbkUrl, nil)
	if err != nil {
		err = errors.New(fmt.Sprintf("HttpClient.Get(%s),NewRequest error:%s", url, err.Error()))
		return
	}

	for k, v := range this.Header {
		req.Header.Add(k, v[0])
	}

	resp, err := this.c.Do(req)

	if err != nil {
		err = errors.New(fmt.Sprintf("HttpClient.Get(%s),Response error:%s", url, err.Error()))
		return
	}

	defer resp.Body.Close()

	var body string

	if resp.StatusCode == 200 {

		switch resp.Header.Get("Content-Encoding") {
		case "gzip":
			reader, e := gzip.NewReader(resp.Body)
			if e != nil {
				err = errors.New(fmt.Sprintf("HttpClient.Get(%s),Read gzip body error:%s", url, e.Error()))
				return
			}
			for {
				buf := make([]byte, 1024)
				n, err := reader.Read(buf)

				if err != nil && err != io.EOF {
					panic(err)
				}

				if n == 0 {
					break
				}
				body += string(buf)
			}
		default:
			bodyByte, e := ioutil.ReadAll(resp.Body)
			if e != nil {
				err = errors.New(fmt.Sprintf("HttpClient.Get(%s),Read body error:%s", url, e.Error()))
				return
			}
			body = string(bodyByte)
		}
	}

	log.Println(body)

	this.cookies = this.jar.Cookies(req.URL)

	page = this.Dec(string(body))
	return
}

func (this *HttpClient) Post(url, postdata string) (page string, err error) {
	this.clearRedirect()
	//自动转化为GB2312编码
	gbkUrl := this.Enc(url)
	gbkPostdata := this.Enc(postdata)

	req, _ := http.NewRequest("POST", gbkUrl, bytes.NewReader([]byte(gbkPostdata)))
	for k, v := range this.Header {
		req.Header.Add(k, v[0])
	}
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	resp, err := this.c.Do(req)

	if err != nil {
		err = errors.New(fmt.Sprintf("HttpClient.Post(%s,%s),Response error:%s", url, postdata, err.Error()))
		return
	}
	//	log4go.Debug("Response code:%d status:%s", resp.StatusCode, resp.Status)

	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		err = errors.New(fmt.Sprintf("HttpClient.Post(%s,%s),Read body error:%s", url, postdata, err.Error()))
		return
	}

	this.cookies = this.jar.Cookies(req.URL)

	page = this.Dec(string(body))

	//log4go.Finest("HttpClient.Post(%s,%s) returns:", url, postdata)
	//log4go.Finest(page)
	return
}

/*
///尚未测试
func (this *HttpClient) PostValues(surl string, postDict map[string]string) (page string, err error) {
	log4go.Debug("HttpClient.PostValues(%s,%T)", surl, postDict)
	postValues := url.Values{}
	for postKey, PostValue := range postDict {
		postValues.Set(this.Enc(postKey), this.Enc(PostValue))
	}
	postDataStr := postValues.Encode()

	return this.Post(surl, postDataStr)
}
*/
func (this *HttpClient) PostMultipart(u string, w *multipart.Writer, b *bytes.Buffer) (page string, err error) {
	this.clearRedirect()
	//log4go.Debug("HttpClient.PostMultipart(%s,w)", u)
	//自动转化为GB2312编码
	gbkUrl := this.Enc(u)

	req, _ := http.NewRequest("POST", gbkUrl, b)
	for k, v := range this.Header {
		req.Header.Add(k, v[0])
	}
	req.Header.Add("Content-Type", w.FormDataContentType())
	//log4go.Finest("PostMultipart Content-Type: %s", w.FormDataContentType())
	resp, err := this.c.Do(req)

	if err != nil {
		err = errors.New(fmt.Sprintf("HttpClient.PostMultipart ,Response error:%s", err.Error()))
		return
	}
	//log4go.Debug("Response code:%d status:%s", resp.StatusCode, resp.Status)

	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		err = errors.New(fmt.Sprintf("HttpClient.PostMultipart ,Read body error:%s", err.Error()))
		return
	}

	this.cookies = this.jar.Cookies(req.URL)

	page = this.Dec(string(body))

	//log4go.Finest("HttpClient.PostMultipart to url :%s returns :", u)
	//log4go.Finest(page)
	return

}

func (p *HttpClient) clearRedirect() {
	p.redirect = false
	p.redirectUrl = ""
}
func (p *HttpClient) CheckRedirect() (b bool, url string) {
	return p.redirect, p.redirectUrl
}
<|endoftext|>


<commit_before>package importer

import (
	"code.google.com/p/go.tools/go/types"
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"os"
)

type Importer struct {
	Imports map[string]*types.Package // All packages imported by Importer
}

func NewImporter() Importer {
	return Importer{
		Imports: make(map[string]*types.Package),
	}
}

// Import implements the Importer type from go/types.
func (imp Importer) Import(imports map[string]*types.Package, path string) (pkg *types.Package, err error) {
	// types.Importer does not seem to be designed for recursive
	// parsing like we're doing here. Specifically, each nested import
	// will maintain its own imports map. This will lead to duplicate
	// imports and in turn packages, which will lead to funny errors
	// such as "cannot pass argument ip (variable of type net.IP) to
	// variable of type net.IP"
	//
	// To work around this, we keep a global imports map, allImports,
	// to which we add all nested imports, and which we use as the
	// cache, instead of imports.
	//
	// Since all nested imports will also use this importer, there
	// should be no way to end up with duplicate imports.

	// We first try to use GcImport directly. This has the downside of
	// using possibly out-of-date packages, but it has the upside of
	// not having to parse most of the Go standard library.

	buildPkg, buildErr := build.Import(path, ".", 0)
	// If we found no build dir, assume we're dealing with installed
	// but no source. If we found a build dir, only use GcImport if
	// it's in GOROOT. This way we always use up-to-date code for
	// normal packages but avoid parsing the standard library.
	if (buildErr == nil && buildPkg.Goroot) || buildErr != nil {
		pkg, err = types.GcImport(imp.Imports, path)
		if err == nil {
			// We don't use imports, but per API we have to add the package.
			imports[pkg.Path()] = pkg
			imp.Imports[pkg.Path()] = pkg
			return pkg, nil
		}
	}

	// See if we already imported this package
	if pkg = imp.Imports[path]; pkg != nil && pkg.Complete() {
		return pkg, nil
	}

	// allImports failed, try to use go/build
	if buildErr != nil {
		return nil, fmt.Errorf("build.Import failed: %s", buildErr)
	}

	// TODO check if the .a file is up to date and use it instead
	fileSet := token.NewFileSet()

	isGoFile := func(d os.FileInfo) bool {
		allFiles := make([]string, 0, len(buildPkg.GoFiles)+len(buildPkg.CgoFiles))
		allFiles = append(allFiles, buildPkg.GoFiles...)
		allFiles = append(allFiles, buildPkg.CgoFiles...)

		for _, file := range allFiles {
			if file == d.Name() {
				return true
			}
		}
		return false
	}
	pkgs, err := parser.ParseDir(fileSet, buildPkg.Dir, isGoFile, 0)
	if err != nil {
		return nil, err
	}

	delete(pkgs, "documentation")
	var astPkg *ast.Package
	var name string
	for name, astPkg = range pkgs {
		// Use the first non-main package, or the only package we
		// found.
		//
		// NOTE(dh) I can't think of a reason why there should be
		// multiple packages in a single directory, but ParseDir
		// accommodates for that possibility.
		if len(pkgs) == 1 || name != "main" {
			break
		}
	}

	if astPkg == nil {
		return nil, fmt.Errorf("can't find import: %s", name)
	}

	var ff []*ast.File
	for _, f := range astPkg.Files {
		ff = append(ff, f)
	}

	context := types.Config{
		Import: imp.Import,
	}

	pkg, err = context.Check(name, fileSet, ff, nil)
	if err != nil {
		return pkg, err
	}
	pkg.MarkComplete()

	imports[path] = pkg
	imp.Imports[path] = pkg
	return pkg, nil
}
<commit_msg>remove now superfluous MarkComplete call<commit_after>package importer

import (
	"code.google.com/p/go.tools/go/types"
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"os"
)

type Importer struct {
	Imports map[string]*types.Package // All packages imported by Importer
}

func NewImporter() Importer {
	return Importer{
		Imports: make(map[string]*types.Package),
	}
}

// Import implements the Importer type from go/types.
func (imp Importer) Import(imports map[string]*types.Package, path string) (pkg *types.Package, err error) {
	// types.Importer does not seem to be designed for recursive
	// parsing like we're doing here. Specifically, each nested import
	// will maintain its own imports map. This will lead to duplicate
	// imports and in turn packages, which will lead to funny errors
	// such as "cannot pass argument ip (variable of type net.IP) to
	// variable of type net.IP"
	//
	// To work around this, we keep a global imports map, allImports,
	// to which we add all nested imports, and which we use as the
	// cache, instead of imports.
	//
	// Since all nested imports will also use this importer, there
	// should be no way to end up with duplicate imports.

	// We first try to use GcImport directly. This has the downside of
	// using possibly out-of-date packages, but it has the upside of
	// not having to parse most of the Go standard library.

	buildPkg, buildErr := build.Import(path, ".", 0)
	// If we found no build dir, assume we're dealing with installed
	// but no source. If we found a build dir, only use GcImport if
	// it's in GOROOT. This way we always use up-to-date code for
	// normal packages but avoid parsing the standard library.
	if (buildErr == nil && buildPkg.Goroot) || buildErr != nil {
		pkg, err = types.GcImport(imp.Imports, path)
		if err == nil {
			// We don't use imports, but per API we have to add the package.
			imports[pkg.Path()] = pkg
			imp.Imports[pkg.Path()] = pkg
			return pkg, nil
		}
	}

	// See if we already imported this package
	if pkg = imp.Imports[path]; pkg != nil && pkg.Complete() {
		return pkg, nil
	}

	// allImports failed, try to use go/build
	if buildErr != nil {
		return nil, fmt.Errorf("build.Import failed: %s", buildErr)
	}

	// TODO check if the .a file is up to date and use it instead
	fileSet := token.NewFileSet()

	isGoFile := func(d os.FileInfo) bool {
		allFiles := make([]string, 0, len(buildPkg.GoFiles)+len(buildPkg.CgoFiles))
		allFiles = append(allFiles, buildPkg.GoFiles...)
		allFiles = append(allFiles, buildPkg.CgoFiles...)

		for _, file := range allFiles {
			if file == d.Name() {
				return true
			}
		}
		return false
	}
	pkgs, err := parser.ParseDir(fileSet, buildPkg.Dir, isGoFile, 0)
	if err != nil {
		return nil, err
	}

	delete(pkgs, "documentation")
	var astPkg *ast.Package
	var name string
	for name, astPkg = range pkgs {
		// Use the first non-main package, or the only package we
		// found.
		//
		// NOTE(dh) I can't think of a reason why there should be
		// multiple packages in a single directory, but ParseDir
		// accommodates for that possibility.
		if len(pkgs) == 1 || name != "main" {
			break
		}
	}

	if astPkg == nil {
		return nil, fmt.Errorf("can't find import: %s", name)
	}

	var ff []*ast.File
	for _, f := range astPkg.Files {
		ff = append(ff, f)
	}

	context := types.Config{
		Import: imp.Import,
	}

	pkg, err = context.Check(name, fileSet, ff, nil)
	if err != nil {
		return pkg, err
	}

	imports[path] = pkg
	imp.Imports[path] = pkg
	return pkg, nil
}
<|endoftext|>