<commit_before>package web

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"regexp"
	"strconv"
	"strings"

	"github.com/eyecuelab/kit/maputil"
	"github.com/google/jsonapi"
	"github.com/labstack/echo"
)

var reNotJsonApi = regexp.MustCompile("not a jsonapi|EOF")

func notJsonApi(err error) bool {
	return reNotJsonApi.MatchString(err.Error())
}

type (
	ApiContext interface {
		echo.Context

		Payload() *jsonapi.OnePayload
		Attrs(permitted ...string) map[string]interface{}
		AttrKeys() []string
		RequireAttrs(...string) error
		BindAndValidate(interface{}) error
		BindIdParam(*int, ...string) error
		JsonApi(interface{}, int) error
		JsonApiOK(interface{}) error
		ApiError(string, ...int) *echo.HTTPError
		QueryParamTrue(string) (bool, bool)

		RequiredQueryParams(...string) (map[string]string, error)
		OptionalQueryParams(...string) map[string]string
		QParams(...string) (map[string]string, error)
	}

	apiContext struct {
		echo.Context

		payload *jsonapi.OnePayload
	}
)

func (c *apiContext) Payload() *jsonapi.OnePayload {
	return c.payload
}

func (c *apiContext) Attrs(permitted ...string) map[string]interface{} {
	//TODO: remove this once all refactoring is complete
	if len(permitted) == 0 {
		return c.payload.Data.Attributes
	}

	permittedAttrs := make(map[string]interface{})
	for _, p := range permitted {
		permittedAttrs[p] = c.payload.Data.Attributes[p]
	}
	return permittedAttrs
}

func (c *apiContext) AttrKeys() []string {
	return maputil.Keys(c.Attrs())
}

func (c *apiContext) RequireAttrs(required ...string) error {
	missing := make([]string, 0, len(required))

	for _, key := range required {
		if c.payload.Data.Attributes[key] == nil {
			missing = append(missing, key)
			continue
		}
	}

	if len(missing) > 0 {
		return fmt.Errorf("missing required attributes: %v", missing)
	}

	return nil
}

//Before binding we make a copy of the req body and restore it after binding.
//This allows the body to be used again later
func (c *apiContext) Bind(i interface{}) error {
	body, err := c.readRestoreBody()
	if err != nil {
		return err
	}

	ctype := c.Request().Header.Get(echo.HeaderContentType)
	if isJSONAPI(ctype) {
		err = jsonAPIBind(c, i)
	} else {
		err = c.defaultBind(i)
	}

	c.restoreBody(body)

	return err
}

func (c *apiContext) readRestoreBody() ([]byte, error) {
	b, err := ioutil.ReadAll(c.Request().Body)
	c.restoreBody(b)
	return b, err
}

func (c *apiContext) restoreBody(b []byte) {
	c.Request().Body = ioutil.NopCloser(bytes.NewBuffer(b))
}

func (c *apiContext) defaultBind(i interface{}) error {
	db := new(echo.DefaultBinder)
	return db.Bind(i, c)
}

func isJSONAPI(s string) bool {
	const MIMEJsonAPI = "application/vnd.api+json"
	return strings.HasPrefix(s, MIMEJsonAPI)
}

func (c *apiContext) BindAndValidate(i interface{}) error {
	if err := c.Bind(i); err != nil {
		return err
	}
	if err := c.Validate(i); err != nil {
		return err
	}
	return nil
}

func (c *apiContext) JsonApi(i interface{}, status int) error {
	c.Response().Header().Set(echo.HeaderContentType, jsonapi.MediaType)
	c.Response().WriteHeader(status)

	return jsonapi.MarshalPayload(c.Response().Writer, i)
}

func (c *apiContext) JsonApiOK(i interface{}) error {
	return c.JsonApi(i, http.StatusOK)
}

func (c *apiContext) BindIdParam(idValue *int, named ...string) (err error) {
	paramName := "id"
	if len(named) > 0 {
		paramName = named[0]
	}
	*idValue, err = strconv.Atoi(c.Param(paramName))
	return err
}

func (c *apiContext) QueryParamTrue(name string) (val, ok bool) {
	switch strings.ToLower(c.QueryParam(name)) {
	case "true", "1":
		return true, true
	case "false", "0":
		return false, true
	default:
		return false, false
	}
}

func jsonAPIBind(c *apiContext, i interface{}) error {
	buf := new(bytes.Buffer)
	tee := io.TeeReader(c.Request().Body, buf)

	if err := jsonapi.UnmarshalPayload(tee, i); err != nil {
		if notJsonApi(err) {
			return c.ApiError("Request Body is not valid JsonAPI")
		}
		return err
	}

	c.payload = new(jsonapi.OnePayload)
	return json.Unmarshal(buf.Bytes(), c.payload)
}

func (c *apiContext) ApiError(msg string, codes ...int) *echo.HTTPError {
	if len(codes) > 0 {
		return echo.NewHTTPError(codes[0], msg)
	}
	// TODO: return jsonapi error instead
	return echo.NewHTTPError(http.StatusBadRequest, msg)
}

func (c *apiContext) RequiredQueryParams(required ...string) (map[string]string, error) {
	missing := make([]string, 0, len(required))
	params := make(map[string]string)

	for _, key := range required {
		val := c.QueryParam(key)
		if val == "" {
			missing = append(missing, key)
			continue
		}
		params[key] = val
	}

	if len(missing) > 0 {
		return nil, fmt.Errorf("missing required params: %v", missing)
	}

	return params, nil
}

func (c *apiContext) QParams(required ...string) (map[string]string, error) {
	return QParams(c, required...)
}

func QParams(c echo.Context, required ...string) (map[string]string, error) {
	missing := make([]string, 0, len(required))
	params := make(map[string]string)

	for k := range c.QueryParams() {
		params[k] = c.QueryParam(k)
	}

	for _, k := range required {
		if _, ok := params[k]; !ok {
			missing = append(missing, k)
		}
	}

	if len(missing) > 0 {
		return nil, fmt.Errorf("missing required params: %v", missing)
	}

	return params, nil
}

func (c *apiContext) OptionalQueryParams(optional ...string) map[string]string {
	params := make(map[string]string)
	for _, key := range optional {
		val := c.QueryParam(key)
		params[key] = val
	}
	return params
}

func ApiContextMiddleWare() func(echo.HandlerFunc) echo.HandlerFunc {
	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			return next(&apiContext{c, nil})
		}
	}
}

func restrictedValue(value string, allowed []string, errorText string) (string, error) {
	if contains(allowed, value) {
		return value, nil
	}
	return "", fmt.Errorf(errorText, value)
}

func contains(set []string, s string) bool {
	for _, v := range set {
		if s == v {
			return true
		}
	}
	return false
}
<commit_msg>Fix context Attrs returning undefined attributes<commit_after>package web

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"regexp"
	"strconv"
	"strings"

	"github.com/eyecuelab/kit/maputil"
	"github.com/google/jsonapi"
	"github.com/labstack/echo"
)

var reNotJsonApi = regexp.MustCompile("not a jsonapi|EOF")

func notJsonApi(err error) bool {
	return reNotJsonApi.MatchString(err.Error())
}

type (
	ApiContext interface {
		echo.Context

		Payload() *jsonapi.OnePayload
		Attrs(permitted ...string) map[string]interface{}
		AttrKeys() []string
		RequireAttrs(...string) error
		BindAndValidate(interface{}) error
		BindIdParam(*int, ...string) error
		JsonApi(interface{}, int) error
		JsonApiOK(interface{}) error
		ApiError(string, ...int) *echo.HTTPError
		QueryParamTrue(string) (bool, bool)

		RequiredQueryParams(...string) (map[string]string, error)
		OptionalQueryParams(...string) map[string]string
		QParams(...string) (map[string]string, error)
	}

	apiContext struct {
		echo.Context

		payload *jsonapi.OnePayload
	}
)

func (c *apiContext) Payload() *jsonapi.OnePayload {
	return c.payload
}

func (c *apiContext) Attrs(permitted ...string) map[string]interface{} {
	//TODO: remove this once all refactoring is complete
	if len(permitted) == 0 {
		return c.payload.Data.Attributes
	}

	permittedAttrs := make(map[string]interface{})
	var val interface{}
	for _, p := range permitted {
		val = c.payload.Data.Attributes[p]
		if val != nil {
			permittedAttrs[p] = val
		}
	}
	return permittedAttrs
}

func (c *apiContext) AttrKeys() []string {
	return maputil.Keys(c.Attrs())
}

func (c *apiContext) RequireAttrs(required ...string) error {
	missing := make([]string, 0, len(required))

	for _, key := range required {
		if c.payload.Data.Attributes[key] == nil {
			missing = append(missing, key)
			continue
		}
	}

	if len(missing) > 0 {
		return fmt.Errorf("missing required attributes: %v", missing)
	}

	return nil
}

//Before binding we make a copy of the req body and restore it after binding.
//This allows the body to be used again later
func (c *apiContext) Bind(i interface{}) error {
	body, err := c.readRestoreBody()
	if err != nil {
		return err
	}

	ctype := c.Request().Header.Get(echo.HeaderContentType)
	if isJSONAPI(ctype) {
		err = jsonAPIBind(c, i)
	} else {
		err = c.defaultBind(i)
	}

	c.restoreBody(body)

	return err
}

func (c *apiContext) readRestoreBody() ([]byte, error) {
	b, err := ioutil.ReadAll(c.Request().Body)
	c.restoreBody(b)
	return b, err
}

func (c *apiContext) restoreBody(b []byte) {
	c.Request().Body = ioutil.NopCloser(bytes.NewBuffer(b))
}

func (c *apiContext) defaultBind(i interface{}) error {
	db := new(echo.DefaultBinder)
	return db.Bind(i, c)
}

func isJSONAPI(s string) bool {
	const MIMEJsonAPI = "application/vnd.api+json"
	return strings.HasPrefix(s, MIMEJsonAPI)
}

func (c *apiContext) BindAndValidate(i interface{}) error {
	if err := c.Bind(i); err != nil {
		return err
	}
	if err := c.Validate(i); err != nil {
		return err
	}
	return nil
}

func (c *apiContext) JsonApi(i interface{}, status int) error {
	c.Response().Header().Set(echo.HeaderContentType, jsonapi.MediaType)
	c.Response().WriteHeader(status)

	return jsonapi.MarshalPayload(c.Response().Writer, i)
}

func (c *apiContext) JsonApiOK(i interface{}) error {
	return c.JsonApi(i, http.StatusOK)
}

func (c *apiContext) BindIdParam(idValue *int, named ...string) (err error) {
	paramName := "id"
	if len(named) > 0 {
		paramName = named[0]
	}
	*idValue, err = strconv.Atoi(c.Param(paramName))
	return err
}

func (c *apiContext) QueryParamTrue(name string) (val, ok bool) {
	switch strings.ToLower(c.QueryParam(name)) {
	case "true", "1":
		return true, true
	case "false", "0":
		return false, true
	default:
		return false, false
	}
}

func jsonAPIBind(c *apiContext, i interface{}) error {
	buf := new(bytes.Buffer)
	tee := io.TeeReader(c.Request().Body, buf)

	if err := jsonapi.UnmarshalPayload(tee, i); err != nil {
		if notJsonApi(err) {
			return c.ApiError("Request Body is not valid JsonAPI")
		}
		return err
	}

	c.payload = new(jsonapi.OnePayload)
	return json.Unmarshal(buf.Bytes(), c.payload)
}

func (c *apiContext) ApiError(msg string, codes ...int) *echo.HTTPError {
	if len(codes) > 0 {
		return echo.NewHTTPError(codes[0], msg)
	}
	// TODO: return jsonapi error instead
	return echo.NewHTTPError(http.StatusBadRequest, msg)
}

func (c *apiContext) RequiredQueryParams(required ...string) (map[string]string, error) {
	missing := make([]string, 0, len(required))
	params := make(map[string]string)

	for _, key := range required {
		val := c.QueryParam(key)
		if val == "" {
			missing = append(missing, key)
			continue
		}
		params[key] = val
	}

	if len(missing) > 0 {
		return nil, fmt.Errorf("missing required params: %v", missing)
	}

	return params, nil
}

func (c *apiContext) QParams(required ...string) (map[string]string, error) {
	return QParams(c, required...)
}

func QParams(c echo.Context, required ...string) (map[string]string, error) {
	missing := make([]string, 0, len(required))
	params := make(map[string]string)

	for k := range c.QueryParams() {
		params[k] = c.QueryParam(k)
	}

	for _, k := range required {
		if _, ok := params[k]; !ok {
			missing = append(missing, k)
		}
	}

	if len(missing) > 0 {
		return nil, fmt.Errorf("missing required params: %v", missing)
	}

	return params, nil
}

func (c *apiContext) OptionalQueryParams(optional ...string) map[string]string {
	params := make(map[string]string)
	for _, key := range optional {
		val := c.QueryParam(key)
		params[key] = val
	}
	return params
}

func ApiContextMiddleWare() func(echo.HandlerFunc) echo.HandlerFunc {
	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			return next(&apiContext{c, nil})
		}
	}
}

func restrictedValue(value string, allowed []string, errorText string) (string, error) {
	if contains(allowed, value) {
		return value, nil
	}
	return "", fmt.Errorf(errorText, value)
}

func contains(set []string, s string) bool {
	for _, v := range set {
		if s == v {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package whproxy

import (
	"net"
	"net/http"
	"time"

	"github.com/gorilla/websocket"
	"github.com/taskcluster/webhooktunnel/util"
	"github.com/taskcluster/webhooktunnel/wsmux"
)

func (p *proxy) websocketProxy(w http.ResponseWriter, r *http.Request, session *wsmux.Session, tunnelID string) error {
	// at this point, we are sure that r is a http websocket upgrade request
	// connClosure returns the wsmux stream to Dial
	p.logf(tunnelID, r.RemoteAddr, "creating WS bridge: path=%s", r.URL.Path)
	stream, id, err := session.Open()
	p.logf(tunnelID, r.RemoteAddr, "opened new stream for ws: path=%s, streamID=%d", r.URL.Path, id)

	if err != nil {
		p.logerrorf(tunnelID, r.RemoteAddr, "could not create stream: path=%s", r.URL.Path)
		return err
	}
	connClosure := func(network, addr string) (net.Conn, error) {
		return stream, nil
	}
	dialer := &websocket.Dialer{NetDial: connClosure}

	// create new header
	// copy request headers to new header. Avoid websocket headers
	// as these will be added by Dial
	reqHeader := make(http.Header)
	for k, v := range r.Header {
		if k == "Upgrade" ||
			k == "Connection" ||
			k == "Sec-Websocket-Key" ||
			k == "Sec-Websocket-Version" ||
			k == "Sec-Websocket-Extensions" {
			continue
		}
		reqHeader[k] = v
	}

	uri := "ws://" + r.URL.Host + r.URL.RequestURI()
	if !p.domainHosted {
		uri = "ws://" + r.URL.Host + util.ReplaceID(r.URL.RequestURI())
	}
	p.logf(tunnelID, r.RemoteAddr, "dialing ws on stream: %d, url: %s", id, uri)
	tunnelConn, _, err := dialer.Dial(uri, reqHeader)
	if err != nil {
		p.logerrorf(tunnelID, r.RemoteAddr, "could not dial tunnel: path=%s, error: %v", r.URL.Path, err)
		return err
	}
	viewerConn, err := p.upgrader.Upgrade(w, r, nil)
	if err != nil {
		p.logerrorf(tunnelID, r.RemoteAddr, "could not upgrade client connection: path=%s", r.URL.Path)
		// close tunnel connection
		_ = tunnelConn.Close()
		return err
	}

	defer func() {
		session.RemoveStream(id)
		p.logf(tunnelID, r.RemoteAddr, "WS: closed underlying wsmux stream: path= %s, streamID=%d", r.URL.Path, id)
	}()
	// bridge both websocket connections
	return p.bridgeConn(tunnelConn, viewerConn)
}

func (p *proxy) bridgeConn(conn1 *websocket.Conn, conn2 *websocket.Conn) error {
	// set ping and pong handlers
	conn1.SetPingHandler(forwardControl(websocket.PingMessage, conn2))
	conn2.SetPingHandler(forwardControl(websocket.PingMessage, conn1))

	conn1.SetPongHandler(forwardControl(websocket.PongMessage, conn2))
	conn2.SetPongHandler(forwardControl(websocket.PongMessage, conn1))

	// set close handlers
	conn1.SetCloseHandler(func(code int, text string) error {
		return conn2.Close()
	})
	conn2.SetCloseHandler(func(code int, text string) error {
		return conn1.Close()
	})

	// ensure connections are closed after bridge exits
	defer func() {
		_ = conn1.Close()
		_ = conn2.Close()
	}()

	kill := make(chan bool, 1)
	go func() {
		_ = copyWsData(conn1, conn2, kill)
		select {
		case <-kill:
		default:
			close(kill)
		}
	}()
	go func() {
		_ = copyWsData(conn2, conn1, kill)
		select {
		case <-kill:
		default:
			close(kill)
		}
	}()

	<-kill
	return nil
}

func copyWsData(dest *websocket.Conn, src *websocket.Conn, kill chan bool) error {
	for {
		mtype, buf, err := src.ReadMessage()
		if err != nil {
			// Abnormal closure occurs if the websocket is over a wsmux stream
			if websocket.IsUnexpectedCloseError(err, websocket.CloseNormalClosure,
				websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
				return err
			}
			return nil
		}
		err = dest.WriteMessage(mtype, buf)
		if err != nil {
			// Abnormal closure occurs if the websocket is over a wsmux stream
			if websocket.IsUnexpectedCloseError(err, websocket.CloseNormalClosure,
				websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
				return err
			}
			return nil
		}
		select {
		case <-kill:
			return nil
		default:
		}
	}
}

func forwardControl(messageType int, dest *websocket.Conn) func(string) error {
	return func(appData string) error {
		return dest.WriteControl(messageType, []byte(appData), time.Now().Add(1*time.Second))
	}
}
<commit_msg>RequestURI instead of Path<commit_after>package whproxy

import (
	"net"
	"net/http"
	"time"

	"github.com/gorilla/websocket"
	"github.com/taskcluster/webhooktunnel/util"
	"github.com/taskcluster/webhooktunnel/wsmux"
)

func (p *proxy) websocketProxy(w http.ResponseWriter, r *http.Request, session *wsmux.Session, tunnelID string) error {
	// at this point, we are sure that r is a http websocket upgrade request
	// connClosure returns the wsmux stream to Dial
	p.logf(tunnelID, r.RemoteAddr, "creating WS bridge: path=%s", r.URL.RequestURI())
	stream, id, err := session.Open()
	p.logf(tunnelID, r.RemoteAddr, "opened new stream for ws: path=%s, streamID=%d", r.URL.RequestURI(), id)

	if err != nil {
		p.logerrorf(tunnelID, r.RemoteAddr, "could not create stream: path=%s", r.URL.RequestURI())
		return err
	}
	connClosure := func(network, addr string) (net.Conn, error) {
		return stream, nil
	}
	dialer := &websocket.Dialer{NetDial: connClosure}

	// create new header
	// copy request headers to new header. Avoid websocket headers
	// as these will be added by Dial
	reqHeader := make(http.Header)
	for k, v := range r.Header {
		if k == "Upgrade" ||
			k == "Connection" ||
			k == "Sec-Websocket-Key" ||
			k == "Sec-Websocket-Version" ||
			k == "Sec-Websocket-Extensions" {
			continue
		}
		reqHeader[k] = v
	}

	uri := "ws://" + r.URL.Host + r.URL.RequestURI()
	if !p.domainHosted {
		uri = "ws://" + r.URL.Host + util.ReplaceID(r.URL.RequestURI())
	}
	p.logf(tunnelID, r.RemoteAddr, "dialing ws on stream: %d, url: %s", id, uri)
	tunnelConn, _, err := dialer.Dial(uri, reqHeader)
	if err != nil {
		p.logerrorf(tunnelID, r.RemoteAddr, "could not dial tunnel: path=%s, error: %v", r.URL.RequestURI(), err)
		return err
	}
	viewerConn, err := p.upgrader.Upgrade(w, r, nil)
	if err != nil {
		p.logerrorf(tunnelID, r.RemoteAddr, "could not upgrade client connection: path=%s, error: %v", r.URL.RequestURI(), err)
		// close tunnel connection
		_ = tunnelConn.Close()
		return err
	}
	p.logf(tunnelID, r.RemoteAddr, "initiating connection bridge: %d", id)

	defer func() {
		session.RemoveStream(id)
		p.logf(tunnelID, r.RemoteAddr, "WS: closed underlying wsmux stream: path= %s, streamID=%d", r.URL.RequestURI(), id)
	}()
	// bridge both websocket connections
	return p.bridgeConn(tunnelConn, viewerConn)
}

func (p *proxy) bridgeConn(conn1 *websocket.Conn, conn2 *websocket.Conn) error {
	// set ping and pong handlers
	conn1.SetPingHandler(forwardControl(websocket.PingMessage, conn2))
	conn2.SetPingHandler(forwardControl(websocket.PingMessage, conn1))

	conn1.SetPongHandler(forwardControl(websocket.PongMessage, conn2))
	conn2.SetPongHandler(forwardControl(websocket.PongMessage, conn1))

	// set close handlers
	conn1.SetCloseHandler(func(code int, text string) error {
		return conn2.Close()
	})
	conn2.SetCloseHandler(func(code int, text string) error {
		return conn1.Close()
	})

	// ensure connections are closed after bridge exits
	defer func() {
		_ = conn1.Close()
		_ = conn2.Close()
	}()

	kill := make(chan bool, 1)
	go func() {
		_ = copyWsData(conn1, conn2, kill)
		select {
		case <-kill:
		default:
			close(kill)
		}
	}()
	go func() {
		_ = copyWsData(conn2, conn1, kill)
		select {
		case <-kill:
		default:
			close(kill)
		}
	}()

	<-kill
	return nil
}

func copyWsData(dest *websocket.Conn, src *websocket.Conn, kill chan bool) error {
	for {
		mtype, buf, err := src.ReadMessage()
		if err != nil {
			// Abnormal closure occurs if the websocket is over a wsmux stream
			if websocket.IsUnexpectedCloseError(err, websocket.CloseNormalClosure,
				websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
				return err
			}
			return nil
		}
		err = dest.WriteMessage(mtype, buf)
		if err != nil {
			// Abnormal closure occurs if the websocket is over a wsmux stream
			if websocket.IsUnexpectedCloseError(err, websocket.CloseNormalClosure,
				websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
				return err
			}
			return nil
		}
		select {
		case <-kill:
			return nil
		default:
		}
	}
}

func forwardControl(messageType int, dest *websocket.Conn) func(string) error {
	return func(appData string) error {
		return dest.WriteControl(messageType, []byte(appData), time.Now().Add(1*time.Second))
	}
}
<|endoftext|>


<commit_before>// 8 february 2014
package main

import (
//	"syscall"
	"unsafe"
)

// Window styles.
const (
	_WS_BORDER = 0x00800000
	_WS_CAPTION = 0x00C00000
	_WS_CHILD = 0x40000000
	_WS_CHILDWINDOW = 0x40000000
	_WS_CLIPCHILDREN = 0x02000000
	_WS_CLIPSIBLINGS = 0x04000000
	_WS_DISABLED = 0x08000000
	_WS_DLGFRAME = 0x00400000
	_WS_GROUP = 0x00020000
	_WS_HSCROLL = 0x00100000
	_WS_ICONIC = 0x20000000
	_WS_MAXIMIZE = 0x01000000
	_WS_MAXIMIZEBOX = 0x00010000
	_WS_MINIMIZE = 0x20000000
	_WS_MINIMIZEBOX = 0x00020000
	_WS_OVERLAPPED = 0x00000000
	_WS_OVERLAPPEDWINDOW = (_WS_OVERLAPPED | _WS_CAPTION | _WS_SYSMENU | _WS_THICKFRAME | _WS_MINIMIZEBOX | _WS_MAXIMIZEBOX)
	_WS_POPUP = 0x80000000
	_WS_POPUPWINDOW = (_WS_POPUP | _WS_BORDER | _WS_SYSMENU)
	_WS_SIZEBOX = 0x00040000
	_WS_SYSMENU = 0x00080000
	_WS_TABSTOP = 0x00010000
	_WS_THICKFRAME = 0x00040000
	_WS_TILED = 0x00000000
	_WS_TILEDWINDOW = (_WS_OVERLAPPED | _WS_CAPTION | _WS_SYSMENU | _WS_THICKFRAME | _WS_MINIMIZEBOX | _WS_MAXIMIZEBOX)
	_WS_VISIBLE = 0x10000000
	_WS_VSCROLL = 0x00200000
)

// Extended window styles.
const (
	_WS_EX_ACCEPTFILES = 0x00000010
	_WS_EX_APPWINDOW = 0x00040000
	_WS_EX_CLIENTEDGE = 0x00000200
//	_WS_EX_COMPOSITED = 0x02000000	// [Windows 2000:This style is not supported.]
	_WS_EX_CONTEXTHELP = 0x00000400
	_WS_EX_CONTROLPARENT = 0x00010000
	_WS_EX_DLGMODALFRAME = 0x00000001
	_WS_EX_LAYERED = 0x00080000
	_WS_EX_LAYOUTRTL = 0x00400000
	_WS_EX_LEFT = 0x00000000
	_WS_EX_LEFTSCROLLBAR = 0x00004000
	_WS_EX_LTRREADING = 0x00000000
	_WS_EX_MDICHILD = 0x00000040
	_WS_EX_NOACTIVATE = 0x08000000
	_WS_EX_NOINHERITLAYOUT = 0x00100000
	_WS_EX_NOPARENTNOTIFY = 0x00000004
	_WS_EX_OVERLAPPEDWINDOW = (_WS_EX_WINDOWEDGE | _WS_EX_CLIENTEDGE)
	_WS_EX_PALETTEWINDOW = (_WS_EX_WINDOWEDGE | _WS_EX_TOOLWINDOW | _WS_EX_TOPMOST)
	_WS_EX_RIGHT = 0x00001000
	_WS_EX_RIGHTSCROLLBAR = 0x00000000
	_WS_EX_RTLREADING = 0x00002000
	_WS_EX_STATICEDGE = 0x00020000
	_WS_EX_TOOLWINDOW = 0x00000080
	_WS_EX_TOPMOST = 0x00000008
	_WS_EX_TRANSPARENT = 0x00000020
	_WS_EX_WINDOWEDGE = 0x00000100
)

// bizarrely, this value is given on the page for CreateMDIWindow, but not CreateWindow or CreateWindowEx
// I do it this way because Go won't let me shove the exact value into an int
var (
	__CW_USEDEFAULT uint = 0x80000000
	_CW_USEDEFAULT = int(__CW_USEDEFAULT)
)

// GetSysColor values. These can be cast to HBRUSH (after adding 1) for WNDCLASS as well.
const (
	_COLOR_3DDKSHADOW = 21
	_COLOR_3DFACE = 15
	_COLOR_3DHIGHLIGHT = 20
	_COLOR_3DHILIGHT = 20
	_COLOR_3DLIGHT = 22
	_COLOR_3DSHADOW = 16
	_COLOR_ACTIVEBORDER = 10
	_COLOR_ACTIVECAPTION = 2
	_COLOR_APPWORKSPACE = 12
	_COLOR_BACKGROUND = 1
	_COLOR_BTNFACE = 15
	_COLOR_BTNHIGHLIGHT = 20
	_COLOR_BTNHILIGHT = 20
	_COLOR_BTNSHADOW = 16
	_COLOR_BTNTEXT = 18
	_COLOR_CAPTIONTEXT = 9
	_COLOR_DESKTOP = 1
	_COLOR_GRADIENTACTIVECAPTION = 27
	_COLOR_GRADIENTINACTIVECAPTION = 28
	_COLOR_GRAYTEXT = 17
	_COLOR_HIGHLIGHT = 13
	_COLOR_HIGHLIGHTTEXT = 14
	_COLOR_HOTLIGHT = 26
	_COLOR_INACTIVEBORDER = 11
	_COLOR_INACTIVECAPTION = 3
	_COLOR_INACTIVECAPTIONTEXT = 19
	_COLOR_INFOBK = 24
	_COLOR_INFOTEXT = 23
	_COLOR_MENU = 4
//	COLOR_MENUHILIGHT = 29	// [Windows 2000:This value is not supported.]
//	COLOR_MENUBAR = 30		// [Windows 2000:This value is not supported.]
	_COLOR_MENUTEXT = 7
	_COLOR_SCROLLBAR = 0
	_COLOR_WINDOW = 5
	_COLOR_WINDOWFRAME = 6
	_COLOR_WINDOWTEXT = 8
)

// SetWindowPos hWndInsertAfter values.
const (
	_HWND_BOTTOM = _HWND(1)
	_HWND_TOP = _HWND(0)
)

// SetWindowPos hWndInsertAfter values that Go won't allow as constants.
var (
	__HWND_NOTOPMOST = -2
	_HWND_NOTOPMOST = _HWND(__HWND_NOTOPMOST)
	__HWND_TOPMOST = -1
	_HWND_TOPMOST = _HWND(__HWND_TOPMOST)
)

// SetWindowPos uFlags values.
const (
	_SWP_DRAWFRAME = 0x0020
	_SWP_FRAMECHANGED = 0x0020
	_SWP_HIDEWINDOW = 0x0080
	_SWP_NOACTIVATE = 0x0010
	_SWP_NOCOPYBITS = 0x0100
	_SWP_NOMOVE = 0x0002
	_SWP_NOOWNERZORDER = 0x0200
	_SWP_NOREDRAW = 0x0008
	_SWP_NOREPOSITION = 0x0200
	_SWP_NOSENDCHANGING = 0x0400
	_SWP_NOSIZE = 0x0001
	_SWP_NOZORDER = 0x0004
	_SWP_SHOWWINDOW = 0x0040
	_SWP_ASYNCWINDOWPOS = 0x4000
	_SWP_DEFERERASE = 0x2000
)

// ShowWindow settings.
const (
	_SW_FORCEMINIMIZE = 11
	_SW_HIDE = 0
	_SW_MAXIMIZE = 3
	_SW_MINIMIZE = 6
	_SW_RESTORE = 9
	_SW_SHOW = 5
	_SW_SHOWDEFAULT = 10
	_SW_SHOWMAXIMIZED = 3
	_SW_SHOWMINIMIZED = 2
	_SW_SHOWMINNOACTIVE = 7
	_SW_SHOWNA = 8
	_SW_SHOWNOACTIVATE = 4
	_SW_SHOWNORMAL = 1
)

var (
	_createWindowEx = user32.NewProc("CreateWindowExW")
	_destroyWindow = user32.NewProc("DestroyWindow")
	_getClientRect = user32.NewProc("GetClientRect")
	_enumChildWindows = user32.NewProc("EnumChildWindows")
	_moveWindow = user32.NewProc("MoveWindow")
	_setWindowPos = user32.NewProc("SetWindowPos")
	_setWindowText = user32.NewProc("SetWindowTextW")
	_showWindow = user32.NewProc("ShowWindow")
)

/*
func DestroyWindow(hWnd HWND) (err error) {
	r1, _, err := destroyWindow.Call(uintptr(hWnd))
	if r1 == 0 {		// failure
		return err
	}
	return nil
}

type WNDENUMPROC func(hwnd HWND, lParam LPARAM) (cont bool)
type _WNDENUMPROC func(hwnd HWND, lParam LPARAM) int

func enumChildProc(p WNDENUMPROC) _WNDENUMPROC {
	return func(hwnd HWND, lParam LPARAM) int {
		if p(hwnd, lParam) {
			return TRUE
		}
		return FALSE
	}
}

// TODO figure out how to handle errors
func EnumChildWindows(hWndParent HWND, lpEnumFunc WNDENUMPROC, lParam LPARAM) (err error) {
	enumChildWindows.Call(
		uintptr(hWndParent),
		syscall.NewCallback(enumChildProc(lpEnumFunc)),
		uintptr(lParam))
	return nil
}
*/

// WM_SETICON and WM_GETICON values.
const (
	_ICON_BIG = 1
	_ICON_SMALL = 0
	_ICON_SMALL2 = 2		// WM_GETICON only?
)

// Window messages.
const (
	_MN_GETHMENU = 0x01E1
	_WM_ERASEBKGND = 0x0014
	_WM_GETFONT = 0x0031
	_WM_GETTEXT = 0x000D
	_WM_GETTEXTLENGTH = 0x000E
	_WM_SETFONT = 0x0030
	_WM_SETICON = 0x0080
	_WM_SETTEXT = 0x000C
)

// WM_INPUTLANGCHANGEREQUEST values.
const (
	_INPUTLANGCHANGE_BACKWARD = 0x0004
	_INPUTLANGCHANGE_FORWARD = 0x0002
	_INPUTLANGCHANGE_SYSCHARSET = 0x0001
)

// WM_NCCALCSIZE return values.
const (
	_WVR_ALIGNTOP = 0x0010
	_WVR_ALIGNRIGHT = 0x0080
	_WVR_ALIGNLEFT = 0x0020
	_WVR_ALIGNBOTTOM = 0x0040
	_WVR_HREDRAW = 0x0100
	_WVR_VREDRAW = 0x0200
	_WVR_REDRAW = 0x0300
	_WVR_VALIDRECTS = 0x0400
)

// WM_SHOWWINDOW reasons (lParam).
const (
	_SW_OTHERUNZOOM = 4
	_SW_OTHERZOOM = 2
	_SW_PARENTCLOSING = 1
	_SW_PARENTOPENING = 3
)

// WM_SIZE values.
const (
	_SIZE_MAXHIDE = 4
	_SIZE_MAXIMIZED = 2
	_SIZE_MAXSHOW = 3
	_SIZE_MINIMIZED = 1
	_SIZE_RESTORED = 0
)

// WM_SIZING edge values (wParam).
const (
	_WMSZ_BOTTOM = 6
	_WMSZ_BOTTOMLEFT = 7
	_WMSZ_BOTTOMRIGHT = 8
	_WMSZ_LEFT = 1
	_WMSZ_RIGHT = 2
	_WMSZ_TOP = 3
	_WMSZ_TOPLEFT = 4
	_WMSZ_TOPRIGHT = 5
)

// WM_STYLECHANGED and WM_STYLECHANGING values (wParam).
const (
	_GWL_EXSTYLE = -20
	_GWL_STYLE = -16
)

// Window notifications.
const (
	_WM_ACTIVATEAPP = 0x001C
	_WM_CANCELMODE = 0x001F
	_WM_CHILDACTIVATE = 0x0022
	_WM_CLOSE = 0x0010
	_WM_COMPACTING = 0x0041
	_WM_CREATE = 0x0001
	_WM_DESTROY = 0x0002
//	_WM_DPICHANGED = 0x02E0		// Windows 8.1 and newer only
	_WM_ENABLE = 0x000A
	_WM_ENTERSIZEMOVE = 0x0231
	_WM_EXITSIZEMOVE = 0x0232
	_WM_GETICON = 0x007F
	_WM_GETMINMAXINFO = 0x0024
	_WM_INPUTLANGCHANGE = 0x0051
	_WM_INPUTLANGCHANGEREQUEST = 0x0050
	_WM_MOVE = 0x0003
	_WM_MOVING = 0x0216
	_WM_NCACTIVATE = 0x0086
	_WM_NCCALCSIZE = 0x0083
	_WM_NCCREATE = 0x0081
	_WM_NCDESTROY = 0x0082
	_WM_NULL = 0x0000
	_WM_QUERYDRAGICON = 0x0037
	_WM_QUERYOPEN = 0x0013
	_WM_QUIT = 0x0012
	_WM_SHOWWINDOW = 0x0018
	_WM_SIZE = 0x0005
	_WM_SIZING = 0x0214
	_WM_STYLECHANGED = 0x007D
	_WM_STYLECHANGING = 0x007C
//	_WM_THEMECHANGED = 0x031A		// Windows XP and newer only
//	_WM_USERCHANGED = 0x0054			// Windows XP only: [Note  This message is not supported as of Windows Vista.; also listed as not supported by server Windows]
	_WM_WINDOWPOSCHANGED = 0x0047
	_WM_WINDOWPOSCHANGING = 0x0046
)

type _MINMAXINFO struct {
	PtReserved		_POINT
	PtMaxSize			_POINT
	PtMaxPosition		_POINT
	PtMinTrackSize		_POINT
	PtMaxTrackSize	_POINT
}

func (l _LPARAM) MINMAXINFO() *_MINMAXINFO {
	return (*_MINMAXINFO)(unsafe.Pointer(l))
}
<commit_msg>Removing more leftover stuff.<commit_after>// 8 february 2014
package main

import (
//	"syscall"
	"unsafe"
)

// Window styles.
const (
	_WS_BORDER = 0x00800000
	_WS_CAPTION = 0x00C00000
	_WS_CHILD = 0x40000000
	_WS_CHILDWINDOW = 0x40000000
	_WS_CLIPCHILDREN = 0x02000000
	_WS_CLIPSIBLINGS = 0x04000000
	_WS_DISABLED = 0x08000000
	_WS_DLGFRAME = 0x00400000
	_WS_GROUP = 0x00020000
	_WS_HSCROLL = 0x00100000
	_WS_ICONIC = 0x20000000
	_WS_MAXIMIZE = 0x01000000
	_WS_MAXIMIZEBOX = 0x00010000
	_WS_MINIMIZE = 0x20000000
	_WS_MINIMIZEBOX = 0x00020000
	_WS_OVERLAPPED = 0x00000000
	_WS_OVERLAPPEDWINDOW = (_WS_OVERLAPPED | _WS_CAPTION | _WS_SYSMENU | _WS_THICKFRAME | _WS_MINIMIZEBOX | _WS_MAXIMIZEBOX)
	_WS_POPUP = 0x80000000
	_WS_POPUPWINDOW = (_WS_POPUP | _WS_BORDER | _WS_SYSMENU)
	_WS_SIZEBOX = 0x00040000
	_WS_SYSMENU = 0x00080000
	_WS_TABSTOP = 0x00010000
	_WS_THICKFRAME = 0x00040000
	_WS_TILED = 0x00000000
	_WS_TILEDWINDOW = (_WS_OVERLAPPED | _WS_CAPTION | _WS_SYSMENU | _WS_THICKFRAME | _WS_MINIMIZEBOX | _WS_MAXIMIZEBOX)
	_WS_VISIBLE = 0x10000000
	_WS_VSCROLL = 0x00200000
)

// Extended window styles.
const (
	_WS_EX_ACCEPTFILES = 0x00000010
	_WS_EX_APPWINDOW = 0x00040000
	_WS_EX_CLIENTEDGE = 0x00000200
//	_WS_EX_COMPOSITED = 0x02000000	// [Windows 2000:This style is not supported.]
	_WS_EX_CONTEXTHELP = 0x00000400
	_WS_EX_CONTROLPARENT = 0x00010000
	_WS_EX_DLGMODALFRAME = 0x00000001
	_WS_EX_LAYERED = 0x00080000
	_WS_EX_LAYOUTRTL = 0x00400000
	_WS_EX_LEFT = 0x00000000
	_WS_EX_LEFTSCROLLBAR = 0x00004000
	_WS_EX_LTRREADING = 0x00000000
	_WS_EX_MDICHILD = 0x00000040
	_WS_EX_NOACTIVATE = 0x08000000
	_WS_EX_NOINHERITLAYOUT = 0x00100000
	_WS_EX_NOPARENTNOTIFY = 0x00000004
	_WS_EX_OVERLAPPEDWINDOW = (_WS_EX_WINDOWEDGE | _WS_EX_CLIENTEDGE)
	_WS_EX_PALETTEWINDOW = (_WS_EX_WINDOWEDGE | _WS_EX_TOOLWINDOW | _WS_EX_TOPMOST)
	_WS_EX_RIGHT = 0x00001000
	_WS_EX_RIGHTSCROLLBAR = 0x00000000
	_WS_EX_RTLREADING = 0x00002000
	_WS_EX_STATICEDGE = 0x00020000
	_WS_EX_TOOLWINDOW = 0x00000080
	_WS_EX_TOPMOST = 0x00000008
	_WS_EX_TRANSPARENT = 0x00000020
	_WS_EX_WINDOWEDGE = 0x00000100
)

// bizarrely, this value is given on the page for CreateMDIWindow, but not CreateWindow or CreateWindowEx
// I do it this way because Go won't let me shove the exact value into an int
var (
	__CW_USEDEFAULT uint = 0x80000000
	_CW_USEDEFAULT = int(__CW_USEDEFAULT)
)

// GetSysColor values. These can be cast to HBRUSH (after adding 1) for WNDCLASS as well.
const (
	_COLOR_3DDKSHADOW = 21
	_COLOR_3DFACE = 15
	_COLOR_3DHIGHLIGHT = 20
	_COLOR_3DHILIGHT = 20
	_COLOR_3DLIGHT = 22
	_COLOR_3DSHADOW = 16
	_COLOR_ACTIVEBORDER = 10
	_COLOR_ACTIVECAPTION = 2
	_COLOR_APPWORKSPACE = 12
	_COLOR_BACKGROUND = 1
	_COLOR_BTNFACE = 15
	_COLOR_BTNHIGHLIGHT = 20
	_COLOR_BTNHILIGHT = 20
	_COLOR_BTNSHADOW = 16
	_COLOR_BTNTEXT = 18
	_COLOR_CAPTIONTEXT = 9
	_COLOR_DESKTOP = 1
	_COLOR_GRADIENTACTIVECAPTION = 27
	_COLOR_GRADIENTINACTIVECAPTION = 28
	_COLOR_GRAYTEXT = 17
	_COLOR_HIGHLIGHT = 13
	_COLOR_HIGHLIGHTTEXT = 14
	_COLOR_HOTLIGHT = 26
	_COLOR_INACTIVEBORDER = 11
	_COLOR_INACTIVECAPTION = 3
	_COLOR_INACTIVECAPTIONTEXT = 19
	_COLOR_INFOBK = 24
	_COLOR_INFOTEXT = 23
	_COLOR_MENU = 4
//	COLOR_MENUHILIGHT = 29	// [Windows 2000:This value is not supported.]
//	COLOR_MENUBAR = 30		// [Windows 2000:This value is not supported.]
	_COLOR_MENUTEXT = 7
	_COLOR_SCROLLBAR = 0
	_COLOR_WINDOW = 5
	_COLOR_WINDOWFRAME = 6
	_COLOR_WINDOWTEXT = 8
)

// SetWindowPos hWndInsertAfter values.
const (
	_HWND_BOTTOM = _HWND(1)
	_HWND_TOP = _HWND(0)
)

// SetWindowPos hWndInsertAfter values that Go won't allow as constants.
var (
	__HWND_NOTOPMOST = -2
	_HWND_NOTOPMOST = _HWND(__HWND_NOTOPMOST)
	__HWND_TOPMOST = -1
	_HWND_TOPMOST = _HWND(__HWND_TOPMOST)
)

// SetWindowPos uFlags values.
const (
	_SWP_DRAWFRAME = 0x0020
	_SWP_FRAMECHANGED = 0x0020
	_SWP_HIDEWINDOW = 0x0080
	_SWP_NOACTIVATE = 0x0010
	_SWP_NOCOPYBITS = 0x0100
	_SWP_NOMOVE = 0x0002
	_SWP_NOOWNERZORDER = 0x0200
	_SWP_NOREDRAW = 0x0008
	_SWP_NOREPOSITION = 0x0200
	_SWP_NOSENDCHANGING = 0x0400
	_SWP_NOSIZE = 0x0001
	_SWP_NOZORDER = 0x0004
	_SWP_SHOWWINDOW = 0x0040
	_SWP_ASYNCWINDOWPOS = 0x4000
	_SWP_DEFERERASE = 0x2000
)

// ShowWindow settings.
const (
	_SW_FORCEMINIMIZE = 11
	_SW_HIDE = 0
	_SW_MAXIMIZE = 3
	_SW_MINIMIZE = 6
	_SW_RESTORE = 9
	_SW_SHOW = 5
	_SW_SHOWDEFAULT = 10
	_SW_SHOWMAXIMIZED = 3
	_SW_SHOWMINIMIZED = 2
	_SW_SHOWMINNOACTIVE = 7
	_SW_SHOWNA = 8
	_SW_SHOWNOACTIVATE = 4
	_SW_SHOWNORMAL = 1
)

var (
	_createWindowEx = user32.NewProc("CreateWindowExW")
	_getClientRect = user32.NewProc("GetClientRect")
	_moveWindow = user32.NewProc("MoveWindow")
	_setWindowPos = user32.NewProc("SetWindowPos")
	_setWindowText = user32.NewProc("SetWindowTextW")
	_showWindow = user32.NewProc("ShowWindow")
)

// WM_SETICON and WM_GETICON values.
const (
	_ICON_BIG = 1
	_ICON_SMALL = 0
	_ICON_SMALL2 = 2		// WM_GETICON only?
)

// Window messages.
const (
	_MN_GETHMENU = 0x01E1
	_WM_ERASEBKGND = 0x0014
	_WM_GETFONT = 0x0031
	_WM_GETTEXT = 0x000D
	_WM_GETTEXTLENGTH = 0x000E
	_WM_SETFONT = 0x0030
	_WM_SETICON = 0x0080
	_WM_SETTEXT = 0x000C
)

// WM_INPUTLANGCHANGEREQUEST values.
const (
	_INPUTLANGCHANGE_BACKWARD = 0x0004
	_INPUTLANGCHANGE_FORWARD = 0x0002
	_INPUTLANGCHANGE_SYSCHARSET = 0x0001
)

// WM_NCCALCSIZE return values.
const (
	_WVR_ALIGNTOP = 0x0010
	_WVR_ALIGNRIGHT = 0x0080
	_WVR_ALIGNLEFT = 0x0020
	_WVR_ALIGNBOTTOM = 0x0040
	_WVR_HREDRAW = 0x0100
	_WVR_VREDRAW = 0x0200
	_WVR_REDRAW = 0x0300
	_WVR_VALIDRECTS = 0x0400
)

// WM_SHOWWINDOW reasons (lParam).
const (
	_SW_OTHERUNZOOM = 4
	_SW_OTHERZOOM = 2
	_SW_PARENTCLOSING = 1
	_SW_PARENTOPENING = 3
)

// WM_SIZE values.
const (
	_SIZE_MAXHIDE = 4
	_SIZE_MAXIMIZED = 2
	_SIZE_MAXSHOW = 3
	_SIZE_MINIMIZED = 1
	_SIZE_RESTORED = 0
)

// WM_SIZING edge values (wParam).
const (
	_WMSZ_BOTTOM = 6
	_WMSZ_BOTTOMLEFT = 7
	_WMSZ_BOTTOMRIGHT = 8
	_WMSZ_LEFT = 1
	_WMSZ_RIGHT = 2
	_WMSZ_TOP = 3
	_WMSZ_TOPLEFT = 4
	_WMSZ_TOPRIGHT = 5
)

// WM_STYLECHANGED and WM_STYLECHANGING values (wParam).
const (
	_GWL_EXSTYLE = -20
	_GWL_STYLE = -16
)

// Window notifications.
const (
	_WM_ACTIVATEAPP = 0x001C
	_WM_CANCELMODE = 0x001F
	_WM_CHILDACTIVATE = 0x0022
	_WM_CLOSE = 0x0010
	_WM_COMPACTING = 0x0041
	_WM_CREATE = 0x0001
	_WM_DESTROY = 0x0002
//	_WM_DPICHANGED = 0x02E0		// Windows 8.1 and newer only
	_WM_ENABLE = 0x000A
	_WM_ENTERSIZEMOVE = 0x0231
	_WM_EXITSIZEMOVE = 0x0232
	_WM_GETICON = 0x007F
	_WM_GETMINMAXINFO = 0x0024
	_WM_INPUTLANGCHANGE = 0x0051
	_WM_INPUTLANGCHANGEREQUEST = 0x0050
	_WM_MOVE = 0x0003
	_WM_MOVING = 0x0216
	_WM_NCACTIVATE = 0x0086
	_WM_NCCALCSIZE = 0x0083
	_WM_NCCREATE = 0x0081
	_WM_NCDESTROY = 0x0082
	_WM_NULL = 0x0000
	_WM_QUERYDRAGICON = 0x0037
	_WM_QUERYOPEN = 0x0013
	_WM_QUIT = 0x0012
	_WM_SHOWWINDOW = 0x0018
	_WM_SIZE = 0x0005
	_WM_SIZING = 0x0214
	_WM_STYLECHANGED = 0x007D
	_WM_STYLECHANGING = 0x007C
//	_WM_THEMECHANGED = 0x031A		// Windows XP and newer only
//	_WM_USERCHANGED = 0x0054			// Windows XP only: [Note  This message is not supported as of Windows Vista.; also listed as not supported by server Windows]
	_WM_WINDOWPOSCHANGED = 0x0047
	_WM_WINDOWPOSCHANGING = 0x0046
)

type _MINMAXINFO struct {
	PtReserved		_POINT
	PtMaxSize			_POINT
	PtMaxPosition		_POINT
	PtMinTrackSize		_POINT
	PtMaxTrackSize	_POINT
}

func (l _LPARAM) MINMAXINFO() *_MINMAXINFO {
	return (*_MINMAXINFO)(unsafe.Pointer(l))
}
<|endoftext|>


<commit_before>package vegeta

import (
	"testing"
	"time"
)

func TestNewMetrics(t *testing.T) {
	t.Parallel()

	m := NewMetrics([]Result{
		Result{500, time.Unix(0, 0), 100 * time.Millisecond, 10, 30, "Internal server error"},
		Result{200, time.Unix(1, 0), 20 * time.Millisecond, 20, 20, ""},
		Result{200, time.Unix(2, 0), 30 * time.Millisecond, 30, 10, ""},
	})

	for field, values := range map[string][]float64{
		"BytesIn.Mean":  []float64{m.BytesIn.Mean, 20.0},
		"BytesOut.Mean": []float64{m.BytesOut.Mean, 20.0},
		"Sucess":        []float64{m.Success, 0.6666666666666666},
	} {
		if values[0] != values[1] {
			t.Errorf("%s: want: %f, got: %f", field, values[1], values[0])
		}
	}

	for field, values := range map[string][]time.Duration{
		"Latencies.Max":  []time.Duration{m.Latencies.Max, 100 * time.Millisecond},
		"Latencies.Mean": []time.Duration{m.Latencies.Mean, 50 * time.Millisecond},
		"Latencies.P50":  []time.Duration{m.Latencies.P50, 20 * time.Millisecond},
		"Latencies.P95":  []time.Duration{m.Latencies.P95, 30 * time.Millisecond},
		"Latencies.P99":  []time.Duration{m.Latencies.P99, 30 * time.Millisecond},
		"Duration":       []time.Duration{m.Duration, 2 * time.Second},
	} {
		if values[0] != values[1] {
			t.Errorf("%s: want: %s, got: %s", field, values[1], values[0])
		}
	}

	for field, values := range map[string][]uint64{
		"BytesOut.Total": []uint64{m.BytesOut.Total, 60},
		"BytesIn.Total":  []uint64{m.BytesIn.Total, 60},
		"Requests":       []uint64{m.Requests, 3},
	} {
		if values[0] != values[1] {
			t.Errorf("%s: want: %d, got: %d", field, values[1], values[0])
		}
	}

	if len(m.StatusCodes) != 2 || m.StatusCodes["200"] != 2 || m.StatusCodes["500"] != 1 {
		t.Errorf("StatusCodes: want: %v, got: %v", map[int]int{200: 2, 500: 1}, m.StatusCodes)
	}

	err := "Internal server error"
	if len(m.Errors) != 1 || m.Errors[0] != err {
		t.Errorf("Errors: want: %v, got: %v", []string{err}, m.Errors)
	}
}
<commit_msg>Add stub test for NewMetrics panic<commit_after>package vegeta

import (
	"testing"
	"time"
)

func TestNewMetrics(t *testing.T) {
	t.Parallel()

	m := NewMetrics([]Result{
		Result{500, time.Unix(0, 0), 100 * time.Millisecond, 10, 30, "Internal server error"},
		Result{200, time.Unix(1, 0), 20 * time.Millisecond, 20, 20, ""},
		Result{200, time.Unix(2, 0), 30 * time.Millisecond, 30, 10, ""},
	})

	for field, values := range map[string][]float64{
		"BytesIn.Mean":  []float64{m.BytesIn.Mean, 20.0},
		"BytesOut.Mean": []float64{m.BytesOut.Mean, 20.0},
		"Sucess":        []float64{m.Success, 0.6666666666666666},
	} {
		if values[0] != values[1] {
			t.Errorf("%s: want: %f, got: %f", field, values[1], values[0])
		}
	}

	for field, values := range map[string][]time.Duration{
		"Latencies.Max":  []time.Duration{m.Latencies.Max, 100 * time.Millisecond},
		"Latencies.Mean": []time.Duration{m.Latencies.Mean, 50 * time.Millisecond},
		"Latencies.P50":  []time.Duration{m.Latencies.P50, 20 * time.Millisecond},
		"Latencies.P95":  []time.Duration{m.Latencies.P95, 30 * time.Millisecond},
		"Latencies.P99":  []time.Duration{m.Latencies.P99, 30 * time.Millisecond},
		"Duration":       []time.Duration{m.Duration, 2 * time.Second},
	} {
		if values[0] != values[1] {
			t.Errorf("%s: want: %s, got: %s", field, values[1], values[0])
		}
	}

	for field, values := range map[string][]uint64{
		"BytesOut.Total": []uint64{m.BytesOut.Total, 60},
		"BytesIn.Total":  []uint64{m.BytesIn.Total, 60},
		"Requests":       []uint64{m.Requests, 3},
	} {
		if values[0] != values[1] {
			t.Errorf("%s: want: %d, got: %d", field, values[1], values[0])
		}
	}

	if len(m.StatusCodes) != 2 || m.StatusCodes["200"] != 2 || m.StatusCodes["500"] != 1 {
		t.Errorf("StatusCodes: want: %v, got: %v", map[int]int{200: 2, 500: 1}, m.StatusCodes)
	}

	err := "Internal server error"
	if len(m.Errors) != 1 || m.Errors[0] != err {
		t.Errorf("Errors: want: %v, got: %v", []string{err}, m.Errors)
	}
}

func TestNewMetricsEmptyResults(t *testing.T) {
  _ = NewMetrics([]Result{})
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"net/http"
	"os"

	mp "github.com/mackerelio/go-mackerel-plugin-helper"
	"github.com/mackerelio/mackerel-agent/logging"
)

var logger = logging.GetLogger("metrics.plugin.jmx-jolokia")

// JmxJolokiaPlugin mackerel plugin for Jolokia
type JmxJolokiaPlugin struct {
	Target   string
	Tempfile string
}

// JmxJolokiaResponse response for Jolokia
type JmxJolokiaResponse struct {
	Status    uint32
	Timestamp uint32
	Request   map[string]interface{}
	Value     map[string]interface{}
	Error     string
}

var graphdef = map[string](mp.Graphs){
	"jolokia.memory.heap_memory_usage": mp.Graphs{
		Label: "HeapMemoryUsage",
		Unit:  "bytes",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "init", Label: "init", Diff: false, Type: "uint64"},
			mp.Metrics{Name: "committed", Label: "committed", Diff: false, Type: "uint64"},
			mp.Metrics{Name: "max", Label: "max", Diff: false, Type: "uint64"},
			mp.Metrics{Name: "used", Label: "used", Diff: false, Type: "uint64"},
		},
	},
	"jolokia.memory.non_heap_memory_usage": mp.Graphs{
		Label: "NonHeapMemoryUsage",
		Unit:  "bytes",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "nonInit", Label: "init", Diff: false, Type: "uint64"},
			mp.Metrics{Name: "nonCommitted", Label: "committed", Diff: false, Type: "uint64"},
			mp.Metrics{Name: "nonMax", Label: "max", Diff: false, Type: "uint64"},
			mp.Metrics{Name: "nonUsed", Label: "used", Diff: false, Type: "uint64"},
		},
	},
	"jolokia.class_load": mp.Graphs{
		Label: "ClassLoading",
		Unit:  "integer",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "LoadedClassCount", Label: "loaded", Diff: false, Type: "uint64"},
			mp.Metrics{Name: "UnloadedClassCount", Label: "unloaded", Diff: false, Type: "uint64"},
			mp.Metrics{Name: "TotalLoadedClassCount", Label: "total", Diff: false, Type: "uint64"},
		},
	},
	"jolokia.thread": mp.Graphs{
		Label: "Threading",
		Unit:  "integer",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "ThreadCount", Label: "thread", Diff: false, Type: "uint64"},
		},
	},
	"jolokia.ops.cpu_load": mp.Graphs{
		Label: "CpuLoad",
		Unit:  "percentage",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "ProcessCpuLoad", Label: "process", Diff: false, Type: "float64", Scale: 100},
			mp.Metrics{Name: "SystemCpuLoad", Label: "system", Diff: false, Type: "float64", Scale: 100},
		},
	},
}

// FetchMetrics interface for mackerelplugin
func (j JmxJolokiaPlugin) FetchMetrics() (map[string]interface{}, error) {
	stat := make(map[string]interface{})
	if err := j.fetchMemory(stat); err != nil {
		logger.Warningf(err.Error())
	}

	if err := j.fetchClassLoad(stat); err != nil {
		logger.Warningf(err.Error())
	}

	if err := j.fetchThread(stat); err != nil {
		logger.Warningf(err.Error())
	}

	if err := j.fetchOperatingSystem(stat); err != nil {
		logger.Warningf(err.Error())
	}

	return stat, nil
}

func (j JmxJolokiaPlugin) fetchMemory(stat map[string]interface{}) error {
	resp, err := j.executeGetRequest("java.lang:type=Memory")
	if err != nil {
		return err
	}
	heap := resp.Value["HeapMemoryUsage"].(map[string]interface{})
	stat["init"] = heap["init"]
	stat["committed"] = heap["committed"]
	stat["max"] = heap["max"]
	stat["used"] = heap["used"]

	nonHeap := resp.Value["NonHeapMemoryUsage"].(map[string]interface{})
	stat["nonInit"] = nonHeap["init"]
	stat["nonCommitted"] = nonHeap["committed"]
	stat["nonMax"] = nonHeap["max"]
	stat["nonUsed"] = nonHeap["used"]

	return nil
}

func (j JmxJolokiaPlugin) fetchClassLoad(stat map[string]interface{}) error {
	resp, err := j.executeGetRequest("java.lang:type=ClassLoading")
	if err != nil {
		return err
	}
	stat["LoadedClassCount"] = resp.Value["LoadedClassCount"]
	stat["UnloadedClassCount"] = resp.Value["UnloadedClassCount"]
	stat["TotalLoadedClassCount"] = resp.Value["TotalLoadedClassCount"]

	return nil
}

func (j JmxJolokiaPlugin) fetchThread(stat map[string]interface{}) error {
	resp, err := j.executeGetRequest("java.lang:type=Threading")
	if err != nil {
		return err
	}
	stat["ThreadCount"] = resp.Value["ThreadCount"]

	return nil
}

func (j JmxJolokiaPlugin) fetchOperatingSystem(stat map[string]interface{}) error {
	resp, err := j.executeGetRequest("java.lang:type=OperatingSystem")
	if err != nil {
		return err
	}
	stat["ProcessCpuLoad"] = resp.Value["ProcessCpuLoad"]
	stat["SystemCpuLoad"] = resp.Value["SystemCpuLoad"]

	return nil
}

func (j JmxJolokiaPlugin) executeGetRequest(mbean string) (*JmxJolokiaResponse, error) {
	resp, err := http.Get(j.Target + mbean)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var respJ JmxJolokiaResponse
	dec := json.NewDecoder(resp.Body)
	if err := dec.Decode(&respJ); err != nil {
		return nil, err
	}
	return &respJ, nil
}

// GraphDefinition interface for mackerelplugin
func (j JmxJolokiaPlugin) GraphDefinition() map[string](mp.Graphs) {
	return graphdef
}

func main() {
	optHost := flag.String("host", "localhost", "Hostname")
	optPort := flag.String("port", "8778", "Port")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	var jmxJolokia JmxJolokiaPlugin
	jmxJolokia.Target = fmt.Sprintf("http://%s:%s/jolokia/read/", *optHost, *optPort)

	helper := mp.NewMackerelPlugin(jmxJolokia)
	if *optTempfile != "" {
		helper.Tempfile = *optTempfile
	} else {
		helper.Tempfile = fmt.Sprintf("/tmp/mackerel-plugin-jmx-jolokia-%s-%s", *optHost, *optPort)
	}

	if os.Getenv("MACKEREL_AGENT_PLUGIN_META") != "" {
		helper.OutputDefinitions()
	} else {
		helper.OutputValues()
	}
}
<commit_msg>add prefix Jmx<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"net/http"
	"os"

	mp "github.com/mackerelio/go-mackerel-plugin-helper"
	"github.com/mackerelio/mackerel-agent/logging"
)

var logger = logging.GetLogger("metrics.plugin.jmx-jolokia")

// JmxJolokiaPlugin mackerel plugin for Jolokia
type JmxJolokiaPlugin struct {
	Target   string
	Tempfile string
}

// JmxJolokiaResponse response for Jolokia
type JmxJolokiaResponse struct {
	Status    uint32
	Timestamp uint32
	Request   map[string]interface{}
	Value     map[string]interface{}
	Error     string
}

var graphdef = map[string](mp.Graphs){
	"jolokia.memory.heap_memory_usage": mp.Graphs{
		Label: "Jmx HeapMemoryUsage",
		Unit:  "bytes",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "init", Label: "init", Diff: false, Type: "uint64"},
			mp.Metrics{Name: "committed", Label: "committed", Diff: false, Type: "uint64"},
			mp.Metrics{Name: "max", Label: "max", Diff: false, Type: "uint64"},
			mp.Metrics{Name: "used", Label: "used", Diff: false, Type: "uint64"},
		},
	},
	"jolokia.memory.non_heap_memory_usage": mp.Graphs{
		Label: "Jmx NonHeapMemoryUsage",
		Unit:  "bytes",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "nonInit", Label: "init", Diff: false, Type: "uint64"},
			mp.Metrics{Name: "nonCommitted", Label: "committed", Diff: false, Type: "uint64"},
			mp.Metrics{Name: "nonMax", Label: "max", Diff: false, Type: "uint64"},
			mp.Metrics{Name: "nonUsed", Label: "used", Diff: false, Type: "uint64"},
		},
	},
	"jolokia.class_load": mp.Graphs{
		Label: "Jmx ClassLoading",
		Unit:  "integer",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "LoadedClassCount", Label: "loaded", Diff: false, Type: "uint64"},
			mp.Metrics{Name: "UnloadedClassCount", Label: "unloaded", Diff: false, Type: "uint64"},
			mp.Metrics{Name: "TotalLoadedClassCount", Label: "total", Diff: false, Type: "uint64"},
		},
	},
	"jolokia.thread": mp.Graphs{
		Label: "Jmx Threading",
		Unit:  "integer",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "ThreadCount", Label: "thread", Diff: false, Type: "uint64"},
		},
	},
	"jolokia.ops.cpu_load": mp.Graphs{
		Label: "Jmx CpuLoad",
		Unit:  "percentage",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "ProcessCpuLoad", Label: "process", Diff: false, Type: "float64", Scale: 100},
			mp.Metrics{Name: "SystemCpuLoad", Label: "system", Diff: false, Type: "float64", Scale: 100},
		},
	},
}

// FetchMetrics interface for mackerelplugin
func (j JmxJolokiaPlugin) FetchMetrics() (map[string]interface{}, error) {
	stat := make(map[string]interface{})
	if err := j.fetchMemory(stat); err != nil {
		logger.Warningf(err.Error())
	}

	if err := j.fetchClassLoad(stat); err != nil {
		logger.Warningf(err.Error())
	}

	if err := j.fetchThread(stat); err != nil {
		logger.Warningf(err.Error())
	}

	if err := j.fetchOperatingSystem(stat); err != nil {
		logger.Warningf(err.Error())
	}

	return stat, nil
}

func (j JmxJolokiaPlugin) fetchMemory(stat map[string]interface{}) error {
	resp, err := j.executeGetRequest("java.lang:type=Memory")
	if err != nil {
		return err
	}
	heap := resp.Value["HeapMemoryUsage"].(map[string]interface{})
	stat["init"] = heap["init"]
	stat["committed"] = heap["committed"]
	stat["max"] = heap["max"]
	stat["used"] = heap["used"]

	nonHeap := resp.Value["NonHeapMemoryUsage"].(map[string]interface{})
	stat["nonInit"] = nonHeap["init"]
	stat["nonCommitted"] = nonHeap["committed"]
	stat["nonMax"] = nonHeap["max"]
	stat["nonUsed"] = nonHeap["used"]

	return nil
}

func (j JmxJolokiaPlugin) fetchClassLoad(stat map[string]interface{}) error {
	resp, err := j.executeGetRequest("java.lang:type=ClassLoading")
	if err != nil {
		return err
	}
	stat["LoadedClassCount"] = resp.Value["LoadedClassCount"]
	stat["UnloadedClassCount"] = resp.Value["UnloadedClassCount"]
	stat["TotalLoadedClassCount"] = resp.Value["TotalLoadedClassCount"]

	return nil
}

func (j JmxJolokiaPlugin) fetchThread(stat map[string]interface{}) error {
	resp, err := j.executeGetRequest("java.lang:type=Threading")
	if err != nil {
		return err
	}
	stat["ThreadCount"] = resp.Value["ThreadCount"]

	return nil
}

func (j JmxJolokiaPlugin) fetchOperatingSystem(stat map[string]interface{}) error {
	resp, err := j.executeGetRequest("java.lang:type=OperatingSystem")
	if err != nil {
		return err
	}
	stat["ProcessCpuLoad"] = resp.Value["ProcessCpuLoad"]
	stat["SystemCpuLoad"] = resp.Value["SystemCpuLoad"]

	return nil
}

func (j JmxJolokiaPlugin) executeGetRequest(mbean string) (*JmxJolokiaResponse, error) {
	resp, err := http.Get(j.Target + mbean)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var respJ JmxJolokiaResponse
	dec := json.NewDecoder(resp.Body)
	if err := dec.Decode(&respJ); err != nil {
		return nil, err
	}
	return &respJ, nil
}

// GraphDefinition interface for mackerelplugin
func (j JmxJolokiaPlugin) GraphDefinition() map[string](mp.Graphs) {
	return graphdef
}

func main() {
	optHost := flag.String("host", "localhost", "Hostname")
	optPort := flag.String("port", "8778", "Port")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	var jmxJolokia JmxJolokiaPlugin
	jmxJolokia.Target = fmt.Sprintf("http://%s:%s/jolokia/read/", *optHost, *optPort)

	helper := mp.NewMackerelPlugin(jmxJolokia)
	if *optTempfile != "" {
		helper.Tempfile = *optTempfile
	} else {
		helper.Tempfile = fmt.Sprintf("/tmp/mackerel-plugin-jmx-jolokia-%s-%s", *optHost, *optPort)
	}

	if os.Getenv("MACKEREL_AGENT_PLUGIN_META") != "" {
		helper.OutputDefinitions()
	} else {
		helper.OutputValues()
	}
}
<|endoftext|>


<commit_before>package shell

import (
	"flag"
	"fmt"
	"github.com/chrislusf/seaweedfs/weed/pb/master_pb"
	"github.com/chrislusf/seaweedfs/weed/storage/types"
	"github.com/chrislusf/seaweedfs/weed/wdclient"
	"io"
	"path/filepath"
	"time"

	"github.com/chrislusf/seaweedfs/weed/storage/needle"
)

func init() {
	Commands = append(Commands, &commandVolumeTierMove{})
}

type commandVolumeTierMove struct {
}

func (c *commandVolumeTierMove) Name() string {
	return "volume.tier.move"
}

func (c *commandVolumeTierMove) Help() string {
	return `change a volume from one disk type to another

	volume.tier.move -fromDiskType=hdd -toDiskType=ssd [-collectionPattern=""] [-fullPercent=95] [-quietFor=1h]

	Even if the volume is replicated, only one replica will be changed and the rest replicas will be dropped.
	So "volume.fix.replication" and "volume.balance" should be followed.

`
}

func (c *commandVolumeTierMove) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) {

	if err = commandEnv.confirmIsLocked(); err != nil {
		return
	}

	tierCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
	collectionPattern := tierCommand.String("collectionPattern", "", "match with wildcard characters '*' and '?'")
	fullPercentage := tierCommand.Float64("fullPercent", 95, "the volume reaches the percentage of max volume size")
	quietPeriod := tierCommand.Duration("quietFor", 24*time.Hour, "select volumes without no writes for this period")
	source := tierCommand.String("fromDiskType", "", "the source disk type")
	target := tierCommand.String("toDiskType", "", "the target disk type")
	applyChange := tierCommand.Bool("force", false, "actually apply the changes")
	if err = tierCommand.Parse(args); err != nil {
		return nil
	}

	fromDiskType := types.ToDiskType(*source)
	toDiskType := types.ToDiskType(*target)

	if fromDiskType == toDiskType {
		return fmt.Errorf("source tier %s is the same as target tier %s", fromDiskType, toDiskType)
	}

	// collect topology information
	topologyInfo, volumeSizeLimitMb, err := collectTopologyInfo(commandEnv)
	if err != nil {
		return err
	}

	// collect all volumes that should change
	volumeIds, err := collectVolumeIdsForTierChange(commandEnv, topologyInfo, volumeSizeLimitMb, fromDiskType, *collectionPattern, *fullPercentage, *quietPeriod)
	if err != nil {
		return err
	}
	fmt.Printf("tier move volumes: %v\n", volumeIds)

	_, allLocations := collectVolumeReplicaLocations(topologyInfo)
	for _, vid := range volumeIds {
		if err = doVolumeTierMove(commandEnv, writer, vid, toDiskType, allLocations, *applyChange); err != nil {
			fmt.Printf("tier move volume %d: %v\n", vid, err)
		}
	}

	return nil
}

func isOneOf(server string, locations []wdclient.Location) bool {
	for _, loc := range locations {
		if server == loc.Url {
			return true
		}
	}
	return false
}

func doVolumeTierMove(commandEnv *CommandEnv, writer io.Writer, vid needle.VolumeId, toDiskType types.DiskType, allLocations []location, applyChanges bool) (err error) {
	// find volume location
	locations, found := commandEnv.MasterClient.GetLocations(uint32(vid))
	if !found {
		return fmt.Errorf("volume %d not found", vid)
	}

	// find one server with the most empty volume slots with target disk type
	hasFoundTarget := false
	keepDataNodesSorted(allLocations, toDiskType)
	fn := capacityByFreeVolumeCount(toDiskType)
	for _, dst := range allLocations {
		if fn(dst.dataNode) > 0 && !hasFoundTarget {
			// ask the volume server to replicate the volume
			if isOneOf(dst.dataNode.Id, locations) {
				continue
			}
			sourceVolumeServer := ""
			for _, loc := range locations {
				if loc.Url != dst.dataNode.Id {
					sourceVolumeServer = loc.Url
				}
			}
			if sourceVolumeServer == "" {
				continue
			}
			fmt.Fprintf(writer, "moving volume %d from %s to %s with disk type %s ...\n", vid, sourceVolumeServer, dst.dataNode.Id, toDiskType.ReadableString())
			hasFoundTarget = true

			if !applyChanges {
				break
			}

			// mark all replicas as read only
			if err = markVolumeReadonly(commandEnv.option.GrpcDialOption, vid, locations); err != nil {
				return fmt.Errorf("mark volume %d as readonly on %s: %v", vid, locations[0].Url, err)
			}
			if err = LiveMoveVolume(commandEnv.option.GrpcDialOption, vid, sourceVolumeServer, dst.dataNode.Id, 5*time.Second, toDiskType.ReadableString()); err != nil {
				return fmt.Errorf("move volume %d %s => %s : %v", vid, locations[0].Url, dst.dataNode.Id, err)
			}

			// remove the remaining replicas
			for _, loc := range locations {
				if loc.Url != dst.dataNode.Id {
					if err = deleteVolume(commandEnv.option.GrpcDialOption, vid, loc.Url); err != nil {
						fmt.Fprintf(writer, "failed to delete volume %d on %s: %v\n", vid, loc.Url, err)
					}
				}
			}
		}
	}

	if !hasFoundTarget {
		fmt.Fprintf(writer, "can not find disk type %s for volume %d\n", toDiskType.ReadableString(), vid)
	}

	return nil
}

func collectVolumeIdsForTierChange(commandEnv *CommandEnv, topologyInfo *master_pb.TopologyInfo, volumeSizeLimitMb uint64, sourceTier types.DiskType, collectionPattern string, fullPercentage float64, quietPeriod time.Duration) (vids []needle.VolumeId, err error) {

	quietSeconds := int64(quietPeriod / time.Second)
	nowUnixSeconds := time.Now().Unix()

	fmt.Printf("collect %s volumes quiet for: %d seconds\n", sourceTier, quietSeconds)

	vidMap := make(map[uint32]bool)
	eachDataNode(topologyInfo, func(dc string, rack RackId, dn *master_pb.DataNodeInfo) {
		for _, diskInfo := range dn.DiskInfos {
			for _, v := range diskInfo.VolumeInfos {
				// check collection name pattern
				if collectionPattern != "" {
					matched, err := filepath.Match(collectionPattern, v.Collection)
					if err != nil {
						return
					}
					if !matched {
						continue
					}
				}

				if v.ModifiedAtSecond+quietSeconds < nowUnixSeconds && types.ToDiskType(v.DiskType) == sourceTier {
					if float64(v.Size) > fullPercentage/100*float64(volumeSizeLimitMb)*1024*1024 {
						vidMap[v.Id] = true
					}
				}
			}
		}
	})

	for vid := range vidMap {
		vids = append(vids, needle.VolumeId(vid))
	}

	return
}
<commit_msg>skip not found error on deletion<commit_after>package shell

import (
	"flag"
	"fmt"
	"github.com/chrislusf/seaweedfs/weed/pb/master_pb"
	"github.com/chrislusf/seaweedfs/weed/storage/types"
	"github.com/chrislusf/seaweedfs/weed/wdclient"
	"io"
	"path/filepath"
	"strings"
	"time"

	"github.com/chrislusf/seaweedfs/weed/storage/needle"
)

func init() {
	Commands = append(Commands, &commandVolumeTierMove{})
}

type commandVolumeTierMove struct {
}

func (c *commandVolumeTierMove) Name() string {
	return "volume.tier.move"
}

func (c *commandVolumeTierMove) Help() string {
	return `change a volume from one disk type to another

	volume.tier.move -fromDiskType=hdd -toDiskType=ssd [-collectionPattern=""] [-fullPercent=95] [-quietFor=1h]

	Even if the volume is replicated, only one replica will be changed and the rest replicas will be dropped.
	So "volume.fix.replication" and "volume.balance" should be followed.

`
}

func (c *commandVolumeTierMove) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) {

	if err = commandEnv.confirmIsLocked(); err != nil {
		return
	}

	tierCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
	collectionPattern := tierCommand.String("collectionPattern", "", "match with wildcard characters '*' and '?'")
	fullPercentage := tierCommand.Float64("fullPercent", 95, "the volume reaches the percentage of max volume size")
	quietPeriod := tierCommand.Duration("quietFor", 24*time.Hour, "select volumes without no writes for this period")
	source := tierCommand.String("fromDiskType", "", "the source disk type")
	target := tierCommand.String("toDiskType", "", "the target disk type")
	applyChange := tierCommand.Bool("force", false, "actually apply the changes")
	if err = tierCommand.Parse(args); err != nil {
		return nil
	}

	fromDiskType := types.ToDiskType(*source)
	toDiskType := types.ToDiskType(*target)

	if fromDiskType == toDiskType {
		return fmt.Errorf("source tier %s is the same as target tier %s", fromDiskType, toDiskType)
	}

	// collect topology information
	topologyInfo, volumeSizeLimitMb, err := collectTopologyInfo(commandEnv)
	if err != nil {
		return err
	}

	// collect all volumes that should change
	volumeIds, err := collectVolumeIdsForTierChange(commandEnv, topologyInfo, volumeSizeLimitMb, fromDiskType, *collectionPattern, *fullPercentage, *quietPeriod)
	if err != nil {
		return err
	}
	fmt.Printf("tier move volumes: %v\n", volumeIds)

	_, allLocations := collectVolumeReplicaLocations(topologyInfo)
	for _, vid := range volumeIds {
		if err = doVolumeTierMove(commandEnv, writer, vid, toDiskType, allLocations, *applyChange); err != nil {
			fmt.Printf("tier move volume %d: %v\n", vid, err)
		}
	}

	return nil
}

func isOneOf(server string, locations []wdclient.Location) bool {
	for _, loc := range locations {
		if server == loc.Url {
			return true
		}
	}
	return false
}

func doVolumeTierMove(commandEnv *CommandEnv, writer io.Writer, vid needle.VolumeId, toDiskType types.DiskType, allLocations []location, applyChanges bool) (err error) {
	// find volume location
	locations, found := commandEnv.MasterClient.GetLocations(uint32(vid))
	if !found {
		return fmt.Errorf("volume %d not found", vid)
	}

	// find one server with the most empty volume slots with target disk type
	hasFoundTarget := false
	keepDataNodesSorted(allLocations, toDiskType)
	fn := capacityByFreeVolumeCount(toDiskType)
	for _, dst := range allLocations {
		if fn(dst.dataNode) > 0 && !hasFoundTarget {
			// ask the volume server to replicate the volume
			if isOneOf(dst.dataNode.Id, locations) {
				continue
			}
			sourceVolumeServer := ""
			for _, loc := range locations {
				if loc.Url != dst.dataNode.Id {
					sourceVolumeServer = loc.Url
				}
			}
			if sourceVolumeServer == "" {
				continue
			}
			fmt.Fprintf(writer, "moving volume %d from %s to %s with disk type %s ...\n", vid, sourceVolumeServer, dst.dataNode.Id, toDiskType.ReadableString())
			hasFoundTarget = true

			if !applyChanges {
				break
			}

			// mark all replicas as read only
			if err = markVolumeReadonly(commandEnv.option.GrpcDialOption, vid, locations); err != nil {
				return fmt.Errorf("mark volume %d as readonly on %s: %v", vid, locations[0].Url, err)
			}
			if err = LiveMoveVolume(commandEnv.option.GrpcDialOption, vid, sourceVolumeServer, dst.dataNode.Id, 5*time.Second, toDiskType.ReadableString()); err != nil {
				return fmt.Errorf("move volume %d %s => %s : %v", vid, locations[0].Url, dst.dataNode.Id, err)
			}

			// remove the remaining replicas
			for _, loc := range locations {
				if loc.Url != dst.dataNode.Id {
					if err = deleteVolume(commandEnv.option.GrpcDialOption, vid, loc.Url); err != nil {
						if !strings.Contains(err.Error(), "not found") {
							fmt.Fprintf(writer, "failed to delete volume %d on %s: %v\n", vid, loc.Url, err)
						}
					}
				}
			}
		}
	}

	if !hasFoundTarget {
		fmt.Fprintf(writer, "can not find disk type %s for volume %d\n", toDiskType.ReadableString(), vid)
	}

	return nil
}

func collectVolumeIdsForTierChange(commandEnv *CommandEnv, topologyInfo *master_pb.TopologyInfo, volumeSizeLimitMb uint64, sourceTier types.DiskType, collectionPattern string, fullPercentage float64, quietPeriod time.Duration) (vids []needle.VolumeId, err error) {

	quietSeconds := int64(quietPeriod / time.Second)
	nowUnixSeconds := time.Now().Unix()

	fmt.Printf("collect %s volumes quiet for: %d seconds\n", sourceTier, quietSeconds)

	vidMap := make(map[uint32]bool)
	eachDataNode(topologyInfo, func(dc string, rack RackId, dn *master_pb.DataNodeInfo) {
		for _, diskInfo := range dn.DiskInfos {
			for _, v := range diskInfo.VolumeInfos {
				// check collection name pattern
				if collectionPattern != "" {
					matched, err := filepath.Match(collectionPattern, v.Collection)
					if err != nil {
						return
					}
					if !matched {
						continue
					}
				}

				if v.ModifiedAtSecond+quietSeconds < nowUnixSeconds && types.ToDiskType(v.DiskType) == sourceTier {
					if float64(v.Size) > fullPercentage/100*float64(volumeSizeLimitMb)*1024*1024 {
						vidMap[v.Id] = true
					}
				}
			}
		}
	})

	for vid := range vidMap {
		vids = append(vids, needle.VolumeId(vid))
	}

	return
}
<|endoftext|>


<commit_before>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package diskmanager_test

import (
	"time"

	gc "gopkg.in/check.v1"

	"github.com/juju/juju/storage"
	coretesting "github.com/juju/juju/testing"
	"github.com/juju/juju/worker/diskmanager"
)

var _ = gc.Suite(&DiskManagerWorkerSuite{})

type DiskManagerWorkerSuite struct {
	coretesting.BaseSuite
}

func (s *DiskManagerWorkerSuite) SetUpTest(c *gc.C) {
	s.BaseSuite.SetUpTest(c)
	s.PatchValue(diskmanager.BlockDeviceInUse, func(storage.BlockDevice) (bool, error) {
		return false, nil
	})
}

func (s *DiskManagerWorkerSuite) TestWorkerNoOp(c *gc.C) {
	done := make(chan struct{})
	var setDevices BlockDeviceSetterFunc = func(devices []storage.BlockDevice) error {
		close(done)
		return nil
	}

	var listDevices diskmanager.ListBlockDevicesFunc = func() ([]storage.BlockDevice, error) {
		return []storage.BlockDevice{{DeviceName: "whatever"}}, nil
	}

	w := diskmanager.NewWorker(listDevices, setDevices)
	defer w.Wait()
	defer w.Kill()

	select {
	case <-done:
	case <-time.After(coretesting.LongWait):
		c.Fatalf("timed out waiting for diskmanager to update")
	}
}

func (s *DiskManagerWorkerSuite) TestBlockDeviceChanges(c *gc.C) {
	var oldDevices []storage.BlockDevice
	var devicesSet [][]storage.BlockDevice
	var setDevices BlockDeviceSetterFunc = func(devices []storage.BlockDevice) error {
		devicesSet = append(devicesSet, devices)
		return nil
	}

	var listDevices diskmanager.ListBlockDevicesFunc = func() ([]storage.BlockDevice, error) {
		return []storage.BlockDevice{{DeviceName: "sda"}}, nil
	}
	for i := 0; i < 2; i++ {
		err := diskmanager.DoWork(listDevices, setDevices, &oldDevices)
		c.Assert(err, gc.IsNil)
	}

	listDevices = func() ([]storage.BlockDevice, error) {
		return []storage.BlockDevice{{DeviceName: "sdb"}}, nil
	}
	err := diskmanager.DoWork(listDevices, setDevices, &oldDevices)
	c.Assert(err, gc.IsNil)

	// diskmanager only calls the BlockDeviceSetter when it sees
	// a change in disks.
	c.Assert(devicesSet, gc.HasLen, 2)
	c.Assert(devicesSet[0], gc.DeepEquals, []storage.BlockDevice{{
		DeviceName: "sda",
	}})
	c.Assert(devicesSet[1], gc.DeepEquals, []storage.BlockDevice{{
		DeviceName: "sdb",
	}})
}

func (s *DiskManagerWorkerSuite) TestBlockDevicesSorted(c *gc.C) {
	var devicesSet [][]storage.BlockDevice
	var setDevices BlockDeviceSetterFunc = func(devices []storage.BlockDevice) error {
		devicesSet = append(devicesSet, devices)
		return nil
	}

	var listDevices diskmanager.ListBlockDevicesFunc = func() ([]storage.BlockDevice, error) {
		return []storage.BlockDevice{{
			DeviceName: "sdb",
		}, {
			DeviceName: "sda",
		}, {
			DeviceName: "sdc",
		}}, nil
	}
	err := diskmanager.DoWork(listDevices, setDevices, new([]storage.BlockDevice))
	c.Assert(err, gc.IsNil)

	// The block Devices should be sorted when passed to the block
	// device setter.
	c.Assert(devicesSet, gc.DeepEquals, [][]storage.BlockDevice{{{
		DeviceName: "sda",
	}, {
		DeviceName: "sdb",
	}, {
		DeviceName: "sdc",
	}}})
}

type BlockDeviceSetterFunc func([]storage.BlockDevice) error

func (f BlockDeviceSetterFunc) SetMachineBlockDevices(devices []storage.BlockDevice) error {
	return f(devices)
}
<commit_msg>worker/diskmanager: fix test name<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package diskmanager_test

import (
	"time"

	gc "gopkg.in/check.v1"

	"github.com/juju/juju/storage"
	coretesting "github.com/juju/juju/testing"
	"github.com/juju/juju/worker/diskmanager"
)

var _ = gc.Suite(&DiskManagerWorkerSuite{})

type DiskManagerWorkerSuite struct {
	coretesting.BaseSuite
}

func (s *DiskManagerWorkerSuite) SetUpTest(c *gc.C) {
	s.BaseSuite.SetUpTest(c)
	s.PatchValue(diskmanager.BlockDeviceInUse, func(storage.BlockDevice) (bool, error) {
		return false, nil
	})
}

func (s *DiskManagerWorkerSuite) TestWorker(c *gc.C) {
	done := make(chan struct{})
	var setDevices BlockDeviceSetterFunc = func(devices []storage.BlockDevice) error {
		close(done)
		return nil
	}

	var listDevices diskmanager.ListBlockDevicesFunc = func() ([]storage.BlockDevice, error) {
		return []storage.BlockDevice{{DeviceName: "whatever"}}, nil
	}

	w := diskmanager.NewWorker(listDevices, setDevices)
	defer w.Wait()
	defer w.Kill()

	select {
	case <-done:
	case <-time.After(coretesting.LongWait):
		c.Fatalf("timed out waiting for diskmanager to update")
	}
}

func (s *DiskManagerWorkerSuite) TestBlockDeviceChanges(c *gc.C) {
	var oldDevices []storage.BlockDevice
	var devicesSet [][]storage.BlockDevice
	var setDevices BlockDeviceSetterFunc = func(devices []storage.BlockDevice) error {
		devicesSet = append(devicesSet, devices)
		return nil
	}

	var listDevices diskmanager.ListBlockDevicesFunc = func() ([]storage.BlockDevice, error) {
		return []storage.BlockDevice{{DeviceName: "sda"}}, nil
	}
	for i := 0; i < 2; i++ {
		err := diskmanager.DoWork(listDevices, setDevices, &oldDevices)
		c.Assert(err, gc.IsNil)
	}

	listDevices = func() ([]storage.BlockDevice, error) {
		return []storage.BlockDevice{{DeviceName: "sdb"}}, nil
	}
	err := diskmanager.DoWork(listDevices, setDevices, &oldDevices)
	c.Assert(err, gc.IsNil)

	// diskmanager only calls the BlockDeviceSetter when it sees
	// a change in disks.
	c.Assert(devicesSet, gc.HasLen, 2)
	c.Assert(devicesSet[0], gc.DeepEquals, []storage.BlockDevice{{
		DeviceName: "sda",
	}})
	c.Assert(devicesSet[1], gc.DeepEquals, []storage.BlockDevice{{
		DeviceName: "sdb",
	}})
}

func (s *DiskManagerWorkerSuite) TestBlockDevicesSorted(c *gc.C) {
	var devicesSet [][]storage.BlockDevice
	var setDevices BlockDeviceSetterFunc = func(devices []storage.BlockDevice) error {
		devicesSet = append(devicesSet, devices)
		return nil
	}

	var listDevices diskmanager.ListBlockDevicesFunc = func() ([]storage.BlockDevice, error) {
		return []storage.BlockDevice{{
			DeviceName: "sdb",
		}, {
			DeviceName: "sda",
		}, {
			DeviceName: "sdc",
		}}, nil
	}
	err := diskmanager.DoWork(listDevices, setDevices, new([]storage.BlockDevice))
	c.Assert(err, gc.IsNil)

	// The block Devices should be sorted when passed to the block
	// device setter.
	c.Assert(devicesSet, gc.DeepEquals, [][]storage.BlockDevice{{{
		DeviceName: "sda",
	}, {
		DeviceName: "sdb",
	}, {
		DeviceName: "sdc",
	}}})
}

type BlockDeviceSetterFunc func([]storage.BlockDevice) error

func (f BlockDeviceSetterFunc) SetMachineBlockDevices(devices []storage.BlockDevice) error {
	return f(devices)
}
<|endoftext|>


<commit_before>package lib

import (
	"bytes"
	"crypto/tls"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/davecgh/go-spew/spew"
)

type redirectCaughtError struct{}

func (c redirectCaughtError) Error() string {
	return "redirect caught"
}

type proxyServer struct {
	local     string
	remote    string
	log       io.Writer
	rewriters []Rewriter
	mappings  []Mapping

	server http.Server
	client http.Client
}

func init() {
	_ = spew.UnsafeDisabled
}

func (p *proxyServer) ServeHTTP(outgoing http.ResponseWriter, incoming *http.Request) {
	var err error

	hostMappings := buildHostMappings(p.mappings, incoming.Host)
	for _, rewriter := range p.rewriters {
		rewriter.SetMappings(hostMappings)
	}

	proxyRequest, err := p.buildProxyRequest(incoming)
	var response *http.Response

	if err == nil {
		response, err = p.client.Do(proxyRequest)

		if isRedirectError(err) {
			err = nil
		}
	}

	if err != nil {
		fmt.Fprintf(p.log, "error during proxy request: %v\n", err)
		http.Error(outgoing, err.Error(), http.StatusBadGateway)
	} else {
		p.sendProxyResponse(incoming, response, outgoing)
	}
}

func isRedirectError(err error) (q bool) {
	urlError, q := err.(*url.Error)
	if !q {
		return
	}

	_, q = urlError.Err.(redirectCaughtError)

	return
}

func (p *proxyServer) buildProxyRequest(incoming *http.Request) (outgoing *http.Request, err error) {
	outgoing, err = http.NewRequest(incoming.Method, p.remote+incoming.RequestURI, incoming.Body)

	for key, values := range incoming.Header {
		for _, value := range values {
			outgoing.Header.Add(key, value)
		}
	}

	// We save compression for later
	outgoing.Header.Del("accept-encoding")

	for _, rewriter := range p.rewriters {
		if rewriter, ok := rewriter.(IncomingHeaderRewriter); ok {
			rewriter.RewriteIncomingHeaders(outgoing.Header)
		}
	}

	return
}

func (p *proxyServer) sendProxyResponse(request *http.Request, response *http.Response, outgoing http.ResponseWriter) {
	defer response.Body.Close()

	outgoingHeaders := outgoing.Header()

	for key, values := range response.Header {
		for _, value := range values {
			outgoingHeaders.Add(key, value)
		}
	}

	outgoingHeaders.Del("content-length")
	outgoingHeaders.Del("set-cookie")

	// We need to remove any domain information set on the cookies
	for _, cookie := range response.Cookies() {
		cookie.Domain = ""

		http.SetCookie(outgoing, cookie)
	}

	responseRewriters := make([]ResponseRewriter, 0, len(p.rewriters))

	for _, rewriter := range p.rewriters {
		if r, ok := rewriter.(ResponseRewriter); ok {
			if r.Matches(request, response) {
				responseRewriters = append(responseRewriters, r)
			}
		}

		if r, ok := rewriter.(HeaderRewriter); ok {
			r.RewriteHeaders(outgoingHeaders)
		}
	}

	outgoing.WriteHeader(response.StatusCode)

	if len(responseRewriters) > 0 {
		bodyData, err := ioutil.ReadAll(response.Body)

		if err == nil {
			for _, rewriter := range responseRewriters {
				bodyData = rewriter.RewriteResponse(bodyData)
			}
		} else {
			bodyData = make([]byte, 0)
		}

		io.Copy(outgoing, bytes.NewBuffer(bodyData))
	} else {
		io.Copy(outgoing, response.Body)
	}
}

func (p *proxyServer) Start() <-chan error {
	c := make(chan error, 1)

	go func() {
		c <- p.server.ListenAndServe()
	}()

	fmt.Fprintf(p.log, "proxying requests for %s to %s\n", p.local, p.remote)

	return c
}

func (p *proxyServer) AddRewriter(r Rewriter) {
	p.rewriters = append(p.rewriters, r)
}

func newProxyServer(m Mapping, mappings []Mapping, log io.Writer, sslAllowInsecure bool) (p *proxyServer, err error) {
	p = &proxyServer{
		local:     m.local,
		remote:    m.remote,
		log:       log,
		rewriters: make([]Rewriter, 0),
		mappings:  mappings,
	}

	p.server = http.Server{
		Addr:    p.local,
		Handler: p,
	}

	var tlsConfig *tls.Config
	if sslAllowInsecure {
		tlsConfig = &tls.Config{
			InsecureSkipVerify: true,
		}
	}

	p.client = http.Client{
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			return redirectCaughtError{}
		},
		Transport: &http.Transport{
			TLSClientConfig: tlsConfig,
		},
	}

	return
}
<commit_msg>Compress upstream communication, recompress rewritten response if applicable.<commit_after>package lib

import (
	"bytes"
	"compress/gzip"
	"crypto/tls"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"

	"github.com/davecgh/go-spew/spew"
)

type redirectCaughtError struct{}

func (c redirectCaughtError) Error() string {
	return "redirect caught"
}

type proxyServer struct {
	local     string
	remote    string
	log       io.Writer
	rewriters []Rewriter
	mappings  []Mapping

	server http.Server
	client http.Client
}

func init() {
	_ = spew.UnsafeDisabled
}

func (p *proxyServer) ServeHTTP(outgoing http.ResponseWriter, incoming *http.Request) {
	var err error

	hostMappings := buildHostMappings(p.mappings, incoming.Host)
	for _, rewriter := range p.rewriters {
		rewriter.SetMappings(hostMappings)
	}

	proxyRequest, err := p.buildProxyRequest(incoming)
	var response *http.Response

	if err == nil {
		response, err = p.client.Do(proxyRequest)

		if isRedirectError(err) {
			err = nil
		}
	}

	if err != nil {
		fmt.Fprintf(p.log, "error during proxy request: %v\n", err)
		http.Error(outgoing, err.Error(), http.StatusBadGateway)
	} else {
		p.sendProxyResponse(incoming, response, outgoing)
	}
}

func isRedirectError(err error) (q bool) {
	urlError, q := err.(*url.Error)
	if !q {
		return
	}

	_, q = urlError.Err.(redirectCaughtError)

	return
}

func (p *proxyServer) buildProxyRequest(incoming *http.Request) (outgoing *http.Request, err error) {
	outgoing, err = http.NewRequest(incoming.Method, p.remote+incoming.RequestURI, incoming.Body)

	for key, values := range incoming.Header {
		for _, value := range values {
			outgoing.Header.Add(key, value)
		}
	}

	for _, rewriter := range p.rewriters {
		if rewriter, ok := rewriter.(IncomingHeaderRewriter); ok {
			rewriter.RewriteIncomingHeaders(outgoing.Header)
		}
	}

	// We also try to compress upstream communication
	outgoing.Header.Set("accept-encoding", "gzip")

	return
}

func (p *proxyServer) sendProxyResponse(request *http.Request, response *http.Response, outgoing http.ResponseWriter) {
	defer response.Body.Close()

	// Transfer headers
	outgoingHeaders := outgoing.Header()
	for key, values := range response.Header {
		for _, value := range values {
			outgoingHeaders.Add(key, value)
		}
	}

	// Content length will be recalculated as content may be rewritten
	outgoingHeaders.Del("content-length")

	// We need to remove any domain information set on the cookies
	outgoingHeaders.Del("set-cookie")
	for _, cookie := range response.Cookies() {
		cookie.Domain = ""

		http.SetCookie(outgoing, cookie)
	}

	// First rewrite pass: identify matching content rewriters and process headers
	responseRewriters := make([]ResponseRewriter, 0, len(p.rewriters))

	for _, rewriter := range p.rewriters {
		if r, ok := rewriter.(ResponseRewriter); ok {
			if r.Matches(request, response) {
				responseRewriters = append(responseRewriters, r)
			}
		}

		if r, ok := rewriter.(HeaderRewriter); ok {
			r.RewriteHeaders(outgoingHeaders)
		}
	}

	// Handle compression
	bodyWriter := outgoing.(io.Writer)
	bodyReader := response.Body.(io.Reader)

	if response.Header.Get("content-encoding") == "gzip" {
		var err error
		bodyReader, err = gzip.NewReader(bodyReader)
		if err != nil {
			http.Error(outgoing, err.Error(), http.StatusBadGateway)
			return
		}

		// We are ignoring any q-value here, so this is wrong for the case q=0
		if strings.Contains(request.Header.Get("accept-encoding"), "gzip") {
			bodyWriter = gzip.NewWriter(bodyWriter)
			outgoingHeaders.Set("content-encoding", "gzip")
		} else {
			outgoingHeaders.Del("content-encoding")
		}
	}

	// Send headers
	outgoing.WriteHeader(response.StatusCode)

	// If there are any body rewriters, we read the response into memory and process it
	if len(responseRewriters) > 0 {
		bodyData, err := ioutil.ReadAll(bodyReader)

		if err == nil {
			for _, rewriter := range responseRewriters {
				bodyData = rewriter.RewriteResponse(bodyData)
			}
		} else {
			bodyData = make([]byte, 0)
		}

		bodyReader = bytes.NewBuffer(bodyData)
	}

	// Send body
	io.Copy(bodyWriter, bodyReader)

	// Close writer if applicable (looking at you, gzip)
	if bodyWriter, ok := bodyWriter.(io.Closer); ok {
		bodyWriter.Close()
	}
}

func (p *proxyServer) Start() <-chan error {
	c := make(chan error, 1)

	go func() {
		c <- p.server.ListenAndServe()
	}()

	fmt.Fprintf(p.log, "proxying requests for %s to %s\n", p.local, p.remote)

	return c
}

func (p *proxyServer) AddRewriter(r Rewriter) {
	p.rewriters = append(p.rewriters, r)
}

func newProxyServer(m Mapping, mappings []Mapping, log io.Writer, sslAllowInsecure bool) (p *proxyServer, err error) {
	p = &proxyServer{
		local:     m.local,
		remote:    m.remote,
		log:       log,
		rewriters: make([]Rewriter, 0),
		mappings:  mappings,
	}

	p.server = http.Server{
		Addr:    p.local,
		Handler: p,
	}

	var tlsConfig *tls.Config
	if sslAllowInsecure {
		tlsConfig = &tls.Config{
			InsecureSkipVerify: true,
		}
	}

	p.client = http.Client{
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			return redirectCaughtError{}
		},
		Transport: &http.Transport{
			// We rather handle compression ourselves
			DisableCompression: true,
			TLSClientConfig:    tlsConfig,
		},
	}

	return
}
<|endoftext|>


<commit_before>// Config package provides an implementation of ConfigProvider. Currently it only reads from a JSON file, but
// this may be extended to support other forms.
package config

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"strings"
)

// Concrete storage for the configuration values read. The map keys are dot-delimited.
type Config map[string]interface{}

// Create a new, empty Config
func NewConfig() Config {
	return make(map[string]interface{})
}

// ReadFromFile is a helper that reads the configuration from JSON in the provided path with default
// options. It is equivalent to creating a new empty config, and calling AddFile with no prefix.
func ReadFromFile(path string) (Config, error) {
	result := NewConfig()

	e := result.AddFile(path, "", false)

	return result, e
}

// ReadFromEnv is a helper that reads the configuration from environment variables. It is equivalent
// to creating a new empty config, and calling AddEnvironment with no destination prefix
// (i.e. environment variables with the given prefix are added to config at the top level.)
func ReadFromEnv(prefix string) Config {
	result := NewConfig()

	result.AddEnvironment(prefix, "", false)

	return result
}

// AddFile will read a config file and merge configuration that it contains into the Config
// instance. destPrefix can be used to ensure all settings from this file appear within a
// specific namespace within the Config instance. It can be "", in which case the new config
// entries occur at the top level.
// If settings in this file already exist in config, override determines whether the new settings
// will override existing settings (yes if true, no if false.)
// The config file is JSON and will generally contain a single object. The properties of that
// object form the top-level name space for Get().
func (c Config) AddFile(path string, destPrefix string, override bool) error {
	// read file
	data, e := ioutil.ReadFile(path)
	if e != nil {
		return e
	}

	// decode json
	var v interface{}
	e = json.Unmarshal(data, &v)
	if e != nil {
		return e
	}

	// Get this as a map
	nested := v.(map[string]interface{})

	c.nestedMerge(nested, destPrefix, override)

	return nil

}

// AddEnvironment will add configuration properties from the environment. Only environment
// variables that start with sourcePrefix will be added; if sourcePrefix is "", all environment
// variables will be added. destPrefix can be used to ensure that all settings to be loaded from
// the environment appear within a specific namespace within the Config instance. That too can be ""
// in which case the new Config entries appear at the top level.
// If settings in this file already exist in config, override determines whether the new settings
// will override existing settings (yes if true, no if false.)
func (c Config) AddEnvironment(sourcePrefix string, destPrefix string, override bool) {
	envs := os.Environ()
	nested := make(map[string]interface{})

	fmt.Printf("AddEnvironment: %s\n", envs)
	for _, x := range envs {
		// split on =
		kv := strings.SplitN(x, "=", 2)
		fmt.Printf("kv=%s\n", kv)

		// if name starts with the prefix, add it to nested for merging
		if len(kv) >= 1 && strings.HasPrefix(kv[0], sourcePrefix) {
			// this is a candidate
			var v string
			if len(kv) == 2 {
				v = kv[1]
			} else {
				v = ""
			}
			nested[kv[0]] = v
		}
	}

	fmt.Printf("AddEnvironment: nested: %s\n", nested)
	// merge it into the config
	c.nestedMerge(nested, destPrefix, override)
}

func (c Config) nestedMerge(object map[string]interface{}, prefix string, override bool) {
	p := prefix + "."
	if p == "." {
		p = ""
	}

	for k, v := range object {
		if reflect.TypeOf(v).Kind() == reflect.Map {
			// if 'v' is a map of interface{}, recursively add.
			c.nestedMerge(v.(map[string]interface{}), p+k, override)
		} else {
			// otherwise just add the property, using the prefix. If the value exists, use
			// override.
			_, exists := c[p+k]
			if !exists || override {
				c[p+k] = v
			}
		}
	}
}

// Get looks up an object in the map via a key. The key can have "." separators for names;
// this will go into the structure as appropriate. It will return nil if a key maps to an undefined
// property, or where a partial key is not an object.
func (c Config) Get(key string) interface{} {
	return c[key]
}

func (c Config) HasKey(key string) bool {
	return c[key] != nil
}

// AsString returns a key from the configuration (using Get), but returning it as a string.
// If the key is not defined, it returns "".
func (c Config) GetString(key string) string {
	v := c.Get(key)
	if v == nil {
		return ""
	}
	return fmt.Sprintf("%v", v)
}

// AsInt returns a key from the configuration as an integer. Integer values in the
// json file are retrieved as float64. This will return an error if it's not a float64.
// Otherwise will convert it to an int (with truncation).
func (c Config) GetInt(key string) int {
	v := c.Get(key)
	vv, ok := v.(float64)
	if !ok {
		return 0
	}
	return int(vv)
}

func (c Config) GetBool(key string) bool {
	v := c.Get(key)
	vv, ok := v.(bool)
	if !ok {
		return false
	}
	return bool(vv)
}

func (c Config) GetStringArray(key string) []string {
	v := c.Get(key)
	vv, ok := v.([]string)
	if !ok {
		return []string{}
	}
	return []string(vv)
}

func (c Config) AddDefault(key string, value string) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,"",false)
	return true,nil
}

func (c Config) AddDefaultOverride(key string, value string) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,"",true)
	return true,nil
}

func (c Config) AddDefaultString(key string, value string) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,"",false)
	return true,nil
}

func (c Config) AddDefaultStringOverride(key string, value string) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,"",true)
	return true,nil
}

func (c Config) AddDefaultBool(key string, value bool) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,"",false)
	return true,nil
}

func (c Config) AddDefaultBoolOverride(key string, value bool) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,"",true)
	return true,nil
}

func (c Config) AddDefaultStringArray(key string, value []string) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,"",false)
	return true,nil
}

func (c Config) AddDefaultStringArrayOverride(key string, value []string) (bool,error) {
	fmt.Println(key)
	fmt.Println(value)
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,"",true)
	return true,nil
}

func (c Config) AddDefaultInt(key string, value int) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,"",false)
	return true,nil
}

func (c Config) AddDefaultIntOverride(key string, value int) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,"",true)
	return true,nil
}

func (c Config) AddDefaultWithSection(key string, value string, section string) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,section,false)
	return true,nil
}

func (c Config) AddDefaultOverrideWithSection(key string, value string, section string) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,section,true)
	return true,nil
}

func (c Config) AddDefaultStringWithSection(key string, value string, section string) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,section,false)
	return true,nil
}

func (c Config) AddDefaultStringOverrideWithSection(key string, value string, section string) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,section,true)
	return true,nil
}

func (c Config) AddDefaultIntWithSection(key string, value int, section string) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,section,false)
	return true,nil
}

func (c Config) AddDefaultIntOverrideWithSection(key string, value int, section string) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,section,true)
	return true,nil
}
<commit_msg>Update config.go<commit_after>// Config package provides an implementation of ConfigProvider. Currently it only reads from a JSON file, but
// this may be extended to support other forms.
package config

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"strings"
)

// Concrete storage for the configuration values read. The map keys are dot-delimited.
type Config map[string]interface{}

// Create a new, empty Config
func NewConfig() Config {
	return make(map[string]interface{})
}

// ReadFromFile is a helper that reads the configuration from JSON in the provided path with default
// options. It is equivalent to creating a new empty config, and calling AddFile with no prefix.
func ReadFromFile(path string) (Config, error) {
	result := NewConfig()

	e := result.AddFile(path, "", false)

	return result, e
}

// ReadFromEnv is a helper that reads the configuration from environment variables. It is equivalent
// to creating a new empty config, and calling AddEnvironment with no destination prefix
// (i.e. environment variables with the given prefix are added to config at the top level.)
func ReadFromEnv(prefix string) Config {
	result := NewConfig()

	result.AddEnvironment(prefix, "", false)

	return result
}

// AddFile will read a config file and merge configuration that it contains into the Config
// instance. destPrefix can be used to ensure all settings from this file appear within a
// specific namespace within the Config instance. It can be "", in which case the new config
// entries occur at the top level.
// If settings in this file already exist in config, override determines whether the new settings
// will override existing settings (yes if true, no if false.)
// The config file is JSON and will generally contain a single object. The properties of that
// object form the top-level name space for Get().
func (c Config) AddFile(path string, destPrefix string, override bool) error {
	// read file
	data, e := ioutil.ReadFile(path)
	if e != nil {
		return e
	}

	// decode json
	var v interface{}
	e = json.Unmarshal(data, &v)
	if e != nil {
		return e
	}

	// Get this as a map
	nested := v.(map[string]interface{})

	c.nestedMerge(nested, destPrefix, override)

	return nil

}

// AddEnvironment will add configuration properties from the environment. Only environment
// variables that start with sourcePrefix will be added; if sourcePrefix is "", all environment
// variables will be added. destPrefix can be used to ensure that all settings to be loaded from
// the environment appear within a specific namespace within the Config instance. That too can be ""
// in which case the new Config entries appear at the top level.
// If settings in this file already exist in config, override determines whether the new settings
// will override existing settings (yes if true, no if false.)
func (c Config) AddEnvironment(sourcePrefix string, destPrefix string, override bool) {
	envs := os.Environ()
	nested := make(map[string]interface{})

	fmt.Printf("AddEnvironment: %s\n", envs)
	for _, x := range envs {
		// split on =
		kv := strings.SplitN(x, "=", 2)
		fmt.Printf("kv=%s\n", kv)

		// if name starts with the prefix, add it to nested for merging
		if len(kv) >= 1 && strings.HasPrefix(kv[0], sourcePrefix) {
			// this is a candidate
			var v string
			if len(kv) == 2 {
				v = kv[1]
			} else {
				v = ""
			}
			nested[kv[0]] = v
		}
	}

	fmt.Printf("AddEnvironment: nested: %s\n", nested)
	// merge it into the config
	c.nestedMerge(nested, destPrefix, override)
}

func (c Config) nestedMerge(object map[string]interface{}, prefix string, override bool) {
	p := prefix + "."
	if p == "." {
		p = ""
	}

	for k, v := range object {
		if reflect.TypeOf(v).Kind() == reflect.Map {
			// if 'v' is a map of interface{}, recursively add.
			c.nestedMerge(v.(map[string]interface{}), p+k, override)
		} else {
			// otherwise just add the property, using the prefix. If the value exists, use
			// override.
			_, exists := c[p+k]
			if !exists || override {
				c[p+k] = v
			}
		}
	}
}

// Get looks up an object in the map via a key. The key can have "." separators for names;
// this will go into the structure as appropriate. It will return nil if a key maps to an undefined
// property, or where a partial key is not an object.
func (c Config) Get(key string) interface{} {
	return c[key]
}

func (c Config) HasKey(key string) bool {
	return c[key] != nil
}

// AsString returns a key from the configuration (using Get), but returning it as a string.
// If the key is not defined, it returns "".
func (c Config) GetString(key string) string {
	v := c.Get(key)
	if v == nil {
		return ""
	}
	return fmt.Sprintf("%v", v)
}

// AsInt returns a key from the configuration as an integer. Integer values in the
// json file are retrieved as float64. This will return an error if it's not a float64.
// Otherwise will convert it to an int (with truncation).
func (c Config) GetInt(key string) int {
	v := c.Get(key)
	vv, ok := v.(float64)
	if !ok {
		return 0
	}
	return int(vv)
}

func (c Config) GetBool(key string) bool {
	v := c.Get(key)
	vv, ok := v.(bool)
	if !ok {
		return false
	}
	return bool(vv)
}

func (c Config) GetStringArray(key string) []string {
	v := c.Get(key)
	vv, ok := v.([]string)
	if !ok {
		return []string{}
	}
	return []string(vv)
}

func (c Config) AddDefault(key string, value string) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,"",false)
	return true,nil
}

func (c Config) AddDefaultOverride(key string, value string) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,"",true)
	return true,nil
}

func (c Config) AddDefaultString(key string, value string) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,"",false)
	return true,nil
}

func (c Config) AddDefaultStringOverride(key string, value string) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,"",true)
	return true,nil
}

func (c Config) AddDefaultBool(key string, value bool) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,"",false)
	return true,nil
}

func (c Config) AddDefaultBoolOverride(key string, value bool) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,"",true)
	return true,nil
}

func (c Config) AddDefaultStringArray(key string, value []string) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,"",false)
	return true,nil
}

func (c Config) AddDefaultStringArrayOverride(key string, value []string) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,"",true)
	return true,nil
}

func (c Config) AddDefaultInt(key string, value int) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,"",false)
	return true,nil
}

func (c Config) AddDefaultIntOverride(key string, value int) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,"",true)
	return true,nil
}

func (c Config) AddDefaultWithSection(key string, value string, section string) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,section,false)
	return true,nil
}

func (c Config) AddDefaultOverrideWithSection(key string, value string, section string) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,section,true)
	return true,nil
}

func (c Config) AddDefaultStringWithSection(key string, value string, section string) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,section,false)
	return true,nil
}

func (c Config) AddDefaultStringOverrideWithSection(key string, value string, section string) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,section,true)
	return true,nil
}

func (c Config) AddDefaultIntWithSection(key string, value int, section string) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,section,false)
	return true,nil
}

func (c Config) AddDefaultIntOverrideWithSection(key string, value int, section string) (bool,error) {
	tmp := make(map[string]interface{})
	tmp[key] = value
	c.nestedMerge(tmp,section,true)
	return true,nil
}
<|endoftext|>


<commit_before>/*
   Copyright 2014 Outbrain Inc.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package process

import (
	"crypto/rand"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"time"
)

func GetHash(input []byte) string {
	hasher := sha256.New()
	hasher.Write(input)
	return hex.EncodeToString(hasher.Sum(nil))
}

func GetRandomData() []byte {
	size := 64
	rb := make([]byte, size)
	_, _ = rand.Read(rb)
	return rb
}

// Token is used to identify and validate requests to this service
type Token struct {
	Hash string
}

var ProcessToken *Token = NewToken()

func NewToken() *Token {
	return &Token{
		Hash: GetHash(GetRandomData()),
	}
}

func PrettyUniqueToken() string {
	return fmt.Sprintf("%d:%s", time.Now().Nanosecond(), NewToken().Hash)
}
<commit_msg>improved PrettyUniqueToken()<commit_after>/*
   Copyright 2014 Outbrain Inc.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package process

import (
	"crypto/rand"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"time"
)

func GetHash(input []byte) string {
	hasher := sha256.New()
	hasher.Write(input)
	return hex.EncodeToString(hasher.Sum(nil))
}

func GetRandomData() []byte {
	size := 64
	rb := make([]byte, size)
	_, _ = rand.Read(rb)
	return rb
}

// Token is used to identify and validate requests to this service
type Token struct {
	Hash string
}

var ProcessToken *Token = NewToken()

func NewToken() *Token {
	return &Token{
		Hash: GetHash(GetRandomData()),
	}
}

func PrettyUniqueToken() string {
	return fmt.Sprintf("%d:%s", time.Now().UnixNano(), NewToken().Hash)
}
<|endoftext|>