<commit_before>package main

import (
  "bytes"
  "crypto/hmac"
  "crypto/sha512"
  "flag"
  "fmt"
  "io/ioutil"
  "encoding/base64"
  "encoding/json"
  "encoding/hex"
//  "hash"
  "math"
  "net/http"
  "net/url"
  // "reflect"
  "golang.org/x/crypto/scrypt"
  )

const GetSaltURL string = "https://keybase.io/_/api/1.0/getsalt.json"
const LoginURL string = "https://keybase.io/_/api/1.0/login.json"

type User struct {
  Name string
  Salt string
}

type Session struct {
  CSRFToken string
  LoginSession string
}

func (user *User) GetSalt() (Session, error) {
  fmt.Println(user.Name)

  getSaltUrl, err := url.Parse(GetSaltURL)
  if err != nil {
    return Session{}, err
  }

//  fmt.Println(getSaltUrl)

  params := url.Values{}
  params.Add("email_or_username", user.Name)

  getSaltUrl.RawQuery = params.Encode()

//  fmt.Println(getSaltUrl)

  resp, err := http.Get(getSaltUrl.String())
  if err != nil {
    return Session{}, err
  }
  defer resp.Body.Close()

  body, err := ioutil.ReadAll(resp.Body)
  if err != nil {
    return Session{}, err
  }

//  fmt.Println(string(body))

  var saltParams map[string]interface{}
  err = json.Unmarshal(body, &saltParams)
  if err != nil {
    return Session{}, err
  }

//  fmt.Println(saltParams)
  // fmt.Println("----------RETURNED VALUES----------")
  // for k, v := range saltParams {
  //   fmt.Println(k, ":", v, ":", reflect.TypeOf(v))
  // }

  user.Salt = saltParams["salt"].(string)
  csrfToken := saltParams["csrf_token"].(string)
  loginSession := saltParams["login_session"].(string)

  // fmt.Println("CSRF Token:", csrfToken)
  // fmt.Println("Salt:", user.Salt)
  // fmt.Println("Login Session:", loginSession)
//  fmt.Printf("%+v", saltParams)

  session := Session{CSRFToken: csrfToken, LoginSession: loginSession}

  return session, nil

}

func (user *User) Login(session Session, passphrase string) (error) {

  decodedSalt, err := hex.DecodeString(user.Salt)
  if err != nil {
    return err
  }

  hash, err := scrypt.Key([]byte(passphrase), decodedSalt, int(math.Pow(2, 15)), 8, 1, 224)
  if err != nil {
    return err
  }

  pwh := hash[192:224]

  b64Session, err := base64.StdEncoding.DecodeString(session.LoginSession)
  if err != nil {
    return err
  }

  fmt.Println("Hash:", pwh)
  fmt.Println("Hash HEX:", hex.EncodeToString(pwh))
  fmt.Println("Hash b64:", b64Session)

  hmac := hmac.New(sha512.New, pwh)
  hmac.Write(b64Session)

  fmt.Println("HMAC:", hmac.Sum(nil))
  fmt.Println("HMAC STD B64:", base64.StdEncoding.EncodeToString(hmac.Sum(nil)))
  fmt.Println("HMAC URL B64:", base64.URLEncoding.EncodeToString(hmac.Sum(nil)))
  fmt.Println("HMAC HEX:", hex.EncodeToString(hmac.Sum(nil)))

  loginValues := map[string]string{"email_or_username" : user.Name,
                                   "hmac_pwh" : hex.EncodeToString(hmac.Sum(nil)),
                                   "login_session" : session.LoginSession}

  loginJSON, err := json.Marshal(loginValues)
  if err != nil {
    return err
  }

  fmt.Println("JSON Login Data:", string(loginJSON))

//  return err

  // params := url.Values{}
  // params.Add("email_or_username", user.Name)
  // params.Add("hmac_pwh", hex.EncodeToString(hmac.Sum(nil)))
  // params.Add("login_session", session.LoginSession)

  client := &http.Client{}
  r, err := http.NewRequest("POST", LoginURL, bytes.NewBuffer(loginJSON))
//  r, err := http.NewRequest("POST", LoginURL, bytes.NewBufferString(params.Encode()))
  if err != nil {
    return err
  }

  r.Header.Add("Content-Type", "application/json")
  fmt.Println(r)
  fmt.Println(r.Body)

//  return err

  resp, err := client.Do(r)
  if err != nil {
    return err
  }
  defer resp.Body.Close()

  body, err := ioutil.ReadAll(resp.Body)
  if err != nil {
    return err
  }

  fmt.Println(string(body))

  return err
}

func main() {
  user := flag.String("user", "", "User name or e-mail address.")
  passphrase := flag.String("passphrase", "", "Passphrase.")

   flag.Parse()
  // fmt.Println("User: ", *user)
  // fmt.Println("Passphrase: ", *passphrase)

  me := User{Name: *user}

  session, err := me.GetSalt()
  if err != nil {
    panic(err)
  }

  err = me.Login(session, *passphrase)

  // fmt.Println("Session:", "CSRFToken:", session.CSRFToken, ":LoginSession:", session.LoginSession)
  // fmt.Println("Me:", me)
}
<commit_msg>Cleaned the ever living shit out of the code.<commit_after>package main

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha512"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"math"
	"net/http"
	"net/url"

	"golang.org/x/crypto/scrypt"
)

// Session stores information related to identifying a user to Keybase.io.
type Session struct {
	CSRFToken     string
	SessionCookie string
}

// User stores information related to a Keybase.io user.
type User struct {
	Name string
	Salt string

	Identity *Session
}

// The URL used to get a user's salt.
const GetSaltURL string = "https://keybase.io/_/api/1.0/getsalt.json"

// The URL used to log into Keybase.io.
const LoginURL string = "https://keybase.io/_/api/1.0/login.json"

// Performs an HTTP GET operation against a selected URL with specified
// parameters. The function will return the response's body and any cookies
// received since those are the values that Keybase's API rely on.
func get(URL string, params url.Values) ([]byte, []*http.Cookie, error) {

	getURL, err := url.Parse(GetSaltURL)
	if err != nil {
		return nil, nil, err
	}

	getURL.RawQuery = params.Encode()

	client := &http.Client{}
	request, err := http.NewRequest("GET", getURL.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	resp, err := client.Do(request)
	if err != nil {
		return nil, nil, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, nil, err
	}

	return body, resp.Cookies(), nil
}

// Performs an HTTP POST operation against a selected URL with the specified
// JSON values. The function will return the response's body and any cookies
// received since those are the values that Keybase's API rely on.
func post(URL string, params map[string]string) ([]byte, []*http.Cookie, error) {

	jsonValues, err := json.Marshal(params)
	if err != nil {
		return nil, nil, err
	}

	client := &http.Client{}
	request, err := http.NewRequest("POST", URL, bytes.NewBuffer(jsonValues))
	if err != nil {
		return nil, nil, err
	}

	request.Header.Add("Content-Type", "application/json")

	resp, err := client.Do(request)
	if err != nil {
		return nil, nil, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, nil, err
	}

	return body, resp.Cookies(), nil
}

// Takes the body returned from Keybase.io's getsalt API call and parses into
// retrieves the desired data from the returned JSON data.
func parseSalt(body []byte) (string, string, string, error) {
	var saltParams map[string]interface{}
	err := json.Unmarshal(body, &saltParams)
	if err != nil {
		return "", "", "", err
	}

	salt := saltParams["salt"].(string)
	csrfToken := saltParams["csrf_token"].(string)
	loginSession := saltParams["login_session"].(string)

	return salt, csrfToken, loginSession, nil
}

func (user *User) getSalt() (string, error) {
	params := url.Values{}
	params.Add("email_or_username", user.Name)

	body, _, err := get(GetSaltURL, params)
	if err != nil {
		return "", err
	}

	salt, csrfToken, loginSession, err := parseSalt(body)
	if err != nil {
		return "", err
	}

	user.Salt = salt
	user.Identity.CSRFToken = csrfToken

	return loginSession, nil
}

// Hash's a user's password use Scrypt.
func hashPassphrase(salt string, passphrase string) ([]byte, error) {
	decodedSalt, err := hex.DecodeString(salt)
	if err != nil {
		return nil, err
	}

	hash, err := scrypt.Key([]byte(passphrase), decodedSalt, int(math.Pow(2, 15)), 8, 1, 224)
	if err != nil {
		return nil, err
	}

	pwh := hash[192:224]

	return pwh, nil
}

// Creates an HMAC from the user's hashed password at the current login session.
func getHMAC(pwh []byte, loginSession string) ([]byte, error) {
	b64Session, err := base64.StdEncoding.DecodeString(loginSession)
	if err != nil {
		return nil, err
	}

	hmacPWH := hmac.New(sha512.New, pwh)
	hmacPWH.Write(b64Session)

	return hmacPWH.Sum(nil), nil
}

// Login creates a session for a user.
func (user *User) Login(passphrase string) error {
	loginSession, err := user.getSalt()
	if err != nil {
		return err
	}

	pwh, err := hashPassphrase(user.Salt, passphrase)
	if err != nil {
		return err
	}

	hmacPWH, err := getHMAC(pwh, loginSession)
	if err != nil {
		return nil
	}

	loginValues := map[string]string{"email_or_username": user.Name,
		"hmac_pwh":      hex.EncodeToString(hmacPWH),
		"login_session": loginSession}

	body, cookies, err := post(LoginURL, loginValues)
	if err != nil {
		return err
	}

	for _, cookie := range cookies {
		if cookie.Name == "session" {
			user.Identity.SessionCookie = cookie.Value
		}
	}

	fmt.Println("Login Body:", string(body))

	return nil
}

func main() {
	user := flag.String("user", "", "User name or e-mail address.")
	passphrase := flag.String("passphrase", "", "Passphrase.")

	flag.Parse()

	session := Session{}
	me := User{Name: *user, Identity: &session}

	err := me.Login(*passphrase)
	if err != nil {
		panic(err)
	}

	// salt, csrfToken, loginSession, err := GetSalt(*user)
	// if err != nil {
	// 	panic(err)
	// }

	//	Login(*user, salt, loginSession, *passphrase)

	fmt.Println("Returned GetSalt values for user", *user, ".")
	fmt.Println("salt:", me.Salt)
	fmt.Println("csrfToken:", me.Identity.CSRFToken)
	// fmt.Println("loginSession:", loginSession)
}
<|endoftext|>


<commit_before>package web

import (
	"github.com/Masterminds/cookoo"
	"log"
	"net/http"
	"runtime"
)

// Create a new Cookoo web server.
//
// Important details:
//
// - A URIPathResolver is used for resolving request names.
// - The following datasources are added to the Context:
//   * url: A URLDatasource (Provides access to parts of the URL)
//   * path: A PathDatasource (Provides access to parts of a path. E.g. "/foo/bar")
//   * query: A QueryParameterDatasource (Provides access to URL query parameters.)
//   * post: A FormValuesDatasource (Provides access to form data or the body of a request.)
// - The following context variables are set:
//   * http.Request: A pointer to the http.Request object
//   * http.ResponseWriter: The response writer.
//   * server.Address: The server's address and port (NOT ALWAYS PRESENT)
// - The handler includes logic to redirect "not found" errors to a path named "@404" if present.
//
// Context Params:
//
// - server.Address: If this key exists in the context, it will be used to determine the host/port the
//   server runes on. EXPERIMENTAL. Default is ":8080".
//
// Example:
//
//    package main
//
//    import (
//      //This is the path to Cookoo
//      "github.com/Masterminds/cookoo/src/cookoo"
//      "github.com/Masterminds/cookoo/src/cookoo/web"
//      "fmt"
//    )
//
//    func main() {
//      // Build a new Cookoo app.
//      handler, registry, router, context := cookoo.Cookoo()
//
//      // Fill the registry.
//      registry.Route("GET /", "The index").Does(web.Flush, "example").
//      	Using("content").WithDefault("Hello World")
//      	Using("writer").From(
//
//    	// Create a server
//    	web.Serve(reg, router, cookoo.SyncContext(cxt))
//    }
//
// Note that we synchronize the context before passing it into Serve(). This
// is optional because each handler gets its own copy of the context already.
// However, if commands pass the context to goroutines, the context ought to be
// synchronized to avoid race conditions.
//
// Note that copies of the context are not synchronized with each other.
// So by declaring the context synchronized here, you
// are not therefore synchronizing across handlers.
func Serve(reg *cookoo.Registry, router *cookoo.Router, cxt cookoo.Context) {
	defer shutdown(router, cxt)

	addr := cxt.Get("server.Address", ":8080").(string)

	handler := NewCookooHandler(reg, router, cxt)
	http.Handle("/", handler)
	err := http.ListenAndServe(addr, nil)
	if err != nil {
		cxt.Logf("error", "Caught error while serving: %s", err)
		if router.HasRoute("@crash") {
			router.HandleRequest("@crash", cxt, false)
		}
	}
	// TODO: Need to figure out how to trap signals here, instead of outside.
}
func shutdown(router *cookoo.Router, cxt cookoo.Context) {
	log.Print("Shutdown")
	if router.HasRoute("@shutdown") {
		router.HandleRequest("@shutdown", cxt, false)
	}
}

// The handler for Cookoo.
// You way use this handler in your own web apps, or you can use
// the Serve() function to create and manage a handler for you.
type CookooHandler struct {
	Registry    *cookoo.Registry
	Router      *cookoo.Router
	BaseContext cookoo.Context
}

// Create a new Cookoo HTTP handler.
//
// This will create an HTTP hanlder, but will not automatically attach it to a server. Implementors
// can take the handler and attach it to an existing HTTP server wiht http.HandleFunc() or
// http.ListenAndServe().
//
// For simple web servers, using this package's Serve() function may be the easier route.
//
// Important details:
//
// - A URIPathResolver is used for resolving request names.
// - The following datasources are added to the Context:
//   * url: A URLDatasource (Provides access to parts of the URL)
//   * path: A PathDatasource (Provides access to parts of a path. E.g. "/foo/bar")
//   * query: A QueryParameterDatasource (Provides access to URL query parameters.)
//   * post: A FormValuesDatasource (Provides access to form data or the body of a request.)
// - The following context variables are set:
//   * http.Request: A pointer to the http.Request object
//   * http.ResponseWriter: The response writer.
//   * server.Address: The server's address and port (NOT ALWAYS PRESENT)
func NewCookooHandler(reg *cookoo.Registry, router *cookoo.Router, cxt cookoo.Context) *CookooHandler {
	handler := new(CookooHandler)
	handler.Registry = reg
	handler.Router = router
	handler.BaseContext = cxt

	// Use the URI oriented request resolver in this package.
	resolver := new(URIPathResolver)
	resolver.Init(reg)
	router.SetRequestResolver(resolver)

	return handler
}

// Adds the built-in HTTP-specific datasources.
func (h *CookooHandler) addDatasources(cxt cookoo.Context, req *http.Request) {
	parsedURL := req.URL
	urlDS := new(URLDatasource).Init(parsedURL)
	queryDS := new(QueryParameterDatasource).Init(parsedURL.Query())
	formDS := new(FormValuesDatasource).Init(req)
	pathDS := new(PathDatasource).Init(parsedURL.Path)

	cxt.AddDatasource("url", urlDS)
	cxt.AddDatasource("query", queryDS)
	// cxt.AddDatasource("q", queryDS)
	cxt.AddDatasource("post", formDS)
	cxt.AddDatasource("path", pathDS)
}

// ServeHTTP is the Cookoo request handling function.
//
// This is capable of handling HTTP and HTTPS requests.
func (h *CookooHandler) ServeHTTP(res http.ResponseWriter, req *http.Request) {
	// First we need to clone the context so we have a mutable copy.
	cxt := h.BaseContext.Copy()
	// Trap panics and make them 500 errors:
	defer func() {
		// fmt.Printf("Deferred function executed for path %s\n", req.URL.Path)
		if err := recover(); err != nil {
			//log.Printf("FOUND ERROR: %v", err)
			where := cxt.Get("command.Name", "<unknown>").(string)
			rname := cxt.Get("route.Name", "<unknown>").(string)
			h.BaseContext.Logf("error", "CookooHandler trapped a panic on route '%s' in command '%s': %v", rname, where, err)

			// Buffer for a stack trace.
			// This is pretty much always worthless, as the stack has been
			// unwound up to here.
			stack := make([]byte, 8192)
			size := runtime.Stack(stack, false)
			h.BaseContext.Logf("error", "Stack: %s", stack)

			if size == 8192 {
				h.BaseContext.Logf("error", "<truncated stack trace at 8192 bytes>")
			}

			http.Error(res, "An internal error occurred.", http.StatusInternalServerError)
		}
	}()

	cxt.Put("http.Request", req)
	cxt.Put("http.ResponseWriter", res)

	// Next, we add the datasources for URL and Query params.
	h.addDatasources(cxt, req)

	// Find the route
	path := req.Method + " " + req.URL.Path

	cxt.Logf("info", "Handling request for %s\n", path)

	// If a route matches, run it.
	err := h.Router.HandleRequest(path, cxt, true)
	if err != nil {
		switch err.(type) {

		// For a 404, we bail.
		case *cookoo.RouteError:
			if h.Router.HasRoute("@404") {
				h.Router.HandleRequest("@404", cxt, false)
			} else {
				http.NotFound(res, req)
			}
			return
		// For any other, we go to a 500.
		case *cookoo.FatalError:
			cxt.Logf("error", "Fatal Error on route '%s': %s", path, err)
		default:
			cxt.Logf("error", "Untagged error on route '%s': %v (%T)", path, err, err)
		}

		if h.Router.HasRoute("@500") {
			cxt.Put("error", err)
			h.Router.HandleRequest("@500", cxt, false)
		} else {
			// Passing the error back to the client is a bad default.
			//http.Error(res, err.Error(), http.StatusInternalServerError)
			http.Error(res, "Internal error processing the request.", http.StatusInternalServerError)
		}
	}
}
<commit_msg>Minor docs fix to web server example.<commit_after>package web

import (
	"github.com/Masterminds/cookoo"
	"log"
	"net/http"
	"runtime"
)

// Create a new Cookoo web server.
//
// Important details:
//
// - A URIPathResolver is used for resolving request names.
// - The following datasources are added to the Context:
//   * url: A URLDatasource (Provides access to parts of the URL)
//   * path: A PathDatasource (Provides access to parts of a path. E.g. "/foo/bar")
//   * query: A QueryParameterDatasource (Provides access to URL query parameters.)
//   * post: A FormValuesDatasource (Provides access to form data or the body of a request.)
// - The following context variables are set:
//   * http.Request: A pointer to the http.Request object
//   * http.ResponseWriter: The response writer.
//   * server.Address: The server's address and port (NOT ALWAYS PRESENT)
// - The handler includes logic to redirect "not found" errors to a path named "@404" if present.
//
// Context Params:
//
// - server.Address: If this key exists in the context, it will be used to determine the host/port the
//   server runes on. EXPERIMENTAL. Default is ":8080".
//
// Example:
//
//    package main
//
//    import (
//      //This is the path to Cookoo
//      "github.com/Masterminds/cookoo"
//      "github.com/Masterminds/cookoo/web"
//      "fmt"
//    )
//
//    func main() {
//      // Build a new Cookoo app.
//      handler, registry, router, context := cookoo.Cookoo()
//
//      // Fill the registry.
//      registry.Route("GET /", "The index").Does(web.Flush, "example").
//      	Using("content").WithDefault("Hello World")
//
//    	// Create a server
//    	web.Serve(reg, router, cookoo.SyncContext(cxt))
//    }
//
// Note that we synchronize the context before passing it into Serve(). This
// is optional because each handler gets its own copy of the context already.
// However, if commands pass the context to goroutines, the context ought to be
// synchronized to avoid race conditions.
//
// Note that copies of the context are not synchronized with each other.
// So by declaring the context synchronized here, you
// are not therefore synchronizing across handlers.
func Serve(reg *cookoo.Registry, router *cookoo.Router, cxt cookoo.Context) {
	defer shutdown(router, cxt)

	addr := cxt.Get("server.Address", ":8080").(string)

	handler := NewCookooHandler(reg, router, cxt)
	http.Handle("/", handler)
	err := http.ListenAndServe(addr, nil)
	if err != nil {
		cxt.Logf("error", "Caught error while serving: %s", err)
		if router.HasRoute("@crash") {
			router.HandleRequest("@crash", cxt, false)
		}
	}
	// TODO: Need to figure out how to trap signals here, instead of outside.
}
func shutdown(router *cookoo.Router, cxt cookoo.Context) {
	log.Print("Shutdown")
	if router.HasRoute("@shutdown") {
		router.HandleRequest("@shutdown", cxt, false)
	}
}

// The handler for Cookoo.
// You way use this handler in your own web apps, or you can use
// the Serve() function to create and manage a handler for you.
type CookooHandler struct {
	Registry    *cookoo.Registry
	Router      *cookoo.Router
	BaseContext cookoo.Context
}

// Create a new Cookoo HTTP handler.
//
// This will create an HTTP hanlder, but will not automatically attach it to a server. Implementors
// can take the handler and attach it to an existing HTTP server wiht http.HandleFunc() or
// http.ListenAndServe().
//
// For simple web servers, using this package's Serve() function may be the easier route.
//
// Important details:
//
// - A URIPathResolver is used for resolving request names.
// - The following datasources are added to the Context:
//   * url: A URLDatasource (Provides access to parts of the URL)
//   * path: A PathDatasource (Provides access to parts of a path. E.g. "/foo/bar")
//   * query: A QueryParameterDatasource (Provides access to URL query parameters.)
//   * post: A FormValuesDatasource (Provides access to form data or the body of a request.)
// - The following context variables are set:
//   * http.Request: A pointer to the http.Request object
//   * http.ResponseWriter: The response writer.
//   * server.Address: The server's address and port (NOT ALWAYS PRESENT)
func NewCookooHandler(reg *cookoo.Registry, router *cookoo.Router, cxt cookoo.Context) *CookooHandler {
	handler := new(CookooHandler)
	handler.Registry = reg
	handler.Router = router
	handler.BaseContext = cxt

	// Use the URI oriented request resolver in this package.
	resolver := new(URIPathResolver)
	resolver.Init(reg)
	router.SetRequestResolver(resolver)

	return handler
}

// Adds the built-in HTTP-specific datasources.
func (h *CookooHandler) addDatasources(cxt cookoo.Context, req *http.Request) {
	parsedURL := req.URL
	urlDS := new(URLDatasource).Init(parsedURL)
	queryDS := new(QueryParameterDatasource).Init(parsedURL.Query())
	formDS := new(FormValuesDatasource).Init(req)
	pathDS := new(PathDatasource).Init(parsedURL.Path)

	cxt.AddDatasource("url", urlDS)
	cxt.AddDatasource("query", queryDS)
	// cxt.AddDatasource("q", queryDS)
	cxt.AddDatasource("post", formDS)
	cxt.AddDatasource("path", pathDS)
}

// ServeHTTP is the Cookoo request handling function.
//
// This is capable of handling HTTP and HTTPS requests.
func (h *CookooHandler) ServeHTTP(res http.ResponseWriter, req *http.Request) {
	// First we need to clone the context so we have a mutable copy.
	cxt := h.BaseContext.Copy()
	// Trap panics and make them 500 errors:
	defer func() {
		// fmt.Printf("Deferred function executed for path %s\n", req.URL.Path)
		if err := recover(); err != nil {
			//log.Printf("FOUND ERROR: %v", err)
			where := cxt.Get("command.Name", "<unknown>").(string)
			rname := cxt.Get("route.Name", "<unknown>").(string)
			h.BaseContext.Logf("error", "CookooHandler trapped a panic on route '%s' in command '%s': %v", rname, where, err)

			// Buffer for a stack trace.
			// This is pretty much always worthless, as the stack has been
			// unwound up to here.
			stack := make([]byte, 8192)
			size := runtime.Stack(stack, false)
			h.BaseContext.Logf("error", "Stack: %s", stack)

			if size == 8192 {
				h.BaseContext.Logf("error", "<truncated stack trace at 8192 bytes>")
			}

			http.Error(res, "An internal error occurred.", http.StatusInternalServerError)
		}
	}()

	cxt.Put("http.Request", req)
	cxt.Put("http.ResponseWriter", res)

	// Next, we add the datasources for URL and Query params.
	h.addDatasources(cxt, req)

	// Find the route
	path := req.Method + " " + req.URL.Path

	cxt.Logf("info", "Handling request for %s\n", path)

	// If a route matches, run it.
	err := h.Router.HandleRequest(path, cxt, true)
	if err != nil {
		switch err.(type) {

		// For a 404, we bail.
		case *cookoo.RouteError:
			if h.Router.HasRoute("@404") {
				h.Router.HandleRequest("@404", cxt, false)
			} else {
				http.NotFound(res, req)
			}
			return
		// For any other, we go to a 500.
		case *cookoo.FatalError:
			cxt.Logf("error", "Fatal Error on route '%s': %s", path, err)
		default:
			cxt.Logf("error", "Untagged error on route '%s': %v (%T)", path, err, err)
		}

		if h.Router.HasRoute("@500") {
			cxt.Put("error", err)
			h.Router.HandleRequest("@500", cxt, false)
		} else {
			// Passing the error back to the client is a bad default.
			//http.Error(res, err.Error(), http.StatusInternalServerError)
			http.Error(res, "Internal error processing the request.", http.StatusInternalServerError)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 PingCAP, 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package kv

import (
	"github.com/juju/errors"
	"github.com/pingcap/tidb/util/codec"
)

var (
	// ErrClosed is used when close an already closed txn.
	ErrClosed = errors.New("Error: Transaction already closed")
	// ErrNotExist is used when try to get an entry with an unexist key from KV store.
	ErrNotExist = errors.New("Error: key not exist")
	// ErrKeyExists is used when try to put an entry to KV store.
	ErrKeyExists = errors.New("Error: key already exist")
	// ErrConditionNotMatch is used when condition is not met.
	ErrConditionNotMatch = errors.New("Error: Condition not match")
	// ErrLockConflict is used when try to lock an already locked key.
	ErrLockConflict = errors.New("Error: Lock conflict")
	// ErrLazyConditionPairsNotMatch is used when value in store differs from expect pairs.
	ErrLazyConditionPairsNotMatch = errors.New("Error: Lazy condition pairs not match")
	// ErrRetryable is used when KV store occurs RPC error or some other
	// errors which SQL layer can safely retry.
	ErrRetryable = errors.New("Error: KV error safe to retry")
)

var (
	// keyPrefix is used to avoid key conflict with some database metadata keys.
	keyPrefix    = []byte("z")
	codecEncoder = &encoder{
		codec.EncodeKey,
		codec.DecodeKey,
	}

	defaultEncoder = codecEncoder
)

type encoder struct {
	enc func(...interface{}) ([]byte, error)
	dec func([]byte) ([]interface{}, error)
}

// EncodeValue encodes values before it is stored to the KV store.
func EncodeValue(values ...interface{}) ([]byte, error) {
	return defaultEncoder.enc(values...)
}

// DecodeValue decodes values after it is fetched from the KV store.
func DecodeValue(data []byte) ([]interface{}, error) {
	return defaultEncoder.dec(data)
}

// NextUntil applies FnKeyCmp to each entry of the iterator until meets some condition.
// It will stop when fn returns true, or iterator is invalid or occur error
func NextUntil(it Iterator, fn FnKeyCmp) error {
	var err error
	for it.Valid() && !fn([]byte(it.Key())) {
		err = it.Next()
		if err != nil {
			return errors.Trace(err)
		}
	}
	return nil
}

type decodeKeyIter struct {
	iter Iterator
}

// NewDecodeKeyIter returns an iterator with Key() auto decoded.
func NewDecodeKeyIter(iter Iterator) Iterator {
	return &decodeKeyIter{
		iter: iter,
	}
}

func (iter *decodeKeyIter) Next() error {
	return iter.iter.Next()
}

func (iter *decodeKeyIter) Value() []byte {
	return iter.iter.Value()
}

func (iter *decodeKeyIter) Key() string {
	return string([]byte(iter.iter.Key()))
}

func (iter *decodeKeyIter) Valid() bool {
	return iter.iter.Valid()
}

func (iter *decodeKeyIter) Close() {
	iter.iter.Close()
}
<commit_msg>kv: remove prefix z<commit_after>// Copyright 2015 PingCAP, 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package kv

import (
	"github.com/juju/errors"
	"github.com/pingcap/tidb/util/codec"
)

var (
	// ErrClosed is used when close an already closed txn.
	ErrClosed = errors.New("Error: Transaction already closed")
	// ErrNotExist is used when try to get an entry with an unexist key from KV store.
	ErrNotExist = errors.New("Error: key not exist")
	// ErrKeyExists is used when try to put an entry to KV store.
	ErrKeyExists = errors.New("Error: key already exist")
	// ErrConditionNotMatch is used when condition is not met.
	ErrConditionNotMatch = errors.New("Error: Condition not match")
	// ErrLockConflict is used when try to lock an already locked key.
	ErrLockConflict = errors.New("Error: Lock conflict")
	// ErrLazyConditionPairsNotMatch is used when value in store differs from expect pairs.
	ErrLazyConditionPairsNotMatch = errors.New("Error: Lazy condition pairs not match")
	// ErrRetryable is used when KV store occurs RPC error or some other
	// errors which SQL layer can safely retry.
	ErrRetryable = errors.New("Error: KV error safe to retry")
)

var (
	codecEncoder = &encoder{
		codec.EncodeKey,
		codec.DecodeKey,
	}

	defaultEncoder = codecEncoder
)

type encoder struct {
	enc func(...interface{}) ([]byte, error)
	dec func([]byte) ([]interface{}, error)
}

// EncodeValue encodes values before it is stored to the KV store.
func EncodeValue(values ...interface{}) ([]byte, error) {
	return defaultEncoder.enc(values...)
}

// DecodeValue decodes values after it is fetched from the KV store.
func DecodeValue(data []byte) ([]interface{}, error) {
	return defaultEncoder.dec(data)
}

// NextUntil applies FnKeyCmp to each entry of the iterator until meets some condition.
// It will stop when fn returns true, or iterator is invalid or occur error
func NextUntil(it Iterator, fn FnKeyCmp) error {
	var err error
	for it.Valid() && !fn([]byte(it.Key())) {
		err = it.Next()
		if err != nil {
			return errors.Trace(err)
		}
	}
	return nil
}

type decodeKeyIter struct {
	iter Iterator
}

// NewDecodeKeyIter returns an iterator with Key() auto decoded.
func NewDecodeKeyIter(iter Iterator) Iterator {
	return &decodeKeyIter{
		iter: iter,
	}
}

func (iter *decodeKeyIter) Next() error {
	return iter.iter.Next()
}

func (iter *decodeKeyIter) Value() []byte {
	return iter.iter.Value()
}

func (iter *decodeKeyIter) Key() string {
	return string([]byte(iter.iter.Key()))
}

func (iter *decodeKeyIter) Valid() bool {
	return iter.iter.Valid()
}

func (iter *decodeKeyIter) Close() {
	iter.iter.Close()
}
<|endoftext|>


<commit_before>package async

import (
	"container/list"
	"errors"
	"sync"
	"time"
)

type operation struct {
	method   func(*KVData) (interface{}, error)
	callback chan interface{}
}

type keyInfo struct {
	key    interface{}
	expire int64
}

type KVData struct {
	pairs   map[interface{}]interface{}
	keys    *list.List
	index   map[interface{}]*list.Element
	ttl     int64 //second
	maxSize int
}

func newKVData() *KVData {
	return &KVData{
		pairs:   make(map[interface{}]interface{}),
		keys:    list.New(),
		index:   make(map[interface{}]*list.Element),
		ttl:     0,
		maxSize: 0,
	}
}

func (kv *KVData) drain() {
	if kv.maxSize == 0 {
		return
	}
	offset := kv.keys.Len() - kv.maxSize
	for offset > 0 {
		ele := kv.keys.Front()
		info := ele.Value.(*keyInfo)
		kv.keys.Remove(ele)
		delete(kv.index, info.key)
		delete(kv.pairs, info.key)
		offset = offset - 1
	}
}

func (kv *KVData) updateLRU(keys []interface{}) {
	now := time.Now().Unix() + kv.ttl
	for _, key := range keys {
		ele, ok := kv.index[key]
		if ok {
			info := ele.Value.(*keyInfo)
			info.expire = now
			kv.keys.MoveToBack(ele)
		}
	}
}

func (kv *KVData) updateTTL() {
	if kv.ttl <= 0 {
		return
	}
	now := time.Now().Unix()
	for {
		if kv.keys.Len() == 0 {
			break
		}
		ele := kv.keys.Front()
		info := ele.Value.(*keyInfo)
		if info.expire > now {
			break
		}
		delete(kv.pairs, info.key)
		delete(kv.index, info.key)
		kv.keys.Remove(ele)
	}
}

func (kv *KVData) Set(key interface{}, value interface{}) {
	defer kv.drain()
	kv.pairs[key] = value

	ele, ok := kv.index[key]
	if ok {
		kv.keys.MoveToFront(ele)
		info := ele.Value.(*keyInfo)
		info.expire = time.Now().Unix() + kv.ttl
	} else {
		info := &keyInfo{key: key, expire: time.Now().Unix() + kv.ttl}
		kv.keys.PushBack(info)
		kv.index[key] = kv.keys.Back()
	}
}

func (kv *KVData) MSet(keys []interface{}, values []interface{}) {
	defer kv.drain()
	for i, key := range keys {
		kv.pairs[key] = values[i]

		ele, ok := kv.index[key]
		if ok {
			kv.keys.MoveToFront(ele)
			info := ele.Value.(*keyInfo)
			info.expire = time.Now().Unix() + kv.ttl
		} else {
			info := &keyInfo{key: key, expire: time.Now().Unix() + kv.ttl}
			kv.keys.PushBack(info)
			kv.index[key] = kv.keys.Back()
		}
	}
}

func (kv *KVData) Merge(pairs map[interface{}]interface{}) {
	defer kv.drain()
	for key, value := range pairs {
		kv.pairs[key] = value

		ele, ok := kv.index[key]
		if ok {
			kv.keys.MoveToFront(ele)
			info := ele.Value.(*keyInfo)
			info.expire = time.Now().Unix() + kv.ttl
		} else {
			info := &keyInfo{key: key, expire: time.Now().Unix() + kv.ttl}
			kv.keys.PushBack(info)
			kv.index[key] = kv.keys.Back()
		}
	}
}

func (kv *KVData) Get(key interface{}) (interface{}, error) {
	defer kv.updateLRU([]interface{}{key})
	value, ok := kv.pairs[key]
	if ok {
		return value, nil
	} else {
		return nil, errors.New("Invalid Key")
	}
}

func (kv *KVData) MGet(keys []interface{}) []interface{} {
	defer kv.updateLRU(keys)
	values := make([]interface{}, len(keys))
	for i, key := range keys {
		value, ok := kv.pairs[key]
		if ok {
			values[i] = value
		} else {
			values[i] = errors.New("Invalid Key")
		}
	}
	return values
}

func (kv *KVData) Del(key interface{}) {
	delete(kv.pairs, key)
	ele, ok := kv.index[key]
	if ok {
		kv.keys.Remove(ele)
		delete(kv.index, key)
	}
}

func (kv *KVData) MDel(keys []interface{}) {
	for _, key := range keys {
		kv.Del(key)
	}
}

func (kv *KVData) Keys() []interface{} {
	keys := make([]interface{}, len(kv.pairs))
	index := 0
	for key, _ := range kv.pairs {
		keys[index] = key
		index = index + 1
	}
	return keys
}

func (kv *KVData) Clean() {
	for {
		ele := kv.keys.Front()
		if ele == nil {
			return
		}
		info := ele.Value.(*keyInfo)
		kv.keys.Remove(ele)
		delete(kv.index, info.key)
		delete(kv.pairs, info.key)
	}
}

type KVCache struct {
	data      *KVData
	queue     chan operation
	shutdown  chan bool
	desLock   sync.RWMutex
	isDestory bool
}

func NewKVCache() *KVCache {
	kv := &KVCache{
		data:      newKVData(),
		queue:     make(chan operation, 16),
		shutdown:  make(chan bool),
		isDestory: false,
	}
	go kv.runloop()
	return kv
}

func (kv *KVCache) TTL(ttl int64) *KVCache {
	kv.data.ttl = ttl
	return kv
}

func (kv *KVCache) MaxSize(maxSize int) *KVCache {
	kv.data.maxSize = maxSize
	return kv
}

func (kv *KVCache) runloop() {
	timer := time.NewTimer(1 * time.Second)
	for {
		select {
		case <-kv.shutdown:
			{
				timer.Stop()
				goto End
			}
		case <-timer.C:
			{
				if kv.data.ttl > 0 {
					kv.data.updateTTL()
				}
				timer.Reset(1 * time.Second)
			}
		case op := <-kv.queue:
			{
				func() {
					defer func() {
						if e := recover(); e != nil {
							op.callback <- e.(error)
						}
					}()
					res, err := op.method(kv.data)
					if err != nil {
						op.callback <- err
					} else {
						op.callback <- res
					}
				}()
			}
		}
	}
End:
	close(kv.queue)
}

func (kv *KVCache) Commit(method func(*KVData) (interface{}, error)) (interface{}, error) {
	kv.desLock.RLock()
	if kv.isDestory {
		kv.desLock.RUnlock()
		return nil, errors.New("Invalid KVCache")
	}
	output := make(chan interface{})
	defer close(output)
	kv.queue <- operation{method: method, callback: output}
	kv.desLock.RUnlock()

	select {
	case res := <-output:
		{
			switch res.(type) {
			case error:
				{
					return nil, res.(error)
				}
			default:
				{
					return res, nil
				}
			}
		}
	}
}

func (kv *KVCache) Destory() {
	kv.desLock.Lock()
	defer kv.desLock.Unlock()
	if kv.isDestory {
		return
	}
	kv.isDestory = true
	kv.shutdown <- true
	close(kv.shutdown)
}
<commit_msg>Update kvcache.go<commit_after>package async

import (
	"container/list"
	"errors"
	"sync"
	"time"
)

type operation struct {
	method   func(*KVData) (interface{}, error)
	callback chan interface{}
}

type keyInfo struct {
	key    interface{}
	expire int64
}

type KVData struct {
	pairs   map[interface{}]interface{}
	keys    *list.List
	index   map[interface{}]*list.Element
	ttl     int64 //second
	maxSize int
}

func newKVData() *KVData {
	return &KVData{
		pairs:   make(map[interface{}]interface{}),
		keys:    list.New(),
		index:   make(map[interface{}]*list.Element),
		ttl:     0,
		maxSize: 0,
	}
}

func (kv *KVData) drain() {
	if kv.maxSize == 0 {
		return
	}
	offset := kv.keys.Len() - kv.maxSize
	for offset > 0 {
		ele := kv.keys.Front()
		info := ele.Value.(*keyInfo)
		kv.keys.Remove(ele)
		delete(kv.index, info.key)
		delete(kv.pairs, info.key)
		offset = offset - 1
	}
}

func (kv *KVData) updateLRU(keys []interface{}) {
	now := time.Now().Unix() + kv.ttl
	for _, key := range keys {
		ele, ok := kv.index[key]
		if ok {
			info := ele.Value.(*keyInfo)
			info.expire = now
			kv.keys.MoveToBack(ele)
		}
	}
}

func (kv *KVData) updateTTL() {
	if kv.ttl <= 0 {
		return
	}
	now := time.Now().Unix()
	for {
		if kv.keys.Len() == 0 {
			break
		}
		ele := kv.keys.Front()
		info := ele.Value.(*keyInfo)
		if info.expire > now {
			break
		}
		delete(kv.pairs, info.key)
		delete(kv.index, info.key)
		kv.keys.Remove(ele)
	}
}

func (kv *KVData) Set(key interface{}, value interface{}) {
	defer kv.drain()
	kv.pairs[key] = value

	ele, ok := kv.index[key]
	if ok {
		kv.keys.MoveToBack(ele)
		info := ele.Value.(*keyInfo)
		info.expire = time.Now().Unix() + kv.ttl
	} else {
		info := &keyInfo{key: key, expire: time.Now().Unix() + kv.ttl}
		kv.keys.PushBack(info)
		kv.index[key] = kv.keys.Back()
	}
}

func (kv *KVData) MSet(keys []interface{}, values []interface{}) {
	defer kv.drain()
	for i, key := range keys {
		kv.pairs[key] = values[i]

		ele, ok := kv.index[key]
		if ok {
			kv.keys.MoveToBack(ele)
			info := ele.Value.(*keyInfo)
			info.expire = time.Now().Unix() + kv.ttl
		} else {
			info := &keyInfo{key: key, expire: time.Now().Unix() + kv.ttl}
			kv.keys.PushBack(info)
			kv.index[key] = kv.keys.Back()
		}
	}
}

func (kv *KVData) Merge(pairs map[interface{}]interface{}) {
	defer kv.drain()
	for key, value := range pairs {
		kv.pairs[key] = value

		ele, ok := kv.index[key]
		if ok {
			kv.keys.MoveToBack(ele)
			info := ele.Value.(*keyInfo)
			info.expire = time.Now().Unix() + kv.ttl
		} else {
			info := &keyInfo{key: key, expire: time.Now().Unix() + kv.ttl}
			kv.keys.PushBack(info)
			kv.index[key] = kv.keys.Back()
		}
	}
}

func (kv *KVData) Get(key interface{}) (interface{}, error) {
	defer kv.updateLRU([]interface{}{key})
	value, ok := kv.pairs[key]
	if ok {
		return value, nil
	} else {
		return nil, errors.New("Invalid Key")
	}
}

func (kv *KVData) MGet(keys []interface{}) []interface{} {
	defer kv.updateLRU(keys)
	values := make([]interface{}, len(keys))
	for i, key := range keys {
		value, ok := kv.pairs[key]
		if ok {
			values[i] = value
		} else {
			values[i] = errors.New("Invalid Key")
		}
	}
	return values
}

func (kv *KVData) Del(key interface{}) {
	delete(kv.pairs, key)
	ele, ok := kv.index[key]
	if ok {
		kv.keys.Remove(ele)
		delete(kv.index, key)
	}
}

func (kv *KVData) MDel(keys []interface{}) {
	for _, key := range keys {
		kv.Del(key)
	}
}

func (kv *KVData) Keys() []interface{} {
	keys := make([]interface{}, len(kv.pairs))
	index := 0
	for key, _ := range kv.pairs {
		keys[index] = key
		index = index + 1
	}
	return keys
}

func (kv *KVData) Clean() {
	for {
		ele := kv.keys.Front()
		if ele == nil {
			return
		}
		info := ele.Value.(*keyInfo)
		kv.keys.Remove(ele)
		delete(kv.index, info.key)
		delete(kv.pairs, info.key)
	}
}

type KVCache struct {
	data      *KVData
	queue     chan operation
	shutdown  chan bool
	desLock   sync.RWMutex
	isDestory bool
}

func NewKVCache() *KVCache {
	kv := &KVCache{
		data:      newKVData(),
		queue:     make(chan operation, 16),
		shutdown:  make(chan bool),
		isDestory: false,
	}
	go kv.runloop()
	return kv
}

func (kv *KVCache) TTL(ttl int64) *KVCache {
	kv.data.ttl = ttl
	return kv
}

func (kv *KVCache) MaxSize(maxSize int) *KVCache {
	kv.data.maxSize = maxSize
	return kv
}

func (kv *KVCache) runloop() {
	timer := time.NewTimer(1 * time.Second)
	for {
		select {
		case <-kv.shutdown:
			{
				timer.Stop()
				goto End
			}
		case <-timer.C:
			{
				if kv.data.ttl > 0 {
					kv.data.updateTTL()
				}
				timer.Reset(1 * time.Second)
			}
		case op := <-kv.queue:
			{
				func() {
					defer func() {
						if e := recover(); e != nil {
							op.callback <- e.(error)
						}
					}()
					res, err := op.method(kv.data)
					if err != nil {
						op.callback <- err
					} else {
						op.callback <- res
					}
				}()
			}
		}
	}
End:
	close(kv.queue)
}

func (kv *KVCache) Commit(method func(*KVData) (interface{}, error)) (interface{}, error) {
	kv.desLock.RLock()
	if kv.isDestory {
		kv.desLock.RUnlock()
		return nil, errors.New("Invalid KVCache")
	}
	output := make(chan interface{})
	defer close(output)
	kv.queue <- operation{method: method, callback: output}
	kv.desLock.RUnlock()

	select {
	case res := <-output:
		{
			switch res.(type) {
			case error:
				{
					return nil, res.(error)
				}
			default:
				{
					return res, nil
				}
			}
		}
	}
}

func (kv *KVCache) Destory() {
	kv.desLock.Lock()
	defer kv.desLock.Unlock()
	if kv.isDestory {
		return
	}
	kv.isDestory = true
	kv.shutdown <- true
	close(kv.shutdown)
}
<|endoftext|>


<commit_before>package pubsub

import (
	"testing"
	"time"
)

// TODO: split the tests.

// Test a simple pub sub case.
func TestMonolithic(t *testing.T) {
	data := map[string]bool{
		"foo": true, "bar": true, "golang": true}
	z := Broker{
		PubAddr:    "tcp://127.0.0.1:5000",
		SubAddr:    "tcp://127.0.0.1:5001",
		BufferSize: 100}

	go func() {
		t.Fatal(z.Run())
	}()

	doPub := func(key string) {
		pub, err := z.NewPublisher()
		if err != nil {
			t.Fatal(err)
		}
		defer pub.Stop()
		for {
			for value, _ := range data {
				if err := pub.Publish(key, value); err != nil {
					t.Fatal(err)
				}
			}
			// continue to publish the same values as many times as
			// possible -- hence for {} -- as it appears normal for
			// zeromq to drop the initial values. we want the below
			// subscriber to catch some values at least.
		}
	}

	done := make(chan bool)

	go func() {
		sub := z.Subscribe("test")

		// retrieve at least three values from the eternal publishers.
		count := 3
		for count > 0 {
			msg := <-sub.Ch
			if msg.Key == "test" {
				if _, ok := data[msg.Value]; !ok {
					sub.Stop()
					t.Fatalf("got garbage data: %s", msg.Value)
				}
				count -= 1
			}
		}
		err := sub.Stop()
		if err != nil {
			t.Fatal(err)
		}
		done <- true
	}()

	go doPub("test")
	go doPub("ignore")

	select {
	case <-done:
		// all done
	case <-time.After(1 * time.Second):
		t.Fatal("not enough values from subscription")
	}
}
<commit_msg>skip failing test on Go 1.1, for a while.<commit_after>package pubsub

import (
	"testing"
	"time"
)

// TODO: split the tests.

// Test a simple pub sub case.
func TestMonolithic(t *testing.T) {
	// XXX
	t.Skip("Bug 99013")

	data := map[string]bool{
		"foo": true, "bar": true, "golang": true}
	z := Broker{
		PubAddr:    "tcp://127.0.0.1:5000",
		SubAddr:    "tcp://127.0.0.1:5001",
		BufferSize: 100}

	go func() {
		t.Fatal(z.Run())
	}()

	doPub := func(key string) {
		pub, err := z.NewPublisher()
		if err != nil {
			t.Fatal(err)
		}
		defer pub.Stop()
		for {
			for value, _ := range data {
				if err := pub.Publish(key, value); err != nil {
					t.Fatal(err)
				}
			}
			// continue to publish the same values as many times as
			// possible -- hence for {} -- as it appears normal for
			// zeromq to drop the initial values. we want the below
			// subscriber to catch some values at least.
		}
	}

	done := make(chan bool)

	go func() {
		sub := z.Subscribe("test")

		// retrieve at least three values from the eternal publishers.
		count := 3
		for count > 0 {
			msg := <-sub.Ch
			if msg.Key == "test" {
				if _, ok := data[msg.Value]; !ok {
					sub.Stop()
					t.Fatalf("got garbage data: %s", msg.Value)
				}
				count -= 1
			}
		}
		err := sub.Stop()
		if err != nil {
			t.Fatal(err)
		}
		done <- true
	}()

	go doPub("test")
	go doPub("ignore")

	select {
	case <-done:
		// all done
	case <-time.After(1 * time.Second):
		t.Fatal("not enough values from subscription")
	}
}
<|endoftext|>


<commit_before>package main

import (
	//"code.google.com/p/go.net/websocket"
  "github.com/gorilla/websocket"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"queue"
	"runtime"
	"strings"
	"time"
)

const debug = true
const maxSizeHealthStatusQueue = 60 // Magnificent Server Log Store Size, displays the last 60 seconds in the plot

type Msg struct {
	MessageId string
	Content   string
	TimeStamp int64
}

// Client connection consists of the websocket and the client ip
type Client struct {
	websocket *websocket.Conn
	clientIP  string
}

type ServiceMonitor struct {
	errChan           chan error // unbuffered channel
	errChanWebsock    chan error // unbuffered channel
	activeClients     map[string]Client
	healthStatusChan  chan bool
	alertChan         chan string
	newClientChan     chan Client
	alertQueue        *queue.Queue
	healthStatusQueue *queue.Queue
}

func NewServiceMonitor() *ServiceMonitor {
	m := ServiceMonitor{}
	m.activeClients = make(map[string]Client)
	m.errChan = make(chan error)
	m.healthStatusChan = make(chan bool, 10)
	m.alertChan = make(chan string, 10)
	m.newClientChan = make(chan Client, 10)
	m.alertQueue = queue.NewQueue()
	m.healthStatusQueue = queue.NewQueue()
	return &m
}

func BoolToString(value bool) string {
	if value {
		return "1"
	}
	return "0"
}

func (m *ServiceMonitor) sendClientMsg(msg *Msg, ip string) {
  if err := m.activeClients[ip].websocket.WriteJSON(msg); err != nil {
		// we could not send the message to a peer
		log.Println("Could not send message to:", ip, err.Error())
		log.Println("Client disconnected:", ip)
		delete(m.activeClients, ip)
	}
}

func (m *ServiceMonitor) sendBroadcastMsg(msg *Msg) {
	for ip, _ := range m.activeClients {
    if err := m.activeClients[ip].websocket.WriteJSON(msg); err != nil {
			// we could not send the message to a peer
			log.Println("Could not send message to:", ip, err.Error())
			log.Println("Client disconnected:", ip)
			delete(m.activeClients, ip)
		}
	}
}

func (m *ServiceMonitor) sendQueueData(ip string) {
	for i := 0; i < m.healthStatusQueue.Len(); i++ {
		e, found := m.healthStatusQueue.Get(i)

		if found {
			if msg, ok := e.(*Msg); ok {
				m.sendClientMsg(msg, ip)
			}
		}
	}

	for i := 0; i < m.alertQueue.Len(); i++ {
		e, found := m.alertQueue.Get(i)

		if found {
			if msg, ok := e.(*Msg); ok {
				m.sendClientMsg(msg, ip)
			}
		}
	}
}

// this routine handles all outgoing websocket messages
func (m *ServiceMonitor) pushDataToClients() {
	for {
		select {
		// a new Client is connecting
		case newClient := <-m.newClientChan:
			// send current Queue data to the new connecting client
			m.activeClients[newClient.clientIP] = newClient
			m.sendQueueData(newClient.clientIP)

		// broadcast a new health status message to all clients
		// newHealthStatus == 1 ... deamon failure
		// newHealthStatus == 0 ... deamon ok
		case newHealthStatus := <-m.healthStatusChan:
			msg := Msg{"Plot", BoolToString(newHealthStatus), time.Now().UnixNano() / int64(time.Millisecond)}
			m.sendBroadcastMsg(&msg)
			// add msg to HealthStatusQueue
			if m.healthStatusQueue.Len() < maxSizeHealthStatusQueue {
				m.healthStatusQueue.Push(&msg)
			} else {
				m.healthStatusQueue.Pop()
				m.healthStatusQueue.Push(&msg)
			}

		// broadcast an alert message to all clients
		case newAlert := <-m.alertChan:
			msg := Msg{"Alert", newAlert, time.Now().UnixNano() / int64(time.Millisecond)}
			m.sendBroadcastMsg(&msg)
			// add msg to alertQueue
			if m.alertQueue.Len() < maxSizeHealthStatusQueue {
				m.alertQueue.Push(&msg)
			} else {
				m.alertQueue.Pop()
				m.alertQueue.Push(&msg)
			}
		}
	}
}

// WebSocket handler to handle clients
func (m *ServiceMonitor) wsHandler(ws http.ResponseWriter, r *http.Request) {
  conn, err := websocket.Upgrade(ws, r, nil, 1024, 1024)
  if _, ok := err.(websocket.HandshakeError); ok {
    http.Error(ws, "Not a websocket handshake", 400)
    return
  } else if err != nil {
    log.Println(err)
    return
  }
  defer conn.Close()

	client := conn.RemoteAddr().String()
	if debug {
		log.Println("New client connected:", client)
	}

	m.newClientChan <- Client{conn, client}

	// wait for errChan, so the websocket stays open otherwise it'll close
	err = <-m.errChanWebsock
}

// handler for the main page
func HomeHandler(response http.ResponseWriter, request *http.Request) {
	response.Header().Set("Content-type", "text/html")
	webpage, err := ioutil.ReadFile("home.html")

	if err != nil {
		http.Error(response, fmt.Sprintf("home.html file error %v", err), 500)
	}

	fmt.Fprint(response, string(webpage))
}

func (m *ServiceMonitor) parseResponse(resp string) {
	if !strings.Contains(resp, "Magnificent!") {
		m.healthStatusChan <- false
	} else {
		m.healthStatusChan <- true
		m.alertChan <- "Server has failed"
	}
}

func (m *ServiceMonitor) requestDaemonStatus(url string) {
	response, err := http.Get(url)
	if err != nil {
		if debug {
			log.Println("requestDeamonStatus failed:", err.Error())
			return
		}
	}

	defer response.Body.Close()
	contents, err := ioutil.ReadAll(response.Body)
	if err != nil {
		if debug {
			log.Println("requestDeamonStatus failed:", err.Error())
			return
		}
	}

	resp := string(contents)
	m.parseResponse(resp)
}

func (m *ServiceMonitor) monitorDaemon(url string, time_interval time.Duration) {
	for {
		m.requestDaemonStatus(url)
		time.Sleep(time_interval * time.Second)
	}
}

func (m *ServiceMonitor) startHTTPServer() {
	http.Handle("/", http.HandlerFunc(HomeHandler))
  http.HandleFunc("/sock", m.wsHandler)

	err := http.ListenAndServe(":8080", nil)
	m.errChanWebsock <- err
	m.errChan <- err
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	m := NewServiceMonitor()

	go m.startHTTPServer()
	go m.pushDataToClients()
	go m.monitorDaemon("http://localhost:12345/", 1)

	err := <-m.errChan
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}
}
<commit_msg>formatted code<commit_after>package main

import (
	"fmt"
	"github.com/gorilla/websocket"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"queue"
	"runtime"
	"strings"
	"time"
)

const debug = true
const maxSizeHealthStatusQueue = 60 // Magnificent Server Log Store Size, displays the last 60 seconds in the plot

type Msg struct {
	MessageId string
	Content   string
	TimeStamp int64
}

// Client connection consists of the websocket and the client ip
type Client struct {
	websocket *websocket.Conn
	clientIP  string
}

type ServiceMonitor struct {
	errChan           chan error // unbuffered channel
	errChanWebsock    chan error // unbuffered channel
	activeClients     map[string]Client
	healthStatusChan  chan bool
	alertChan         chan string
	newClientChan     chan Client
	alertQueue        *queue.Queue
	healthStatusQueue *queue.Queue
}

func NewServiceMonitor() *ServiceMonitor {
	m := ServiceMonitor{}
	m.activeClients = make(map[string]Client)
	m.errChan = make(chan error)
	m.healthStatusChan = make(chan bool, 10)
	m.alertChan = make(chan string, 10)
	m.newClientChan = make(chan Client, 10)
	m.alertQueue = queue.NewQueue()
	m.healthStatusQueue = queue.NewQueue()
	return &m
}

func BoolToString(value bool) string {
	if value {
		return "1"
	}
	return "0"
}

func (m *ServiceMonitor) sendClientMsg(msg *Msg, ip string) {
	if err := m.activeClients[ip].websocket.WriteJSON(msg); err != nil {
		// we could not send the message to a peer
		log.Println("Could not send message to:", ip, err.Error())
		log.Println("Client disconnected:", ip)
		delete(m.activeClients, ip)
	}
}

func (m *ServiceMonitor) sendBroadcastMsg(msg *Msg) {
	for ip, _ := range m.activeClients {
		if err := m.activeClients[ip].websocket.WriteJSON(msg); err != nil {
			// we could not send the message to a peer
			log.Println("Could not send message to:", ip, err.Error())
			log.Println("Client disconnected:", ip)
			delete(m.activeClients, ip)
		}
	}
}

func (m *ServiceMonitor) sendQueueData(ip string) {
	for i := 0; i < m.healthStatusQueue.Len(); i++ {
		e, found := m.healthStatusQueue.Get(i)

		if found {
			if msg, ok := e.(*Msg); ok {
				m.sendClientMsg(msg, ip)
			}
		}
	}

	for i := 0; i < m.alertQueue.Len(); i++ {
		e, found := m.alertQueue.Get(i)

		if found {
			if msg, ok := e.(*Msg); ok {
				m.sendClientMsg(msg, ip)
			}
		}
	}
}

// this routine handles all outgoing websocket messages
func (m *ServiceMonitor) pushDataToClients() {
	for {
		select {
		// a new Client is connecting
		case newClient := <-m.newClientChan:
			// send current Queue data to the new connecting client
			m.activeClients[newClient.clientIP] = newClient
			m.sendQueueData(newClient.clientIP)

		// broadcast a new health status message to all clients
		// newHealthStatus == 1 ... deamon failure
		// newHealthStatus == 0 ... deamon ok
		case newHealthStatus := <-m.healthStatusChan:
			msg := Msg{"Plot", BoolToString(newHealthStatus), time.Now().UnixNano() / int64(time.Millisecond)}
			m.sendBroadcastMsg(&msg)
			// add msg to HealthStatusQueue
			if m.healthStatusQueue.Len() < maxSizeHealthStatusQueue {
				m.healthStatusQueue.Push(&msg)
			} else {
				m.healthStatusQueue.Pop()
				m.healthStatusQueue.Push(&msg)
			}

		// broadcast an alert message to all clients
		case newAlert := <-m.alertChan:
			msg := Msg{"Alert", newAlert, time.Now().UnixNano() / int64(time.Millisecond)}
			m.sendBroadcastMsg(&msg)
			// add msg to alertQueue
			if m.alertQueue.Len() < maxSizeHealthStatusQueue {
				m.alertQueue.Push(&msg)
			} else {
				m.alertQueue.Pop()
				m.alertQueue.Push(&msg)
			}
		}
	}
}

// WebSocket handler to handle clients
func (m *ServiceMonitor) wsHandler(ws http.ResponseWriter, r *http.Request) {
	conn, err := websocket.Upgrade(ws, r, nil, 1024, 1024)
	if _, ok := err.(websocket.HandshakeError); ok {
		http.Error(ws, "Not a websocket handshake", 400)
		return
	} else if err != nil {
		log.Println(err)
		return
	}
	defer conn.Close()

	client := conn.RemoteAddr().String()
	if debug {
		log.Println("New client connected:", client)
	}

	m.newClientChan <- Client{conn, client}

	// wait for errChan, so the websocket stays open otherwise it'll close
	err = <-m.errChanWebsock
}

// handler for the main page
func HomeHandler(response http.ResponseWriter, request *http.Request) {
	response.Header().Set("Content-type", "text/html")
	webpage, err := ioutil.ReadFile("home.html")

	if err != nil {
		http.Error(response, fmt.Sprintf("home.html file error %v", err), 500)
	}

	fmt.Fprint(response, string(webpage))
}

func (m *ServiceMonitor) parseResponse(resp string) {
	if !strings.Contains(resp, "Magnificent!") {
		m.healthStatusChan <- false
	} else {
		m.healthStatusChan <- true
		m.alertChan <- "Server has failed"
	}
}

func (m *ServiceMonitor) requestDaemonStatus(url string) {
	response, err := http.Get(url)
	if err != nil {
		if debug {
			log.Println("requestDeamonStatus failed:", err.Error())
			return
		}
	}

	defer response.Body.Close()
	contents, err := ioutil.ReadAll(response.Body)
	if err != nil {
		if debug {
			log.Println("requestDeamonStatus failed:", err.Error())
			return
		}
	}

	resp := string(contents)
	m.parseResponse(resp)
}

func (m *ServiceMonitor) monitorDaemon(url string, time_interval time.Duration) {
	for {
		m.requestDaemonStatus(url)
		time.Sleep(time_interval * time.Second)
	}
}

func (m *ServiceMonitor) startHTTPServer() {
	http.Handle("/", http.HandlerFunc(HomeHandler))
	http.HandleFunc("/sock", m.wsHandler)

	err := http.ListenAndServe(":8080", nil)
	m.errChanWebsock <- err
	m.errChan <- err
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	m := NewServiceMonitor()

	go m.startHTTPServer()
	go m.pushDataToClients()
	go m.monitorDaemon("http://localhost:12345/", 1)

	err := <-m.errChan
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Package inotify implements a wrapper for the Linux inotify system.

Example:
    watcher, err := inotify.NewWatcher()
    if err != nil {
        log.Fatal(err)
    }
    err = watcher.Watch("/tmp")
    if err != nil {
        log.Fatal(err)
    }
    for {
        select {
        case ev := <-watcher.Event:
            log.Println("event:", ev)
        case err := <-watcher.Error:
            log.Println("error:", err)
        }
    }

*/
package inotify

import (
	"errors"
	"fmt"
	"io"
	"os"
	"strings"
	"sync"
	"syscall"
	"unsafe"
)

const (
	EPOLL_MAX_EVENTS	= 64
	EPOLL_TIMEOUT		= 1 * 1000
)

type Event struct {
	Mask   uint32 // Mask of events
	Cookie uint32 // Unique cookie associating related events (for rename(2))
	Name   string // File name (optional)
}

type watch struct {
	wd    uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
	flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
}

type Watcher struct {
	mu       sync.Mutex
	fd       int               // File descriptor (as returned by the inotify_init() syscall)
	watches  map[string]*watch // Map of inotify watches (key: path)
	paths    map[int]string    // Map of watched paths (key: watch descriptor)
	Error    chan error        // Errors are sent on this channel
	Event    chan *Event       // Events are returned on this channel
	done     chan bool         // Channel for sending a "quit message" to the reader goroutine
	isClosed bool              // Set to true when Close() is first called
}

// NewWatcher creates and returns a new inotify instance using inotify_init(2)
func NewWatcher() (*Watcher, error) {
	fd, errno := syscall.InotifyInit()
	if fd == -1 {
		return nil, os.NewSyscallError("inotify_init", errno)
	}
	w := &Watcher{
		fd:      fd,
		watches: make(map[string]*watch),
		paths:   make(map[int]string),
		Event:   make(chan *Event),
		Error:   make(chan error),
		done:    make(chan bool, 1),
	}

	go w.epollEvents()
	return w, nil
}

// Close closes an inotify watcher instance
// It sends a message to the reader goroutine to quit and removes all watches
// associated with the inotify instance
func (w *Watcher) Close() error {
	if w.isClosed {
		return nil
	}

	// Send "quit" message to the reader goroutine
	w.done <- true
	for path := range w.watches {
		w.RemoveWatch(path)
	}

	return nil
}

// AddWatch adds path to the watched file set.
// The flags are interpreted as described in inotify_add_watch(2).
func (w *Watcher) AddWatch(path string, flags uint32) error {
	if w.isClosed {
		return errors.New("inotify instance already closed")
	}

	watchEntry, found := w.watches[path]
	if found {
		watchEntry.flags |= flags
		flags |= syscall.IN_MASK_ADD
	}

	w.mu.Lock() // synchronize with readEvents goroutine
	defer w.mu.Unlock()

	wd, err := syscall.InotifyAddWatch(w.fd, path, flags)
	if err != nil {
		return &os.PathError{
			Op:   "inotify_add_watch",
			Path: path,
			Err:  err,
		}
	}

	if !found {
		w.watches[path] = &watch{wd: uint32(wd), flags: flags}
		w.paths[wd] = path
	}
	return nil
}

// Watch adds path to the watched file set, watching all events.
func (w *Watcher) Watch(path string) error {
	return w.AddWatch(path, IN_ALL_EVENTS)
}

// RemoveWatch removes path from the watched file set.
func (w *Watcher) RemoveWatch(path string) error {
	watch, ok := w.watches[path]
	if !ok {
		return errors.New(fmt.Sprintf("can't remove non-existent inotify watch for: %s", path))
	}
	success, errno := syscall.InotifyRmWatch(w.fd, watch.wd)
	if success == -1 {
		return os.NewSyscallError("inotify_rm_watch", errno)
	}
	delete(w.watches, path)
	return nil
}

func (w *Watcher) epollEvents() {
	epfd, err := syscall.EpollCreate1(0)
	if err != nil {
		w.Error <- os.NewSyscallError("epoll_create1", err)
		return
	}
	defer syscall.Close(epfd)

	events := make([]syscall.EpollEvent, EPOLL_MAX_EVENTS) // XXX: MAGIC NUMBER
	event := &syscall.EpollEvent{syscall.EPOLLIN, int32(w.fd), 0}
	if err = syscall.EpollCtl(epfd, syscall.EPOLL_CTL_ADD, w.fd, event); err != nil {
		w.Error <- os.NewSyscallError("epoll_ctl", err)
		return
	}

	for {
		nevents, err := syscall.EpollWait(epfd, events, EPOLL_TIMEOUT)

		// See if there is a message on the "done" channel
		var done bool
		select {
		case done = <-w.done:
		default:
		}
		if done {
			close(w.Event)
			err := syscall.Close(w.fd)
			if err != nil {
				w.Error <- os.NewSyscallError("close", err)
			}
			close(w.Error)
			w.isClosed = true
			return
		}
		if err != nil {
			w.Error <- os.NewSyscallError("epoll_wait", err)
			continue
		}
		if nevents == 0 {
			continue
		}

		for i := 0; i < nevents; i++ {
			if events[i].Fd != event.Fd {
				continue
			}
			err = w.readEvents()
			if err == io.EOF {
				return
			} else if err != nil {
				w.Error <- os.NewSyscallError("epoll_wait", err)
			}
		}
	}
}

// readEvents reads from the inotify file descriptor, converts the
// received events into Event objects and sends them via the Event channel
func (w *Watcher) readEvents() error {
	var buf [syscall.SizeofInotifyEvent * 4096]byte

	n, err := syscall.Read(w.fd, buf[:])

	// If EOF or a "done" message is received
	switch {
	case err == syscall.EINTR:
		return nil
	case err != nil:
		return os.NewSyscallError("read", err)
	case n == 0:
		return errors.New("inotify: possibilly too many events occurred at once")
	case n < syscall.SizeofInotifyEvent:
		// brefore: syscall.Read
		//   while n < syscall.SizeofInotifyEvent:
		// 	  syscall.Ioctl(w.fd, syscall.FIONREAD, &n);
		//
		return errors.New("inotify: short read in readEvents()")
	}

	var offset uint32 = 0
	// We don't know how many events we just read into the buffer
	// While the offset points to at least one whole event...
	for offset <= uint32(n-syscall.SizeofInotifyEvent) {
		// Point "raw" to the event in the buffer
		raw := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset]))
		event := new(Event)
		event.Mask = uint32(raw.Mask)
		event.Cookie = uint32(raw.Cookie)
		nameLen := uint32(raw.Len)
		// If the event happened to the watched directory or the watched file, the kernel
		// doesn't append the filename to the event, but we would like to always fill the
		// the "Name" field with a valid filename. We retrieve the path of the watch from
		// the "paths" map.
		w.mu.Lock()
		event.Name = w.paths[int(raw.Wd)]
		w.mu.Unlock()
		if nameLen > 0 {
			// Point "bytes" at the first byte of the filename
			bytes := (*[syscall.PathMax]byte)(unsafe.Pointer(&buf[offset+syscall.SizeofInotifyEvent]))
			// The filename is padded with NUL bytes. TrimRight() gets rid of those.
			event.Name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
		}
		// Send the event on the events channel
		w.Event <- event

		// Move to the next event in the buffer
		offset += syscall.SizeofInotifyEvent + nameLen
	}

	return nil
}

// String formats the event e in the form
// "filename: 0xEventMask = IN_ACCESS|IN_ATTRIB_|..."
func (e *Event) String() string {
	var events string = ""

	m := e.Mask
	for _, b := range eventBits {
		if m&b.Value != 0 {
			m &^= b.Value
			events += "|" + b.Name
		}
	}

	if m != 0 {
		events += fmt.Sprintf("|%#x", m)
	}
	if len(events) > 0 {
		events = " == " + events[1:]
	}

	return fmt.Sprintf("%q: %#x%s", e.Name, e.Mask, events)
}

const (
	// Options for inotify_init() are not exported
	// IN_CLOEXEC    uint32 = syscall.IN_CLOEXEC
	// IN_NONBLOCK   uint32 = syscall.IN_NONBLOCK

	// Options for AddWatch
	IN_DONT_FOLLOW uint32 = syscall.IN_DONT_FOLLOW
	IN_ONESHOT     uint32 = syscall.IN_ONESHOT
	IN_ONLYDIR     uint32 = syscall.IN_ONLYDIR

	// The "IN_MASK_ADD" option is not exported, as AddWatch
	// adds it automatically, if there is already a watch for the given path
	// IN_MASK_ADD      uint32 = syscall.IN_MASK_ADD

	// Events
	IN_ACCESS        uint32 = syscall.IN_ACCESS
	IN_ALL_EVENTS    uint32 = syscall.IN_ALL_EVENTS
	IN_ATTRIB        uint32 = syscall.IN_ATTRIB
	IN_CLOSE         uint32 = syscall.IN_CLOSE
	IN_CLOSE_NOWRITE uint32 = syscall.IN_CLOSE_NOWRITE
	IN_CLOSE_WRITE   uint32 = syscall.IN_CLOSE_WRITE
	IN_CREATE        uint32 = syscall.IN_CREATE
	IN_DELETE        uint32 = syscall.IN_DELETE
	IN_DELETE_SELF   uint32 = syscall.IN_DELETE_SELF
	IN_MODIFY        uint32 = syscall.IN_MODIFY
	IN_MOVE          uint32 = syscall.IN_MOVE
	IN_MOVED_FROM    uint32 = syscall.IN_MOVED_FROM
	IN_MOVED_TO      uint32 = syscall.IN_MOVED_TO
	IN_MOVE_SELF     uint32 = syscall.IN_MOVE_SELF
	IN_OPEN          uint32 = syscall.IN_OPEN

	// Special events
	IN_ISDIR      uint32 = syscall.IN_ISDIR
	IN_IGNORED    uint32 = syscall.IN_IGNORED
	IN_Q_OVERFLOW uint32 = syscall.IN_Q_OVERFLOW
	IN_UNMOUNT    uint32 = syscall.IN_UNMOUNT
)

var eventBits = []struct {
	Value uint32
	Name  string
}{
	{IN_ACCESS, "IN_ACCESS"},
	{IN_ATTRIB, "IN_ATTRIB"},
	{IN_CLOSE, "IN_CLOSE"},
	{IN_CLOSE_NOWRITE, "IN_CLOSE_NOWRITE"},
	{IN_CLOSE_WRITE, "IN_CLOSE_WRITE"},
	{IN_CREATE, "IN_CREATE"},
	{IN_DELETE, "IN_DELETE"},
	{IN_DELETE_SELF, "IN_DELETE_SELF"},
	{IN_MODIFY, "IN_MODIFY"},
	{IN_MOVE, "IN_MOVE"},
	{IN_MOVED_FROM, "IN_MOVED_FROM"},
	{IN_MOVED_TO, "IN_MOVED_TO"},
	{IN_MOVE_SELF, "IN_MOVE_SELF"},
	{IN_OPEN, "IN_OPEN"},
	{IN_ISDIR, "IN_ISDIR"},
	{IN_IGNORED, "IN_IGNORED"},
	{IN_Q_OVERFLOW, "IN_Q_OVERFLOW"},
	{IN_UNMOUNT, "IN_UNMOUNT"},
}
<commit_msg>[inotify] move epoll init functions to constructor<commit_after>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Package inotify implements a wrapper for the Linux inotify system.

Example:
    watcher, err := inotify.NewWatcher()
    if err != nil {
        log.Fatal(err)
    }
    err = watcher.Watch("/tmp")
    if err != nil {
        log.Fatal(err)
    }
    for {
        select {
        case ev := <-watcher.Event:
            log.Println("event:", ev)
        case err := <-watcher.Error:
            log.Println("error:", err)
        }
    }

*/
package inotify

import (
	"errors"
	"fmt"
	"io"
	"os"
	"strings"
	"sync"
	"syscall"
	"unsafe"
)

const (
	EPOLL_MAX_EVENTS	= 64
	EPOLL_TIMEOUT		= 1 * 1000
)

type Event struct {
	Mask   uint32 // Mask of events
	Cookie uint32 // Unique cookie associating related events (for rename(2))
	Name   string // File name (optional)
}

type watch struct {
	wd    uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
	flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
}

type Watcher struct {
	mu       sync.Mutex
	fd       int               // File descriptor (as returned by the inotify_init() syscall)
	watches  map[string]*watch // Map of inotify watches (key: path)
	paths    map[int]string    // Map of watched paths (key: watch descriptor)
	Error    chan error        // Errors are sent on this channel
	Event    chan *Event       // Events are returned on this channel
	done     chan bool         // Channel for sending a "quit message" to the reader goroutine
	isClosed bool              // Set to true when Close() is first called
}

// NewWatcher creates and returns a new inotify instance using inotify_init(2)
func NewWatcher() (*Watcher, error) {
	fd, errno := syscall.InotifyInit()
	if fd == -1 {
		return nil, os.NewSyscallError("inotify_init", errno)
	}
	w := &Watcher{
		fd:      fd,
		watches: make(map[string]*watch),
		paths:   make(map[int]string),
		Event:   make(chan *Event),
		Error:   make(chan error),
		done:    make(chan bool, 1),
	}

	epfd, err := syscall.EpollCreate1(0)
	if err != nil {
		return nil, err
	}
	event := &syscall.EpollEvent{syscall.EPOLLIN, int32(w.fd), 0}
	if err = syscall.EpollCtl(epfd, syscall.EPOLL_CTL_ADD, w.fd, event); err != nil {
		return nil, err
	}
	go w.epollEvents(epfd)

	return w, nil
}

// Close closes an inotify watcher instance
// It sends a message to the reader goroutine to quit and removes all watches
// associated with the inotify instance
func (w *Watcher) Close() error {
	if w.isClosed {
		return nil
	}

	// Send "quit" message to the reader goroutine
	w.done <- true
	for path := range w.watches {
		w.RemoveWatch(path)
	}

	return nil
}

// AddWatch adds path to the watched file set.
// The flags are interpreted as described in inotify_add_watch(2).
func (w *Watcher) AddWatch(path string, flags uint32) error {
	if w.isClosed {
		return errors.New("inotify instance already closed")
	}

	watchEntry, found := w.watches[path]
	if found {
		watchEntry.flags |= flags
		flags |= syscall.IN_MASK_ADD
	}

	w.mu.Lock() // synchronize with readEvents goroutine
	defer w.mu.Unlock()

	wd, err := syscall.InotifyAddWatch(w.fd, path, flags)
	if err != nil {
		return &os.PathError{
			Op:   "inotify_add_watch",
			Path: path,
			Err:  err,
		}
	}

	if !found {
		w.watches[path] = &watch{wd: uint32(wd), flags: flags}
		w.paths[wd] = path
	}
	return nil
}

// Watch adds path to the watched file set, watching all events.
func (w *Watcher) Watch(path string) error {
	return w.AddWatch(path, IN_ALL_EVENTS)
}

// RemoveWatch removes path from the watched file set.
func (w *Watcher) RemoveWatch(path string) error {
	watch, ok := w.watches[path]
	if !ok {
		return errors.New(fmt.Sprintf("can't remove non-existent inotify watch for: %s", path))
	}
	success, errno := syscall.InotifyRmWatch(w.fd, watch.wd)
	if success == -1 {
		return os.NewSyscallError("inotify_rm_watch", errno)
	}
	delete(w.watches, path)
	return nil
}

func (w *Watcher) epollEvents(epfd int) {
	events := make([]syscall.EpollEvent, EPOLL_MAX_EVENTS) // XXX: MAGIC NUMBER
	defer syscall.Close(epfd)

	for {
		nevents, err := syscall.EpollWait(epfd, events, EPOLL_TIMEOUT)

		// See if there is a message on the "done" channel
		var done bool
		select {
		case done = <-w.done:
		default:
		}
		if done {
			close(w.Event)
			err := syscall.Close(w.fd)
			if err != nil {
				w.Error <- os.NewSyscallError("close", err)
			}
			close(w.Error)
			w.isClosed = true
			return
		}
		if err != nil {
			w.Error <- os.NewSyscallError("epoll_wait", err)
			continue
		}
		if nevents == 0 {
			continue
		}

		for i := 0; i < nevents; i++ {
			if events[i].Fd != int32(w.fd) {
				continue
			}
			err = w.readEvents()
			if err == io.EOF {
				return
			} else if err != nil {
				w.Error <- os.NewSyscallError("epoll_wait", err)
			}
		}
	}
}

// readEvents reads from the inotify file descriptor, converts the
// received events into Event objects and sends them via the Event channel
func (w *Watcher) readEvents() error {
	var buf [syscall.SizeofInotifyEvent * 4096]byte

	n, err := syscall.Read(w.fd, buf[:])

	// If EOF or a "done" message is received
	switch {
	case err == syscall.EINTR:
		return nil
	case err != nil:
		return os.NewSyscallError("read", err)
	case n == 0:
		return errors.New("inotify: possibilly too many events occurred at once")
	case n < syscall.SizeofInotifyEvent:
		// brefore: syscall.Read
		//   while n < syscall.SizeofInotifyEvent:
		// 	  syscall.Ioctl(w.fd, syscall.FIONREAD, &n);
		//
		return errors.New("inotify: short read in readEvents()")
	}

	var offset uint32 = 0
	// We don't know how many events we just read into the buffer
	// While the offset points to at least one whole event...
	for offset <= uint32(n-syscall.SizeofInotifyEvent) {
		// Point "raw" to the event in the buffer
		raw := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset]))
		event := new(Event)
		event.Mask = uint32(raw.Mask)
		event.Cookie = uint32(raw.Cookie)
		nameLen := uint32(raw.Len)
		// If the event happened to the watched directory or the watched file, the kernel
		// doesn't append the filename to the event, but we would like to always fill the
		// the "Name" field with a valid filename. We retrieve the path of the watch from
		// the "paths" map.
		w.mu.Lock()
		event.Name = w.paths[int(raw.Wd)]
		w.mu.Unlock()
		if nameLen > 0 {
			// Point "bytes" at the first byte of the filename
			bytes := (*[syscall.PathMax]byte)(unsafe.Pointer(&buf[offset+syscall.SizeofInotifyEvent]))
			// The filename is padded with NUL bytes. TrimRight() gets rid of those.
			event.Name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
		}
		// Send the event on the events channel
		w.Event <- event

		// Move to the next event in the buffer
		offset += syscall.SizeofInotifyEvent + nameLen
	}

	return nil
}

// String formats the event e in the form
// "filename: 0xEventMask = IN_ACCESS|IN_ATTRIB_|..."
func (e *Event) String() string {
	var events string = ""

	m := e.Mask
	for _, b := range eventBits {
		if m&b.Value != 0 {
			m &^= b.Value
			events += "|" + b.Name
		}
	}

	if m != 0 {
		events += fmt.Sprintf("|%#x", m)
	}
	if len(events) > 0 {
		events = " == " + events[1:]
	}

	return fmt.Sprintf("%q: %#x%s", e.Name, e.Mask, events)
}

const (
	// Options for inotify_init() are not exported
	// IN_CLOEXEC    uint32 = syscall.IN_CLOEXEC
	// IN_NONBLOCK   uint32 = syscall.IN_NONBLOCK

	// Options for AddWatch
	IN_DONT_FOLLOW uint32 = syscall.IN_DONT_FOLLOW
	IN_ONESHOT     uint32 = syscall.IN_ONESHOT
	IN_ONLYDIR     uint32 = syscall.IN_ONLYDIR

	// The "IN_MASK_ADD" option is not exported, as AddWatch
	// adds it automatically, if there is already a watch for the given path
	// IN_MASK_ADD      uint32 = syscall.IN_MASK_ADD

	// Events
	IN_ACCESS        uint32 = syscall.IN_ACCESS
	IN_ALL_EVENTS    uint32 = syscall.IN_ALL_EVENTS
	IN_ATTRIB        uint32 = syscall.IN_ATTRIB
	IN_CLOSE         uint32 = syscall.IN_CLOSE
	IN_CLOSE_NOWRITE uint32 = syscall.IN_CLOSE_NOWRITE
	IN_CLOSE_WRITE   uint32 = syscall.IN_CLOSE_WRITE
	IN_CREATE        uint32 = syscall.IN_CREATE
	IN_DELETE        uint32 = syscall.IN_DELETE
	IN_DELETE_SELF   uint32 = syscall.IN_DELETE_SELF
	IN_MODIFY        uint32 = syscall.IN_MODIFY
	IN_MOVE          uint32 = syscall.IN_MOVE
	IN_MOVED_FROM    uint32 = syscall.IN_MOVED_FROM
	IN_MOVED_TO      uint32 = syscall.IN_MOVED_TO
	IN_MOVE_SELF     uint32 = syscall.IN_MOVE_SELF
	IN_OPEN          uint32 = syscall.IN_OPEN

	// Special events
	IN_ISDIR      uint32 = syscall.IN_ISDIR
	IN_IGNORED    uint32 = syscall.IN_IGNORED
	IN_Q_OVERFLOW uint32 = syscall.IN_Q_OVERFLOW
	IN_UNMOUNT    uint32 = syscall.IN_UNMOUNT
)

var eventBits = []struct {
	Value uint32
	Name  string
}{
	{IN_ACCESS, "IN_ACCESS"},
	{IN_ATTRIB, "IN_ATTRIB"},
	{IN_CLOSE, "IN_CLOSE"},
	{IN_CLOSE_NOWRITE, "IN_CLOSE_NOWRITE"},
	{IN_CLOSE_WRITE, "IN_CLOSE_WRITE"},
	{IN_CREATE, "IN_CREATE"},
	{IN_DELETE, "IN_DELETE"},
	{IN_DELETE_SELF, "IN_DELETE_SELF"},
	{IN_MODIFY, "IN_MODIFY"},
	{IN_MOVE, "IN_MOVE"},
	{IN_MOVED_FROM, "IN_MOVED_FROM"},
	{IN_MOVED_TO, "IN_MOVED_TO"},
	{IN_MOVE_SELF, "IN_MOVE_SELF"},
	{IN_OPEN, "IN_OPEN"},
	{IN_ISDIR, "IN_ISDIR"},
	{IN_IGNORED, "IN_IGNORED"},
	{IN_Q_OVERFLOW, "IN_Q_OVERFLOW"},
	{IN_UNMOUNT, "IN_UNMOUNT"},
}
<|endoftext|>


<commit_before>package inproc

import (
	"errors"
	"fmt"
	"io"
	"math"
	"net"
	"strconv"
	"sync"
	"time"

	"github.com/anacrolix/missinggo"
)

var (
	mu       sync.Mutex
	cond         = sync.Cond{L: &mu}
	nextPort int = 1
	conns        = map[int]*packetConn{}
)

type addr struct {
	Port int
}

func (addr) Network() string {
	return "inproc"
}

func (me addr) String() string {
	return fmt.Sprintf("127.0.0.1:%d", me.Port)
}

func getPort() (port int) {
	mu.Lock()
	defer mu.Unlock()
	port = nextPort
	nextPort++
	return
}

func ResolveAddr(network, str string) (net.Addr, error) {
	return ResolveInprocAddr(network, str)
}

func ResolveInprocAddr(network, str string) (addr addr, err error) {
	if str == "" {
		addr.Port = getPort()
		return
	}
	_, p, err := net.SplitHostPort(str)
	if err != nil {
		return
	}
	i64, err := strconv.ParseInt(p, 10, 0)
	if err != nil {
		return
	}
	addr.Port = int(i64)
	if addr.Port == 0 {
		addr.Port = getPort()
	}
	return
}

func ListenPacket(network, addrStr string) (nc net.PacketConn, err error) {
	addr, err := ResolveInprocAddr(network, addrStr)
	if err != nil {
		return
	}
	mu.Lock()
	defer mu.Unlock()
	if _, ok := conns[addr.Port]; ok {
		err = errors.New("address in use")
		return
	}
	pc := &packetConn{
		addr:          addr,
		readDeadline:  newCondDeadline(&cond),
		writeDeadline: newCondDeadline(&cond),
	}
	conns[addr.Port] = pc
	nc = pc
	return
}

type packet struct {
	data []byte
	addr addr
}

type packetConn struct {
	closed        bool
	addr          addr
	reads         []packet
	readDeadline  *condDeadline
	writeDeadline *condDeadline
}

func (me *packetConn) Close() error {
	mu.Lock()
	defer mu.Unlock()
	me.closed = true
	delete(conns, me.addr.Port)
	cond.Broadcast()
	return nil
}

func (me *packetConn) LocalAddr() net.Addr {
	return me.addr
}

var errTimeout = errors.New("i/o timeout")

func (me *packetConn) WriteTo(b []byte, na net.Addr) (n int, err error) {
	n = len(b)
	mu.Lock()
	defer mu.Unlock()
	if me.writeDeadline.exceeded() {
		err = errTimeout
		return
	}
	port := missinggo.AddrPort(na)
	c, ok := conns[port]
	if !ok {
		// log.Printf("no conn for port %d", port)
		return
	}
	c.reads = append(c.reads, packet{append([]byte(nil), b...), me.addr})
	cond.Broadcast()
	return
}

func (me *packetConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
	mu.Lock()
	defer mu.Unlock()
	for {
		if len(me.reads) != 0 {
			r := me.reads[0]
			me.reads = me.reads[1:]
			n = copy(b, r.data)
			addr = r.addr
			// log.Println(addr)
			return
		}
		if me.closed {
			err = io.EOF
			return
		}
		if me.readDeadline.exceeded() {
			err = errTimeout
			return
		}
		cond.Wait()
	}
}

func (me *packetConn) SetDeadline(t time.Time) error {
	me.writeDeadline.setDeadline(t)
	me.readDeadline.setDeadline(t)
	return nil
}

func (me *packetConn) SetReadDeadline(t time.Time) error {
	me.readDeadline.setDeadline(t)
	return nil
}

func (me *packetConn) SetWriteDeadline(t time.Time) error {
	me.writeDeadline.setDeadline(t)
	return nil
}

func newCondDeadline(cond *sync.Cond) (ret *condDeadline) {
	ret = &condDeadline{
		timer: time.AfterFunc(math.MaxInt64, func() {
			mu.Lock()
			ret._exceeded = true
			mu.Unlock()
			cond.Broadcast()
		}),
	}
	ret.setDeadline(time.Time{})
	return
}

type condDeadline struct {
	mu        sync.Mutex
	_exceeded bool
	timer     *time.Timer
}

func (me *condDeadline) setDeadline(t time.Time) {
	me.mu.Lock()
	defer me.mu.Unlock()
	me._exceeded = false
	if t.IsZero() {
		me.timer.Stop()
		return
	}
	me.timer.Reset(t.Sub(time.Now()))
}

func (me *condDeadline) exceeded() bool {
	me.mu.Lock()
	defer me.mu.Unlock()
	return me._exceeded
}
<commit_msg>inproc: Use empty null host in addr strings<commit_after>package inproc

import (
	"errors"
	"fmt"
	"io"
	"math"
	"net"
	"strconv"
	"sync"
	"time"

	"github.com/anacrolix/missinggo"
)

var (
	mu       sync.Mutex
	cond         = sync.Cond{L: &mu}
	nextPort int = 1
	conns        = map[int]*packetConn{}
)

type addr struct {
	Port int
}

func (addr) Network() string {
	return "inproc"
}

func (me addr) String() string {
	return fmt.Sprintf(":%d", me.Port)
}

func getPort() (port int) {
	mu.Lock()
	defer mu.Unlock()
	port = nextPort
	nextPort++
	return
}

func ResolveAddr(network, str string) (net.Addr, error) {
	return ResolveInprocAddr(network, str)
}

func ResolveInprocAddr(network, str string) (addr addr, err error) {
	if str == "" {
		addr.Port = getPort()
		return
	}
	_, p, err := net.SplitHostPort(str)
	if err != nil {
		return
	}
	i64, err := strconv.ParseInt(p, 10, 0)
	if err != nil {
		return
	}
	addr.Port = int(i64)
	if addr.Port == 0 {
		addr.Port = getPort()
	}
	return
}

func ListenPacket(network, addrStr string) (nc net.PacketConn, err error) {
	addr, err := ResolveInprocAddr(network, addrStr)
	if err != nil {
		return
	}
	mu.Lock()
	defer mu.Unlock()
	if _, ok := conns[addr.Port]; ok {
		err = errors.New("address in use")
		return
	}
	pc := &packetConn{
		addr:          addr,
		readDeadline:  newCondDeadline(&cond),
		writeDeadline: newCondDeadline(&cond),
	}
	conns[addr.Port] = pc
	nc = pc
	return
}

type packet struct {
	data []byte
	addr addr
}

type packetConn struct {
	closed        bool
	addr          addr
	reads         []packet
	readDeadline  *condDeadline
	writeDeadline *condDeadline
}

func (me *packetConn) Close() error {
	mu.Lock()
	defer mu.Unlock()
	me.closed = true
	delete(conns, me.addr.Port)
	cond.Broadcast()
	return nil
}

func (me *packetConn) LocalAddr() net.Addr {
	return me.addr
}

var errTimeout = errors.New("i/o timeout")

func (me *packetConn) WriteTo(b []byte, na net.Addr) (n int, err error) {
	n = len(b)
	mu.Lock()
	defer mu.Unlock()
	if me.writeDeadline.exceeded() {
		err = errTimeout
		return
	}
	port := missinggo.AddrPort(na)
	c, ok := conns[port]
	if !ok {
		// log.Printf("no conn for port %d", port)
		return
	}
	c.reads = append(c.reads, packet{append([]byte(nil), b...), me.addr})
	cond.Broadcast()
	return
}

func (me *packetConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
	mu.Lock()
	defer mu.Unlock()
	for {
		if len(me.reads) != 0 {
			r := me.reads[0]
			me.reads = me.reads[1:]
			n = copy(b, r.data)
			addr = r.addr
			// log.Println(addr)
			return
		}
		if me.closed {
			err = io.EOF
			return
		}
		if me.readDeadline.exceeded() {
			err = errTimeout
			return
		}
		cond.Wait()
	}
}

func (me *packetConn) SetDeadline(t time.Time) error {
	me.writeDeadline.setDeadline(t)
	me.readDeadline.setDeadline(t)
	return nil
}

func (me *packetConn) SetReadDeadline(t time.Time) error {
	me.readDeadline.setDeadline(t)
	return nil
}

func (me *packetConn) SetWriteDeadline(t time.Time) error {
	me.writeDeadline.setDeadline(t)
	return nil
}

func newCondDeadline(cond *sync.Cond) (ret *condDeadline) {
	ret = &condDeadline{
		timer: time.AfterFunc(math.MaxInt64, func() {
			mu.Lock()
			ret._exceeded = true
			mu.Unlock()
			cond.Broadcast()
		}),
	}
	ret.setDeadline(time.Time{})
	return
}

type condDeadline struct {
	mu        sync.Mutex
	_exceeded bool
	timer     *time.Timer
}

func (me *condDeadline) setDeadline(t time.Time) {
	me.mu.Lock()
	defer me.mu.Unlock()
	me._exceeded = false
	if t.IsZero() {
		me.timer.Stop()
		return
	}
	me.timer.Reset(t.Sub(time.Now()))
}

func (me *condDeadline) exceeded() bool {
	me.mu.Lock()
	defer me.mu.Unlock()
	return me._exceeded
}
<|endoftext|>


<commit_before>package mp

<commit_msg>Update source files<commit_after>package mp

const (
	// 下面6个类型(包括view类型)的按钮是在公众平台官网发布的菜单按钮类型
	ButtonTypeText  = "text"
	ButtonTypeImage = "img"
	ButtonTypePhoto = "photo"
	ButtonTypeVideo = "video"
	ButtonTypeVoice = "voice"
	// 上面5个类型的按钮不能通过API设置

	ButtonTypeView  = "view"  // 跳转URL
	ButtonTypeClick = "click" // 点击推事件

	ButtonTypeMiniprogram = "miniprogram" // 小程序

	// 下面的按钮类型仅支持微信 iPhone5.4.1 以上版本, 和 Android5.4 以上版本的微信用户,
	// 旧版本微信用户点击后将没有回应, 开发者也不能正常接收到事件推送
	ButtonTypeScanCodePush    = "scancode_push"      // 扫码推事件
	ButtonTypeScanCodeWaitMsg = "scancode_waitmsg"   // 扫码带提示
	ButtonTypePicSysPhoto     = "pic_sysphoto"       // 系统拍照发图
	ButtonTypePicPhotoOrAlbum = "pic_photo_or_album" // 拍照或者相册发图
	ButtonTypePicWeixin       = "pic_weixin"         // 微信相册发图
	ButtonTypeLocationSelect  = "location_select"    // 发送位置

	// 下面的按钮类型专门给第三方平台旗下未微信认证(具体而言, 是资质认证未通过)的订阅号准备的事件类型,
	// 它们是没有事件推送的, 能力相对受限, 其他类型的公众号不必使用
	ButtonTypeMediaId     = "media_id"     // 下发消息
	ButtonTypeViewLimited = "view_limited" // 跳转图文消息URL
)

type Menu struct {
	Buttons   []Button   `json:"button,omitempty"`
	MatchRule *MatchRule `json:"matchrule,omitempty"`
	MenuId    int64      `json:"menuid,omitempty"`
}

type MatchRule struct {
	GroupID            *int64 `json:"group_id,omitempty"`
	Sex                *int   `json:"sex,omitempty"`
	Country            string `json:"country,omitempty"`
	Province           string `json:"province,omitempty"`
	City               string `json:"city,omitempty"`
	ClientPlatformType *int   `json:"client_platform_type,omitempty"`
	Language           string `json:"language,omitempty"`
}

type Button struct {
	Type       string   `json:"type,omitempty"`
	SubButtons []Button `json:"sub_button,omitempty"`
	Name       string   `json:"name,omitempty"`     // 菜单标题，不超过16个字节，子菜单不超过60个字节
	Key        string   `json:"key,omitempty"`      // 菜单KEY值，用于消息接口推送，不超过128字节
	URL        string   `json:"url,omitempty"`      // 网页链接，用户点击菜单可打开链接，不超过1024字节
	MediaID    string   `json:"media_id,omitempty"` // 调用新增永久素材接口返回的合法media_id
	AppID      string   `json:"appid,omitempty"`    // 小程序AppID（仅认证公众号可配置）
	PagePath   string   `json:"pagepath"`           // 小程序的页面路径
}

func NewButtonWithSubButtons(name string, subButtons []Button) *Button {
	return &Button{
		Name:       name,
		SubButtons: subButtons,
	}
}

func NewButtonForClick(name, key string) *Button {
	return &Button{
		Type: ButtonTypeClick,
		Name: name,
		Key:  key,
	}
}

func NewButtonForView(name, url string) *Button {
	return &Button{
		Type: ButtonTypeView,
		Name: name,
		URL:  url,
	}
}

func NewButtonForMiniprogram(name, url, appID, pagePath string) *Button {
	return &Button{
		Type:     ButtonTypeMiniprogram,
		Name:     name,
		URL:      url,
		AppID:    appID,
		PagePath: pagePath,
	}
}

func NewButtonForScanCodePush(name, key string) *Button {
	return &Button{
		Type: ButtonTypeScanCodePush,
		Name: name,
		Key:  key,
	}
}

func NewButtonForScanCodeWaitMsg(name, key string) *Button {
	return &Button{
		Type: ButtonTypeScanCodeWaitMsg,
		Name: name,
		Key:  key,
	}
}

func NewButtonForPicSysPhoto(name, key string) *Button {
	return &Button{
		Type: ButtonTypePicSysPhoto,
		Name: name,
		Key:  key,
	}
}

func NewButtonForPicPhotoOrAlbum(name, key string) *Button {
	return &Button{
		Type: ButtonTypePicPhotoOrAlbum,
		Name: name,
		Key:  key,
	}
}

func NewButtonForPicWeixin(name, key string) *Button {
	return &Button{
		Type: ButtonTypePicWeixin,
		Name: name,
		Key:  key,
	}
}

func NewButtonForLocationSelect(name, key string) *Button {
	return &Button{
		Type: ButtonTypeLocationSelect,
		Name: name,
		Key:  key,
	}
}

func NewButtonForMediaId(name, mediaID string) *Button {
	return &Button{
		Type:    ButtonTypeMediaId,
		Name:    name,
		MediaID: mediaID,
	}
}

func NewButtonForViewLimited(name, mediaID string) *Button {
	return &Button{
		Type:    ButtonTypeViewLimited,
		Name:    name,
		MediaID: mediaID,
	}
}

func (c *Client) CreateMenu(menu *Menu) error {
	u := BASE_URL.Join("/menu/create")

	var rep Err
	return c.Post(u, menu, &rep)
}

func (c *Client) GetMenu() (menu *Menu, conditionalMenus []Menu, err error) {
	u := BASE_URL.Join("/menu/get")

	var rep struct {
		Err
		Menu             Menu   `json:"menu"`
		ConditionalMenus []Menu `json:"conditionalmenu"`
	}

	err = c.Get(u, &rep)
	if err != nil {
		return
	}

	return &rep.Menu, rep.ConditionalMenus, nil
}

func (c *Client) DeleteMenu() error {
	u := BASE_URL.Join("/menu/delete")

	var rep Err
	return c.Get(u, &rep)
}
<|endoftext|>


<commit_before>// user
package mp

import ()

const ()

type Group struct {
	Id    int    `json:"id"`
	Name  string `json:"name"`
	Count int    `json:"count,omitempty"`
}

type User struct {
	Subscribe     bool   `json:"subscribe"`
	OpenId        string `json:"openid"`
	Nickname      string `json:"nickname"`
	Sex           int    `json:"sex"`
	Language      string `json:"language"`
	City          string `json:"city"`
	Province      string `json:"province"`
	Country       string `json:"country"`
	HeadImgUrl    string `json:"headimgurl"`
	SubscribeTime int64  `json:"subscribe_time"`
}

type OpenIdList struct {
	OpenIds []string `json:"openid"`
}

type UserFollowersResponse struct {
	Total      int        `json:"total"`
	Count      int        `json:"count"`
	Data       OpenIdList `json:"data"`
	NextOpenId string     `json:"next_openid"`
	Error
}
<commit_msg>fix bug<commit_after>// user
package mp

import ()

const ()

type Group struct {
	Id    int    `json:"id"`
	Name  string `json:"name"`
	Count int    `json:"count,omitempty"`
}

type User struct {
	Subscribe     int    `json:"subscribe"`
	OpenId        string `json:"openid"`
	Nickname      string `json:"nickname"`
	Sex           int    `json:"sex"`
	Language      string `json:"language"`
	City          string `json:"city"`
	Province      string `json:"province"`
	Country       string `json:"country"`
	HeadImgUrl    string `json:"headimgurl"`
	SubscribeTime int64  `json:"subscribe_time"`
}

type OpenIdList struct {
	OpenIds []string `json:"openid"`
}

type UserFollowersResponse struct {
	Total      int        `json:"total"`
	Count      int        `json:"count"`
	Data       OpenIdList `json:"data"`
	NextOpenId string     `json:"next_openid"`
	Error
}
<|endoftext|>


<commit_before><commit_msg>Fix various bugs<commit_after><|endoftext|>


<commit_before>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package winfsnotify

import (
	"os"
	"time"
	"testing"
)

func expect(t *testing.T, eventstream <-chan *Event, name string, mask uint32) {
	t.Logf(`expected: "%s": 0x%x`, name, mask)
	select {
	case event := <-eventstream:
		if event == nil {
			t.Fatal("nil event received")
		}
		t.Logf("received: %s", event)
		if event.Name != name || event.Mask != mask {
			t.Fatal("did not receive expected event")
		}
	case <-time.After(1e9):
		t.Fatal("timed out waiting for event")
	}
}

func TestNotifyEvents(t *testing.T) {
	watcher, err := NewWatcher()
	if err != nil {
		t.Fatalf("NewWatcher() failed: %s", err)
	}

	testDir := "TestNotifyEvents.testdirectory"
	testFile := testDir + "/TestNotifyEvents.testfile"
	testFile2 := testFile + ".new"
	const mask = FS_ALL_EVENTS & ^(FS_ATTRIB|FS_CLOSE) | FS_IGNORED

	// Add a watch for testDir
	os.RemoveAll(testDir)
	if err = os.Mkdir(testDir, 0777); err != nil {
		t.Fatalf("Failed to create test directory", err)
	}
	defer os.RemoveAll(testDir)
	err = watcher.AddWatch(testDir, mask)
	if err != nil {
		t.Fatalf("Watcher.Watch() failed: %s", err)
	}

	// Receive errors on the error channel on a separate goroutine
	go func() {
		for err := range watcher.Error {
			t.Fatalf("error received: %s", err)
		}
	}()

	// Create a file
	file, err := os.Create(testFile)
	if err != nil {
		t.Fatalf("creating test file failed: %s", err)
	}
	expect(t, watcher.Event, testFile, FS_CREATE)

	err = watcher.AddWatch(testFile, mask)
	if err != nil {
		t.Fatalf("Watcher.Watch() failed: %s", err)
	}

	if _, err = file.WriteString("hello, world"); err != nil {
		t.Fatalf("failed to write to test file: %s", err)
	}
	if err = file.Sync(); err != nil {
		t.Fatalf("failed to sync test file: %s", err)
	}
	expect(t, watcher.Event, testFile, FS_MODIFY)
	expect(t, watcher.Event, testFile, FS_MODIFY)

	if err = file.Close(); err != nil {
		t.Fatalf("failed to close test file: %s", err)
	}

	if err = os.Rename(testFile, testFile2); err != nil {
		t.Fatalf("failed to rename test file: %s", err)
	}
	expect(t, watcher.Event, testFile, FS_MOVED_FROM)
	expect(t, watcher.Event, testFile2, FS_MOVED_TO)
	expect(t, watcher.Event, testFile, FS_MOVE_SELF)

	if err = os.RemoveAll(testDir); err != nil {
		t.Fatalf("failed to remove test directory: %s", err)
	}
	expect(t, watcher.Event, testFile2, FS_DELETE_SELF)
	expect(t, watcher.Event, testFile2, FS_IGNORED)
	expect(t, watcher.Event, testFile2, FS_DELETE)
	expect(t, watcher.Event, testDir, FS_DELETE_SELF)
	expect(t, watcher.Event, testDir, FS_IGNORED)

	t.Log("calling Close()")
	if err = watcher.Close(); err != nil {
		t.Fatalf("failed to close watcher: %s", err)
	}
}

func TestNotifyClose(t *testing.T) {
	watcher, _ := NewWatcher()
	watcher.Close()

	done := false
	go func() {
		watcher.Close()
		done = true
	}()

	time.Sleep(50e6) // 50 ms
	if !done {
		t.Fatal("double Close() test failed: second Close() call didn't return")
	}

	err := watcher.Watch("_test")
	if err == nil {
		t.Fatal("expected error on Watch() after Close(), got nil")
	}
}
<commit_msg>exp/winfsnotify: fix test<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package winfsnotify

import (
	"os"
	"time"
	"testing"
)

func expect(t *testing.T, eventstream <-chan *Event, name string, mask uint32) {
	t.Logf(`expected: "%s": 0x%x`, name, mask)
	select {
	case event := <-eventstream:
		if event == nil {
			t.Fatal("nil event received")
		}
		t.Logf("received: %s", event)
		if event.Name != name || event.Mask != mask {
			t.Fatal("did not receive expected event")
		}
	case <-time.After(1e9):
		t.Fatal("timed out waiting for event")
	}
}

func TestNotifyEvents(t *testing.T) {
	watcher, err := NewWatcher()
	if err != nil {
		t.Fatalf("NewWatcher() failed: %s", err)
	}

	testDir := "TestNotifyEvents.testdirectory"
	testFile := testDir + "/TestNotifyEvents.testfile"
	testFile2 := testFile + ".new"
	const mask = FS_ALL_EVENTS & ^(FS_ATTRIB|FS_CLOSE) | FS_IGNORED

	// Add a watch for testDir
	os.RemoveAll(testDir)
	if err = os.Mkdir(testDir, 0777); err != nil {
		t.Fatalf("Failed to create test directory", err)
	}
	defer os.RemoveAll(testDir)
	err = watcher.AddWatch(testDir, mask)
	if err != nil {
		t.Fatalf("Watcher.Watch() failed: %s", err)
	}

	// Receive errors on the error channel on a separate goroutine
	go func() {
		for err := range watcher.Error {
			t.Fatalf("error received: %s", err)
		}
	}()

	// Create a file
	file, err := os.Create(testFile)
	if err != nil {
		t.Fatalf("creating test file failed: %s", err)
	}
	expect(t, watcher.Event, testFile, FS_CREATE)

	err = watcher.AddWatch(testFile, mask)
	if err != nil {
		t.Fatalf("Watcher.Watch() failed: %s", err)
	}

	if _, err = file.WriteString("hello, world"); err != nil {
		t.Fatalf("failed to write to test file: %s", err)
	}
	if err = file.Close(); err != nil {
		t.Fatalf("failed to close test file: %s", err)
	}
	expect(t, watcher.Event, testFile, FS_MODIFY)
	expect(t, watcher.Event, testFile, FS_MODIFY)

	if err = os.Rename(testFile, testFile2); err != nil {
		t.Fatalf("failed to rename test file: %s", err)
	}
	expect(t, watcher.Event, testFile, FS_MOVED_FROM)
	expect(t, watcher.Event, testFile2, FS_MOVED_TO)
	expect(t, watcher.Event, testFile, FS_MOVE_SELF)

	if err = os.RemoveAll(testDir); err != nil {
		t.Fatalf("failed to remove test directory: %s", err)
	}
	expect(t, watcher.Event, testFile2, FS_DELETE_SELF)
	expect(t, watcher.Event, testFile2, FS_IGNORED)
	expect(t, watcher.Event, testFile2, FS_DELETE)
	expect(t, watcher.Event, testDir, FS_DELETE_SELF)
	expect(t, watcher.Event, testDir, FS_IGNORED)

	t.Log("calling Close()")
	if err = watcher.Close(); err != nil {
		t.Fatalf("failed to close watcher: %s", err)
	}
}

func TestNotifyClose(t *testing.T) {
	watcher, _ := NewWatcher()
	watcher.Close()

	done := false
	go func() {
		watcher.Close()
		done = true
	}()

	time.Sleep(50e6) // 50 ms
	if !done {
		t.Fatal("double Close() test failed: second Close() call didn't return")
	}

	err := watcher.Watch("_test")
	if err == nil {
		t.Fatal("expected error on Watch() after Close(), got nil")
	}
}
<|endoftext|>