<commit_before>package main

import (
	"compress/gzip"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/couchbaselabs/cbfs/config"
	"github.com/dustin/gomemcached"
	"github.com/dustin/gomemcached/client"
)

func doGetConfig(w http.ResponseWriter, req *http.Request) {
	err := updateConfig()
	if err != nil && !gomemcached.IsNotFound(err) {
		log.Printf("Error updating config: %v", err)
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(200)

	e := json.NewEncoder(w)
	err = e.Encode(&globalConfig)
	if err != nil {
		log.Printf("Error sending config: %v", err)
	}
}

func putConfig(w http.ResponseWriter, req *http.Request) {
	d := json.NewDecoder(req.Body)
	conf := cbfsconfig.CBFSConfig{}

	err := d.Decode(&conf)
	if err != nil {
		w.WriteHeader(500)
		fmt.Fprintf(w, "Error reading config: %v", err)
		return
	}

	err = StoreConfig(conf)
	if err != nil {
		w.WriteHeader(500)
		fmt.Fprintf(w, "Error writing config: %v", err)
		return
	}

	err = updateConfig()
	if err != nil {
		log.Printf("Error fetching newly stored config: %v", err)
	}

	w.WriteHeader(204)
}

func doList(w http.ResponseWriter, req *http.Request) {
	w.WriteHeader(200)
	explen := getHash().Size() * 2
	filepath.Walk(*root, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !info.IsDir() && !strings.HasPrefix(info.Name(), "tmp") &&
			len(info.Name()) == explen {
			_, e := w.Write([]byte(info.Name() + "\n"))
			return e
		}
		return nil
	})
}

func doListTasks(w http.ResponseWriter, req *http.Request) {
	tasks, err := listRunningTasks()
	if err != nil {
		w.WriteHeader(500)
		fmt.Fprintf(w, "Error listing tasks:  %v", err)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(200)

	// Reformat for more APIish output.
	output := map[string]map[string]TaskState{}

	for _, tl := range tasks {
		// Remove node prefix from local task names.
		npre := tl.Node + "/"

		for k, v := range tl.Tasks {
			if strings.HasPrefix(k, npre) {
				delete(tl.Tasks, k)
				tl.Tasks[k[len(npre):]] = v
			}
		}
		output[tl.Node] = tl.Tasks
	}

	e := json.NewEncoder(w)
	err = e.Encode(output)
	if err != nil {
		log.Printf("Error encoding running tasks list: %v", err)
	}
}

func doGetMeta(w http.ResponseWriter, req *http.Request, path string) {
	got := fileMeta{}
	err := couchbase.Get(shortName(path), &got)
	if err != nil {
		log.Printf("Error getting file %#v: %v", path, err)
		w.WriteHeader(404)
		w.Write([]byte(err.Error()))
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(200)
	if got.Userdata == nil {
		w.Write([]byte("{}"))
	} else {
		w.Write(*got.Userdata)
	}
}

func putMeta(w http.ResponseWriter, req *http.Request, path string) {
	got := fileMeta{}
	casid := uint64(0)
	k := shortName(path)
	err := couchbase.Gets(k, &got, &casid)
	if err != nil {
		log.Printf("Error getting file %#v: %v", path, err)
		w.WriteHeader(404)
		w.Write([]byte(err.Error()))
		return
	}

	r := json.RawMessage{}
	err = json.NewDecoder(req.Body).Decode(&r)
	if err != nil {
		w.WriteHeader(400)
		w.Write([]byte(err.Error()))
		return
	}

	got.Userdata = &r
	b := mustEncode(&got)

	err = couchbase.Do(k, func(mc *memcached.Client, vb uint16) error {
		req := &gomemcached.MCRequest{
			Opcode:  gomemcached.SET,
			VBucket: vb,
			Key:     []byte(path),
			Cas:     casid,
			Opaque:  0,
			Extras:  []byte{0, 0, 0, 0, 0, 0, 0, 0},
			Body:    b}
		resp, err := mc.Send(req)
		if err != nil {
			return err
		}
		if resp.Status != gomemcached.SUCCESS {
			return resp
		}
		return nil
	})

	if err == nil {
		w.WriteHeader(201)
	} else {
		w.WriteHeader(500)
		w.Write([]byte(err.Error()))
	}
}

func doListNodes(w http.ResponseWriter, req *http.Request) {
	nl, err := findAllNodes()
	if err != nil {
		log.Printf("Error executing nodes view: %v", err)
		w.WriteHeader(500)
		fmt.Fprintf(w, "Error generating node list: %v", err)
		return
	}

	respob := map[string]map[string]interface{}{}
	for _, node := range nl {
		age := time.Since(node.Time)
		respob[node.name] = map[string]interface{}{
			"size":       node.storageSize,
			"addr":       node.Address(),
			"starttime":  node.Started,
			"hbtime":     node.Time,
			"hbage_ms":   age.Nanoseconds() / 1e6,
			"hbage_str":  age.String(),
			"used":       node.Used,
			"free":       node.Free,
			"addr_raw":   node.Addr,
			"bindaddr":   node.BindAddr,
			"framesbind": node.FrameBind,
		}
		// Grandfathering these in.
		if !node.Started.IsZero() {
			uptime := time.Since(node.Started)
			respob[node.name]["uptime_ms"] = uptime.Nanoseconds() / 1e6
			respob[node.name]["uptime_str"] = uptime.String()
		}

	}

	w.Header().Set("Content-Type", "application/json")
	w.Write(mustEncode(respob))
}

func doGetFramesData(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(200)
	w.Write(mustEncode(getFramesInfos()))
}

func proxyViewRequest(w http.ResponseWriter, req *http.Request,
	path string) {

	node := couchbase.Nodes[rand.Intn(len(couchbase.Nodes))]
	u, err := url.Parse(node.CouchAPIBase)
	if err != nil {
		w.WriteHeader(http.StatusBadGateway)
		return
	}

	u.Path = "/" + path
	u.RawQuery = req.URL.RawQuery

	client := &http.Client{
		Transport: TimeoutTransport(*viewTimeout),
	}

	res, err := client.Get(u.String())
	if err != nil {
		w.WriteHeader(http.StatusBadGateway)
		return
	}
	defer res.Body.Close()

	for k, vs := range res.Header {
		w.Header()[k] = vs
	}

	output := io.Writer(w)

	if canGzip(req) {
		w.Header().Set("Content-Encoding", "gzip")
		w.Header().Del("Content-Length")
		gz := gzip.NewWriter(w)
		defer gz.Close()
		output = gz
	}
	w.WriteHeader(res.StatusCode)

	io.Copy(output, res.Body)
}

func proxyCRUDGet(w http.ResponseWriter, req *http.Request,
	path string) {

	val, err := couchbase.GetRaw(path)
	if err != nil {
		w.WriteHeader(404)
		fmt.Fprintf(w, "Error getting value: %v", err)
		return
	}
	w.WriteHeader(200)
	w.Write(val)
}

func proxyCRUDPut(w http.ResponseWriter, req *http.Request,
	path string) {

	data, err := ioutil.ReadAll(req.Body)
	if err != nil {
		w.WriteHeader(500)
		fmt.Fprintf(w, "Error reading data: %v", err)
		return
	}

	err = couchbase.SetRaw(path, 0, data)
	if err != nil {
		w.WriteHeader(500)
		fmt.Fprintf(w, "Error storing value: %v", err)
		return
	}

	w.WriteHeader(204)
}

func proxyCRUDDelete(w http.ResponseWriter, req *http.Request,
	path string) {

	err := couchbase.Delete(path)
	if err != nil {
		w.WriteHeader(500)
		fmt.Fprintf(w, "Error deleting value: %v", err)
		return
	}

	w.WriteHeader(204)
}

func doListDocs(w http.ResponseWriter, req *http.Request,
	path string) {

	// trim off trailing slash early so we handle them consistently
	if strings.HasSuffix(path, "/") {
		path = path[0 : len(path)-1]
	}

	includeMeta := req.FormValue("includeMeta")
	depthString := req.FormValue("depth")
	depth := 1
	if depthString != "" {
		i, err := strconv.Atoi(depthString)
		if err != nil {
			w.WriteHeader(400)
			fmt.Fprintf(w, "Error processing depth parameter: %v", err)
			return
		}
		depth = i
	}

	fl, err := listFiles(path, includeMeta == "true", depth)
	if err != nil {
		log.Printf("Error executing file browse view: %v", err)
		w.WriteHeader(500)
		fmt.Fprintf(w, "Error generating file list: %v", err)
		return
	}

	if len(fl.Dirs) == 0 && len(fl.Files) == 0 {
		w.WriteHeader(404)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(200)

	e := json.NewEncoder(w)
	err = e.Encode(fl)
	if err != nil {
		log.Printf("Error writing json stream: %v", err)
	}
}

func doPing(w http.ResponseWriter, req *http.Request) {
	w.WriteHeader(204)
}

func doInduceTask(w http.ResponseWriter, req *http.Request, taskName string) {
	err := induceTask(taskName)
	switch err {
	case noSuchTask:
		http.Error(w, fmt.Sprintf("No such task: %q", taskName), 404)
	case taskAlreadyQueued, nil:
		w.WriteHeader(202)
	default:
		http.Error(w, err.Error(), 500)
	}
}
<commit_msg>Fix long path handling in crud proxy.<commit_after>package main

import (
	"compress/gzip"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/couchbaselabs/cbfs/config"
	"github.com/dustin/gomemcached"
	"github.com/dustin/gomemcached/client"
)

func doGetConfig(w http.ResponseWriter, req *http.Request) {
	err := updateConfig()
	if err != nil && !gomemcached.IsNotFound(err) {
		log.Printf("Error updating config: %v", err)
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(200)

	e := json.NewEncoder(w)
	err = e.Encode(&globalConfig)
	if err != nil {
		log.Printf("Error sending config: %v", err)
	}
}

func putConfig(w http.ResponseWriter, req *http.Request) {
	d := json.NewDecoder(req.Body)
	conf := cbfsconfig.CBFSConfig{}

	err := d.Decode(&conf)
	if err != nil {
		w.WriteHeader(500)
		fmt.Fprintf(w, "Error reading config: %v", err)
		return
	}

	err = StoreConfig(conf)
	if err != nil {
		w.WriteHeader(500)
		fmt.Fprintf(w, "Error writing config: %v", err)
		return
	}

	err = updateConfig()
	if err != nil {
		log.Printf("Error fetching newly stored config: %v", err)
	}

	w.WriteHeader(204)
}

func doList(w http.ResponseWriter, req *http.Request) {
	w.WriteHeader(200)
	explen := getHash().Size() * 2
	filepath.Walk(*root, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !info.IsDir() && !strings.HasPrefix(info.Name(), "tmp") &&
			len(info.Name()) == explen {
			_, e := w.Write([]byte(info.Name() + "\n"))
			return e
		}
		return nil
	})
}

func doListTasks(w http.ResponseWriter, req *http.Request) {
	tasks, err := listRunningTasks()
	if err != nil {
		w.WriteHeader(500)
		fmt.Fprintf(w, "Error listing tasks:  %v", err)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(200)

	// Reformat for more APIish output.
	output := map[string]map[string]TaskState{}

	for _, tl := range tasks {
		// Remove node prefix from local task names.
		npre := tl.Node + "/"

		for k, v := range tl.Tasks {
			if strings.HasPrefix(k, npre) {
				delete(tl.Tasks, k)
				tl.Tasks[k[len(npre):]] = v
			}
		}
		output[tl.Node] = tl.Tasks
	}

	e := json.NewEncoder(w)
	err = e.Encode(output)
	if err != nil {
		log.Printf("Error encoding running tasks list: %v", err)
	}
}

func doGetMeta(w http.ResponseWriter, req *http.Request, path string) {
	got := fileMeta{}
	err := couchbase.Get(shortName(path), &got)
	if err != nil {
		log.Printf("Error getting file %#v: %v", path, err)
		w.WriteHeader(404)
		w.Write([]byte(err.Error()))
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(200)
	if got.Userdata == nil {
		w.Write([]byte("{}"))
	} else {
		w.Write(*got.Userdata)
	}
}

func putMeta(w http.ResponseWriter, req *http.Request, path string) {
	got := fileMeta{}
	casid := uint64(0)
	k := shortName(path)
	err := couchbase.Gets(k, &got, &casid)
	if err != nil {
		log.Printf("Error getting file %#v: %v", path, err)
		w.WriteHeader(404)
		w.Write([]byte(err.Error()))
		return
	}

	r := json.RawMessage{}
	err = json.NewDecoder(req.Body).Decode(&r)
	if err != nil {
		w.WriteHeader(400)
		w.Write([]byte(err.Error()))
		return
	}

	got.Userdata = &r
	b := mustEncode(&got)

	err = couchbase.Do(k, func(mc *memcached.Client, vb uint16) error {
		req := &gomemcached.MCRequest{
			Opcode:  gomemcached.SET,
			VBucket: vb,
			Key:     []byte(path),
			Cas:     casid,
			Opaque:  0,
			Extras:  []byte{0, 0, 0, 0, 0, 0, 0, 0},
			Body:    b}
		resp, err := mc.Send(req)
		if err != nil {
			return err
		}
		if resp.Status != gomemcached.SUCCESS {
			return resp
		}
		return nil
	})

	if err == nil {
		w.WriteHeader(201)
	} else {
		w.WriteHeader(500)
		w.Write([]byte(err.Error()))
	}
}

func doListNodes(w http.ResponseWriter, req *http.Request) {
	nl, err := findAllNodes()
	if err != nil {
		log.Printf("Error executing nodes view: %v", err)
		w.WriteHeader(500)
		fmt.Fprintf(w, "Error generating node list: %v", err)
		return
	}

	respob := map[string]map[string]interface{}{}
	for _, node := range nl {
		age := time.Since(node.Time)
		respob[node.name] = map[string]interface{}{
			"size":       node.storageSize,
			"addr":       node.Address(),
			"starttime":  node.Started,
			"hbtime":     node.Time,
			"hbage_ms":   age.Nanoseconds() / 1e6,
			"hbage_str":  age.String(),
			"used":       node.Used,
			"free":       node.Free,
			"addr_raw":   node.Addr,
			"bindaddr":   node.BindAddr,
			"framesbind": node.FrameBind,
		}
		// Grandfathering these in.
		if !node.Started.IsZero() {
			uptime := time.Since(node.Started)
			respob[node.name]["uptime_ms"] = uptime.Nanoseconds() / 1e6
			respob[node.name]["uptime_str"] = uptime.String()
		}

	}

	w.Header().Set("Content-Type", "application/json")
	w.Write(mustEncode(respob))
}

func doGetFramesData(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(200)
	w.Write(mustEncode(getFramesInfos()))
}

func proxyViewRequest(w http.ResponseWriter, req *http.Request,
	path string) {

	node := couchbase.Nodes[rand.Intn(len(couchbase.Nodes))]
	u, err := url.Parse(node.CouchAPIBase)
	if err != nil {
		w.WriteHeader(http.StatusBadGateway)
		return
	}

	u.Path = "/" + path
	u.RawQuery = req.URL.RawQuery

	client := &http.Client{
		Transport: TimeoutTransport(*viewTimeout),
	}

	res, err := client.Get(u.String())
	if err != nil {
		w.WriteHeader(http.StatusBadGateway)
		return
	}
	defer res.Body.Close()

	for k, vs := range res.Header {
		w.Header()[k] = vs
	}

	output := io.Writer(w)

	if canGzip(req) {
		w.Header().Set("Content-Encoding", "gzip")
		w.Header().Del("Content-Length")
		gz := gzip.NewWriter(w)
		defer gz.Close()
		output = gz
	}
	w.WriteHeader(res.StatusCode)

	io.Copy(output, res.Body)
}

func proxyCRUDGet(w http.ResponseWriter, req *http.Request,
	path string) {

	val, err := couchbase.GetRaw(shortName(path))
	if err != nil {
		w.WriteHeader(404)
		fmt.Fprintf(w, "Error getting value: %v", err)
		return
	}
	w.WriteHeader(200)
	w.Write(val)
}

func proxyCRUDPut(w http.ResponseWriter, req *http.Request,
	path string) {

	data, err := ioutil.ReadAll(req.Body)
	if err != nil {
		w.WriteHeader(500)
		fmt.Fprintf(w, "Error reading data: %v", err)
		return
	}

	err = couchbase.SetRaw(shortName(path), 0, data)
	if err != nil {
		w.WriteHeader(500)
		fmt.Fprintf(w, "Error storing value: %v", err)
		return
	}

	w.WriteHeader(204)
}

func proxyCRUDDelete(w http.ResponseWriter, req *http.Request,
	path string) {

	err := couchbase.Delete(shortName(path))
	if err != nil {
		w.WriteHeader(500)
		fmt.Fprintf(w, "Error deleting value: %v", err)
		return
	}

	w.WriteHeader(204)
}

func doListDocs(w http.ResponseWriter, req *http.Request,
	path string) {

	// trim off trailing slash early so we handle them consistently
	if strings.HasSuffix(path, "/") {
		path = path[0 : len(path)-1]
	}

	includeMeta := req.FormValue("includeMeta")
	depthString := req.FormValue("depth")
	depth := 1
	if depthString != "" {
		i, err := strconv.Atoi(depthString)
		if err != nil {
			w.WriteHeader(400)
			fmt.Fprintf(w, "Error processing depth parameter: %v", err)
			return
		}
		depth = i
	}

	fl, err := listFiles(path, includeMeta == "true", depth)
	if err != nil {
		log.Printf("Error executing file browse view: %v", err)
		w.WriteHeader(500)
		fmt.Fprintf(w, "Error generating file list: %v", err)
		return
	}

	if len(fl.Dirs) == 0 && len(fl.Files) == 0 {
		w.WriteHeader(404)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(200)

	e := json.NewEncoder(w)
	err = e.Encode(fl)
	if err != nil {
		log.Printf("Error writing json stream: %v", err)
	}
}

func doPing(w http.ResponseWriter, req *http.Request) {
	w.WriteHeader(204)
}

func doInduceTask(w http.ResponseWriter, req *http.Request, taskName string) {
	err := induceTask(taskName)
	switch err {
	case noSuchTask:
		http.Error(w, fmt.Sprintf("No such task: %q", taskName), 404)
	case taskAlreadyQueued, nil:
		w.WriteHeader(202)
	default:
		http.Error(w, err.Error(), 500)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"net/url"
	"path"
	"strconv"
	"strings"

	"github.com/bruston/handlers/gzipped"
)

const (
	errWantInteger           = "n must be an integer"
	errStreamingNotSupported = "your client does not support streaming"
	maxBytes                 = 102400
	maxLines                 = 100
	loopback                 = "127.0.0.1"
)

func defaultHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if o := r.Header.Get("Origin"); o != "" {
			w.Header().Set("Access-Control-Allow-Origin", o)
			w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
			w.Header().Set("Access-Control-Allow-Headers",
				"Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
			if r.Method == "OPTIONS" {
				return
			}
		}
		h.ServeHTTP(w, r)
	})
}

func main() {
	listen := flag.String("listen", "127.0.0.1:8090", "The host and port to listen on.")
	flag.Parse()
	http.HandleFunc("/headers", headers)
	http.HandleFunc("/status/", status)
	http.HandleFunc("/ip", ip)
	http.HandleFunc("/get", get)
	http.Handle("/gzip", gzipped.New(http.HandlerFunc(gzip)))
	http.HandleFunc("/user-agent", userAgent)
	http.HandleFunc("/bytes/", writeBytes)
	http.HandleFunc("/stream/", stream)
	http.HandleFunc("/redirect-to", redirectTo)
	http.Handle("/basic-auth/", basicAuth(false))
	http.Handle("/hidden-basic-auth/", basicAuth(true))
	log.Fatal(http.ListenAndServe(*listen, defaultHandler(http.DefaultServeMux)))
}

func jsonHeader(w http.ResponseWriter) {
	w.Header().Set("Content-type", "application/json")
}

func writeJSON(w http.ResponseWriter, data interface{}, code int) error {
	jsonHeader(w)
	w.WriteHeader(code)
	return json.NewEncoder(w).Encode(data)
}

func headers(w http.ResponseWriter, r *http.Request) {
	writeJSON(w, r.Header, http.StatusOK)
}

func status(w http.ResponseWriter, r *http.Request) {
	code, err := strconv.Atoi(path.Base(r.URL.Path))
	if err != nil {
		http.Error(w, "status code must be an integer", http.StatusBadRequest)
		return
	}
	w.WriteHeader(code)
}

func getOrigin(r *http.Request) string {
	host, _, _ := net.SplitHostPort(r.RemoteAddr)
	if forwarded := r.Header.Get("X-Forwarded-For"); forwarded != "" && forwarded != host {
		if host == loopback {
			return forwarded
		}
		host = fmt.Sprintf("%s, %s", forwarded, host)
	}
	return host
}

func ip(w http.ResponseWriter, r *http.Request) {
	var o struct {
		Origin string `json:"origin"`
	}
	o.Origin = getOrigin(r)
	writeJSON(w, o, http.StatusOK)
}

type request struct {
	Args    url.Values  `json:"args"`
	Gzipped bool        `json:"gzipped,omitempty"`
	Headers http.Header `json:"headers"`
	Origin  string      `json:"origin"`
	URL     string      `json:"url"`
}

func rawURL(r *http.Request) string {
	var scheme string
	if r.TLS == nil {
		scheme = "http"
	} else {
		scheme = "https"
	}
	return scheme + "://" + r.Host + r.URL.String()
}

func getReq(r *http.Request) request {
	return request{
		Args:    r.URL.Query(),
		Headers: r.Header,
		Origin:  getOrigin(r),
		URL:     rawURL(r),
	}
}

func get(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
		return
	}
	writeJSON(w, getReq(r), http.StatusOK)
}

func gzip(w http.ResponseWriter, r *http.Request) {
	req := getReq(r)
	if _, ok := w.(gzipped.GzipResponseWriter); ok {
		req.Gzipped = true
	}
	writeJSON(w, req, http.StatusOK)
}

func userAgent(w http.ResponseWriter, r *http.Request) {
	var resp struct {
		UserAgent string `json:"user-agent"`
	}
	resp.UserAgent = r.Header.Get("User-Agent")
	writeJSON(w, resp, http.StatusOK)
}

func writeBytes(w http.ResponseWriter, r *http.Request) {
	n, err := strconv.Atoi(path.Base(r.URL.Path))
	if err != nil || n < 0 || n > maxBytes {
		http.Error(w, fmt.Sprintf("number of bytes must be in range: 0 - %d", maxBytes), http.StatusBadRequest)
		return
	}
	b := make([]byte, n)
	if _, err := rand.Read(b); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.Write(b)
}

func min(a, b int) int {
	if a <= b {
		return a
	}
	return b
}

func stream(w http.ResponseWriter, r *http.Request) {
	n, err := strconv.Atoi(path.Base(r.URL.Path))
	if err != nil || n < 0 {
		http.Error(w, errWantInteger, http.StatusBadRequest)
		return
	}
	n = min(n, maxLines)
	f, ok := w.(http.Flusher)
	if !ok {
		http.Error(w, errStreamingNotSupported, http.StatusBadRequest)
		return
	}
	req := getReq(r)
	jsonHeader(w)
	for i := 0; i < n; i++ {
		if err := json.NewEncoder(w).Encode(req); err != nil {
			return
		}
		f.Flush()
	}
}

func redirectTo(w http.ResponseWriter, r *http.Request) {
	dst := r.URL.Query().Get("url")
	if _, err := url.Parse(dst); dst == "" || err != nil {
		http.Error(w, "bad URL", http.StatusBadRequest)
		return
	}
	http.Redirect(w, r, dst, http.StatusFound)
}

type authedResponse struct {
	Authenticated bool   `json:"authenticated"`
	User          string `json:"user"`
}

func basicAuth(hidden bool) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		params := strings.Split(r.URL.Path, "/")
		if len(params) < 4 {
			http.NotFound(w, r)
			return
		}
		u, p, ok := r.BasicAuth()
		if !ok || u != params[2] || p != params[3] {
			if !hidden {
				w.Header().Set("WWW-Authenticate", "Basic realm=\"httpdump\"")
				w.WriteHeader(http.StatusUnauthorized)
			} else {
				http.NotFound(w, r)
			}
			return
		}
		writeJSON(w, authedResponse{true, u}, http.StatusOK)
	})
}
<commit_msg>Add delay endpoint<commit_after>package main

import (
	"crypto/rand"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"net/url"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/bruston/handlers/gzipped"
)

const (
	errWantInteger           = "n must be an integer"
	errStreamingNotSupported = "your client does not support streaming"
	maxBytes                 = 102400
	maxLines                 = 100
	loopback                 = "127.0.0.1"
)

func defaultHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if o := r.Header.Get("Origin"); o != "" {
			w.Header().Set("Access-Control-Allow-Origin", o)
			w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
			w.Header().Set("Access-Control-Allow-Headers",
				"Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
			if r.Method == "OPTIONS" {
				return
			}
		}
		h.ServeHTTP(w, r)
	})
}

func main() {
	listen := flag.String("listen", "127.0.0.1:8090", "The host and port to listen on.")
	flag.Parse()
	http.HandleFunc("/headers", headers)
	http.HandleFunc("/status/", status)
	http.HandleFunc("/ip", ip)
	http.HandleFunc("/get", get)
	http.Handle("/gzip", gzipped.New(http.HandlerFunc(gzip)))
	http.HandleFunc("/user-agent", userAgent)
	http.HandleFunc("/bytes/", writeBytes)
	http.HandleFunc("/stream/", stream)
	http.HandleFunc("/redirect-to", redirectTo)
	http.Handle("/basic-auth/", basicAuth(false))
	http.Handle("/hidden-basic-auth/", basicAuth(true))
	http.HandleFunc("/delay/", delay)
	log.Fatal(http.ListenAndServe(*listen, defaultHandler(http.DefaultServeMux)))
}

func jsonHeader(w http.ResponseWriter) {
	w.Header().Set("Content-type", "application/json")
}

func writeJSON(w http.ResponseWriter, data interface{}, code int) error {
	jsonHeader(w)
	w.WriteHeader(code)
	return json.NewEncoder(w).Encode(data)
}

func headers(w http.ResponseWriter, r *http.Request) {
	writeJSON(w, r.Header, http.StatusOK)
}

func status(w http.ResponseWriter, r *http.Request) {
	code, err := strconv.Atoi(path.Base(r.URL.Path))
	if err != nil {
		http.Error(w, "status code must be an integer", http.StatusBadRequest)
		return
	}
	w.WriteHeader(code)
}

func getOrigin(r *http.Request) string {
	host, _, _ := net.SplitHostPort(r.RemoteAddr)
	if forwarded := r.Header.Get("X-Forwarded-For"); forwarded != "" && forwarded != host {
		if host == loopback {
			return forwarded
		}
		host = fmt.Sprintf("%s, %s", forwarded, host)
	}
	return host
}

func ip(w http.ResponseWriter, r *http.Request) {
	var o struct {
		Origin string `json:"origin"`
	}
	o.Origin = getOrigin(r)
	writeJSON(w, o, http.StatusOK)
}

type request struct {
	Args    url.Values  `json:"args"`
	Gzipped bool        `json:"gzipped,omitempty"`
	Headers http.Header `json:"headers"`
	Origin  string      `json:"origin"`
	URL     string      `json:"url"`
}

func rawURL(r *http.Request) string {
	var scheme string
	if r.TLS == nil {
		scheme = "http"
	} else {
		scheme = "https"
	}
	return scheme + "://" + r.Host + r.URL.String()
}

func getReq(r *http.Request) request {
	return request{
		Args:    r.URL.Query(),
		Headers: r.Header,
		Origin:  getOrigin(r),
		URL:     rawURL(r),
	}
}

func get(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
		return
	}
	writeJSON(w, getReq(r), http.StatusOK)
}

func gzip(w http.ResponseWriter, r *http.Request) {
	req := getReq(r)
	if _, ok := w.(gzipped.GzipResponseWriter); ok {
		req.Gzipped = true
	}
	writeJSON(w, req, http.StatusOK)
}

func userAgent(w http.ResponseWriter, r *http.Request) {
	var resp struct {
		UserAgent string `json:"user-agent"`
	}
	resp.UserAgent = r.Header.Get("User-Agent")
	writeJSON(w, resp, http.StatusOK)
}

func writeBytes(w http.ResponseWriter, r *http.Request) {
	n, err := strconv.Atoi(path.Base(r.URL.Path))
	if err != nil || n < 0 || n > maxBytes {
		http.Error(w, fmt.Sprintf("number of bytes must be in range: 0 - %d", maxBytes), http.StatusBadRequest)
		return
	}
	b := make([]byte, n)
	if _, err := rand.Read(b); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.Write(b)
}

func min(a, b int) int {
	if a <= b {
		return a
	}
	return b
}

func stream(w http.ResponseWriter, r *http.Request) {
	n, err := strconv.Atoi(path.Base(r.URL.Path))
	if err != nil || n < 0 {
		http.Error(w, errWantInteger, http.StatusBadRequest)
		return
	}
	n = min(n, maxLines)
	f, ok := w.(http.Flusher)
	if !ok {
		http.Error(w, errStreamingNotSupported, http.StatusBadRequest)
		return
	}
	req := getReq(r)
	jsonHeader(w)
	for i := 0; i < n; i++ {
		if err := json.NewEncoder(w).Encode(req); err != nil {
			return
		}
		f.Flush()
	}
}

func redirectTo(w http.ResponseWriter, r *http.Request) {
	dst := r.URL.Query().Get("url")
	if _, err := url.Parse(dst); dst == "" || err != nil {
		http.Error(w, "bad URL", http.StatusBadRequest)
		return
	}
	http.Redirect(w, r, dst, http.StatusFound)
}

type authedResponse struct {
	Authenticated bool   `json:"authenticated"`
	User          string `json:"user"`
}

func basicAuth(hidden bool) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		params := strings.Split(r.URL.Path, "/")
		if len(params) < 4 {
			http.NotFound(w, r)
			return
		}
		u, p, ok := r.BasicAuth()
		if !ok || u != params[2] || p != params[3] {
			if !hidden {
				w.Header().Set("WWW-Authenticate", "Basic realm=\"httpdump\"")
				w.WriteHeader(http.StatusUnauthorized)
			} else {
				http.NotFound(w, r)
			}
			return
		}
		writeJSON(w, authedResponse{true, u}, http.StatusOK)
	})
}

func delay(w http.ResponseWriter, r *http.Request) {
	n, err := strconv.Atoi(path.Base(r.URL.Path))
	if err != nil {
		http.Error(w, "you must specify a delay", http.StatusBadRequest)
		return
	}
	n = min(n, 10)
	if n > 0 {
		<-time.After(time.Second * time.Duration(n))
	}
	writeJSON(w, getReq(r), http.StatusOK)
}
<|endoftext|>


<commit_before>package sshchat

import (
	"fmt"
	"net"
	"strings"
	"time"

	"github.com/shazow/ssh-chat/chat"
	"github.com/shazow/ssh-chat/chat/message"
	"github.com/shazow/ssh-chat/internal/humantime"
	"github.com/shazow/ssh-chat/internal/sanitize"
	"github.com/shazow/ssh-chat/sshd"
)

// Identity is a container for everything that identifies a client.
type Identity struct {
	sshd.Connection
	id      string
	symbol  string // symbol is displayed as a prefix to the name
	created time.Time
}

// NewIdentity returns a new identity object from an sshd.Connection.
func NewIdentity(conn sshd.Connection) *Identity {
	return &Identity{
		Connection: conn,
		id:         sanitize.Name(conn.Name()),
		created:    time.Now(),
	}
}

// ID returns the name for the Identity
func (i Identity) ID() string {
	return i.id
}

// SetID Changes the Identity's name
func (i *Identity) SetID(id string) {
	i.id = id
}

// SetName Changes the Identity's name
func (i *Identity) SetName(name string) {
	i.SetID(name)
}

func (i *Identity) SetSymbol(symbol string) {
	i.symbol = symbol
}

// Name returns the name for the Identity
func (i Identity) Name() string {
	if i.symbol != "" {
		return i.symbol + " " + i.id
	}
	return i.id
}

// Whois returns a whois description for non-admin users.
func (i Identity) Whois(room *chat.Room) string {
	fingerprint := "(no public key)"
	if i.PublicKey() != nil {
		fingerprint = sshd.Fingerprint(i.PublicKey())
	}
	// TODO: Rewrite this using strings.Builder like WhoisAdmin

	awayMsg := ""
	if m, ok := room.MemberByID(i.ID()); ok {
		isAway, awaySince, awayMessage := m.GetAway()
		if isAway {
			awayMsg = fmt.Sprintf("%s > away: (%s ago) %s", message.Newline, humantime.Since(awaySince), awayMessage)
		}
	}
	return "name: " + i.Name() + message.Newline +
		" > fingerprint: " + fingerprint + message.Newline +
		" > client: " + sanitize.Data(string(i.ClientVersion()), 64) + message.Newline +
		" > joined: " + humantime.Since(i.created) + " ago" +
		awayMsg
}

// WhoisAdmin returns a whois description for admin users.
func (i Identity) WhoisAdmin(room *chat.Room) string {
	ip, _, _ := net.SplitHostPort(i.RemoteAddr().String())
	fingerprint := "(no public key)"
	if i.PublicKey() != nil {
		fingerprint = sshd.Fingerprint(i.PublicKey())
	}

	out := strings.Builder{}
	out.WriteString("name: " + i.Name() + message.Newline +
		" > ip: " + ip + message.Newline +
		" > fingerprint: " + fingerprint + message.Newline +
		" > client: " + sanitize.Data(string(i.ClientVersion()), 64) + message.Newline +
		" > joined: " + humantime.Since(i.created) + " ago")

	if member, ok := room.MemberByID(i.ID()); ok {
		// Add room-specific whois
		// FIXME: Should these always be present, even if they're false? Maybe
		// change that once we add room context to Whois() above.
		if !member.LastMsg().IsZero() {
			out.WriteString(message.Newline + " > room/messaged: " + humantime.Since(member.LastMsg()) + " ago")
		}
		if room.IsOp(member.User) {
			out.WriteString(message.Newline + " > room/op: true")
		}
	}

	return out.String()
}
<commit_msg>/away: Fix output for admin whois<commit_after>package sshchat

import (
	"fmt"
	"net"
	"strings"
	"time"

	"github.com/shazow/ssh-chat/chat"
	"github.com/shazow/ssh-chat/chat/message"
	"github.com/shazow/ssh-chat/internal/humantime"
	"github.com/shazow/ssh-chat/internal/sanitize"
	"github.com/shazow/ssh-chat/sshd"
)

// Identity is a container for everything that identifies a client.
type Identity struct {
	sshd.Connection
	id      string
	symbol  string // symbol is displayed as a prefix to the name
	created time.Time
}

// NewIdentity returns a new identity object from an sshd.Connection.
func NewIdentity(conn sshd.Connection) *Identity {
	return &Identity{
		Connection: conn,
		id:         sanitize.Name(conn.Name()),
		created:    time.Now(),
	}
}

// ID returns the name for the Identity
func (i Identity) ID() string {
	return i.id
}

// SetID Changes the Identity's name
func (i *Identity) SetID(id string) {
	i.id = id
}

// SetName Changes the Identity's name
func (i *Identity) SetName(name string) {
	i.SetID(name)
}

func (i *Identity) SetSymbol(symbol string) {
	i.symbol = symbol
}

// Name returns the name for the Identity
func (i Identity) Name() string {
	if i.symbol != "" {
		return i.symbol + " " + i.id
	}
	return i.id
}

// Whois returns a whois description for non-admin users.
func (i Identity) Whois(room *chat.Room) string {
	fingerprint := "(no public key)"
	if i.PublicKey() != nil {
		fingerprint = sshd.Fingerprint(i.PublicKey())
	}
	// TODO: Rewrite this using strings.Builder like WhoisAdmin

	awayMsg := ""
	if m, ok := room.MemberByID(i.ID()); ok {
		isAway, awaySince, awayMessage := m.GetAway()
		if isAway {
			awayMsg = fmt.Sprintf("%s > away: (%s ago) %s", message.Newline, humantime.Since(awaySince), awayMessage)
		}
	}
	return "name: " + i.Name() + message.Newline +
		" > fingerprint: " + fingerprint + message.Newline +
		" > client: " + sanitize.Data(string(i.ClientVersion()), 64) + message.Newline +
		" > joined: " + humantime.Since(i.created) + " ago" +
		awayMsg
}

// WhoisAdmin returns a whois description for admin users.
func (i Identity) WhoisAdmin(room *chat.Room) string {
	ip, _, _ := net.SplitHostPort(i.RemoteAddr().String())
	fingerprint := "(no public key)"
	if i.PublicKey() != nil {
		fingerprint = sshd.Fingerprint(i.PublicKey())
	}

	out := strings.Builder{}
	out.WriteString("name: " + i.Name() + message.Newline +
		" > ip: " + ip + message.Newline +
		" > fingerprint: " + fingerprint + message.Newline +
		" > client: " + sanitize.Data(string(i.ClientVersion()), 64) + message.Newline +
		" > joined: " + humantime.Since(i.created) + " ago")

	if member, ok := room.MemberByID(i.ID()); ok {
		// Add room-specific whois
		if isAway, awaySince, awayMessage := member.GetAway(); isAway {
			fmt.Fprintf(&out, message.Newline+" > away: (%s ago) %s", humantime.Since(awaySince), awayMessage)
		}
		// FIXME: Should these always be present, even if they're false? Maybe
		// change that once we add room context to Whois() above.
		if !member.LastMsg().IsZero() {
			out.WriteString(message.Newline + " > room/messaged: " + humantime.Since(member.LastMsg()) + " ago")
		}
		if room.IsOp(member.User) {
			out.WriteString(message.Newline + " > room/op: true")
		}
	}

	return out.String()
}
<|endoftext|>


<commit_before>package igb

import (
	"fmt"
	"io"
	"log"
	"os"
	"strings"

	"github.com/chzyer/readline"
	"github.com/goby-lang/goby/compiler/bytecode"
	"github.com/goby-lang/goby/compiler/lexer"
	"github.com/goby-lang/goby/compiler/parser"
	"github.com/goby-lang/goby/vm"
	"github.com/looplab/fsm"
)

const (
	prompt    = "\033[32m»\033[0m "
	prompt2   = "\033[31m*\033[0m "
	echo      = "#=>"
	interrupt = "^C"
	exit      = "exit"
	help      = "help"

	readyToExec = "readyToExec"
	Waiting     = "waiting"
	waitEnded   = "waitEnded"
)

var sm = fsm.NewFSM(
	readyToExec,
	fsm.Events{
		{Name: Waiting, Src: []string{waitEnded, readyToExec}, Dst: Waiting},
		{Name: waitEnded, Src: []string{Waiting}, Dst: waitEnded},
		{Name: readyToExec, Src: []string{waitEnded, readyToExec}, Dst: readyToExec},
	},
	fsm.Callbacks{},
)

var cmds []string

var completer = readline.NewPrefixCompleter(
	readline.PcItem(help),
	readline.PcItem(exit),
)

// Start starts goby's REPL.
func StartIgb(version string) {
	var err error
	rl, err := readline.NewEx(&readline.Config{
		Prompt:              prompt,
		HistoryFile:         "/tmp/readline.tmp",
		AutoComplete:        completer,
		InterruptPrompt:     interrupt,
		EOFPrompt:           exit,
		HistorySearchFold:   true,
		FuncFilterInputRune: filterInput,
	})
	if err != nil {
		panic(err)
	}
	defer rl.Close()

	log.SetOutput(rl.Stderr())

	println("Goby", version)

	// Initialize VM
	v := vm.New(os.Getenv("GOBY_ROOT"), []string{})
	v.SetClassISIndexTable("")
	v.SetMethodISIndexTable("")
	v.InitForREPL()

	// Initialize parser, lexer is not important here
	l := lexer.New("")
	p := parser.New(l)

	program, _ := p.ParseProgram()

	// Initialize code generator, and it will behavior a little different in REPL mode.
	g := bytecode.NewGenerator()
	g.REPL = true
	g.InitTopLevelScope(program)

	for {
		line, err := rl.Readline()

		if err == io.EOF {
			break
		}

		if err == readline.ErrInterrupt {
			if len(line) == 0 {
				break
			} else {
				rl.SetPrompt(prompt2)
				continue
			}
		}

		line = strings.TrimSpace(line)

		switch {
		case line == help:
			usage(rl.Stderr())
			continue
		case line == exit:
			println("Bye!")
			return
		case line == "":
			continue
		}

		l := lexer.New(line)
		p.Lexer = l
		program, perr := p.ParseProgram()

		if perr != nil {
			if perr.IsEOF() {
				if !sm.Is(Waiting) {
					rl.SetPrompt(prompt2)
					sm.Event(Waiting)
				}

				rl.SetPrompt(prompt2)
				cmds = append(cmds, line)
				continue
			}

			if perr.IsUnexpectedEnd() {
				rl.SetPrompt(prompt2)
				sm.Event(waitEnded)
				cmds = append(cmds, line)
			} else {
				rl.SetPrompt(prompt)
				fmt.Println(perr.Message)
				continue
			}

		}

		if sm.Is(Waiting) {
			rl.SetPrompt(prompt2)
			cmds = append(cmds, line)
			continue
		}

		if sm.Is(waitEnded) {
			l := lexer.New(string(strings.Join(cmds, "\n")))
			p.Lexer = l

			// Test if current input can be properly parsed.
			program, perr = p.ParseProgram()

			/*
				   This could mean there still are statements not ended, for example:

				   ```ruby
				   class Foo
					 def bar
					 end # This make state changes to WaitEnded
				   # But here still needs an "end"
				   ```
			*/

			if perr != nil {
				if !perr.IsEOF() {
					fmt.Println(perr.Message)
				}
				continue
			}

			// If everything goes well, reset state and statements buffer
			rl.SetPrompt(prompt)
			sm.Event(readyToExec)
			cmds = []string{}
		}
		if sm.Is(readyToExec) {
			instructions := g.GenerateInstructions(program.Statements)
			g.ResetInstructionSets()
			v.REPLExec(instructions)

			r := v.GetREPLResult()
			println(echo, r)
		}
	}
}

// Polymorphic helper functions --------------------------------------------

func filterInput(r rune) (rune, bool) {
	switch r {
	// block CtrlZ feature
	case readline.CharCtrlZ:
		return r, false
	}
	return r, true
}

// Other helper functions --------------------------------------------------

func usage(w io.Writer) {
	io.WriteString(w, "commands:\n")
	io.WriteString(w, completer.Tree("   "))
}
<commit_msg>Fix the case of unexpected end line<commit_after>package igb

import (
	"fmt"
	"io"
	"log"
	"os"
	"strings"

	"github.com/chzyer/readline"
	"github.com/goby-lang/goby/compiler/bytecode"
	"github.com/goby-lang/goby/compiler/lexer"
	"github.com/goby-lang/goby/compiler/parser"
	"github.com/goby-lang/goby/vm"
	"github.com/looplab/fsm"
)

const (
	prompt    = "\033[32m»\033[0m "
	prompt2   = "\033[31m*\033[0m "
	echo      = "#=>"
	interrupt = "^C"
	exit      = "exit"
	help      = "help"

	readyToExec = "readyToExec"
	Waiting     = "waiting"
	waitEnded   = "waitEnded"
)

var sm = fsm.NewFSM(
	readyToExec,
	fsm.Events{
		{Name: Waiting, Src: []string{waitEnded, readyToExec}, Dst: Waiting},
		{Name: waitEnded, Src: []string{Waiting}, Dst: waitEnded},
		{Name: readyToExec, Src: []string{waitEnded, readyToExec}, Dst: readyToExec},
	},
	fsm.Callbacks{},
)

var cmds []string

var completer = readline.NewPrefixCompleter(
	readline.PcItem(help),
	readline.PcItem(exit),
)

// Start starts goby's REPL.
func StartIgb(version string) {
	var err error
	rl, err := readline.NewEx(&readline.Config{
		Prompt:              prompt,
		HistoryFile:         "/tmp/readline.tmp",
		AutoComplete:        completer,
		InterruptPrompt:     interrupt,
		EOFPrompt:           exit,
		HistorySearchFold:   true,
		FuncFilterInputRune: filterInput,
	})
	if err != nil {
		panic(err)
	}
	defer rl.Close()

	log.SetOutput(rl.Stderr())

	println("Goby", version)

	// Initialize VM
	v := vm.New(os.Getenv("GOBY_ROOT"), []string{})
	v.SetClassISIndexTable("")
	v.SetMethodISIndexTable("")
	v.InitForREPL()

	// Initialize parser, lexer is not important here
	l := lexer.New("")
	p := parser.New(l)

	program, _ := p.ParseProgram()

	// Initialize code generator, and it will behavior a little different in REPL mode.
	g := bytecode.NewGenerator()
	g.REPL = true
	g.InitTopLevelScope(program)

	for {
		line, err := rl.Readline()

		if err == io.EOF {
			break
		}

		// Pressing ctrl-C
		if err == readline.ErrInterrupt {
			if len(line) == 0 {
				break
			} else {
				rl.SetPrompt(prompt2)
				continue
			}
		}

		line = strings.TrimSpace(line)

		switch {
		case line == help:
			usage(rl.Stderr())
			continue
		case line == exit:
			println("Bye!")
			return
		case line == "":
			continue
		}

		l := lexer.New(line)
		p.Lexer = l
		program, perr := p.ParseProgram()

		if perr != nil {
			if perr.IsEOF() {
				if !sm.Is(Waiting) {
					rl.SetPrompt(prompt2)
					sm.Event(Waiting)
				}

				rl.SetPrompt(prompt2)
				cmds = append(cmds, line)
				continue
			}

			if perr.IsUnexpectedEnd() {
				rl.SetPrompt(prompt)
				fmt.Println(perr.Message)
				sm.Event(waitEnded)
				cmds = []string{}
				continue
			} else {
				rl.SetPrompt(prompt)
				fmt.Println(perr.Message)
				continue
			}

		}

		if sm.Is(Waiting) {
			rl.SetPrompt(prompt2)
			cmds = append(cmds, line)
			continue
		}

		if sm.Is(waitEnded) {
			l := lexer.New(string(strings.Join(cmds, "\n")))
			p.Lexer = l

			// Test if current input can be properly parsed.
			program, perr = p.ParseProgram()

			/*
				   This could mean there still are statements not ended, for example:

				   ```ruby
				   class Foo
					 def bar
					 end # This make state changes to WaitEnded
				   # But here still needs an "end"
				   ```
			*/

			if perr != nil {
				if !perr.IsEOF() {
					fmt.Println(perr.Message)
				}
				continue
			}

			// If everything goes well, reset state and statements buffer
			rl.SetPrompt(prompt)
			sm.Event(readyToExec)
			cmds = []string{}
		}
		if sm.Is(readyToExec) {
			instructions := g.GenerateInstructions(program.Statements)
			g.ResetInstructionSets()
			v.REPLExec(instructions)

			r := v.GetREPLResult()
			println(echo, r)
		}
	}
}

// Polymorphic helper functions --------------------------------------------

func filterInput(r rune) (rune, bool) {
	switch r {
	// block CtrlZ feature
	case readline.CharCtrlZ:
		return r, false
	}
	return r, true
}

// Other helper functions --------------------------------------------------

func usage(w io.Writer) {
	io.WriteString(w, "commands:\n")
	io.WriteString(w, completer.Tree("   "))
}
<|endoftext|>


<commit_before>// errorcheck -G=0

// Copyright 2021 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.

// Don't crash while reporting the error.

package p

func _() {
	type T = T // ERROR "T uses T"
}
<commit_msg>cmd/compile: fix test case for unified IR (fix build)<commit_after>// errorcheck -G=0

// Copyright 2021 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.

// Don't crash while reporting the error.

package p

func _() {
	type T = T // ERROR "T uses T|invalid recursive type T"
}
<|endoftext|>


<commit_before>// +build integration,!no-etcd

package integration

import (
	"testing"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"

	"github.com/miekg/dns"
	testutil "github.com/openshift/origin/test/util"
)

func TestDNS(t *testing.T) {
	masterConfig, _, err := testutil.StartTestAllInOne()
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	// verify service DNS entry is visible
	stop := make(chan struct{})
	util.Until(func() {
		m1 := &dns.Msg{
			MsgHdr:   dns.MsgHdr{Id: dns.Id(), RecursionDesired: true},
			Question: []dns.Question{{"kubernetes.default.local.", dns.TypeA, dns.ClassINET}},
		}
		in, err := dns.Exchange(m1, masterConfig.DNSConfig.BindAddress)
		if err != nil {
			t.Logf("unexpected error: %v", err)
			return
		}
		if len(in.Answer) != 1 {
			t.Logf("unexpected answer: %#v", in)
			return
		}
		if a, ok := in.Answer[0].(*dns.A); ok {
			if a.A == nil {
				t.Errorf("expected an A record with an IP: %#v", a)
			}
		} else {
			t.Errorf("expected an A record: %#v", in)
		}
		t.Log(in)
		close(stop)
	}, 50*time.Millisecond, stop)

	// verify recursive DNS lookup is visible when expected
	tests := []struct {
		dnsQuestionName   string
		recursionExpected bool
	}{
		{
			dnsQuestionName:   "foo.kubernetes.default.local.",
			recursionExpected: false,
		},
		{
			dnsQuestionName:   "www.google.com.",
			recursionExpected: true,
		},
	}
	for _, tc := range tests {
		m1 := &dns.Msg{
			MsgHdr:   dns.MsgHdr{Id: dns.Id(), RecursionDesired: true},
			Question: []dns.Question{{tc.dnsQuestionName, dns.TypeA, dns.ClassINET}},
		}
		in, err := dns.Exchange(m1, masterConfig.DNSConfig.BindAddress)
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if !tc.recursionExpected && len(in.Answer) != 1 {
			t.Fatalf("did not resolve or unexpected forward resolution: %#v", in)
		} else if tc.recursionExpected && len(in.Answer) == 0 {
			t.Fatalf("expected forward resolution: %#v", in)
		}
		if a, ok := in.Answer[0].(*dns.A); ok {
			if a.A == nil {
				t.Errorf("expected an A record with an IP: %#v", a)
			}
		} else {
			t.Errorf("expected an A record: %#v", in)
		}
		t.Log(in)
	}
}
<commit_msg>Add extra tests for DNS and prepare for Headless services<commit_after>// +build integration,!no-etcd

package integration

import (
	"net"
	"testing"
	"time"

	//kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"

	"github.com/miekg/dns"
	testutil "github.com/openshift/origin/test/util"
)

func TestDNS(t *testing.T) {
	masterConfig, _, err := testutil.StartTestAllInOne()
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	var masterIP net.IP

	// verify service DNS entry is visible
	stop := make(chan struct{})
	util.Until(func() {
		m1 := &dns.Msg{
			MsgHdr:   dns.MsgHdr{Id: dns.Id(), RecursionDesired: true},
			Question: []dns.Question{{"kubernetes.default.local.", dns.TypeA, dns.ClassINET}},
		}
		in, err := dns.Exchange(m1, masterConfig.DNSConfig.BindAddress)
		if err != nil {
			t.Logf("unexpected error: %v", err)
			return
		}
		if len(in.Answer) != 1 {
			t.Logf("unexpected answer: %#v", in)
			return
		}
		if a, ok := in.Answer[0].(*dns.A); ok {
			if a.A == nil {
				t.Fatalf("expected an A record with an IP: %#v", a)
			}
			masterIP = a.A
		} else {
			t.Fatalf("expected an A record: %#v", in)
		}
		t.Log(in)
		close(stop)
	}, 50*time.Millisecond, stop)

	// TODO: uncomment when headless services are supported
	/*client, err := testutil.GetClusterAdminKubeClient(clientFile)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if _, err := client.Services(kapi.NamespaceDefault).Create(&kapi.Service{
		ObjectMeta: kapi.ObjectMeta{
			Name: "headless",
		},
		Spec: kapi.ServiceSpec{
			PortalIP: "None",
			Port:     443,
		},
	}); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if _, err := client.Endpoints(kapi.NamespaceDefault).Create(&kapi.Endpoints{
		ObjectMeta: kapi.ObjectMeta{
			Name: "headless",
		},
		Endpoints: []kapi.Endpoint{
			{
				IP:   "172.0.0.1",
				Port: 2345,
			},
		},
	}); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	headlessIP := net.ParseIP("172.0.0.1")*/

	// verify recursive DNS lookup is visible when expected
	tests := []struct {
		dnsQuestionName   string
		recursionExpected bool
		expect            *net.IP
	}{
		{
			dnsQuestionName:   "foo.kubernetes.default.local.",
			recursionExpected: false,
			expect:            &masterIP,
		},
		{
			dnsQuestionName:   "openshift.default.local.",
			recursionExpected: false,
			expect:            &masterIP,
		},
		/*{
			dnsQuestionName:   "headless.default.local.",
			recursionExpected: false,
			expect:            &headlessIP,
		},*/
		{
			dnsQuestionName:   "www.google.com.",
			recursionExpected: true,
		},
	}
	for _, tc := range tests {
		m1 := &dns.Msg{
			MsgHdr:   dns.MsgHdr{Id: dns.Id(), RecursionDesired: true},
			Question: []dns.Question{{tc.dnsQuestionName, dns.TypeA, dns.ClassINET}},
		}
		in, err := dns.Exchange(m1, masterConfig.DNSConfig.BindAddress)
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if !tc.recursionExpected && len(in.Answer) != 1 {
			t.Fatalf("did not resolve or unexpected forward resolution: %#v", in)
		} else if tc.recursionExpected && len(in.Answer) == 0 {
			t.Fatalf("expected forward resolution: %#v", in)
		}
		if a, ok := in.Answer[0].(*dns.A); ok {
			if a.A == nil {
				t.Errorf("expected an A record with an IP: %#v", a)
			} else {
				if tc.expect != nil && tc.expect.String() != a.A.String() {
					t.Errorf("A record has a different IP than the test case: %v / %v", a.A, *tc.expect)
				}
			}
		} else {
			t.Errorf("expected an A record: %#v", in)
		}
		t.Log(in)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"crypto/md5"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/cheggaaa/pb"
	"image"
	"image/gif"
	"image/jpeg"
	"image/png"
	"io"
	"log"
	"math"
	"os"
	"os/user"
	"path"
	"path/filepath"
	"strings"
)

var subdivisions *int
var tolerance *int

var scratchDir string

type pictable [][][]uint64

func MkPictable(dx int, dy int) pictable {
	pic := make([][][]uint64, dx) /* type declaration */
	for i := range pic {
		pic[i] = make([][]uint64, dy) /* again the type? */
		for j := range pic[i] {
			pic[i][j] = []uint64{0, 0, 0}
		}
	}
	return pic
}

func absdiff(a uint64, b uint64) uint64 {
	return uint64(math.Abs(float64(a) - float64(b)))
}

func init() {
	subdivisions = flag.Int("subdivisions", 10, "Slices per axis")
	tolerance = flag.Int("tolerance", 100, "Color delta tolerance, higher = more tolerant")
	flag.Parse()

	if flag.NArg() < 1 {
		fmt.Println("usage: imgdedup [options] [<directories>/files]")
		flag.PrintDefaults()
		os.Exit(1)
	}

	image.RegisterFormat("png", "png", png.Decode, png.DecodeConfig)
	image.RegisterFormat("jpeg", "jpeg", jpeg.Decode, jpeg.DecodeConfig)
	image.RegisterFormat("gif", "gif", gif.Decode, gif.DecodeConfig)
}

func init() {
	usr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}
	scratchDir = path.Join(usr.HomeDir, ".imgdedup")

	if _, err := os.Stat(scratchDir); err != nil {
		if os.IsNotExist(err) {
			os.Mkdir(scratchDir, 0700)
		} else {
			log.Fatal(err)
		}
	}
}

func scanImg(file *os.File) (pictable, error) {
	m, _, err := image.Decode(file)
	if err != nil {
		return nil, err
	}
	bounds := m.Bounds()

	avgdata := MkPictable(*subdivisions, *subdivisions)

	for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
		for x := bounds.Min.X; x < bounds.Max.X; x++ {
			rX := int64(math.Floor((float64(x) / float64(bounds.Max.X)) * float64(*subdivisions)))
			rY := int64(math.Floor((float64(y) / float64(bounds.Max.Y)) * float64(*subdivisions)))

			r, g, b, _ := m.At(x, y).RGBA()
			avgdata[rX][rY][0] += uint64((float32(r) / 65535) * 255)
			avgdata[rX][rY][1] += uint64((float32(g) / 65535) * 255)
			avgdata[rX][rY][2] += uint64((float32(b) / 65535) * 255)
		}
	}

	divisor := uint64((bounds.Max.X / *subdivisions) * (bounds.Max.Y / *subdivisions))

	for rX := 0; rX < *subdivisions; rX++ {
		for rY := 0; rY < *subdivisions; rY++ {
			avgdata[rX][rY][0] = avgdata[rX][rY][0] / divisor
			avgdata[rX][rY][1] = avgdata[rX][rY][1] / divisor
			avgdata[rX][rY][2] = avgdata[rX][rY][2] / divisor
		}
	}

	return avgdata, nil
}

func loadCache(cachename string) (pictable, error) {

	file, err := os.Open(cachename)
	if err != nil {
		return nil, err
	}
	r := bufio.NewReader(file)

	var avgdata pictable

	dec := json.NewDecoder(r)

	err = dec.Decode(&avgdata)
	if err != nil {
		return nil, err
	}

	return avgdata, nil
}

func storeCache(cachename string, avgdata *pictable) {
	fo, err := os.Create(cachename)
	if err != nil {
		panic(err)
	}

	enc := json.NewEncoder(fo)
	enc.Encode(avgdata)
	fo.Close()
}

func main() {

	imgdata := make(map[string]pictable)

	fileList := getFiles(flag.Args())

	bar := pb.StartNew(len(fileList))

	for _, imgpath := range fileList {

		bar.Increment()

		file, err := os.Open(imgpath)
		if err != nil {
			log.Fatal(err)
		}

		fExt := strings.ToLower(filepath.Ext(imgpath))
		if fExt == ".png" || fExt == ".jpg" || fExt == ".jpeg" || fExt == ".gif" {

			fi, err := file.Stat()
			if err != nil {
				log.Fatal(err)
			}

			h := md5.New()
			io.WriteString(h, imgpath+"|"+string(*subdivisions)+"|"+string(fi.Size())+string(fi.ModTime().UnixNano()))
			cachename := path.Join(scratchDir, fmt.Sprintf("%x", h.Sum(nil))+".tmp")

			var avgdata pictable

			avgdata, err = loadCache(cachename)
			if err != nil {

				avgdata, err = scanImg(file)
				if err != nil {
					log.Print(imgpath, err)
					continue
				}

				storeCache(cachename, &avgdata)
			}

			imgdata[imgpath] = avgdata

			file.Close()

		} else {
			file.Close()
		}
	}

	fileLength := len(fileList)

	for i := 0; i < fileLength-1; i++ {
		for j := i + 1; j < fileLength-1; j++ {

			filename1 := fileList[i]
			filename2 := fileList[j]

			avgdata1, ok1 := imgdata[filename1]
			avgdata2, ok2 := imgdata[filename2]

			if ok1 && ok2 {

				if filename1 == filename2 {
					continue
				}

				var xdiff uint64 = 0

				for rX := 0; rX < *subdivisions; rX++ {
					for rY := 0; rY < *subdivisions; rY++ {
						aa := avgdata1[rX][rY]
						bb := avgdata2[rX][rY]

						xdiff += absdiff(absdiff(absdiff(aa[0], bb[0]), absdiff(aa[1], bb[1])), absdiff(aa[2], bb[2]))
					}
				}

				if xdiff < uint64(*tolerance) {
					fmt.Println(filename1, filename2)
					fmt.Println(xdiff)
				}

			}

		}
	}

}

func getFiles(paths []string) []string {
	var fileList []string

	for _, imgpath := range paths {

		file, err := os.Open(imgpath)
		if err != nil {
			log.Fatal(err)
		}

		fi, err := file.Stat()
		if err != nil {
			log.Fatal(err)
		}

		switch mode := fi.Mode(); {
		case mode.IsDir():
			// fmt.Println("directory")
			filepath.Walk(imgpath, func(path string, f os.FileInfo, err error) error {

				submode := f.Mode()
				if submode.IsRegular() {
					fpath, _ := filepath.Abs(path)
					fileList = append(fileList, fpath)
				}

				return nil
			})
		case mode.IsRegular():
			// fmt.Println("file")
			fpath, _ := filepath.Abs(imgpath)
			fileList = append(fileList, fpath)
		}

		file.Close()

	}

	return fileList
}
<commit_msg>Remember to close cache files<commit_after>package main

import (
	"bufio"
	"crypto/md5"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/cheggaaa/pb"
	"image"
	"image/gif"
	"image/jpeg"
	"image/png"
	"io"
	"log"
	"math"
	"os"
	"os/user"
	"path"
	"path/filepath"
	"strings"
)

var subdivisions *int
var tolerance *int

var scratchDir string

type pictable [][][]uint64

func MkPictable(dx int, dy int) pictable {
	pic := make([][][]uint64, dx) /* type declaration */
	for i := range pic {
		pic[i] = make([][]uint64, dy) /* again the type? */
		for j := range pic[i] {
			pic[i][j] = []uint64{0, 0, 0}
		}
	}
	return pic
}

func absdiff(a uint64, b uint64) uint64 {
	return uint64(math.Abs(float64(a) - float64(b)))
}

func init() {
	subdivisions = flag.Int("subdivisions", 10, "Slices per axis")
	tolerance = flag.Int("tolerance", 100, "Color delta tolerance, higher = more tolerant")
	flag.Parse()

	if flag.NArg() < 1 {
		fmt.Println("usage: imgdedup [options] [<directories>/files]")
		flag.PrintDefaults()
		os.Exit(1)
	}

	image.RegisterFormat("png", "png", png.Decode, png.DecodeConfig)
	image.RegisterFormat("jpeg", "jpeg", jpeg.Decode, jpeg.DecodeConfig)
	image.RegisterFormat("gif", "gif", gif.Decode, gif.DecodeConfig)
}

func init() {
	usr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}
	scratchDir = path.Join(usr.HomeDir, ".imgdedup")

	if _, err := os.Stat(scratchDir); err != nil {
		if os.IsNotExist(err) {
			os.Mkdir(scratchDir, 0700)
		} else {
			log.Fatal(err)
		}
	}
}

func scanImg(file *os.File) (pictable, error) {
	m, _, err := image.Decode(file)
	if err != nil {
		return nil, err
	}
	bounds := m.Bounds()

	avgdata := MkPictable(*subdivisions, *subdivisions)

	for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
		for x := bounds.Min.X; x < bounds.Max.X; x++ {
			rX := int64(math.Floor((float64(x) / float64(bounds.Max.X)) * float64(*subdivisions)))
			rY := int64(math.Floor((float64(y) / float64(bounds.Max.Y)) * float64(*subdivisions)))

			r, g, b, _ := m.At(x, y).RGBA()
			avgdata[rX][rY][0] += uint64((float32(r) / 65535) * 255)
			avgdata[rX][rY][1] += uint64((float32(g) / 65535) * 255)
			avgdata[rX][rY][2] += uint64((float32(b) / 65535) * 255)
		}
	}

	divisor := uint64((bounds.Max.X / *subdivisions) * (bounds.Max.Y / *subdivisions))

	for rX := 0; rX < *subdivisions; rX++ {
		for rY := 0; rY < *subdivisions; rY++ {
			avgdata[rX][rY][0] = avgdata[rX][rY][0] / divisor
			avgdata[rX][rY][1] = avgdata[rX][rY][1] / divisor
			avgdata[rX][rY][2] = avgdata[rX][rY][2] / divisor
		}
	}

	return avgdata, nil
}

func loadCache(cachename string) (pictable, error) {

	file, err := os.Open(cachename)
	defer file.Close()
	if err != nil {
		return nil, err
	}
	r := bufio.NewReader(file)

	var avgdata pictable

	dec := json.NewDecoder(r)

	err = dec.Decode(&avgdata)
	if err != nil {
		return nil, err
	}

	return avgdata, nil
}

func storeCache(cachename string, avgdata *pictable) {
	fo, err := os.Create(cachename)
	defer fo.Close()
	if err != nil {
		panic(err)
	}

	enc := json.NewEncoder(fo)
	enc.Encode(avgdata)
}

func main() {

	imgdata := make(map[string]pictable)

	fileList := getFiles(flag.Args())

	bar := pb.StartNew(len(fileList))

	for _, imgpath := range fileList {

		bar.Increment()

		file, err := os.Open(imgpath)
		if err != nil {
			log.Fatal(err)
		}

		fExt := strings.ToLower(filepath.Ext(imgpath))
		if fExt == ".png" || fExt == ".jpg" || fExt == ".jpeg" || fExt == ".gif" {

			fi, err := file.Stat()
			if err != nil {
				log.Fatal(err)
			}

			h := md5.New()
			io.WriteString(h, imgpath+"|"+string(*subdivisions)+"|"+string(fi.Size())+string(fi.ModTime().UnixNano()))
			cachename := path.Join(scratchDir, fmt.Sprintf("%x", h.Sum(nil))+".tmp")

			var avgdata pictable

			avgdata, err = loadCache(cachename)
			if err != nil {

				avgdata, err = scanImg(file)
				if err != nil {
					log.Print(imgpath, err)
					continue
				}

				storeCache(cachename, &avgdata)
			}

			imgdata[imgpath] = avgdata

			file.Close()

		} else {
			file.Close()
		}
	}

	fileLength := len(fileList)

	for i := 0; i < fileLength-1; i++ {
		for j := i + 1; j < fileLength-1; j++ {

			filename1 := fileList[i]
			filename2 := fileList[j]

			avgdata1, ok1 := imgdata[filename1]
			avgdata2, ok2 := imgdata[filename2]

			if ok1 && ok2 {

				if filename1 == filename2 {
					continue
				}

				var xdiff uint64 = 0

				for rX := 0; rX < *subdivisions; rX++ {
					for rY := 0; rY < *subdivisions; rY++ {
						aa := avgdata1[rX][rY]
						bb := avgdata2[rX][rY]

						xdiff += absdiff(absdiff(absdiff(aa[0], bb[0]), absdiff(aa[1], bb[1])), absdiff(aa[2], bb[2]))
					}
				}

				if xdiff < uint64(*tolerance) {
					fmt.Println(filename1, filename2)
					fmt.Println(xdiff)
				}

			}

		}
	}

}

func getFiles(paths []string) []string {
	var fileList []string

	for _, imgpath := range paths {

		file, err := os.Open(imgpath)
		if err != nil {
			log.Fatal(err)
		}

		fi, err := file.Stat()
		if err != nil {
			log.Fatal(err)
		}

		switch mode := fi.Mode(); {
		case mode.IsDir():
			// fmt.Println("directory")
			filepath.Walk(imgpath, func(path string, f os.FileInfo, err error) error {

				submode := f.Mode()
				if submode.IsRegular() {
					fpath, _ := filepath.Abs(path)
					fileList = append(fileList, fpath)
				}

				return nil
			})
		case mode.IsRegular():
			// fmt.Println("file")
			fpath, _ := filepath.Abs(imgpath)
			fileList = append(fileList, fpath)
		}

		file.Close()

	}

	return fileList
}
<|endoftext|>


<commit_before>// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package ghttp

import (
    "github.com/gogf/gf/g/container/gvar"
    "github.com/gogf/gf/g/encoding/gjson"
    "github.com/gogf/gf/g/os/gtime"
    "github.com/gogf/gf/g/text/gregex"
    "github.com/gogf/gf/third/github.com/fatih/structs"
    "io/ioutil"
    "net/http"
    "strings"
)

// 请求对象
type Request struct {
    *http.Request
    parsedGet     bool                    // GET参数是否已经解析
    parsedPost    bool                    // POST参数是否已经解析
    queryVars     map[string][]string     // GET参数
    routerVars    map[string][]string     // 路由解析参数
    exit          bool                    // 是否退出当前请求流程执行
    Id            int                     // 请求id(唯一)
    Server        *Server                 // 请求关联的服务器对象
    Cookie        *Cookie                 // 与当前请求绑定的Cookie对象(并发安全)
    Session       *Session                // 与当前请求绑定的Session对象(并发安全)
    Response      *Response               // 对应请求的返回数据操作对象
    Router        *Router                 // 匹配到的路由对象
    EnterTime     int64                   // 请求进入时间(微秒)
    LeaveTime     int64                   // 请求完成时间(微秒)
    params        map[string]interface{}  // 开发者自定义参数(请求流程中有效)
    parsedHost    string                  // 解析过后不带端口号的服务器域名名称
    clientIp      string                  // 解析过后的客户端IP地址
    rawContent    []byte                  // 客户端提交的原始参数
    isFileRequest bool                    // 是否为静态文件请求(非服务请求，当静态文件存在时，优先级会被服务请求高，被识别为文件请求)
}

// 创建一个Request对象
func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request {
    request := &Request {
        routerVars : make(map[string][]string),
        Id         : s.servedCount.Add(1),
        Server     : s,
        Request    : r,
        Response   : newResponse(s, w),
        EnterTime  : gtime.Microsecond(),
    }
    // 会话处理
    request.Cookie           = GetCookie(request)
    request.Session          = GetSession(request)
    request.Response.request = request
    return request
}

// 获取Web Socket连接对象(如果是非WS请求会失败，注意检查返回的error结果)
func (r *Request) WebSocket() (*WebSocket, error) {
    if conn, err := wsUpgrader.Upgrade(r.Response.ResponseWriter.ResponseWriter, r.Request, nil); err == nil {
        return &WebSocket {
            conn,
        }, nil
    } else {
        return nil, err
    }
}

// 获得指定名称的参数字符串(Router/GET/POST)，同 GetRequestString
// 这是常用方法的简化别名
func (r *Request) Get(key string, def ... string) string {
    return r.GetRequestString(key, def...)
}

// 建议都用该参数替代参数获取
func (r *Request) GetVar(key string, def ... interface{}) gvar.VarRead {
    return r.GetRequestVar(key, def...)
}

// 获取原始请求输入二进制。
func (r *Request) GetRaw() []byte {
    err := error(nil)
    if r.rawContent == nil {
        r.rawContent, err = ioutil.ReadAll(r.Body)
        if err != nil {
            r.Error("error reading request body: ", err)
        }
    }
    return r.rawContent
}

// 获取原始请求输入字符串。
func (r *Request) GetRawString() string {
    return string(r.GetRaw())
}

// 获取原始json请求输入字符串，并解析为json对象
func (r *Request) GetJson() *gjson.Json {
    data := r.GetRaw()
    if len(data) > 0 {
        if j, err := gjson.DecodeToJson(data); err == nil {
            return j
        } else {
            r.Error(err, ": ", string(data))
        }
    }
    return nil
}

func (r *Request) GetString(key string, def ... string) string {
    return r.GetRequestString(key, def...)
}

func (r *Request) GetInt(key string, def ... int) int {
    return r.GetRequestInt(key, def...)
}

func (r *Request) GetInts(key string, def ... []int) []int {
    return r.GetRequestInts(key, def...)
}

func (r *Request) GetUint(key string, def ... uint) uint {
    return r.GetRequestUint(key, def...)
}

func (r *Request) GetFloat32(key string, def ... float32) float32 {
    return r.GetRequestFloat32(key, def...)
}

func (r *Request) GetFloat64(key string, def ... float64) float64 {
    return r.GetRequestFloat64(key, def...)
}

func (r *Request) GetFloats(key string, def ... []float64) []float64 {
    return r.GetRequestFloats(key, def...)
}

func (r *Request) GetArray(key string, def ... []string) []string {
    return r.GetRequestArray(key, def...)
}

func (r *Request) GetStrings(key string, def ... []string) []string {
    return r.GetRequestStrings(key, def...)
}

func (r *Request) GetInterfaces(key string, def ... []interface{}) []interface{} {
    return r.GetRequestInterfaces(key, def...)
}

func (r *Request) GetMap(def...map[string]string) map[string]string {
    return r.GetRequestMap(def...)
}

// 将所有的request参数映射到struct属性上，参数object应当为一个struct对象的指针, mapping为非必需参数，自定义参数与属性的映射关系
func (r *Request) GetToStruct(object interface{}, mapping...map[string]string) {
    r.GetRequestToStruct(object, mapping...)
}

// 仅退出当前逻辑执行函数, 如:服务函数、HOOK函数
func (r *Request) Exit() {
    panic(gEXCEPTION_EXIT)
}

// 退出当前请求执行，后续所有的服务逻辑流程(包括其他的HOOK)将不会执行
func (r *Request) ExitAll() {
    r.exit = true
    panic(gEXCEPTION_EXIT_ALL)
}

// 仅针对HOOK执行，默认情况下HOOK会按照优先级进行调用，当使用ExitHook后当前类型的后续HOOK将不会被调用
func (r *Request) ExitHook() {
    panic(gEXCEPTION_EXIT_HOOK)
}

// 判断当前请求是否停止执行
func (r *Request) IsExited() bool {
    return r.exit
}

// 获取请求的服务端IP/域名
func (r *Request) GetHost() string {
    if len(r.parsedHost) == 0 {
        array, _ := gregex.MatchString(`(.+):(\d+)`, r.Host)
        if len(array) > 1 {
            r.parsedHost = array[1]
        } else {
            r.parsedHost = r.Host
        }
    }
    return r.parsedHost
}

// 判断是否为静态文件请求
func (r *Request) IsFileRequest() bool {
    return r.isFileRequest
}

// 判断是否为AJAX请求
func (r *Request) IsAjaxRequest() bool {
    return strings.EqualFold(r.Header.Get("X-Requested-With"), "XMLHttpRequest")
}

// 获取请求的客户端IP地址
func (r *Request) GetClientIp() string {
    if len(r.clientIp) == 0 {
        if r.clientIp = r.Header.Get("X-Real-IP"); r.clientIp == "" {
            array, _ := gregex.MatchString(`(.+):(\d+)`, r.RemoteAddr)
            if len(array) > 1 {
                r.clientIp = array[1]
            } else {
                r.clientIp = r.RemoteAddr
            }
        }
    }
    return r.clientIp
}

// 获得来源URL地址
func (r *Request) GetReferer() string {
    return r.Header.Get("Referer")
}

// 获得结构体对象的参数名称标签，构成map返回
func (r *Request) getStructParamsTagMap(object interface{}) map[string]string {
    tagMap := make(map[string]string)
    fields := structs.Fields(object)
    for _, field := range fields {
        if tag := field.Tag("params"); tag != "" {
            for _, v := range strings.Split(tag, ",") {
                tagMap[strings.TrimSpace(v)] = field.Name()
            }
        }
    }
    return tagMap
}<commit_msg>add ghttp.Request.GetUrl to get current request URL<commit_after>// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package ghttp

import (
	"fmt"
	"github.com/gogf/gf/g/container/gvar"
    "github.com/gogf/gf/g/encoding/gjson"
    "github.com/gogf/gf/g/os/gtime"
    "github.com/gogf/gf/g/text/gregex"
    "github.com/gogf/gf/third/github.com/fatih/structs"
    "io/ioutil"
    "net/http"
    "strings"
)

// 请求对象
type Request struct {
    *http.Request
    parsedGet     bool                    // GET参数是否已经解析
    parsedPost    bool                    // POST参数是否已经解析
    queryVars     map[string][]string     // GET参数
    routerVars    map[string][]string     // 路由解析参数
    exit          bool                    // 是否退出当前请求流程执行
    Id            int                     // 请求id(唯一)
    Server        *Server                 // 请求关联的服务器对象
    Cookie        *Cookie                 // 与当前请求绑定的Cookie对象(并发安全)
    Session       *Session                // 与当前请求绑定的Session对象(并发安全)
    Response      *Response               // 对应请求的返回数据操作对象
    Router        *Router                 // 匹配到的路由对象
    EnterTime     int64                   // 请求进入时间(微秒)
    LeaveTime     int64                   // 请求完成时间(微秒)
    params        map[string]interface{}  // 开发者自定义参数(请求流程中有效)
    parsedHost    string                  // 解析过后不带端口号的服务器域名名称
    clientIp      string                  // 解析过后的客户端IP地址
    rawContent    []byte                  // 客户端提交的原始参数
    isFileRequest bool                    // 是否为静态文件请求(非服务请求，当静态文件存在时，优先级会被服务请求高，被识别为文件请求)
}

// 创建一个Request对象
func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request {
    request := &Request {
        routerVars : make(map[string][]string),
        Id         : s.servedCount.Add(1),
        Server     : s,
        Request    : r,
        Response   : newResponse(s, w),
        EnterTime  : gtime.Microsecond(),
    }
    // 会话处理
    request.Cookie           = GetCookie(request)
    request.Session          = GetSession(request)
    request.Response.request = request
    return request
}

// 获取Web Socket连接对象(如果是非WS请求会失败，注意检查返回的error结果)
func (r *Request) WebSocket() (*WebSocket, error) {
    if conn, err := wsUpgrader.Upgrade(r.Response.ResponseWriter.ResponseWriter, r.Request, nil); err == nil {
        return &WebSocket {
            conn,
        }, nil
    } else {
        return nil, err
    }
}

// 获得指定名称的参数字符串(Router/GET/POST)，同 GetRequestString
// 这是常用方法的简化别名
func (r *Request) Get(key string, def ... string) string {
    return r.GetRequestString(key, def...)
}

// 建议都用该参数替代参数获取
func (r *Request) GetVar(key string, def ... interface{}) gvar.VarRead {
    return r.GetRequestVar(key, def...)
}

// 获取原始请求输入二进制。
func (r *Request) GetRaw() []byte {
    err := error(nil)
    if r.rawContent == nil {
        r.rawContent, err = ioutil.ReadAll(r.Body)
        if err != nil {
            r.Error("error reading request body: ", err)
        }
    }
    return r.rawContent
}

// 获取原始请求输入字符串。
func (r *Request) GetRawString() string {
    return string(r.GetRaw())
}

// 获取原始json请求输入字符串，并解析为json对象
func (r *Request) GetJson() *gjson.Json {
    data := r.GetRaw()
    if len(data) > 0 {
        if j, err := gjson.DecodeToJson(data); err == nil {
            return j
        } else {
            r.Error(err, ": ", string(data))
        }
    }
    return nil
}

func (r *Request) GetString(key string, def ... string) string {
    return r.GetRequestString(key, def...)
}

func (r *Request) GetInt(key string, def ... int) int {
    return r.GetRequestInt(key, def...)
}

func (r *Request) GetInts(key string, def ... []int) []int {
    return r.GetRequestInts(key, def...)
}

func (r *Request) GetUint(key string, def ... uint) uint {
    return r.GetRequestUint(key, def...)
}

func (r *Request) GetFloat32(key string, def ... float32) float32 {
    return r.GetRequestFloat32(key, def...)
}

func (r *Request) GetFloat64(key string, def ... float64) float64 {
    return r.GetRequestFloat64(key, def...)
}

func (r *Request) GetFloats(key string, def ... []float64) []float64 {
    return r.GetRequestFloats(key, def...)
}

func (r *Request) GetArray(key string, def ... []string) []string {
    return r.GetRequestArray(key, def...)
}

func (r *Request) GetStrings(key string, def ... []string) []string {
    return r.GetRequestStrings(key, def...)
}

func (r *Request) GetInterfaces(key string, def ... []interface{}) []interface{} {
    return r.GetRequestInterfaces(key, def...)
}

func (r *Request) GetMap(def...map[string]string) map[string]string {
    return r.GetRequestMap(def...)
}

// 将所有的request参数映射到struct属性上，参数object应当为一个struct对象的指针, mapping为非必需参数，自定义参数与属性的映射关系
func (r *Request) GetToStruct(object interface{}, mapping...map[string]string) {
    r.GetRequestToStruct(object, mapping...)
}

// 仅退出当前逻辑执行函数, 如:服务函数、HOOK函数
func (r *Request) Exit() {
    panic(gEXCEPTION_EXIT)
}

// 退出当前请求执行，后续所有的服务逻辑流程(包括其他的HOOK)将不会执行
func (r *Request) ExitAll() {
    r.exit = true
    panic(gEXCEPTION_EXIT_ALL)
}

// 仅针对HOOK执行，默认情况下HOOK会按照优先级进行调用，当使用ExitHook后当前类型的后续HOOK将不会被调用
func (r *Request) ExitHook() {
    panic(gEXCEPTION_EXIT_HOOK)
}

// 判断当前请求是否停止执行
func (r *Request) IsExited() bool {
    return r.exit
}

// 获取请求的服务端IP/域名
func (r *Request) GetHost() string {
    if len(r.parsedHost) == 0 {
        array, _ := gregex.MatchString(`(.+):(\d+)`, r.Host)
        if len(array) > 1 {
            r.parsedHost = array[1]
        } else {
            r.parsedHost = r.Host
        }
    }
    return r.parsedHost
}

// 判断是否为静态文件请求
func (r *Request) IsFileRequest() bool {
    return r.isFileRequest
}

// 判断是否为AJAX请求
func (r *Request) IsAjaxRequest() bool {
    return strings.EqualFold(r.Header.Get("X-Requested-With"), "XMLHttpRequest")
}

// 获取请求的客户端IP地址
func (r *Request) GetClientIp() string {
    if len(r.clientIp) == 0 {
        if r.clientIp = r.Header.Get("X-Real-IP"); r.clientIp == "" {
            array, _ := gregex.MatchString(`(.+):(\d+)`, r.RemoteAddr)
            if len(array) > 1 {
                r.clientIp = array[1]
            } else {
                r.clientIp = r.RemoteAddr
            }
        }
    }
    return r.clientIp
}

// 获得当前请求URL地址
func (r *Request) GetUrl() string {
	scheme := "http"
	if r.TLS != nil {
		scheme = "https"
	}
	return fmt.Sprintf(`%s://%s%s`, scheme, r.Host, r.URL.String())
}

// 获得请求来源URL地址
func (r *Request) GetReferer() string {
    return r.Header.Get("Referer")
}

// 获得结构体对象的参数名称标签，构成map返回
func (r *Request) getStructParamsTagMap(object interface{}) map[string]string {
    tagMap := make(map[string]string)
    fields := structs.Fields(object)
    for _, field := range fields {
        if tag := field.Tag("params"); tag != "" {
            for _, v := range strings.Split(tag, ",") {
                tagMap[strings.TrimSpace(v)] = field.Name()
            }
        }
    }
    return tagMap
}<|endoftext|>


<commit_before>/*
 * Copyright 2020 The Knative Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package addressable

import (
	"context"
	"fmt"
	"sync"
	"testing"

	cloudevents "github.com/cloudevents/sdk-go/v2"
	cetest "github.com/cloudevents/sdk-go/v2/test"
	"github.com/google/uuid"
	"github.com/stretchr/testify/assert"
	"k8s.io/apiserver/pkg/storage/names"
	testlib "knative.dev/eventing/test/lib"
)

type EventMutator func(event *cloudevents.Event)

func Send(t *testing.T, addressable Addressable, mutators ...EventMutator) []string {
	return SendN(t, 10, addressable, mutators...)
}

func SendN(t *testing.T, n int, addressable Addressable, mutators ...EventMutator) []string {

	idsChan := make(chan string, n)
	ctx := context.Background()

	// Send n messages to the addressable
	go func() {
		var wg sync.WaitGroup
		wg.Add(n)

		for i := 0; i < n; i++ {

			t.Run(fmt.Sprintf("send event %d", i), func(t *testing.T) {
				// Client isn't thread safe so we need to create one per Goroutine.
				go func(i int) {

					client, err := testlib.NewClient(
						addressable.Namespace,
						t,
					)
					assert.Nil(t, err)

					event := cetest.FullEvent()
					id := uuid.New().String()
					event.SetID(id)

					for _, mutator := range mutators {
						mutator(&event)
					}

					client.Namespace = addressable.Namespace

					name := names.SimpleNameGenerator.GenerateName(fmt.Sprintf("%s-%d", addressable.Name, i))
					client.SendEventToAddressable(ctx, name, addressable.Name, &addressable.TypeMeta, event)

					idsChan <- event.ID()
					wg.Done()
				}(i)
			})
		}

		wg.Wait()
		close(idsChan)
	}()

	ids := make([]string, 0, n)
	for id := range idsChan {
		ids = append(ids, id)
	}

	return ids
}
<commit_msg>SendN sends events in a common t.Run (#2421)<commit_after>/*
 * Copyright 2020 The Knative Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package addressable

import (
	"context"
	"fmt"
	"sync"
	"testing"

	cloudevents "github.com/cloudevents/sdk-go/v2"
	cetest "github.com/cloudevents/sdk-go/v2/test"
	"github.com/google/uuid"
	"github.com/stretchr/testify/assert"
	"k8s.io/apiserver/pkg/storage/names"
	testlib "knative.dev/eventing/test/lib"
)

type EventMutator func(event *cloudevents.Event)

func Send(t *testing.T, addressable Addressable, mutators ...EventMutator) []string {
	return SendN(t, 10, addressable, mutators...)
}

func SendN(t *testing.T, n int, addressable Addressable, mutators ...EventMutator) []string {

	idsChan := make(chan string, n)
	ctx := context.Background()

	// Send n messages to the addressable
	go func() {
		var wg sync.WaitGroup
		wg.Add(n)

		for i := 0; i < n; i++ {
			// Client isn't thread safe so we need to create one per Goroutine.
			go func(i int) {
				client, err := testlib.NewClient(
					addressable.Namespace,
					t,
				)
				assert.Nil(t, err, fmt.Sprintf("event #%d", i))

				event := cetest.FullEvent()
				id := uuid.New().String()
				event.SetID(id)

				for _, mutator := range mutators {
					mutator(&event)
				}

				client.Namespace = addressable.Namespace

				name := names.SimpleNameGenerator.GenerateName(fmt.Sprintf("%s-%d", addressable.Name, i))
				client.SendEventToAddressable(ctx, name, addressable.Name, &addressable.TypeMeta, event)

				idsChan <- event.ID()
				wg.Done()
			}(i)
		}

		wg.Wait()
		close(idsChan)
	}()

	ids := make([]string, 0, n)
	for id := range idsChan {
		ids = append(ids, id)
	}

	return ids
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/sha256"
	"flag"
	"log"
	"net/http"
	"os"
	"runtime"
	"time"

	redigo "github.com/garyburd/redigo/redis"
	"github.com/pierrre/githubhook"
	"github.com/pierrre/imageserver"
	imageserver_cache "github.com/pierrre/imageserver/cache"
	imageserver_cache_memory "github.com/pierrre/imageserver/cache/memory"
	imageserver_cache_redis "github.com/pierrre/imageserver/cache/redis"
	imageserver_graphicsmagick "github.com/pierrre/imageserver/graphicsmagick"
	imageserver_http "github.com/pierrre/imageserver/http"
	imageserver_http_graphicsmagick "github.com/pierrre/imageserver/http/graphicsmagick"
	imageserver_testdata "github.com/pierrre/imageserver/testdata"
)

var (
	flagHTTPAddr            = ":8080"
	flagGitHubWebhookSecret string
	flagHTTPExpires         = time.Duration(7 * 24 * time.Hour)
	flagRedis               = "localhost:6379"
	flagRedisExpire         = time.Duration(7 * 24 * time.Hour)
	flagCacheMemory         = int64(64 * (1 << 20))
)

func main() {
	parseFlags()
	log.Println("Start")
	logEnv()
	startHTTPServer()
}

func parseFlags() {
	flag.StringVar(&flagHTTPAddr, "http", flagHTTPAddr, "HTTP addr")
	flag.DurationVar(&flagHTTPExpires, "http-expires", flagHTTPExpires, "HTTP expires")
	flag.StringVar(&flagGitHubWebhookSecret, "github-webhook-secret", "", "GitHub webhook secret")
	flag.StringVar(&flagRedis, "redis", flagRedis, "Redis addr")
	flag.DurationVar(&flagRedisExpire, "redis-expire", flagRedisExpire, "Redis expire")
	flag.Int64Var(&flagCacheMemory, "cache-memory", flagCacheMemory, "Cache memory")
	flag.Parse()
}

func logEnv() {
	log.Printf("Go version: %s", runtime.Version())
	log.Printf("Go max procs: %d", runtime.GOMAXPROCS(0))
}

func startHTTPServer() {
	http.Handle("/", newImageHTTPHandler())
	http.Handle("/favicon.ico", http.NotFoundHandler())
	if h := newGitHubWebhookHTTPHandler(); h != nil {
		http.Handle("/github_webhook", h)
	}
	log.Printf("Start HTTP server on %s", flagHTTPAddr)
	err := http.ListenAndServe(flagHTTPAddr, nil)
	if err != nil {
		log.Panic(err)
	}
}

func newGitHubWebhookHTTPHandler() http.Handler {
	if flagGitHubWebhookSecret == "" {
		return nil
	}
	return &githubhook.Handler{
		Secret: flagGitHubWebhookSecret,
		Delivery: func(event string, deliveryID string, payload interface{}) {
			log.Printf("Received GitHub webhook: %s", event)
			if event == "push" {
				delay := time.Duration(5 * time.Second)
				log.Printf("Killing process in %s", delay)
				time.AfterFunc(delay, func() {
					log.Println("Killing process now")
					os.Exit(0)
				})
			}
		},
		Error: func(err error, req *http.Request) {
			log.Printf("GitHub webhook error: %s", err)
		},
	}
}

func newImageHTTPHandler() http.Handler {
	handler := http.Handler(&imageserver_http.Handler{
		Parser:   newParser(),
		Server:   newServer(),
		ETagFunc: imageserver_http.NewParamsHashETagFunc(sha256.New),
		ErrorFunc: func(err error, request *http.Request) {
			log.Println("Error:", err)
		},
	})
	if flagHTTPExpires != 0 {
		handler = &imageserver_http.ExpiresHandler{
			Handler: handler,
			Expires: flagHTTPExpires,
		}
	}
	return handler
}

func newParser() imageserver_http.Parser {
	return &imageserver_http.ListParser{
		&imageserver_http.SourceParser{},
		&imageserver_http_graphicsmagick.Parser{},
	}
}

func newServer() imageserver.Server {
	server := newServerTestData()
	server = newServerGraphicsMagick(server)
	server = newServerLimit(server)
	server = newServerCache(server)
	return server
}

func newServerTestData() imageserver.Server {
	return imageserver_testdata.Server
}

func newServerGraphicsMagick(server imageserver.Server) imageserver.Server {
	return &imageserver_graphicsmagick.Server{
		Server:     server,
		Executable: "gm",
		Timeout:    time.Duration(10 * time.Second),
		AllowedFormats: []string{
			"jpeg",
			"png",
			"bmp",
			"gif",
		},
	}
}

func newServerLimit(server imageserver.Server) imageserver.Server {
	return imageserver.NewLimitServer(server, runtime.GOMAXPROCS(0)*2)
}

func newServerCache(server imageserver.Server) imageserver.Server {
	keyGenerator := imageserver_cache.NewParamsHashKeyGenerator(sha256.New)
	if cache := newCacheRedis(); cache != nil {
		server = &imageserver_cache.Server{
			Server:       server,
			Cache:        cache,
			KeyGenerator: keyGenerator,
		}
	}
	if cache := newCacheMemory(); cache != nil {
		server = &imageserver_cache.Server{
			Server:       server,
			Cache:        cache,
			KeyGenerator: keyGenerator,
		}
	}
	return server
}

func newCacheRedis() imageserver_cache.Cache {
	if flagRedis == "" {
		return nil
	}
	cache := imageserver_cache.Cache(&imageserver_cache_redis.Cache{
		Pool: &redigo.Pool{
			Dial: func() (redigo.Conn, error) {
				return redigo.Dial("tcp", flagRedis)
			},
			MaxIdle: 50,
		},
		Expire: flagRedisExpire,
	})
	cache = &imageserver_cache.IgnoreError{
		Cache: cache,
	}
	cache = &imageserver_cache.Async{
		Cache: cache,
	}
	return cache
}

func newCacheMemory() imageserver_cache.Cache {
	if flagCacheMemory <= 0 {
		return nil
	}
	return imageserver_cache_memory.New(flagCacheMemory)
}
<commit_msg>refactor advanced example<commit_after>package main

import (
	"crypto/sha256"
	"flag"
	"log"
	"net/http"
	"os"
	"runtime"
	"time"

	redigo "github.com/garyburd/redigo/redis"
	"github.com/pierrre/githubhook"
	"github.com/pierrre/imageserver"
	imageserver_cache "github.com/pierrre/imageserver/cache"
	imageserver_cache_memory "github.com/pierrre/imageserver/cache/memory"
	imageserver_cache_redis "github.com/pierrre/imageserver/cache/redis"
	imageserver_graphicsmagick "github.com/pierrre/imageserver/graphicsmagick"
	imageserver_http "github.com/pierrre/imageserver/http"
	imageserver_http_graphicsmagick "github.com/pierrre/imageserver/http/graphicsmagick"
	imageserver_testdata "github.com/pierrre/imageserver/testdata"
)

var (
	flagHTTPAddr            = ":8080"
	flagGitHubWebhookSecret string
	flagHTTPExpires         = time.Duration(7 * 24 * time.Hour)
	flagRedis               = "localhost:6379"
	flagRedisExpire         = time.Duration(7 * 24 * time.Hour)
	flagCacheMemory         = int64(64 * (1 << 20))
)

func main() {
	parseFlags()
	log.Println("Start")
	logEnv()
	startHTTPServer()
}

func parseFlags() {
	flag.StringVar(&flagHTTPAddr, "http", flagHTTPAddr, "HTTP addr")
	flag.DurationVar(&flagHTTPExpires, "http-expires", flagHTTPExpires, "HTTP expires")
	flag.StringVar(&flagGitHubWebhookSecret, "github-webhook-secret", "", "GitHub webhook secret")
	flag.StringVar(&flagRedis, "redis", flagRedis, "Redis addr")
	flag.DurationVar(&flagRedisExpire, "redis-expire", flagRedisExpire, "Redis expire")
	flag.Int64Var(&flagCacheMemory, "cache-memory", flagCacheMemory, "Cache memory")
	flag.Parse()
}

func logEnv() {
	log.Printf("Go version: %s", runtime.Version())
	log.Printf("Go max procs: %d", runtime.GOMAXPROCS(0))
}

func startHTTPServer() {
	http.Handle("/", newImageHTTPHandler())
	http.Handle("/favicon.ico", http.NotFoundHandler())
	if h := newGitHubWebhookHTTPHandler(); h != nil {
		http.Handle("/github_webhook", h)
	}
	log.Printf("Start HTTP server on %s", flagHTTPAddr)
	err := http.ListenAndServe(flagHTTPAddr, nil)
	if err != nil {
		log.Panic(err)
	}
}

func newGitHubWebhookHTTPHandler() http.Handler {
	if flagGitHubWebhookSecret == "" {
		return nil
	}
	return &githubhook.Handler{
		Secret: flagGitHubWebhookSecret,
		Delivery: func(event string, deliveryID string, payload interface{}) {
			log.Printf("Received GitHub webhook: %s", event)
			if event == "push" {
				delay := time.Duration(5 * time.Second)
				log.Printf("Killing process in %s", delay)
				time.AfterFunc(delay, func() {
					log.Println("Killing process now")
					os.Exit(0)
				})
			}
		},
		Error: func(err error, req *http.Request) {
			log.Printf("GitHub webhook error: %s", err)
		},
	}
}

func newImageHTTPHandler() http.Handler {
	handler := http.Handler(&imageserver_http.Handler{
		Parser: &imageserver_http.ListParser{
			&imageserver_http.SourceParser{},
			&imageserver_http_graphicsmagick.Parser{},
		},
		Server:   newServer(),
		ETagFunc: imageserver_http.NewParamsHashETagFunc(sha256.New),
		ErrorFunc: func(err error, request *http.Request) {
			log.Printf("Internal error: %s", err)
		},
	})
	if flagHTTPExpires != 0 {
		handler = &imageserver_http.ExpiresHandler{
			Handler: handler,
			Expires: flagHTTPExpires,
		}
	}
	return handler
}

func newServer() imageserver.Server {
	server := imageserver_testdata.Server
	server = newServerGraphicsMagick(server)
	server = newServerLimit(server)
	server = newServerCacheRedis(server)
	server = newServerCacheMemory(server)
	return server
}

func newServerGraphicsMagick(server imageserver.Server) imageserver.Server {
	return &imageserver_graphicsmagick.Server{
		Server:     server,
		Executable: "gm",
		Timeout:    time.Duration(30 * time.Second),
		AllowedFormats: []string{
			"jpeg",
			"png",
			"bmp",
			"gif",
		},
	}
}

func newServerLimit(server imageserver.Server) imageserver.Server {
	return imageserver.NewLimitServer(server, runtime.GOMAXPROCS(0)*2)
}

func newServerCacheRedis(server imageserver.Server) imageserver.Server {
	if flagRedis == "" {
		return server
	}
	var cache imageserver_cache.Cache
	cache = &imageserver_cache_redis.Cache{
		Pool: &redigo.Pool{
			Dial: func() (redigo.Conn, error) {
				return redigo.Dial("tcp", flagRedis)
			},
			MaxIdle: 50,
		},
		Expire: flagRedisExpire,
	}
	cache = &imageserver_cache.IgnoreError{
		Cache: cache,
	}
	cache = &imageserver_cache.Async{
		Cache: cache,
	}
	return &imageserver_cache.Server{
		Server:       server,
		Cache:        cache,
		KeyGenerator: imageserver_cache.NewParamsHashKeyGenerator(sha256.New),
	}
}

func newServerCacheMemory(server imageserver.Server) imageserver.Server {
	if flagCacheMemory <= 0 {
		return server
	}
	return &imageserver_cache.Server{
		Server:       server,
		Cache:        imageserver_cache_memory.New(flagCacheMemory),
		KeyGenerator: imageserver_cache.NewParamsHashKeyGenerator(sha256.New),
	}
}
<|endoftext|>


<commit_before>// +build ignore

package main

import (
	"crypto/sha256"
	//_ "expvar"
	"flag"
	redigo "github.com/garyburd/redigo/redis"
	"github.com/pierrre/imageserver"
	imageserver_cache_async "github.com/pierrre/imageserver/cache/async"
	imageserver_cache_list "github.com/pierrre/imageserver/cache/list"
	imageserver_cache_memory "github.com/pierrre/imageserver/cache/memory"
	imageserver_cache_redis "github.com/pierrre/imageserver/cache/redis"
	imageserver_http "github.com/pierrre/imageserver/http"
	imageserver_http_parser_graphicsmagick "github.com/pierrre/imageserver/http/parser/graphicsmagick"
	imageserver_http_parser_list "github.com/pierrre/imageserver/http/parser/list"
	imageserver_http_parser_source "github.com/pierrre/imageserver/http/parser/source"
	imageserver_processor_graphicsmagick "github.com/pierrre/imageserver/processor/graphicsmagick"
	imageserver_processor_limit "github.com/pierrre/imageserver/processor/limit"
	imageserver_provider_cache "github.com/pierrre/imageserver/provider/cache"
	imageserver_provider_http "github.com/pierrre/imageserver/provider/http"
	"log"
	"net/http"
	//_ "net/http/pprof"
	"os"
	"strconv"
	"time"
)

func main() {
	var verbose bool
	var httpAddr string
	flag.BoolVar(&verbose, "verbose", false, "Verbose")
	flag.StringVar(&httpAddr, "http", ":8080", "Http")
	flag.Parse()

	hostname, err := os.Hostname()
	if err != nil {
		panic(err)
	}

	var cache imageserver.Cache
	cache = &imageserver_cache_redis.RedisCache{
		Pool: &redigo.Pool{
			Dial: func() (redigo.Conn, error) {
				return redigo.Dial("tcp", "localhost:6379")
			},
			MaxIdle: 50,
		},
		Expire: time.Duration(7 * 24 * time.Hour),
	}
	cache = &imageserver_cache_async.AsyncCache{
		Cache: cache,
		ErrFunc: func(err error, key string, image *imageserver.Image, parameters imageserver.Parameters) {
			if verbose {
				log.Println(err)
			}
		},
	}
	cache = imageserver_cache_list.ListCache{
		imageserver_cache_memory.New(10 * 1024 * 1024),
		cache,
	}

	provider := &imageserver_provider_cache.CacheProvider{
		Provider:     &imageserver_provider_http.HTTPProvider{},
		Cache:        cache,
		CacheKeyFunc: imageserver_provider_cache.NewSourceHashCacheKeyFunc(sha256.New),
	}

	var processor imageserver.Processor
	processor = &imageserver_processor_graphicsmagick.GraphicsMagickProcessor{
		Executable: "gm",
		Timeout:    time.Duration(10 * time.Second),
		AllowedFormats: []string{
			"jpeg",
			"png",
			"bmp",
			"gif",
		},
		DefaultQualities: map[string]string{
			"jpeg": "85",
		},
	}
	processor = imageserver_processor_limit.New(processor, 16)

	imageServer := &imageserver.Server{
		Cache:        cache,
		CacheKeyFunc: imageserver.NewParametersHashCacheKeyFunc(sha256.New),
		Provider:     provider,
		Processor:    processor,
	}

	httpImageServer := &imageserver_http.Server{
		Parser: &imageserver_http_parser_list.ListParser{
			&imageserver_http_parser_source.SourceParser{},
			&imageserver_http_parser_graphicsmagick.GraphicsMagickParser{},
		},
		ImageServer: imageServer,
		ETagFunc:    imageserver_http.NewParametersHashETagFunc(sha256.New),
		Expire:      time.Duration(7 * 24 * time.Hour),
		RequestFunc: func(request *http.Request) error {
			url := request.URL
			query := url.Query()
			errorCodeString := query.Get("error")
			if len(errorCodeString) == 0 {
				return nil
			}
			errorCode, err := strconv.Atoi(errorCodeString)
			if err != nil {
				return imageserver.NewError(err.Error())
			}
			return imageserver_http.NewError(errorCode)
		},
		HeaderFunc: func(header http.Header, request *http.Request, err error) {
			header.Set("X-Hostname", hostname)
		},
		ErrorFunc: func(err error, request *http.Request) {
			if verbose {
				log.Println(err)
			}
		},
		ResponseFunc: func(request *http.Request, statusCode int, contentSize int64, err error) {
			if verbose {
				var errString string
				if err != nil {
					errString = err.Error()
				}
				log.Println(request.RemoteAddr, request.Method, strconv.Quote(request.URL.String()), statusCode, contentSize, strconv.Quote(errString))
			}
		},
	}

	http.Handle("/", httpImageServer)
	err = http.ListenAndServe(httpAddr, nil)
	if err != nil {
		panic(err)
	}
}
<commit_msg>improve log<commit_after>// +build ignore

package main

import (
	"crypto/sha256"
	//_ "expvar"
	"flag"
	redigo "github.com/garyburd/redigo/redis"
	"github.com/pierrre/imageserver"
	imageserver_cache_async "github.com/pierrre/imageserver/cache/async"
	imageserver_cache_list "github.com/pierrre/imageserver/cache/list"
	imageserver_cache_memory "github.com/pierrre/imageserver/cache/memory"
	imageserver_cache_redis "github.com/pierrre/imageserver/cache/redis"
	imageserver_http "github.com/pierrre/imageserver/http"
	imageserver_http_parser_graphicsmagick "github.com/pierrre/imageserver/http/parser/graphicsmagick"
	imageserver_http_parser_list "github.com/pierrre/imageserver/http/parser/list"
	imageserver_http_parser_source "github.com/pierrre/imageserver/http/parser/source"
	imageserver_processor_graphicsmagick "github.com/pierrre/imageserver/processor/graphicsmagick"
	imageserver_processor_limit "github.com/pierrre/imageserver/processor/limit"
	imageserver_provider_cache "github.com/pierrre/imageserver/provider/cache"
	imageserver_provider_http "github.com/pierrre/imageserver/provider/http"
	"log"
	"net/http"
	//_ "net/http/pprof"
	"os"
	"strconv"
	"time"
)

func main() {
	var verbose bool
	var httpAddr string
	flag.BoolVar(&verbose, "verbose", false, "Verbose")
	flag.StringVar(&httpAddr, "http", ":8080", "Http")
	flag.Parse()

	hostname, err := os.Hostname()
	if err != nil {
		panic(err)
	}

	var cache imageserver.Cache
	cache = &imageserver_cache_redis.RedisCache{
		Pool: &redigo.Pool{
			Dial: func() (redigo.Conn, error) {
				return redigo.Dial("tcp", "localhost:6379")
			},
			MaxIdle: 50,
		},
		Expire: time.Duration(7 * 24 * time.Hour),
	}
	cache = &imageserver_cache_async.AsyncCache{
		Cache: cache,
		ErrFunc: func(err error, key string, image *imageserver.Image, parameters imageserver.Parameters) {
			if verbose {
				log.Println("Cache error:", err)
			}
		},
	}
	cache = imageserver_cache_list.ListCache{
		imageserver_cache_memory.New(10 * 1024 * 1024),
		cache,
	}

	provider := &imageserver_provider_cache.CacheProvider{
		Provider:     &imageserver_provider_http.HTTPProvider{},
		Cache:        cache,
		CacheKeyFunc: imageserver_provider_cache.NewSourceHashCacheKeyFunc(sha256.New),
	}

	var processor imageserver.Processor
	processor = &imageserver_processor_graphicsmagick.GraphicsMagickProcessor{
		Executable: "gm",
		Timeout:    time.Duration(10 * time.Second),
		AllowedFormats: []string{
			"jpeg",
			"png",
			"bmp",
			"gif",
		},
		DefaultQualities: map[string]string{
			"jpeg": "85",
		},
	}
	processor = imageserver_processor_limit.New(processor, 16)

	imageServer := &imageserver.Server{
		Cache:        cache,
		CacheKeyFunc: imageserver.NewParametersHashCacheKeyFunc(sha256.New),
		Provider:     provider,
		Processor:    processor,
	}

	httpImageServer := &imageserver_http.Server{
		Parser: &imageserver_http_parser_list.ListParser{
			&imageserver_http_parser_source.SourceParser{},
			&imageserver_http_parser_graphicsmagick.GraphicsMagickParser{},
		},
		ImageServer: imageServer,
		ETagFunc:    imageserver_http.NewParametersHashETagFunc(sha256.New),
		Expire:      time.Duration(7 * 24 * time.Hour),
		RequestFunc: func(request *http.Request) error {
			if verbose {
				log.Println("Request:", strconv.Quote(request.URL.String()))
			}
			return nil
		},
		HeaderFunc: func(header http.Header, request *http.Request, err error) {
			header.Set("X-Hostname", hostname)
		},
		ErrorFunc: func(err error, request *http.Request) {
			if verbose {
				log.Println("Error:", err)
			}
		},
		ResponseFunc: func(request *http.Request, statusCode int, contentSize int64, err error) {
			if verbose {
				var errString string
				if err != nil {
					errString = err.Error()
				}
				log.Println("Response:", request.RemoteAddr, request.Method, strconv.Quote(request.URL.String()), statusCode, contentSize, strconv.Quote(errString))
			}
		},
	}

	http.Handle("/", httpImageServer)
	err = http.ListenAndServe(httpAddr, nil)
	if err != nil {
		panic(err)
	}
}
<|endoftext|>