<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"strconv"

	"regexp"

	"html/template"
	"net/http"

	"time"

	"database/sql"
	_ "github.com/mattn/go-sqlite3"
)

const (
	templateDir  = "tmpl/"
	templateView = templateDir + "view.html"

	apiKeyFile = "apikey.txt"

	dbFile = "./data.db"

	paramApiKey    = "apiKey"
	paramTimestamp = "d"
	paramTemp      = "t"
	paramHumidity  = "h"
	paramEvent     = "e"
)

var (
	events map[string]string = map[string]string{
		"DO": "",
		"DC": "",
	}
	templ  = template.Must(template.ParseFiles(templateView))
	apiKey string
	db     *sql.DB
)

type structEvent struct {
	Timestamp time.Time
	Event     string
}
type structMeasurement struct {
	Timestamp             time.Time
	Temperature, Humidity float64
}

type templateData struct {
	TemperatureValues [][]string
	HumidityValues    [][]string
	Events            [][]string
}

func loadApiKey() {
	body, err := ioutil.ReadFile(apiKeyFile)
	if err != nil {
		panic(err)
	}

	if match, _ := regexp.Match("^[0-9a-f]{40}$", body); !match {
		panic("Content of file [" + apiKeyFile + "] is not a valid SHA-1 hash.")
	}

	apiKey = string(body)
}

func openDb() *sql.DB {
	db, err := sql.Open("sqlite3", dbFile)
	if err != nil {
		log.Fatal(err)
	}

	sqlCreate := `
		CREATE TABLE IF NOT EXISTS measurements (
			tstamp timestamp not null primary key,
			temperature float,
			humidity float);
		CREATE TABLE IF NOT EXISTS events (
			tstamp timestamp not null primary key,
			type text);
		`
	_, err = db.Exec(sqlCreate)
	if err != nil {
		log.Printf("%q: %s\n", err, sqlCreate)
		db.Close()
		panic("Failed creating tables.")
	}

	return db
}

func main() {
	loadApiKey()
	db = openDb()
	defer db.Close()

	http.Handle("/", http.HandlerFunc(View))
	http.Handle("/add", http.HandlerFunc(Add))
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))
	err := http.ListenAndServe(":1664", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

func View(w http.ResponseWriter, req *http.Request) {
	log.Println("Call to View")

	rows, err := db.Query(`SELECT "m", strftime('%s', tstamp)*1000 ts, temperature, humidity
				FROM measurements
			UNION
				SELECT "e", strftime('%s', tstamp)*1000 ts, type, ""
				FROM events
			ORDER BY ts ASC`)
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()

	temperatureValues := [][]string{}
	humidityValues := [][]string{}
	events := [][]string{}
	do := ""
	dc := ""

	for rows.Next() {
		var table, tstamp, data1, data2 string
		rows.Scan(&table, &tstamp, &data1, &data2)

		switch table {
		case "m":
			temperatureValues = append(temperatureValues, []string{tstamp, data1})
			humidityValues = append(humidityValues, []string{tstamp, data2})
		case "e":
			switch data1 {
			case "DO":
				if len(do) > 0 {
					log.Printf("WARN: will ignore encountered event DO at %s already set at %s\n", tstamp, do)
				} else {
					do = tstamp
				}
			case "DC":
				if len(do) == 0 {
					log.Printf("WARN: will ignore encountered event DC at %s although DO is not set\n", tstamp)
				} else {
					dc = tstamp
				}
			}

			if len(do) > 0 && len(dc) > 0 {
				events = append(events, []string{do, dc})
				do = ""
				dc = ""
			}
		}

	}

	templ.Execute(w, templateData{temperatureValues, humidityValues, events})
}

func Add(w http.ResponseWriter, req *http.Request) {
	log.Printf("Call to Add with query string: %s\n", req.URL.RawQuery)

	err := req.ParseForm()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	log.Println(req.Form)

	if key, ok := req.Form[paramApiKey]; !ok || key[0] != apiKey {
		http.Error(w, "Api Key is not valid.", http.StatusForbidden)
		return
	}

	measurementsToAdd := []structMeasurement{}
	eventsToAdd := []structEvent{}
	for i, ts := range req.Form[paramTimestamp] {
		tsInt, err := strconv.ParseInt(ts, 0, 64)
		if err != nil {
			log.Print("Error parsing timestamp: ")
			log.Println(err)
			continue
		}
		time := time.Unix(tsInt, 0)

		var t float64 = 0
		if len(req.Form[paramTemp]) > i && req.Form[paramTemp][i] != "" {
			t, err = strconv.ParseFloat(req.Form[paramTemp][i], 64)
			if err != nil {
				log.Printf("Error parsing temperature '%s' for timestamp %s\n\t", req.Form[paramTemp][i], ts)
				log.Println(err)
			}
		}

		var h float64 = 0
		if len(req.Form[paramHumidity]) > i && req.Form[paramHumidity][i] != "" {
			h, err = strconv.ParseFloat(req.Form[paramHumidity][i], 64)
			if err != nil {
				log.Printf("Error parsing humidity '%s' for timestamp %s\n\t", req.Form[paramHumidity][i], ts)
				log.Println(err)
			}
		}

		if t > 0 || h > 0 {
			measurementsToAdd = append(measurementsToAdd, structMeasurement{time, t, h})
		}

		if len(req.Form[paramEvent]) > i && req.Form[paramEvent][i] != "" {
			e := req.Form[paramEvent][i]
			if _, ok := events[e]; !ok {
				log.Printf("Unknown event '%s' for timestamp %s\n", e, ts)
			} else {
				eventsToAdd = append(eventsToAdd, structEvent{time, e})
			}
		}
	}

	mDone, eDone := addDataToDb(measurementsToAdd, eventsToAdd)

	fmt.Fprintf(w, "Added %d measurements and %d events.", mDone, eDone)
}

func addDataToDb(measurements []structMeasurement, events []structEvent) (mDone, eDone int) {
	if measurements == nil || events == nil {
		panic("Parameters slices cannot be nil.")
	}

	if len(measurements)+len(events) == 0 {
		log.Println("No data to save.")
	}

	log.Printf("%d measurements and %d events to add\n", len(measurements), len(events))

	tx, err := db.Begin()
	if err != nil {
		log.Fatal(err)
	}

	if len(measurements) > 0 {
		stmt, err := tx.Prepare("INSERT INTO measurements(tstamp, temperature, humidity) VALUES(?, ?, ?)")
		if err != nil {
			log.Fatal(err)
		}
		defer stmt.Close()

		for _, m := range measurements {
			_, err = stmt.Exec(m.Timestamp, m.Temperature, m.Humidity)
			if err != nil {
				log.Println(err)
			} else {
				mDone++
			}
		}
	}

	if len(events) > 0 {
		stmt, err := tx.Prepare("INSERT INTO events(tstamp, type) VALUES(?, ?)")
		if err != nil {
			log.Fatal(err)
		}
		defer stmt.Close()

		for _, e := range events {
			_, err := stmt.Exec(e.Timestamp, e.Event)
			if err != nil {
				log.Println(err)
			} else {
				eDone++
			}
		}
	}

	tx.Commit()

	return mDone, eDone
}
<commit_msg>Move webserver port in a variable to configure more easily<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"strconv"

	"regexp"

	"html/template"
	"net/http"

	"time"

	"database/sql"
	_ "github.com/mattn/go-sqlite3"
)

const (
	port = ":1664"

	templateDir  = "tmpl/"
	templateView = templateDir + "view.html"

	apiKeyFile = "apikey.txt"

	dbFile = "./data.db"

	paramApiKey    = "apiKey"
	paramTimestamp = "d"
	paramTemp      = "t"
	paramHumidity  = "h"
	paramEvent     = "e"
)

var (
	events map[string]string = map[string]string{
		"DO": "",
		"DC": "",
	}
	templ  = template.Must(template.ParseFiles(templateView))
	apiKey string
	db     *sql.DB
)

type structEvent struct {
	Timestamp time.Time
	Event     string
}
type structMeasurement struct {
	Timestamp             time.Time
	Temperature, Humidity float64
}

type templateData struct {
	TemperatureValues [][]string
	HumidityValues    [][]string
	Events            [][]string
}

func loadApiKey() {
	body, err := ioutil.ReadFile(apiKeyFile)
	if err != nil {
		panic(err)
	}

	if match, _ := regexp.Match("^[0-9a-f]{40}$", body); !match {
		panic("Content of file [" + apiKeyFile + "] is not a valid SHA-1 hash.")
	}

	apiKey = string(body)
}

func openDb() *sql.DB {
	db, err := sql.Open("sqlite3", dbFile)
	if err != nil {
		log.Fatal(err)
	}

	sqlCreate := `
		CREATE TABLE IF NOT EXISTS measurements (
			tstamp timestamp not null primary key,
			temperature float,
			humidity float);
		CREATE TABLE IF NOT EXISTS events (
			tstamp timestamp not null primary key,
			type text);
		`
	_, err = db.Exec(sqlCreate)
	if err != nil {
		log.Printf("%q: %s\n", err, sqlCreate)
		db.Close()
		panic("Failed creating tables.")
	}

	return db
}

func main() {
	loadApiKey()
	db = openDb()
	defer db.Close()

	http.Handle("/", http.HandlerFunc(View))
	http.Handle("/add", http.HandlerFunc(Add))
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))
	err := http.ListenAndServe(port, nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

func View(w http.ResponseWriter, req *http.Request) {
	log.Println("Call to View")

	rows, err := db.Query(`SELECT "m", strftime('%s', tstamp)*1000 ts, temperature, humidity
				FROM measurements
			UNION
				SELECT "e", strftime('%s', tstamp)*1000 ts, type, ""
				FROM events
			ORDER BY ts ASC`)
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()

	temperatureValues := [][]string{}
	humidityValues := [][]string{}
	events := [][]string{}
	do := ""
	dc := ""

	for rows.Next() {
		var table, tstamp, data1, data2 string
		rows.Scan(&table, &tstamp, &data1, &data2)

		switch table {
		case "m":
			temperatureValues = append(temperatureValues, []string{tstamp, data1})
			humidityValues = append(humidityValues, []string{tstamp, data2})
		case "e":
			switch data1 {
			case "DO":
				if len(do) > 0 {
					log.Printf("WARN: will ignore encountered event DO at %s already set at %s\n", tstamp, do)
				} else {
					do = tstamp
				}
			case "DC":
				if len(do) == 0 {
					log.Printf("WARN: will ignore encountered event DC at %s although DO is not set\n", tstamp)
				} else {
					dc = tstamp
				}
			}

			if len(do) > 0 && len(dc) > 0 {
				events = append(events, []string{do, dc})
				do = ""
				dc = ""
			}
		}

	}

	templ.Execute(w, templateData{temperatureValues, humidityValues, events})
}

func Add(w http.ResponseWriter, req *http.Request) {
	log.Printf("Call to Add with query string: %s\n", req.URL.RawQuery)

	err := req.ParseForm()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	log.Println(req.Form)

	if key, ok := req.Form[paramApiKey]; !ok || key[0] != apiKey {
		http.Error(w, "Api Key is not valid.", http.StatusForbidden)
		return
	}

	measurementsToAdd := []structMeasurement{}
	eventsToAdd := []structEvent{}
	for i, ts := range req.Form[paramTimestamp] {
		tsInt, err := strconv.ParseInt(ts, 0, 64)
		if err != nil {
			log.Print("Error parsing timestamp: ")
			log.Println(err)
			continue
		}
		time := time.Unix(tsInt, 0)

		var t float64 = 0
		if len(req.Form[paramTemp]) > i && req.Form[paramTemp][i] != "" {
			t, err = strconv.ParseFloat(req.Form[paramTemp][i], 64)
			if err != nil {
				log.Printf("Error parsing temperature '%s' for timestamp %s\n\t", req.Form[paramTemp][i], ts)
				log.Println(err)
			}
		}

		var h float64 = 0
		if len(req.Form[paramHumidity]) > i && req.Form[paramHumidity][i] != "" {
			h, err = strconv.ParseFloat(req.Form[paramHumidity][i], 64)
			if err != nil {
				log.Printf("Error parsing humidity '%s' for timestamp %s\n\t", req.Form[paramHumidity][i], ts)
				log.Println(err)
			}
		}

		if t > 0 || h > 0 {
			measurementsToAdd = append(measurementsToAdd, structMeasurement{time, t, h})
		}

		if len(req.Form[paramEvent]) > i && req.Form[paramEvent][i] != "" {
			e := req.Form[paramEvent][i]
			if _, ok := events[e]; !ok {
				log.Printf("Unknown event '%s' for timestamp %s\n", e, ts)
			} else {
				eventsToAdd = append(eventsToAdd, structEvent{time, e})
			}
		}
	}

	mDone, eDone := addDataToDb(measurementsToAdd, eventsToAdd)

	fmt.Fprintf(w, "Added %d measurements and %d events.", mDone, eDone)
}

func addDataToDb(measurements []structMeasurement, events []structEvent) (mDone, eDone int) {
	if measurements == nil || events == nil {
		panic("Parameters slices cannot be nil.")
	}

	if len(measurements)+len(events) == 0 {
		log.Println("No data to save.")
	}

	log.Printf("%d measurements and %d events to add\n", len(measurements), len(events))

	tx, err := db.Begin()
	if err != nil {
		log.Fatal(err)
	}

	if len(measurements) > 0 {
		stmt, err := tx.Prepare("INSERT INTO measurements(tstamp, temperature, humidity) VALUES(?, ?, ?)")
		if err != nil {
			log.Fatal(err)
		}
		defer stmt.Close()

		for _, m := range measurements {
			_, err = stmt.Exec(m.Timestamp, m.Temperature, m.Humidity)
			if err != nil {
				log.Println(err)
			} else {
				mDone++
			}
		}
	}

	if len(events) > 0 {
		stmt, err := tx.Prepare("INSERT INTO events(tstamp, type) VALUES(?, ?)")
		if err != nil {
			log.Fatal(err)
		}
		defer stmt.Close()

		for _, e := range events {
			_, err := stmt.Exec(e.Timestamp, e.Event)
			if err != nil {
				log.Println(err)
			} else {
				eDone++
			}
		}
	}

	tx.Commit()

	return mDone, eDone
}
<|endoftext|>


<commit_before><commit_msg>Added scope streaming constant to auth<commit_after><|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"iiif"
	"log"
	"mime"
	"net/http"
	"net/url"
	"regexp"
	"strings"
)

func acceptsLD(req *http.Request) bool {
	for _, h := range req.Header["Accept"] {
		for _, accept := range strings.Split(h, ",") {
			if accept == "application/ld+json" {
				return true
			}
		}
	}

	return false
}

type IIIFHandler struct {
	Base          *url.URL
	BaseRegex     *regexp.Regexp
	BaseOnlyRegex *regexp.Regexp
	FeatureSet    *iiif.FeatureSet
	InfoPathRegex *regexp.Regexp
	TilePath      string
}

func NewIIIFHandler(u *url.URL, widths []int, tp string) *IIIFHandler {
	// Set up the features we support individually, and let the info magic figure
	// out how best to report it
	fs := &iiif.FeatureSet{
		RegionByPx:  true,
		RegionByPct: true,

		SizeByWhListed: true,
		SizeByW:        true,
		SizeByH:        true,
		SizeByPct:      true,
		SizeByWh:       true,
		SizeByForcedWh: true,
		SizeAboveFull:  true,

		RotationBy90s:     true,
		RotationArbitrary: false,
		Mirroring:         true,

		Default: true,
		Color:   true,
		Gray:    true,
		Bitonal: true,

		Jpg:  true,
		Png:  true,
		Gif:  true,
		Tif:  true,
		Jp2:  false,
		Pdf:  false,
		Webp: false,

		BaseUriRedirect:     true,
		Cors:                true,
		JsonldMediaType:     true,
		ProfileLinkHeader:   false,
		CanonicalLinkHeader: false,
	}

	// Set up tile sizes - scale factors are hard-coded for now
	fs.TileSizes = make([]iiif.TileSize, 0)
	sf := []int{1, 2, 4, 8, 16, 32}
	for _, val := range widths {
		fs.TileSizes = append(fs.TileSizes, iiif.TileSize{Width: val, ScaleFactors: sf})
	}

	rprefix := fmt.Sprintf(`^%s`, u.Path)
	return &IIIFHandler{
		Base:          u,
		BaseRegex:     regexp.MustCompile(rprefix + `/([^/]+)`),
		BaseOnlyRegex: regexp.MustCompile(rprefix + `/[^/]+$`),
		InfoPathRegex: regexp.MustCompile(rprefix + `/([^/]+)/info.json$`),
		TilePath:      tp,
		FeatureSet:    fs,
	}
}

func (ih *IIIFHandler) Route(w http.ResponseWriter, req *http.Request) {
	// Pull identifier from base so we know if we're even dealing with a valid
	// file in the first place
	p := req.RequestURI
	parts := ih.BaseRegex.FindStringSubmatch(p)

	// If it didn't even match the base, something weird happened, so we just
	// spit out a generic 404
	if parts == nil {
		http.NotFound(w, req)
		return
	}

	identifier := iiif.ID(parts[1])
	filepath := ih.TilePath + "/" + identifier.Path()

	// Check for base path and redirect if that's all we have
	if ih.BaseOnlyRegex.MatchString(p) {
		http.Redirect(w, req, p+"/info.json", 303)
		return
	}

	// Handle info.json prior to reading the image, in case of cached info
	if ih.InfoPathRegex.MatchString(p) {
		ih.Info(w, req, identifier, filepath)
		return
	}

	// No info path should mean a full command path - start reading the image
	res, err := NewImageResource(identifier, filepath)
	if err != nil {
		newImageResError(w, err)
		return
	}

	if u := iiif.NewURL(p); u.Valid() {
		ih.Command(w, req, u, res)
		return
	}

	// This means the URI was probably a command, but had an invalid syntax
	http.Error(w, "Invalid IIIF request", 400)
}

func (ih *IIIFHandler) Info(w http.ResponseWriter, req *http.Request, identifier iiif.ID, filepath string) {
	info := ih.FeatureSet.Info()
	res, err := NewImageResource(identifier, filepath)
	if err != nil {
		newImageResError(w, err)
		return
	}

	info.Width = res.Decoder.GetWidth()
	info.Height = res.Decoder.GetHeight()

	// The info id is actually the full URL to the resource, not just its ID
	info.ID = ih.Base.String() + "/" + res.ID.String()

	json, err := json.Marshal(info)
	if err != nil {
		log.Printf("ERROR!  Unable to marshal IIIFInfo response: %s", err)
		http.Error(w, "Server error", 500)
		return
	}

	// Set headers - content type is dependent on client
	ct := "application/json"
	if acceptsLD(req) {
		ct = "application/ld+json"
	}
	w.Header().Set("Content-Type", ct)
	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Write(json)
}

func newImageResError(w http.ResponseWriter, err error) {
	switch err {
	case ErrImageDoesNotExist:
		http.Error(w, "Image resource does not exist", 404)
	default:
		http.Error(w, err.Error(), 500)
	}
}

// Handles image processing operations.  Putting resize into the IIIFImageDecoder
// interface is necessary due to the way openjpeg operates on images - we must
// know which layer to decode to get the nearest valid image size when
// doing any resize operations.
func (ih *IIIFHandler) Command(w http.ResponseWriter, req *http.Request, u *iiif.URL, res *ImageResource) {
	// Send last modified time
	if err := sendHeaders(w, req, res.FilePath); err != nil {
		return
	}

	// Do we support this request?  If not, return a 501
	if !ih.FeatureSet.Supported(u) {
		http.Error(w, "Feature not supported", 501)
		return
	}

	img, err := res.Apply(u)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	w.Header().Set("Content-Type", mime.TypeByExtension("."+string(u.Format)))
	if err = EncodeImage(w, img, u.Format); err != nil {
		http.Error(w, "Unable to encode", 500)
		log.Printf("Unable to encode to %s: %s", u.Format, err)
		return
	}
}
<commit_msg>Extract image reading from Info handler<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"iiif"
	"log"
	"mime"
	"net/http"
	"net/url"
	"regexp"
	"strings"
)

func acceptsLD(req *http.Request) bool {
	for _, h := range req.Header["Accept"] {
		for _, accept := range strings.Split(h, ",") {
			if accept == "application/ld+json" {
				return true
			}
		}
	}

	return false
}

type IIIFHandler struct {
	Base          *url.URL
	BaseRegex     *regexp.Regexp
	BaseOnlyRegex *regexp.Regexp
	FeatureSet    *iiif.FeatureSet
	InfoPathRegex *regexp.Regexp
	TilePath      string
}

func NewIIIFHandler(u *url.URL, widths []int, tp string) *IIIFHandler {
	// Set up the features we support individually, and let the info magic figure
	// out how best to report it
	fs := &iiif.FeatureSet{
		RegionByPx:  true,
		RegionByPct: true,

		SizeByWhListed: true,
		SizeByW:        true,
		SizeByH:        true,
		SizeByPct:      true,
		SizeByWh:       true,
		SizeByForcedWh: true,
		SizeAboveFull:  true,

		RotationBy90s:     true,
		RotationArbitrary: false,
		Mirroring:         true,

		Default: true,
		Color:   true,
		Gray:    true,
		Bitonal: true,

		Jpg:  true,
		Png:  true,
		Gif:  true,
		Tif:  true,
		Jp2:  false,
		Pdf:  false,
		Webp: false,

		BaseUriRedirect:     true,
		Cors:                true,
		JsonldMediaType:     true,
		ProfileLinkHeader:   false,
		CanonicalLinkHeader: false,
	}

	// Set up tile sizes - scale factors are hard-coded for now
	fs.TileSizes = make([]iiif.TileSize, 0)
	sf := []int{1, 2, 4, 8, 16, 32}
	for _, val := range widths {
		fs.TileSizes = append(fs.TileSizes, iiif.TileSize{Width: val, ScaleFactors: sf})
	}

	rprefix := fmt.Sprintf(`^%s`, u.Path)
	return &IIIFHandler{
		Base:          u,
		BaseRegex:     regexp.MustCompile(rprefix + `/([^/]+)`),
		BaseOnlyRegex: regexp.MustCompile(rprefix + `/[^/]+$`),
		InfoPathRegex: regexp.MustCompile(rprefix + `/([^/]+)/info.json$`),
		TilePath:      tp,
		FeatureSet:    fs,
	}
}

func (ih *IIIFHandler) Route(w http.ResponseWriter, req *http.Request) {
	// Pull identifier from base so we know if we're even dealing with a valid
	// file in the first place
	p := req.RequestURI
	parts := ih.BaseRegex.FindStringSubmatch(p)

	// If it didn't even match the base, something weird happened, so we just
	// spit out a generic 404
	if parts == nil {
		http.NotFound(w, req)
		return
	}

	identifier := iiif.ID(parts[1])
	filepath := ih.TilePath + "/" + identifier.Path()

	// Check for base path and redirect if that's all we have
	if ih.BaseOnlyRegex.MatchString(p) {
		http.Redirect(w, req, p+"/info.json", 303)
		return
	}

	// Handle info.json prior to reading the image, in case of cached info
	if ih.InfoPathRegex.MatchString(p) {
		ih.Info(w, req, identifier, filepath)
		return
	}

	// No info path should mean a full command path - start reading the image
	res, err := NewImageResource(identifier, filepath)
	if err != nil {
		newImageResError(w, err)
		return
	}

	if u := iiif.NewURL(p); u.Valid() {
		ih.Command(w, req, u, res)
		return
	}

	// This means the URI was probably a command, but had an invalid syntax
	http.Error(w, "Invalid IIIF request", 400)
}

func (ih *IIIFHandler) Info(w http.ResponseWriter, req *http.Request, identifier iiif.ID, filepath string) {
	json, err := ih.buildInfoJSON(w, identifier, filepath)
	if err != nil {
		return
	}

	// Set headers - content type is dependent on client
	ct := "application/json"
	if acceptsLD(req) {
		ct = "application/ld+json"
	}
	w.Header().Set("Content-Type", ct)
	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Write(json)
}

func newImageResError(w http.ResponseWriter, err error) {
	switch err {
	case ErrImageDoesNotExist:
		http.Error(w, "Image resource does not exist", 404)
	default:
		http.Error(w, err.Error(), 500)
	}
}

func (ih *IIIFHandler) buildInfoJSON(w http.ResponseWriter, identifier iiif.ID, filepath string) ([]byte, error) {
	info := ih.FeatureSet.Info()
	res, err := NewImageResource(identifier, filepath)
	if err != nil {
		newImageResError(w, err)
		return nil, err
	}

	info.Width = res.Decoder.GetWidth()
	info.Height = res.Decoder.GetHeight()

	// The info id is actually the full URL to the resource, not just its ID
	info.ID = ih.Base.String() + "/" + res.ID.String()

	json, err := json.Marshal(info)
	if err != nil {
		log.Printf("ERROR!  Unable to marshal IIIFInfo response: %s", err)
		http.Error(w, "Server error", 500)
	}

	return json, err
}

// Handles image processing operations.  Putting resize into the IIIFImageDecoder
// interface is necessary due to the way openjpeg operates on images - we must
// know which layer to decode to get the nearest valid image size when
// doing any resize operations.
func (ih *IIIFHandler) Command(w http.ResponseWriter, req *http.Request, u *iiif.URL, res *ImageResource) {
	// Send last modified time
	if err := sendHeaders(w, req, res.FilePath); err != nil {
		return
	}

	// Do we support this request?  If not, return a 501
	if !ih.FeatureSet.Supported(u) {
		http.Error(w, "Feature not supported", 501)
		return
	}

	img, err := res.Apply(u)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	w.Header().Set("Content-Type", mime.TypeByExtension("."+string(u.Format)))
	if err = EncodeImage(w, img, u.Format); err != nil {
		http.Error(w, "Unable to encode", 500)
		log.Printf("Unable to encode to %s: %s", u.Format, err)
		return
	}
}
<|endoftext|>


<commit_before>package server

import (
	"time"

	log "github.com/sirupsen/logrus"
	v1 "k8s.io/api/core/v1"
	kube_err "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	kube_watch "k8s.io/apimachinery/pkg/watch"

	"github.com/pachyderm/pachyderm/src/client"
	"github.com/pachyderm/pachyderm/src/client/pkg/errors"
	"github.com/pachyderm/pachyderm/src/client/pps"
	"github.com/pachyderm/pachyderm/src/server/pkg/backoff"
	"github.com/pachyderm/pachyderm/src/server/pkg/watch"
)

const pollBackoffTime = 2 * time.Second

// startPipelinePoller starts a new goroutine running pollPipelines
func (m *ppsMaster) startPipelinePoller() {
	m.pollPipelinesMu.Lock()
	defer m.pollPipelinesMu.Unlock()
	m.pollCancel = m.startMonitorThread("pollPipelines", m.pollPipelines)
}

func (m *ppsMaster) cancelPipelinePoller() {
	m.pollPipelinesMu.Lock()
	defer m.pollPipelinesMu.Unlock()
	if m.pollCancel != nil {
		m.pollCancel()
		m.pollCancel = nil
	}
}

// startPipelinePodsPoller starts a new goroutine running pollPipelinePods
func (m *ppsMaster) startPipelinePodsPoller() {
	m.pollPipelinesMu.Lock()
	defer m.pollPipelinesMu.Unlock()
	m.pollPodsCancel = m.startMonitorThread("pollPipelinePods", m.pollPipelinePods)
}

func (m *ppsMaster) cancelPipelinePodsPoller() {
	m.pollPipelinesMu.Lock()
	defer m.pollPipelinesMu.Unlock()
	if m.pollPodsCancel != nil {
		m.pollPodsCancel()
		m.pollPodsCancel = nil
	}
}

// startPipelineEtcdPoller starts a new goroutine running
// pollPipelinesEtcd
func (m *ppsMaster) startPipelineEtcdPoller() {
	m.pollPipelinesMu.Lock()
	defer m.pollPipelinesMu.Unlock()
	m.pollEtcdCancel = m.startMonitorThread("pollPipelinesEtcd", m.pollPipelinesEtcd)
}

func (m *ppsMaster) cancelPipelineEtcdPoller() {
	m.pollPipelinesMu.Lock()
	defer m.pollPipelinesMu.Unlock()
	if m.pollEtcdCancel != nil {
		m.pollEtcdCancel()
		m.pollEtcdCancel = nil
	}
}

//////////////////////////////////////////////////////////////////////////////
//                     PollPipelines Definition                             //
// - As in monitor.go, functions below should not call functions above, to  //
// avoid reentrancy deadlock.                                               //
//////////////////////////////////////////////////////////////////////////////

// pollPipelines generates regular updateEv and deleteEv events for each
// pipeline and sends them to ppsMaster.Run(). By scanning etcd and k8s
// regularly and generating events for them, it prevents pipelines from
// getting orphaned.
func (m *ppsMaster) pollPipelines(pollClient *client.APIClient) {
	ctx := pollClient.Ctx()
	etcdPipelines := map[string]bool{}
	if err := backoff.RetryUntilCancel(ctx, func() error {
		if len(etcdPipelines) == 0 {
			// 1. Get the current set of pipeline RCs.
			//
			// We'll delete any RCs that don't correspond to a live pipeline after
			// querying etcd to determine the set of live pipelines, but we query k8s
			// first to avoid a race (if we were to query etcd first, and
			// CreatePipeline(foo) were to run between querying etcd and querying k8s,
			// then we might delete the RC for brand-new pipeline 'foo'). Even if we
			// do delete a live pipeline's RC, it'll be fixed in the next cycle)
			kc := m.a.env.GetKubeClient().CoreV1().ReplicationControllers(m.a.env.Namespace)
			rcs, err := kc.List(metav1.ListOptions{
				LabelSelector: "suite=pachyderm,pipelineName",
			})
			if err != nil {
				// No sensible error recovery here (e.g .if we can't reach k8s). We'll
				// keep going, and just won't delete any RCs this round.
				log.Errorf("error polling pipeline RCs: %v", err)
			}

			// 2. Replenish 'etcdPipelines' with the set of pipelines currently in
			// etcd. Note that there may be zero, and etcdPipelines may be empty
			if err := m.a.listPipelinePtr(pollClient, nil, 0,
				func(pipeline string, _ *pps.EtcdPipelineInfo) error {
					etcdPipelines[pipeline] = true
					return nil
				}); err != nil {
				// listPipelinePtr results (etcdPipelines) are used by all remaining
				// steps, so if that didn't work, start over and try again
				etcdPipelines = map[string]bool{}
				return errors.Wrap(err, "error polling pipelines")
			}

			// 3. Generate a delete event for orphaned RCs
			if rcs != nil {
				for _, rc := range rcs.Items {
					pipeline, ok := rc.Labels["pipelineName"]
					if !ok {
						return errors.New("'pipelineName' label missing from rc " + rc.Name)
					}
					if !etcdPipelines[pipeline] {
						m.eventCh <- &pipelineEvent{eventType: deleteEv, pipeline: pipeline}
					}
				}
			}

			// 4. Retry if there are no etcd pipelines to read/write
			if len(etcdPipelines) == 0 {
				return backoff.ErrContinue
			}
		}

		// Generate one etcd event for a pipeline (to trigger the pipeline
		// controller) and remove this pipeline from etcdPipelines. Always choose
		// the lexicographically smallest pipeline so that pipelines are always
		// traversed in the same order and the period between polls is stable across
		// all pipelines.
		var pipeline string
		for p := range etcdPipelines {
			if pipeline == "" || p < pipeline {
				pipeline = p
			}
		}

		// always rm 'pipeline', to advance loop
		delete(etcdPipelines, pipeline)

		// generate a pipeline event for 'pipeline'
		log.Debugf("PPS master: polling pipeline %q", pipeline)
		m.eventCh <- &pipelineEvent{eventType: writeEv, pipeline: pipeline}

		// move to next pipeline (after 2s sleep)
		return backoff.ErrContinue
	}, backoff.NewConstantBackOff(pollBackoffTime),
		backoff.NotifyContinue("pollPipelines"),
	); err != nil {
		if ctx.Err() == nil {
			panic("pollPipelines is exiting prematurely which should not happen; restarting pod...")
		}
	}
}

func (m *ppsMaster) pollPipelinePods(pollClient *client.APIClient) {
	ctx := pollClient.Ctx()
	if err := backoff.RetryUntilCancel(ctx, func() error {
		// watchChan will be nil if the Watch call below errors, this means
		// that we won't receive events from k8s and won't be able to detect
		// errors in pods. We could just return that error and retry but that
		// prevents pachyderm from creating pipelines when there's an issue
		// talking to k8s.
		kubePipelineWatch, err := m.a.env.GetKubeClient().CoreV1().Pods(m.a.namespace).Watch(
			metav1.ListOptions{
				LabelSelector: metav1.FormatLabelSelector(metav1.SetAsLabelSelector(
					map[string]string{
						"component": "worker",
					})),
				Watch: true,
			})
		if err != nil {
			return errors.Wrap(err, "failed to watch kubernetes pods")
		}
		defer kubePipelineWatch.Stop()
		for event := range kubePipelineWatch.ResultChan() {
			// if we get an error we restart the watch
			if event.Type == kube_watch.Error {
				return errors.Wrap(kube_err.FromObject(event.Object), "error while watching kubernetes pods")
			} else if event.Type == "" {
				// k8s watches seem to sometimes get stuck in a loop returning events
				// with Type = "". We treat these as errors as otherwise we get an
				// endless stream of them and can't do anything.
				return errors.New("error while watching kubernetes pods: empty event type")
			}
			pod, ok := event.Object.(*v1.Pod)
			if !ok {
				continue // irrelevant event
			}
			if pod.Status.Phase == v1.PodFailed {
				log.Errorf("pod failed because: %s", pod.Status.Message)
			}
			pipelineName := pod.ObjectMeta.Annotations["pipelineName"]
			for _, status := range pod.Status.ContainerStatuses {
				if status.State.Waiting != nil && failures[status.State.Waiting.Reason] {
					if err := m.a.setPipelineCrashing(ctx, pipelineName, status.State.Waiting.Message); err != nil {
						return errors.Wrap(err, "error moving pipeline to CRASHING")
					}
				}
			}
			for _, condition := range pod.Status.Conditions {
				if condition.Type == v1.PodScheduled &&
					condition.Status != v1.ConditionTrue && failures[condition.Reason] {
					if err := m.a.setPipelineCrashing(ctx, pipelineName, condition.Message); err != nil {
						return errors.Wrap(err, "error moving pipeline to CRASHING")
					}
				}
			}
		}
		return backoff.ErrContinue // keep polling until cancelled (RetryUntilCancel)
	}, &backoff.ZeroBackOff{}, backoff.NotifyContinue("pollPipelinePods"),
	); err != nil && ctx.Err() == nil {
		panic("pollPipelinePods is exiting prematurely which should not happen; restarting pod...")
	}
}

// pollPipelinesEtcd watches the 'pipelines' collection in etcd and sends
// writeEv and deleteEv events to the PPS master when it sees them.
//
// pollPipelinesEtcd is unlike the other poll and monitor goroutines in that it
// sees the result of other poll/monitor goroutines' writes. For example, when
// pollPipelinePods (above) observes that a pipeline is crashing and updates its
// state in etcd, the flow for starting monitorPipelineCrashing is:
//
//  k8s watch ─> pollPipelinePods  ╭──> pollPipelinesEtcd  ╭──> m.run()
//                      │          │            │          │      │
//                      ↓          │            ↓          │      ↓
//                  etcd write─────╯       m.eventCh ──────╯   m.step()
//
// most of the other poll/monitor goroutines actually go through
// pollPipelinesEtcd (by writing to etcd, which is then observed by the etcd
// watch below)
func (m *ppsMaster) pollPipelinesEtcd(pollClient *client.APIClient) {
	ctx := pollClient.Ctx()
	if err := backoff.RetryUntilCancel(ctx, func() error {
		// TODO(msteffen) request only keys, since pipeline_controller.go reads
		// fresh values for each event anyway
		pipelineWatcher, err := m.a.pipelines.ReadOnly(ctx).Watch()
		if err != nil {
			return errors.Wrapf(err, "error creating watch")
		}
		defer pipelineWatcher.Close()

		for event := range pipelineWatcher.Watch() {
			if event.Err != nil {
				return errors.Wrapf(event.Err, "event err")
			}
			switch event.Type {
			case watch.EventPut:
				m.eventCh <- &pipelineEvent{
					eventType: writeEv,
					pipeline:  string(event.Key),
					etcdVer:   event.Ver,
					etcdRev:   event.Rev,
				}
			case watch.EventDelete:
				m.eventCh <- &pipelineEvent{
					eventType: deleteEv,
					pipeline:  string(event.Key),
					etcdVer:   event.Ver,
					etcdRev:   event.Rev,
				}
			}
		}
		return backoff.ErrContinue // reset until ctx is cancelled (RetryUntilCancel)
	}, &backoff.ZeroBackOff{}, backoff.NotifyContinue("pollPipelinesEtcd"),
	); err != nil && ctx.Err() == nil {
		panic("pollPipelinesEtcd is exiting prematurely which should not happen; restarting pod...")
	}
}
<commit_msg>Fix comments in pollPipelinePods<commit_after>package server

import (
	"time"

	log "github.com/sirupsen/logrus"
	v1 "k8s.io/api/core/v1"
	kube_err "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	kube_watch "k8s.io/apimachinery/pkg/watch"

	"github.com/pachyderm/pachyderm/src/client"
	"github.com/pachyderm/pachyderm/src/client/pkg/errors"
	"github.com/pachyderm/pachyderm/src/client/pps"
	"github.com/pachyderm/pachyderm/src/server/pkg/backoff"
	"github.com/pachyderm/pachyderm/src/server/pkg/watch"
)

const pollBackoffTime = 2 * time.Second

// startPipelinePoller starts a new goroutine running pollPipelines
func (m *ppsMaster) startPipelinePoller() {
	m.pollPipelinesMu.Lock()
	defer m.pollPipelinesMu.Unlock()
	m.pollCancel = m.startMonitorThread("pollPipelines", m.pollPipelines)
}

func (m *ppsMaster) cancelPipelinePoller() {
	m.pollPipelinesMu.Lock()
	defer m.pollPipelinesMu.Unlock()
	if m.pollCancel != nil {
		m.pollCancel()
		m.pollCancel = nil
	}
}

// startPipelinePodsPoller starts a new goroutine running pollPipelinePods
func (m *ppsMaster) startPipelinePodsPoller() {
	m.pollPipelinesMu.Lock()
	defer m.pollPipelinesMu.Unlock()
	m.pollPodsCancel = m.startMonitorThread("pollPipelinePods", m.pollPipelinePods)
}

func (m *ppsMaster) cancelPipelinePodsPoller() {
	m.pollPipelinesMu.Lock()
	defer m.pollPipelinesMu.Unlock()
	if m.pollPodsCancel != nil {
		m.pollPodsCancel()
		m.pollPodsCancel = nil
	}
}

// startPipelineEtcdPoller starts a new goroutine running
// pollPipelinesEtcd
func (m *ppsMaster) startPipelineEtcdPoller() {
	m.pollPipelinesMu.Lock()
	defer m.pollPipelinesMu.Unlock()
	m.pollEtcdCancel = m.startMonitorThread("pollPipelinesEtcd", m.pollPipelinesEtcd)
}

func (m *ppsMaster) cancelPipelineEtcdPoller() {
	m.pollPipelinesMu.Lock()
	defer m.pollPipelinesMu.Unlock()
	if m.pollEtcdCancel != nil {
		m.pollEtcdCancel()
		m.pollEtcdCancel = nil
	}
}

//////////////////////////////////////////////////////////////////////////////
//                     PollPipelines Definition                             //
// - As in monitor.go, functions below should not call functions above, to  //
// avoid reentrancy deadlock.                                               //
//////////////////////////////////////////////////////////////////////////////

// pollPipelines generates regular updateEv and deleteEv events for each
// pipeline and sends them to ppsMaster.Run(). By scanning etcd and k8s
// regularly and generating events for them, it prevents pipelines from
// getting orphaned.
func (m *ppsMaster) pollPipelines(pollClient *client.APIClient) {
	ctx := pollClient.Ctx()
	etcdPipelines := map[string]bool{}
	if err := backoff.RetryUntilCancel(ctx, func() error {
		if len(etcdPipelines) == 0 {
			// 1. Get the current set of pipeline RCs.
			//
			// We'll delete any RCs that don't correspond to a live pipeline after
			// querying etcd to determine the set of live pipelines, but we query k8s
			// first to avoid a race (if we were to query etcd first, and
			// CreatePipeline(foo) were to run between querying etcd and querying k8s,
			// then we might delete the RC for brand-new pipeline 'foo'). Even if we
			// do delete a live pipeline's RC, it'll be fixed in the next cycle)
			kc := m.a.env.GetKubeClient().CoreV1().ReplicationControllers(m.a.env.Namespace)
			rcs, err := kc.List(metav1.ListOptions{
				LabelSelector: "suite=pachyderm,pipelineName",
			})
			if err != nil {
				// No sensible error recovery here (e.g .if we can't reach k8s). We'll
				// keep going, and just won't delete any RCs this round.
				log.Errorf("error polling pipeline RCs: %v", err)
			}

			// 2. Replenish 'etcdPipelines' with the set of pipelines currently in
			// etcd. Note that there may be zero, and etcdPipelines may be empty
			if err := m.a.listPipelinePtr(pollClient, nil, 0,
				func(pipeline string, _ *pps.EtcdPipelineInfo) error {
					etcdPipelines[pipeline] = true
					return nil
				}); err != nil {
				// listPipelinePtr results (etcdPipelines) are used by all remaining
				// steps, so if that didn't work, start over and try again
				etcdPipelines = map[string]bool{}
				return errors.Wrap(err, "error polling pipelines")
			}

			// 3. Generate a delete event for orphaned RCs
			if rcs != nil {
				for _, rc := range rcs.Items {
					pipeline, ok := rc.Labels["pipelineName"]
					if !ok {
						return errors.New("'pipelineName' label missing from rc " + rc.Name)
					}
					if !etcdPipelines[pipeline] {
						m.eventCh <- &pipelineEvent{eventType: deleteEv, pipeline: pipeline}
					}
				}
			}

			// 4. Retry if there are no etcd pipelines to read/write
			if len(etcdPipelines) == 0 {
				return backoff.ErrContinue
			}
		}

		// Generate one etcd event for a pipeline (to trigger the pipeline
		// controller) and remove this pipeline from etcdPipelines. Always choose
		// the lexicographically smallest pipeline so that pipelines are always
		// traversed in the same order and the period between polls is stable across
		// all pipelines.
		var pipeline string
		for p := range etcdPipelines {
			if pipeline == "" || p < pipeline {
				pipeline = p
			}
		}

		// always rm 'pipeline', to advance loop
		delete(etcdPipelines, pipeline)

		// generate a pipeline event for 'pipeline'
		log.Debugf("PPS master: polling pipeline %q", pipeline)
		m.eventCh <- &pipelineEvent{eventType: writeEv, pipeline: pipeline}

		// move to next pipeline (after 2s sleep)
		return backoff.ErrContinue
	}, backoff.NewConstantBackOff(pollBackoffTime),
		backoff.NotifyContinue("pollPipelines"),
	); err != nil {
		if ctx.Err() == nil {
			panic("pollPipelines is exiting prematurely which should not happen; restarting pod...")
		}
	}
}

// pollPipelinePods creates a kubernetes watch, and for each event:
//   1) Checks if the event concerns a Pod
//   2) Checks if the Pod belongs to a pipeline (pipelineName annotation is set)
//   3) Checks if the Pod is failing
// If all three conditions are met, then the pipline (in 'pipelineName') is set
// to CRASHING
func (m *ppsMaster) pollPipelinePods(pollClient *client.APIClient) {
	ctx := pollClient.Ctx()
	if err := backoff.RetryUntilCancel(ctx, func() error {
		kubePipelineWatch, err := m.a.env.GetKubeClient().CoreV1().Pods(m.a.namespace).Watch(
			metav1.ListOptions{
				LabelSelector: metav1.FormatLabelSelector(metav1.SetAsLabelSelector(
					map[string]string{
						"component": "worker",
					})),
				Watch: true,
			})
		if err != nil {
			return errors.Wrap(err, "failed to watch kubernetes pods")
		}
		defer kubePipelineWatch.Stop()
		for event := range kubePipelineWatch.ResultChan() {
			// if we get an error we restart the watch
			if event.Type == kube_watch.Error {
				return errors.Wrap(kube_err.FromObject(event.Object), "error while watching kubernetes pods")
			} else if event.Type == "" {
				// k8s watches seem to sometimes get stuck in a loop returning events
				// with Type = "". We treat these as errors as otherwise we get an
				// endless stream of them and can't do anything.
				return errors.New("error while watching kubernetes pods: empty event type")
			}
			pod, ok := event.Object.(*v1.Pod)
			if !ok {
				continue // irrelevant event
			}
			if pod.Status.Phase == v1.PodFailed {
				log.Errorf("pod failed because: %s", pod.Status.Message)
			}
			pipelineName := pod.ObjectMeta.Annotations["pipelineName"]
			for _, status := range pod.Status.ContainerStatuses {
				if status.State.Waiting != nil && failures[status.State.Waiting.Reason] {
					if err := m.a.setPipelineCrashing(ctx, pipelineName, status.State.Waiting.Message); err != nil {
						return errors.Wrap(err, "error moving pipeline to CRASHING")
					}
				}
			}
			for _, condition := range pod.Status.Conditions {
				if condition.Type == v1.PodScheduled &&
					condition.Status != v1.ConditionTrue && failures[condition.Reason] {
					if err := m.a.setPipelineCrashing(ctx, pipelineName, condition.Message); err != nil {
						return errors.Wrap(err, "error moving pipeline to CRASHING")
					}
				}
			}
		}
		return backoff.ErrContinue // keep polling until cancelled (RetryUntilCancel)
	}, &backoff.ZeroBackOff{}, backoff.NotifyContinue("pollPipelinePods"),
	); err != nil && ctx.Err() == nil {
		panic("pollPipelinePods is exiting prematurely which should not happen; restarting pod...")
	}
}

// pollPipelinesEtcd watches the 'pipelines' collection in etcd and sends
// writeEv and deleteEv events to the PPS master when it sees them.
//
// pollPipelinesEtcd is unlike the other poll and monitor goroutines in that it
// sees the result of other poll/monitor goroutines' writes. For example, when
// pollPipelinePods (above) observes that a pipeline is crashing and updates its
// state in etcd, the flow for starting monitorPipelineCrashing is:
//
//  k8s watch ─> pollPipelinePods  ╭──> pollPipelinesEtcd  ╭──> m.run()
//                      │          │            │          │      │
//                      ↓          │            ↓          │      ↓
//                  etcd write─────╯       m.eventCh ──────╯   m.step()
//
// most of the other poll/monitor goroutines actually go through
// pollPipelinesEtcd (by writing to etcd, which is then observed by the etcd
// watch below)
func (m *ppsMaster) pollPipelinesEtcd(pollClient *client.APIClient) {
	ctx := pollClient.Ctx()
	if err := backoff.RetryUntilCancel(ctx, func() error {
		// TODO(msteffen) request only keys, since pipeline_controller.go reads
		// fresh values for each event anyway
		pipelineWatcher, err := m.a.pipelines.ReadOnly(ctx).Watch()
		if err != nil {
			return errors.Wrapf(err, "error creating watch")
		}
		defer pipelineWatcher.Close()

		for event := range pipelineWatcher.Watch() {
			if event.Err != nil {
				return errors.Wrapf(event.Err, "event err")
			}
			switch event.Type {
			case watch.EventPut:
				m.eventCh <- &pipelineEvent{
					eventType: writeEv,
					pipeline:  string(event.Key),
					etcdVer:   event.Ver,
					etcdRev:   event.Rev,
				}
			case watch.EventDelete:
				m.eventCh <- &pipelineEvent{
					eventType: deleteEv,
					pipeline:  string(event.Key),
					etcdVer:   event.Ver,
					etcdRev:   event.Rev,
				}
			}
		}
		return backoff.ErrContinue // reset until ctx is cancelled (RetryUntilCancel)
	}, &backoff.ZeroBackOff{}, backoff.NotifyContinue("pollPipelinesEtcd"),
	); err != nil && ctx.Err() == nil {
		panic("pollPipelinesEtcd is exiting prematurely which should not happen; restarting pod...")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"net/http"
	"testing"
	"time"

	cv "github.com/glycerine/goconvey/convey"
)

func TestRequestMisorderingsAreCorrected046(t *testing.T) {

	dn := NewBoundary("downstream")

	ab2lp := make(chan *tunnelPacket)
	lp2ab := make(chan *tunnelPacket)

	longPollDur := 2 * time.Second
	lp := NewLittlePoll(longPollDur, dn, ab2lp, lp2ab)

	dn.Start()
	defer dn.Stop()

	lp.Start()
	defer lp.Stop()

	cv.Convey("Given that requests can arrive out of order (while the two http connection race), we should detect this and re-order both requests into sequence.", t, func() {

		// test *request* reorder alone (distinct from *reply* reordering):

		c2 := NewMockResponseWriter()

		// First send 2 in requestSerial 2, then send 1 in request serial 1,
		// and we should see them arrive 1 then 2 due to the re-ordering logic.
		//
		body2 := []byte("2")
		reqBody2 := bytes.NewBuffer(body2)
		r2, err := http.NewRequest("POST", "http://example.com/", reqBody2)
		panicOn(err)
		pack2 := &tunnelPacket{
			resp:    c2,
			respdup: new(bytes.Buffer),
			request: r2,
			done:    make(chan bool),
			key:     "longpoll_test_key",
			SerReq: SerReq{
				reqBody:       body2,
				requestSerial: 2,
			},
		}

		lp.ab2lp <- pack2

		c1 := NewMockResponseWriter()

		body1 := []byte("1")
		reqBody1 := bytes.NewBuffer(body1)
		r1, err := http.NewRequest("POST", "http://example.com/", reqBody1)
		panicOn(err)

		pack1 := &tunnelPacket{
			resp:    c1,
			respdup: new(bytes.Buffer),
			request: r1,
			done:    make(chan bool),
			key:     "longpoll_test_key",
			SerReq: SerReq{
				reqBody:       body1,
				requestSerial: 1,
			},
		}

		lp.ab2lp <- pack1

		// we won't get pack1 back immediately, but we will get pack2 back,
		// since it was sent first.
		/*
			select {
			case <-pack1.done:
				// good
				po("got back pack1.done")
			case <-time.After(1 * time.Second):
				dn.hist.ShowHistory()
				panic("should have had pack1 be done by now -- if re-ordering is in effect")
			}
		*/

		select {
		case <-pack2.done:
			// good
			po("got back pack2.done")
		case <-time.After(1 * time.Second):
			dn.hist.ShowHistory()
			panic("should have had pack2 be done by now -- if re-ordering is in effect")
		}

		//po("pack1 got back: '%s'", pack1.respdup.Bytes())
		po("pack2 got back: '%s'", pack2.respdup.Bytes())

		dh := dn.hist.GetHistory()
		dh.ShowHistory()

		cv.So(len(dh.absorbHistory), cv.ShouldEqual, 1)
		cv.So(len(dh.generateHistory), cv.ShouldEqual, 1)

		cv.So(string(dh.absorbHistory[0].what), cv.ShouldEqual, "12")
	})
}

/*
func TestRequestMisorderingsAreCorrected047(t *testing.T) {

	dn := NewBoundary("downstream")

	ab2lp := make(chan *tunnelPacket)
	lp2ab := make(chan []byte)

	longPollDur := 2 * time.Second
	lp := NewLittlePoll(longPollDur, dn, ab2lp, lp2ab)

	//	up := NewBoundary("upstream")

	//	ab := NewChaser(ChaserConfig{}, up.Generate, up.Absorb, ab2lp, lp2ab)

	dn.Start()
	defer dn.Stop()

	lp.Start()
	defer lp.Stop()

	//	ab.Start()
	//	defer ab.Stop()

	//	up.Start()
	//	defer up.Stop()

	cv.Convey("Previous test was for request order, this is for reply order: Given that replies can arrive out of order (while the two http connection race), we should detect this and re-order replies into sequence.", t, func() {

		// test reply reorder:

		c2 := NewMockResponseWriter()

		// First send 2 in requestSerial 2, then send 1 in request serial 1,
		// and we should see them arrive 1 then 2 due to the re-ordering logic.
		//
		body2 := []byte("2")
		reqBody2 := bytes.NewBuffer(body2)
		r2, err := http.NewRequest("POST", "http://example.com/", reqBody2)
		panicOn(err)
		pack2 := &tunnelPacket{
			resp:          c2,
			respdup:       new(bytes.Buffer),
			request:       r2,
			reqBody:       body2,
			done:          make(chan bool),
			key:           "longpoll_test_key",
			requestSerial: 2,
		}

		lp.ab2lp <- pack2

		c1 := NewMockResponseWriter()

		body1 := []byte("1")
		reqBody1 := bytes.NewBuffer(body1)
		r1, err := http.NewRequest("POST", "http://example.com/", reqBody1)
		panicOn(err)

		pack1 := &tunnelPacket{
			resp:          c1,
			respdup:       new(bytes.Buffer),
			request:       r1,
			reqBody:       body1,
			done:          make(chan bool),
			key:           "longpoll_test_key",
			requestSerial: 1,
		}

		lp.ab2lp <- pack1
		<-pack1.done
		<-pack2.done

		po("pack1 got back: '%s'", pack1.respdup.Bytes())
		po("pack2 got back: '%s'", pack2.respdup.Bytes())

		dh := dn.hist.GetHistory()

		cv.So(len(dh.absorbHistory), cv.ShouldEqual, 2)
		cv.So(len(dh.generateHistory), cv.ShouldEqual, 0)

		cv.So(dh.absorbHistory[0].what, cv.ShouldEqual, "1")
		cv.So(dh.absorbHistory[1].what, cv.ShouldEqual, "2")
	})
}
*/
<commit_msg>clearer test<commit_after>package main

import (
	"bytes"
	"net/http"
	"testing"
	"time"

	cv "github.com/glycerine/goconvey/convey"
)

func TestRequestMisorderingsAreCorrected046(t *testing.T) {

	dn := NewBoundary("downstream")

	ab2lp := make(chan *tunnelPacket)
	lp2ab := make(chan *tunnelPacket)

	longPollDur := 2 * time.Second
	lp := NewLittlePoll(longPollDur, dn, ab2lp, lp2ab)

	dn.Start()
	defer dn.Stop()

	lp.Start()
	defer lp.Stop()

	cv.Convey("Given that requests can arrive out of order (while the two http connection race), we should detect this and re-order both requests into sequence.", t, func() {

		// test *request* reorder alone (distinct from *reply* reordering):

		c2 := NewMockResponseWriter()

		// First send 2 in requestSerial 2, then send 1 in request serial 1,
		// and we should see them arrive 1 then 2 due to the re-ordering logic.
		//
		body2 := []byte("2")
		reqBody2 := bytes.NewBuffer(body2)
		r2, err := http.NewRequest("POST", "http://example.com/", reqBody2)
		panicOn(err)
		pack2 := &tunnelPacket{
			resp:    c2,
			respdup: new(bytes.Buffer),
			request: r2,
			done:    make(chan bool),
			key:     "longpoll_test_key",
			SerReq: SerReq{
				reqBody:       body2,
				requestSerial: 2,
			},
		}

		lp.ab2lp <- pack2

		c1 := NewMockResponseWriter()

		body1 := []byte("1")
		reqBody1 := bytes.NewBuffer(body1)
		r1, err := http.NewRequest("POST", "http://example.com/", reqBody1)
		panicOn(err)

		pack1 := &tunnelPacket{
			resp:    c1,
			respdup: new(bytes.Buffer),
			request: r1,
			done:    make(chan bool),
			key:     "longpoll_test_key",
			SerReq: SerReq{
				reqBody:       body1,
				requestSerial: 1,
			},
		}

		lp.ab2lp <- pack1

		// we won't get pack1 back immediately, but we will get pack2 back,
		// since it was sent first.
		/*
			select {
			case <-pack1.done:
				// good
				po("got back pack1.done")
			case <-time.After(1 * time.Second):
				dn.hist.ShowHistory()
				panic("should have had pack1 be done by now -- if re-ordering is in effect")
			}
		*/

		select {
		case <-pack2.done:
			// good
			po("got back pack2.done")
		case <-time.After(1 * time.Second):
			dn.hist.ShowHistory()
			panic("should have had pack2 be done by now -- if re-ordering is in effect")
		}

		//po("pack1 got back: '%s'", pack1.respdup.Bytes())
		po("pack2 got back: '%s'", pack2.respdup.Bytes())

		dh := dn.hist.GetHistory()
		dh.ShowHistory()

		cv.So(dh.CountAbsorbs(), cv.ShouldEqual, 1)
		cv.So(dh.CountGenerates(), cv.ShouldEqual, 0)

		cv.So(string(dh.absorbHistory[0].what), cv.ShouldEqual, "12")
	})
}

/*
func TestRequestMisorderingsAreCorrected047(t *testing.T) {

	dn := NewBoundary("downstream")

	ab2lp := make(chan *tunnelPacket)
	lp2ab := make(chan []byte)

	longPollDur := 2 * time.Second
	lp := NewLittlePoll(longPollDur, dn, ab2lp, lp2ab)

	//	up := NewBoundary("upstream")

	//	ab := NewChaser(ChaserConfig{}, up.Generate, up.Absorb, ab2lp, lp2ab)

	dn.Start()
	defer dn.Stop()

	lp.Start()
	defer lp.Stop()

	//	ab.Start()
	//	defer ab.Stop()

	//	up.Start()
	//	defer up.Stop()

	cv.Convey("Previous test was for request order, this is for reply order: Given that replies can arrive out of order (while the two http connection race), we should detect this and re-order replies into sequence.", t, func() {

		// test reply reorder:

		c2 := NewMockResponseWriter()

		// First send 2 in requestSerial 2, then send 1 in request serial 1,
		// and we should see them arrive 1 then 2 due to the re-ordering logic.
		//
		body2 := []byte("2")
		reqBody2 := bytes.NewBuffer(body2)
		r2, err := http.NewRequest("POST", "http://example.com/", reqBody2)
		panicOn(err)
		pack2 := &tunnelPacket{
			resp:          c2,
			respdup:       new(bytes.Buffer),
			request:       r2,
			reqBody:       body2,
			done:          make(chan bool),
			key:           "longpoll_test_key",
			requestSerial: 2,
		}

		lp.ab2lp <- pack2

		c1 := NewMockResponseWriter()

		body1 := []byte("1")
		reqBody1 := bytes.NewBuffer(body1)
		r1, err := http.NewRequest("POST", "http://example.com/", reqBody1)
		panicOn(err)

		pack1 := &tunnelPacket{
			resp:          c1,
			respdup:       new(bytes.Buffer),
			request:       r1,
			reqBody:       body1,
			done:          make(chan bool),
			key:           "longpoll_test_key",
			requestSerial: 1,
		}

		lp.ab2lp <- pack1
		<-pack1.done
		<-pack2.done

		po("pack1 got back: '%s'", pack1.respdup.Bytes())
		po("pack2 got back: '%s'", pack2.respdup.Bytes())

		dh := dn.hist.GetHistory()

		cv.So(len(dh.absorbHistory), cv.ShouldEqual, 2)
		cv.So(len(dh.generateHistory), cv.ShouldEqual, 0)

		cv.So(dh.absorbHistory[0].what, cv.ShouldEqual, "1")
		cv.So(dh.absorbHistory[1].what, cv.ShouldEqual, "2")
	})
}
*/
<|endoftext|>


<commit_before>package turms

import (
	"fmt"
	"github.com/gorilla/websocket"
	"github.com/ugorji/go/codec"
	"golang.org/x/net/context"
	"sync"
	"time"
)

// A Conn represents a connection between two Peers.
type Conn interface {
	Read(context.Context) (Message, error)
	Send(context.Context, Message) error
	Close() error
}

func Pipe() (Conn, Conn) {
	c1 := make(chan Message)
	c2 := make(chan Message)
	return &pipe{
			c1, c2,
		}, &pipe{
			c2, c1,
		}
}

type pipe struct {
	in  chan Message
	out chan Message
}

func (p *pipe) Read(ctx context.Context) (Message, error) {
	select {
	case <-ctx.Done():
		return nil, ctx.Err()
	case msg := <-p.in:
		return msg, nil
	}
}

func (p *pipe) Send(ctx context.Context, msg Message) error {
	select {
	case p.out <- msg:
		return nil
	case <-ctx.Done():
		return ctx.Err()
	}
}

func (p *pipe) Close() error {
	close(p.out)
	return nil
}

const (
	subprotocolIdentifierJSON    = "wamp.2.json"
	subprotocolIdentifierMsgPack = "wamp.2.msgpack"
)

type WebsocketConn struct {
	c       *websocket.Conn
	msgType int
	dec     *codec.Decoder
	decmu   sync.Mutex
	enc     *codec.Encoder
	encmu   sync.Mutex
}

func NewWebsocketConn(c *websocket.Conn) (Conn, error) {
	var (
		subProtocol = c.Subprotocol()
		h           codec.Handle
		msgType     int
	)
	switch subProtocol {
	case subprotocolIdentifierJSON:
		h = &codec.JsonHandle{}
		msgType = websocket.TextMessage
	case subprotocolIdentifierMsgPack:
		h = &codec.MsgpackHandle{}
		msgType = websocket.BinaryMessage
	default:
		return nil, fmt.Errorf("Unsupported subprotocol %s", subProtocol)
	}
	return &WebsocketConn{
		c:       c,
		msgType: msgType,
		dec:     codec.NewDecoderBytes(nil, h),
		enc:     codec.NewEncoderBytes(nil, h),
	}, nil
}

func (c *WebsocketConn) Read(ctx context.Context) (Message, error) {
	res := make(chan msgAndErr, 1)
	c.decmu.Lock()
	defer c.decmu.Unlock()

	go func() {
		_, b, err := c.c.ReadMessage()
		if err != nil {
			res <- msgAndErr{msg: nil, err: err}
			return
		}
		c.dec.ResetBytes(b)
		var msgTyp [1]MessageType
		err = c.dec.Decode(&msgTyp)
		c.dec.ResetBytes(b)
		if err != nil {
			res <- msgAndErr{msg: nil, err: err}
			return
		}
		msg := NewMessage(msgTyp[0])
		err = c.dec.Decode(msg)
		res <- msgAndErr{msg: msg, err: err}
	}()

	select {
	case <-ctx.Done():
		return nil, ctx.Err()
	case r := <-res:
		return r.msg, r.err
	}
}

func (c *WebsocketConn) Send(ctx context.Context, msg Message) error {
	res := make(chan error, 1)

	go func() {
		c.encmu.Lock()
		defer c.encmu.Unlock()

		w, err := c.c.NextWriter(c.msgType)
		if err != nil {
			res <- err
		}
		defer w.Close()
		c.enc.Reset(w)

		res <- c.enc.Encode(msg)
	}()
	select {
	case <-ctx.Done():
		return ctx.Err()
	case err := <-res:
		return err
	}
}

func (c *WebsocketConn) Close() error {
	return c.c.Close()
}

type msgAndErr struct {
	msg Message
	err error
}

func waitForMessage(parentCtx context.Context, c Conn, duration time.Duration) (Message, error) {
	ctx, cancel := context.WithTimeout(parentCtx, duration)
	defer cancel()
	return c.Read(ctx)
}
<commit_msg>pipe.Read returns an io.EOF error when pipe.in is closed<commit_after>package turms

import (
	"fmt"
	"github.com/gorilla/websocket"
	"github.com/ugorji/go/codec"
	"golang.org/x/net/context"
	"io"
	"sync"
	"time"
)

// A Conn represents a connection between two Peers.
type Conn interface {
	Read(context.Context) (Message, error)
	Send(context.Context, Message) error
	Close() error
}

func Pipe() (Conn, Conn) {
	c1 := make(chan Message)
	c2 := make(chan Message)
	return &pipe{
			c1, c2,
		}, &pipe{
			c2, c1,
		}
}

type pipe struct {
	in  chan Message
	out chan Message
}

func (p *pipe) Read(ctx context.Context) (Message, error) {
	select {
	case <-ctx.Done():
		return nil, ctx.Err()
	case msg, ok := <-p.in:
		if !ok {
			return msg, io.EOF
		}
		return msg, nil
	}
}

func (p *pipe) Send(ctx context.Context, msg Message) error {
	select {
	case p.out <- msg:
		return nil
	case <-ctx.Done():
		return ctx.Err()
	}
}

func (p *pipe) Close() error {
	close(p.out)
	return nil
}

const (
	subprotocolIdentifierJSON    = "wamp.2.json"
	subprotocolIdentifierMsgPack = "wamp.2.msgpack"
)

type WebsocketConn struct {
	c       *websocket.Conn
	msgType int
	dec     *codec.Decoder
	decmu   sync.Mutex
	enc     *codec.Encoder
	encmu   sync.Mutex
}

func NewWebsocketConn(c *websocket.Conn) (Conn, error) {
	var (
		subProtocol = c.Subprotocol()
		h           codec.Handle
		msgType     int
	)
	switch subProtocol {
	case subprotocolIdentifierJSON:
		h = &codec.JsonHandle{}
		msgType = websocket.TextMessage
	case subprotocolIdentifierMsgPack:
		h = &codec.MsgpackHandle{}
		msgType = websocket.BinaryMessage
	default:
		return nil, fmt.Errorf("Unsupported subprotocol %s", subProtocol)
	}
	return &WebsocketConn{
		c:       c,
		msgType: msgType,
		dec:     codec.NewDecoderBytes(nil, h),
		enc:     codec.NewEncoderBytes(nil, h),
	}, nil
}

func (c *WebsocketConn) Read(ctx context.Context) (Message, error) {
	res := make(chan msgAndErr, 1)
	c.decmu.Lock()
	defer c.decmu.Unlock()

	go func() {
		_, b, err := c.c.ReadMessage()
		if err != nil {
			res <- msgAndErr{msg: nil, err: err}
			return
		}
		c.dec.ResetBytes(b)
		var msgTyp [1]MessageType
		err = c.dec.Decode(&msgTyp)
		c.dec.ResetBytes(b)
		if err != nil {
			res <- msgAndErr{msg: nil, err: err}
			return
		}
		msg := NewMessage(msgTyp[0])
		err = c.dec.Decode(msg)
		res <- msgAndErr{msg: msg, err: err}
	}()

	select {
	case <-ctx.Done():
		return nil, ctx.Err()
	case r := <-res:
		return r.msg, r.err
	}
}

func (c *WebsocketConn) Send(ctx context.Context, msg Message) error {
	res := make(chan error, 1)

	go func() {
		c.encmu.Lock()
		defer c.encmu.Unlock()

		w, err := c.c.NextWriter(c.msgType)
		if err != nil {
			res <- err
		}
		defer w.Close()
		c.enc.Reset(w)

		res <- c.enc.Encode(msg)
	}()
	select {
	case <-ctx.Done():
		return ctx.Err()
	case err := <-res:
		return err
	}
}

func (c *WebsocketConn) Close() error {
	return c.c.Close()
}

type msgAndErr struct {
	msg Message
	err error
}

func waitForMessage(parentCtx context.Context, c Conn, duration time.Duration) (Message, error) {
	ctx, cancel := context.WithTimeout(parentCtx, duration)
	defer cancel()
	return c.Read(ctx)
}
<|endoftext|>


<commit_before>// Tests of call chaining f(g()) when g has multiple return values (MRVs).
// See https://code.google.com/p/go/issues/detail?id=4573.

package main

func assert(actual, expected int) {
	if actual != expected {
		panic(actual)
	}
}

func g() (int, int) {
	return 5, 7
}

func g2() (float64, float64) {
	return 5, 7
}

func f1v(x int, v ...int) {
	assert(x, 5)
	assert(v[0], 7)
}

func f2(x, y int) {
	assert(x, 5)
	assert(y, 7)
}

func f2v(x, y int, v ...int) {
	assert(x, 5)
	assert(y, 7)
	assert(len(v), 0)
}

func complexArgs() (float64, float64) {
	return 5, 7
}

func appendArgs() ([]string, string) {
	return []string{"foo"}, "bar"
}

func h() (i interface{}, ok bool) {
	m := map[int]string{1: "hi"}
	i, ok = m[1] // string->interface{} conversion within multi-valued expression
	return
}

func main() {
	f1v(g())
	f2(g())
	f2v(g())
	// TODO(gri): the typechecker still doesn't support these cases correctly.
	// if c := complex(complexArgs()); c != 5+7i {
	// 	panic(c)
	// }
	// if s := append(appendArgs()); len(s) != 2 || s[0] != "foo" || s[1] != "bar" {
	// 	panic(s)
	// }

	i, ok := h()
	if !ok || i.(string) != "hi" {
		panic(i)
	}
}
<commit_msg>go.tools/ssa/interp: enable tests of builtin(f()) where f has multiple results.<commit_after>// Tests of call chaining f(g()) when g has multiple return values (MRVs).
// See https://code.google.com/p/go/issues/detail?id=4573.

package main

func assert(actual, expected int) {
	if actual != expected {
		panic(actual)
	}
}

func g() (int, int) {
	return 5, 7
}

func g2() (float64, float64) {
	return 5, 7
}

func f1v(x int, v ...int) {
	assert(x, 5)
	assert(v[0], 7)
}

func f2(x, y int) {
	assert(x, 5)
	assert(y, 7)
}

func f2v(x, y int, v ...int) {
	assert(x, 5)
	assert(y, 7)
	assert(len(v), 0)
}

func complexArgs() (float64, float64) {
	return 5, 7
}

func appendArgs() ([]string, string) {
	return []string{"foo"}, "bar"
}

func h() (i interface{}, ok bool) {
	m := map[int]string{1: "hi"}
	i, ok = m[1] // string->interface{} conversion within multi-valued expression
	return
}

func main() {
	f1v(g())
	f2(g())
	f2v(g())
	if c := complex(complexArgs()); c != 5+7i {
		panic(c)
	}
	if s := append(appendArgs()); len(s) != 2 || s[0] != "foo" || s[1] != "bar" {
		panic(s)
	}
	i, ok := h()
	if !ok || i.(string) != "hi" {
		panic(i)
	}
}
<|endoftext|>


<commit_before>package neptulon

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/binary"
	"encoding/pem"
	"errors"
	"fmt"
	"log"
	"net"
	"time"

	"github.com/neptulon/cmap"
)

// Conn is a full-duplex bidirectional client-server connection.
type Conn struct {
	ID                 string     // Randomly generated unique connection ID
	Data               *cmap.CMap // Thread-safe data store for storing arbitrary data for this connection session
	conn               *tls.Conn
	headerSize         int
	maxMsgSize         int
	readDeadline       time.Duration
	debug              bool
	err                error
	clientDisconnected bool // hack: Whether the client disconnected from server before server closed connection
}

// NewConn creates a new server-side connection object.
// Default values for headerSize, maxMsgSize, and readDeadline are 4 bytes, 4294967295 bytes (4GB), and 300 seconds, respectively.
// Debug mode logs all raw TCP communication.
func NewConn(conn *tls.Conn, headerSize, maxMsgSize, readDeadline int, debug bool) (*Conn, error) {
	if headerSize == 0 {
		headerSize = 4
	}
	if maxMsgSize == 0 {
		maxMsgSize = 4294967295
	}
	if readDeadline == 0 {
		readDeadline = 300
	}

	id, err := GenID()
	if err != nil {
		return nil, err
	}

	return &Conn{
		ID:           id,
		Data:         cmap.New(),
		conn:         conn,
		headerSize:   headerSize,
		maxMsgSize:   maxMsgSize,
		readDeadline: time.Second * time.Duration(readDeadline),
		debug:        debug,
	}, nil
}

// Dial creates a new client side connection to a server at the given network address,
// with optional CA and/or a client certificate (PEM encoded X.509 cert/key).
// Debug mode logs all raw TCP communication.
func Dial(addr string, ca []byte, clientCert []byte, clientCertKey []byte, debug bool) (*Conn, error) {
	var cas *x509.CertPool
	var certs []tls.Certificate
	if ca != nil {
		cas = x509.NewCertPool()
		ok := cas.AppendCertsFromPEM(ca)
		if !ok {
			return nil, errors.New("failed to parse the CA certificate")
		}
	}
	if clientCert != nil {
		tlsCert, err := tls.X509KeyPair(clientCert, clientCertKey)
		if err != nil {
			return nil, fmt.Errorf("failed to parse the client certificate: %v", err)
		}

		c, _ := pem.Decode(clientCert)
		if tlsCert.Leaf, err = x509.ParseCertificate(c.Bytes); err != nil {
			return nil, fmt.Errorf("failed to parse the client certificate: %v", err)
		}

		certs = []tls.Certificate{tlsCert}
	}

	// todo: dial timeout like that of net.Conn.DialTimeout
	c, err := tls.Dial("tcp", addr, &tls.Config{RootCAs: cas, Certificates: certs})
	if err != nil {
		return nil, err
	}

	return NewConn(c, 0, 0, 0, debug)
}

// SetReadDeadline set the read deadline for the connection in seconds.
func (c *Conn) SetReadDeadline(seconds int) {
	c.readDeadline = time.Second * time.Duration(seconds)
}

// Read waits for and reads the next incoming message from the TLS connection.
func (c *Conn) Read() (msg []byte, err error) {
	if err = c.conn.SetReadDeadline(time.Now().Add(c.readDeadline)); err != nil {
		return
	}

	// read the content length header
	h := make([]byte, c.headerSize)
	var n int
	n, err = c.conn.Read(h)
	if err != nil {
		return
	}
	if n != c.headerSize {
		err = fmt.Errorf("expected to read header size %v bytes but instead read %v bytes", c.headerSize, n)
		return
	}

	// calculate the content length
	n = readHeaderBytes(h)

	// read the message content
	msg = make([]byte, n)
	total := 0
	for total < n {
		// todo: log here in case it gets stuck, or there is a dos attack, pumping up cpu usage!
		i, err := c.conn.Read(msg[total:])
		if err != nil {
			err = fmt.Errorf("errored while reading incoming message: %v", err)
			break
		}
		total += i
	}
	if total != n {
		err = fmt.Errorf("expected to read %v bytes instead read %v bytes", n, total)
	}

	if c.debug {
		log.Println("Incoming message:", string(msg))
	}

	return
}

// Write writes given message to the connection.
func (c *Conn) Write(msg []byte) error {
	l := len(msg)
	h := makeHeaderBytes(l, c.headerSize)

	// write the header
	n, err := c.conn.Write(h)
	if err != nil {
		return err
	}
	if n != c.headerSize {
		err = fmt.Errorf("expected to write %v bytes but only wrote %v bytes", l, n)
	}

	// write the body
	// todo: do we need a loop? bufio uses a loop but it might be due to buff length limitation
	n, err = c.conn.Write(msg)
	if err != nil {
		return err
	}
	if n != l {
		err = fmt.Errorf("expected to write %v bytes but only wrote %v bytes", l, n)
	}

	return nil
}

// RemoteAddr returns the remote network address.
func (c *Conn) RemoteAddr() net.Addr {
	return c.conn.RemoteAddr()
}

// ConnectionState returns basic TLS details about the connection.
func (c *Conn) ConnectionState() tls.ConnectionState {
	return c.conn.ConnectionState()
}

// Close closes a connection.
// Note: TCP/IP stack does not guarantee delivery of messages before the connection is closed.
func (c *Conn) Close() error {
	return c.conn.Close() // todo: if conn.err is nil, send a close req and wait ack then close? (or even wait for everything else to finish?)
}

func makeHeaderBytes(h, size int) []byte {
	b := make([]byte, size)
	binary.LittleEndian.PutUint32(b, uint32(h))
	return b
}

func readHeaderBytes(h []byte) int {
	return int(binary.LittleEndian.Uint32(h))
}
<commit_msg>clarify NewConn docs<commit_after>package neptulon

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/binary"
	"encoding/pem"
	"errors"
	"fmt"
	"log"
	"net"
	"time"

	"github.com/neptulon/cmap"
)

// Conn is a full-duplex bidirectional client-server connection.
type Conn struct {
	ID                 string     // Randomly generated unique connection ID
	Data               *cmap.CMap // Thread-safe data store for storing arbitrary data for this connection session
	conn               *tls.Conn
	headerSize         int
	maxMsgSize         int
	readDeadline       time.Duration
	debug              bool
	err                error
	clientDisconnected bool // hack: Whether the client disconnected from server before server closed connection
}

// NewConn creates a new neptulon.Conn object which wraps a given tls.Conn object.
// Default values for headerSize, maxMsgSize, and readDeadline are 4 bytes, 4294967295 bytes (4GB), and 300 seconds, respectively.
// Debug mode logs all raw TCP communication.
func NewConn(conn *tls.Conn, headerSize, maxMsgSize, readDeadline int, debug bool) (*Conn, error) {
	if headerSize == 0 {
		headerSize = 4
	}
	if maxMsgSize == 0 {
		maxMsgSize = 4294967295
	}
	if readDeadline == 0 {
		readDeadline = 300
	}

	id, err := GenID()
	if err != nil {
		return nil, err
	}

	return &Conn{
		ID:           id,
		Data:         cmap.New(),
		conn:         conn,
		headerSize:   headerSize,
		maxMsgSize:   maxMsgSize,
		readDeadline: time.Second * time.Duration(readDeadline),
		debug:        debug,
	}, nil
}

// Dial creates a new client side connection to a server at the given network address,
// with optional CA and/or a client certificate (PEM encoded X.509 cert/key).
// Debug mode logs all raw TCP communication.
func Dial(addr string, ca []byte, clientCert []byte, clientCertKey []byte, debug bool) (*Conn, error) {
	var cas *x509.CertPool
	var certs []tls.Certificate
	if ca != nil {
		cas = x509.NewCertPool()
		ok := cas.AppendCertsFromPEM(ca)
		if !ok {
			return nil, errors.New("failed to parse the CA certificate")
		}
	}
	if clientCert != nil {
		tlsCert, err := tls.X509KeyPair(clientCert, clientCertKey)
		if err != nil {
			return nil, fmt.Errorf("failed to parse the client certificate: %v", err)
		}

		c, _ := pem.Decode(clientCert)
		if tlsCert.Leaf, err = x509.ParseCertificate(c.Bytes); err != nil {
			return nil, fmt.Errorf("failed to parse the client certificate: %v", err)
		}

		certs = []tls.Certificate{tlsCert}
	}

	// todo: dial timeout like that of net.Conn.DialTimeout
	c, err := tls.Dial("tcp", addr, &tls.Config{RootCAs: cas, Certificates: certs})
	if err != nil {
		return nil, err
	}

	return NewConn(c, 0, 0, 0, debug)
}

// SetReadDeadline set the read deadline for the connection in seconds.
func (c *Conn) SetReadDeadline(seconds int) {
	c.readDeadline = time.Second * time.Duration(seconds)
}

// Read waits for and reads the next incoming message from the TLS connection.
func (c *Conn) Read() (msg []byte, err error) {
	if err = c.conn.SetReadDeadline(time.Now().Add(c.readDeadline)); err != nil {
		return
	}

	// read the content length header
	h := make([]byte, c.headerSize)
	var n int
	n, err = c.conn.Read(h)
	if err != nil {
		return
	}
	if n != c.headerSize {
		err = fmt.Errorf("expected to read header size %v bytes but instead read %v bytes", c.headerSize, n)
		return
	}

	// calculate the content length
	n = readHeaderBytes(h)

	// read the message content
	msg = make([]byte, n)
	total := 0
	for total < n {
		// todo: log here in case it gets stuck, or there is a dos attack, pumping up cpu usage!
		i, err := c.conn.Read(msg[total:])
		if err != nil {
			err = fmt.Errorf("errored while reading incoming message: %v", err)
			break
		}
		total += i
	}
	if total != n {
		err = fmt.Errorf("expected to read %v bytes instead read %v bytes", n, total)
	}

	if c.debug {
		log.Println("Incoming message:", string(msg))
	}

	return
}

// Write writes given message to the connection.
func (c *Conn) Write(msg []byte) error {
	l := len(msg)
	h := makeHeaderBytes(l, c.headerSize)

	// write the header
	n, err := c.conn.Write(h)
	if err != nil {
		return err
	}
	if n != c.headerSize {
		err = fmt.Errorf("expected to write %v bytes but only wrote %v bytes", l, n)
	}

	// write the body
	// todo: do we need a loop? bufio uses a loop but it might be due to buff length limitation
	n, err = c.conn.Write(msg)
	if err != nil {
		return err
	}
	if n != l {
		err = fmt.Errorf("expected to write %v bytes but only wrote %v bytes", l, n)
	}

	return nil
}

// RemoteAddr returns the remote network address.
func (c *Conn) RemoteAddr() net.Addr {
	return c.conn.RemoteAddr()
}

// ConnectionState returns basic TLS details about the connection.
func (c *Conn) ConnectionState() tls.ConnectionState {
	return c.conn.ConnectionState()
}

// Close closes a connection.
// Note: TCP/IP stack does not guarantee delivery of messages before the connection is closed.
func (c *Conn) Close() error {
	return c.conn.Close() // todo: if conn.err is nil, send a close req and wait ack then close? (or even wait for everything else to finish?)
}

func makeHeaderBytes(h, size int) []byte {
	b := make([]byte, size)
	binary.LittleEndian.PutUint32(b, uint32(h))
	return b
}

func readHeaderBytes(h []byte) int {
	return int(binary.LittleEndian.Uint32(h))
}
<|endoftext|>


<commit_before>package cors

import "net/http"

const (
	options       string = "OPTIONS"
	allow_origin  string = "Access-Control-Allow-Origin"
	allow_methods string = "Access-Control-Allow-Methods"
	allow_headers string = "Access-Control-Allow-Headers"
	origin        string = "Origin"
	methods       string = "POST, GET, OPTIONS, PUT, DELETE, HEAD, PATCH"
	headers       string = "Accept, Accept-Encoding, Authorization, Content-Length, Content-Type, X-CSRF-Token"
)

type corsHandler struct {
	h http.Handler
}

func CORS() func(http.Handler) http.Handler {
	return func(h http.Handler) http.Handler {
		return &corsHandler{h}
	}
}

func (c *corsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if o := r.Header.Get(origin); o != "" {
		w.Header().Set(allow_origin, o)
	} else {
		w.Header().Set(allow_origin, "*")
	}

	w.Header().Set(allow_headers, headers)
	w.Header().Set(allow_methods, methods)

	if r.Method == options {
		w.WriteHeader(http.StatusOK)
		w.Write(nil)
		return
	}

	c.h.ServeHTTP(w, r)
}
<commit_msg>Added allow credentials<commit_after>package cors

import "net/http"

const (
	options           string = "OPTIONS"
	allow_origin      string = "Access-Control-Allow-Origin"
	allow_methods     string = "Access-Control-Allow-Methods"
	allow_headers     string = "Access-Control-Allow-Headers"
	allow_credentials string = "Access-Control-Allow-Credentials"
	credentials       string = "true"
	origin            string = "Origin"
	methods           string = "POST, GET, OPTIONS, PUT, DELETE, HEAD, PATCH"
	headers           string = "Accept, Accept-Encoding, Authorization, Content-Length, Content-Type, X-CSRF-Token"
)

type corsHandler struct {
	h http.Handler
}

func CORS() func(http.Handler) http.Handler {
	return func(h http.Handler) http.Handler {
		return &corsHandler{h}
	}
}

func (c *corsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if o := r.Header.Get(origin); o != "" {
		w.Header().Set(allow_origin, o)
	} else {
		w.Header().Set(allow_origin, "*")
	}

	w.Header().Set(allow_headers, headers)
	w.Header().Set(allow_credentials, credentials)
	w.Header().Set(allow_methods, methods)

	if r.Method == options {
		w.WriteHeader(http.StatusOK)
		w.Write(nil)
		return
	}

	c.h.ServeHTTP(w, r)
}
<|endoftext|>


<commit_before>package handlers

import (
	"net/http"
	"strconv"
	"strings"
)

// CORSOption represents a functional option for configuring the CORS middleware.
type CORSOption func(*cors) error

type cors struct {
	h                      http.Handler
	allowedHeaders         []string
	allowedMethods         []string
	allowedOrigins         []string
	allowedOriginValidator OriginValidator
	exposedHeaders         []string
	maxAge                 int
	ignoreOptions          bool
	allowCredentials       bool
}

// OriginValidator takes an origin string and returns whether or not that origin is allowed.
type OriginValidator func(string) bool

var (
	defaultCorsMethods = []string{"GET", "HEAD", "POST"}
	defaultCorsHeaders = []string{"Accept", "Accept-Language", "Content-Language", "Origin"}
	// (WebKit/Safari v9 sends the Origin header by default in AJAX requests)
)

const (
	corsOptionMethod           string = "OPTIONS"
	corsAllowOriginHeader      string = "Access-Control-Allow-Origin"
	corsExposeHeadersHeader    string = "Access-Control-Expose-Headers"
	corsMaxAgeHeader           string = "Access-Control-Max-Age"
	corsAllowMethodsHeader     string = "Access-Control-Allow-Methods"
	corsAllowHeadersHeader     string = "Access-Control-Allow-Headers"
	corsAllowCredentialsHeader string = "Access-Control-Allow-Credentials"
	corsRequestMethodHeader    string = "Access-Control-Request-Method"
	corsRequestHeadersHeader   string = "Access-Control-Request-Headers"
	corsOriginHeader           string = "Origin"
	corsVaryHeader             string = "Vary"
	corsOriginMatchAll         string = "*"
)

func (ch *cors) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	origin := r.Header.Get(corsOriginHeader)
	if !ch.isOriginAllowed(origin) {
		if r.Method != corsOptionMethod || ch.ignoreOptions {
			ch.h.ServeHTTP(w, r)
		}

		return
	}

	if r.Method == corsOptionMethod {
		if ch.ignoreOptions {
			ch.h.ServeHTTP(w, r)
			return
		}

		if _, ok := r.Header[corsRequestMethodHeader]; !ok {
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		method := r.Header.Get(corsRequestMethodHeader)
		if !ch.isMatch(method, ch.allowedMethods) {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}

		requestHeaders := strings.Split(r.Header.Get(corsRequestHeadersHeader), ",")
		allowedHeaders := []string{}
		for _, v := range requestHeaders {
			canonicalHeader := http.CanonicalHeaderKey(strings.TrimSpace(v))
			if canonicalHeader == "" || ch.isMatch(canonicalHeader, defaultCorsHeaders) {
				continue
			}

			if !ch.isMatch(canonicalHeader, ch.allowedHeaders) {
				w.WriteHeader(http.StatusForbidden)
				return
			}

			allowedHeaders = append(allowedHeaders, canonicalHeader)
		}

		if len(allowedHeaders) > 0 {
			w.Header().Set(corsAllowHeadersHeader, strings.Join(allowedHeaders, ","))
		}

		if ch.maxAge > 0 {
			w.Header().Set(corsMaxAgeHeader, strconv.Itoa(ch.maxAge))
		}

		if !ch.isMatch(method, defaultCorsMethods) {
			w.Header().Set(corsAllowMethodsHeader, method)
		}
	} else {
		if len(ch.exposedHeaders) > 0 {
			w.Header().Set(corsExposeHeadersHeader, strings.Join(ch.exposedHeaders, ","))
		}
	}

	if ch.allowCredentials {
		w.Header().Set(corsAllowCredentialsHeader, "true")
	}

	if len(ch.allowedOrigins) > 1 {
		w.Header().Set(corsVaryHeader, corsOriginHeader)
	}

	returnOrigin := origin
	if ch.allowedOriginValidator == nil && len(ch.allowedOrigins) == 0 {
		returnOrigin = "*"
	} else {
		for _, o := range ch.allowedOrigins {
			// A configuration of * is different than explicitly setting an allowed
			// origin. Returning arbitrary origin headers an an access control allow
			// origin header is unsafe and is not required by any use case.
			if o == corsOriginMatchAll {
				returnOrigin = "*"
				break
			}
		}
	}
	w.Header().Set(corsAllowOriginHeader, returnOrigin)

	if r.Method == corsOptionMethod {
		return
	}
	ch.h.ServeHTTP(w, r)
}

// CORS provides Cross-Origin Resource Sharing middleware.
// Example:
//
//  import (
//      "net/http"
//
//      "github.com/gorilla/handlers"
//      "github.com/gorilla/mux"
//  )
//
//  func main() {
//      r := mux.NewRouter()
//      r.HandleFunc("/users", UserEndpoint)
//      r.HandleFunc("/projects", ProjectEndpoint)
//
//      // Apply the CORS middleware to our top-level router, with the defaults.
//      http.ListenAndServe(":8000", handlers.CORS()(r))
//  }
//
func CORS(opts ...CORSOption) func(http.Handler) http.Handler {
	return func(h http.Handler) http.Handler {
		ch := parseCORSOptions(opts...)
		ch.h = h
		return ch
	}
}

func parseCORSOptions(opts ...CORSOption) *cors {
	ch := &cors{
		allowedMethods: defaultCorsMethods,
		allowedHeaders: defaultCorsHeaders,
		allowedOrigins: []string{},
	}

	for _, option := range opts {
		option(ch)
	}

	return ch
}

//
// Functional options for configuring CORS.
//

// AllowedHeaders adds the provided headers to the list of allowed headers in a
// CORS request.
// This is an append operation so the headers Accept, Accept-Language,
// and Content-Language are always allowed.
// Content-Type must be explicitly declared if accepting Content-Types other than
// application/x-www-form-urlencoded, multipart/form-data, or text/plain.
func AllowedHeaders(headers []string) CORSOption {
	return func(ch *cors) error {
		for _, v := range headers {
			normalizedHeader := http.CanonicalHeaderKey(strings.TrimSpace(v))
			if normalizedHeader == "" {
				continue
			}

			if !ch.isMatch(normalizedHeader, ch.allowedHeaders) {
				ch.allowedHeaders = append(ch.allowedHeaders, normalizedHeader)
			}
		}

		return nil
	}
}

// AllowedMethods can be used to explicitly allow methods in the
// Access-Control-Allow-Methods header.
// This is a replacement operation so you must also
// pass GET, HEAD, and POST if you wish to support those methods.
func AllowedMethods(methods []string) CORSOption {
	return func(ch *cors) error {
		ch.allowedMethods = []string{}
		for _, v := range methods {
			normalizedMethod := strings.ToUpper(strings.TrimSpace(v))
			if normalizedMethod == "" {
				continue
			}

			if !ch.isMatch(normalizedMethod, ch.allowedMethods) {
				ch.allowedMethods = append(ch.allowedMethods, normalizedMethod)
			}
		}

		return nil
	}
}

// AllowedOrigins sets the allowed origins for CORS requests, as used in the
// 'Allow-Access-Control-Origin' HTTP header.
// Note: Passing in a []string{"*"} will allow any domain.
func AllowedOrigins(origins []string) CORSOption {
	return func(ch *cors) error {
		for _, v := range origins {
			if v == corsOriginMatchAll {
				ch.allowedOrigins = []string{corsOriginMatchAll}
				return nil
			}
		}

		ch.allowedOrigins = origins
		return nil
	}
}

// AllowedOriginValidator sets a function for evaluating allowed origins in CORS requests, represented by the
// 'Allow-Access-Control-Origin' HTTP header.
func AllowedOriginValidator(fn OriginValidator) CORSOption {
	return func(ch *cors) error {
		ch.allowedOriginValidator = fn
		return nil
	}
}

// ExposeHeaders can be used to specify headers that are available
// and will not be stripped out by the user-agent.
func ExposedHeaders(headers []string) CORSOption {
	return func(ch *cors) error {
		ch.exposedHeaders = []string{}
		for _, v := range headers {
			normalizedHeader := http.CanonicalHeaderKey(strings.TrimSpace(v))
			if normalizedHeader == "" {
				continue
			}

			if !ch.isMatch(normalizedHeader, ch.exposedHeaders) {
				ch.exposedHeaders = append(ch.exposedHeaders, normalizedHeader)
			}
		}

		return nil
	}
}

// MaxAge determines the maximum age (in seconds) between preflight requests. A
// maximum of 10 minutes is allowed. An age above this value will default to 10
// minutes.
func MaxAge(age int) CORSOption {
	return func(ch *cors) error {
		// Maximum of 10 minutes.
		if age > 600 {
			age = 600
		}

		ch.maxAge = age
		return nil
	}
}

// IgnoreOptions causes the CORS middleware to ignore OPTIONS requests, instead
// passing them through to the next handler. This is useful when your application
// or framework has a pre-existing mechanism for responding to OPTIONS requests.
func IgnoreOptions() CORSOption {
	return func(ch *cors) error {
		ch.ignoreOptions = true
		return nil
	}
}

// AllowCredentials can be used to specify that the user agent may pass
// authentication details along with the request.
func AllowCredentials() CORSOption {
	return func(ch *cors) error {
		ch.allowCredentials = true
		return nil
	}
}

func (ch *cors) isOriginAllowed(origin string) bool {
	if origin == "" {
		return false
	}

	if ch.allowedOriginValidator != nil {
		return ch.allowedOriginValidator(origin)
	}

	if len(ch.allowedOrigins) == 0 {
		return true
	}

	for _, allowedOrigin := range ch.allowedOrigins {
		if allowedOrigin == origin || allowedOrigin == corsOriginMatchAll {
			return true
		}
	}

	return false
}

func (ch *cors) isMatch(needle string, haystack []string) bool {
	for _, v := range haystack {
		if v == needle {
			return true
		}
	}

	return false
}
<commit_msg>Fix typo in cors.go (#127)<commit_after>package handlers

import (
	"net/http"
	"strconv"
	"strings"
)

// CORSOption represents a functional option for configuring the CORS middleware.
type CORSOption func(*cors) error

type cors struct {
	h                      http.Handler
	allowedHeaders         []string
	allowedMethods         []string
	allowedOrigins         []string
	allowedOriginValidator OriginValidator
	exposedHeaders         []string
	maxAge                 int
	ignoreOptions          bool
	allowCredentials       bool
}

// OriginValidator takes an origin string and returns whether or not that origin is allowed.
type OriginValidator func(string) bool

var (
	defaultCorsMethods = []string{"GET", "HEAD", "POST"}
	defaultCorsHeaders = []string{"Accept", "Accept-Language", "Content-Language", "Origin"}
	// (WebKit/Safari v9 sends the Origin header by default in AJAX requests)
)

const (
	corsOptionMethod           string = "OPTIONS"
	corsAllowOriginHeader      string = "Access-Control-Allow-Origin"
	corsExposeHeadersHeader    string = "Access-Control-Expose-Headers"
	corsMaxAgeHeader           string = "Access-Control-Max-Age"
	corsAllowMethodsHeader     string = "Access-Control-Allow-Methods"
	corsAllowHeadersHeader     string = "Access-Control-Allow-Headers"
	corsAllowCredentialsHeader string = "Access-Control-Allow-Credentials"
	corsRequestMethodHeader    string = "Access-Control-Request-Method"
	corsRequestHeadersHeader   string = "Access-Control-Request-Headers"
	corsOriginHeader           string = "Origin"
	corsVaryHeader             string = "Vary"
	corsOriginMatchAll         string = "*"
)

func (ch *cors) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	origin := r.Header.Get(corsOriginHeader)
	if !ch.isOriginAllowed(origin) {
		if r.Method != corsOptionMethod || ch.ignoreOptions {
			ch.h.ServeHTTP(w, r)
		}

		return
	}

	if r.Method == corsOptionMethod {
		if ch.ignoreOptions {
			ch.h.ServeHTTP(w, r)
			return
		}

		if _, ok := r.Header[corsRequestMethodHeader]; !ok {
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		method := r.Header.Get(corsRequestMethodHeader)
		if !ch.isMatch(method, ch.allowedMethods) {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}

		requestHeaders := strings.Split(r.Header.Get(corsRequestHeadersHeader), ",")
		allowedHeaders := []string{}
		for _, v := range requestHeaders {
			canonicalHeader := http.CanonicalHeaderKey(strings.TrimSpace(v))
			if canonicalHeader == "" || ch.isMatch(canonicalHeader, defaultCorsHeaders) {
				continue
			}

			if !ch.isMatch(canonicalHeader, ch.allowedHeaders) {
				w.WriteHeader(http.StatusForbidden)
				return
			}

			allowedHeaders = append(allowedHeaders, canonicalHeader)
		}

		if len(allowedHeaders) > 0 {
			w.Header().Set(corsAllowHeadersHeader, strings.Join(allowedHeaders, ","))
		}

		if ch.maxAge > 0 {
			w.Header().Set(corsMaxAgeHeader, strconv.Itoa(ch.maxAge))
		}

		if !ch.isMatch(method, defaultCorsMethods) {
			w.Header().Set(corsAllowMethodsHeader, method)
		}
	} else {
		if len(ch.exposedHeaders) > 0 {
			w.Header().Set(corsExposeHeadersHeader, strings.Join(ch.exposedHeaders, ","))
		}
	}

	if ch.allowCredentials {
		w.Header().Set(corsAllowCredentialsHeader, "true")
	}

	if len(ch.allowedOrigins) > 1 {
		w.Header().Set(corsVaryHeader, corsOriginHeader)
	}

	returnOrigin := origin
	if ch.allowedOriginValidator == nil && len(ch.allowedOrigins) == 0 {
		returnOrigin = "*"
	} else {
		for _, o := range ch.allowedOrigins {
			// A configuration of * is different than explicitly setting an allowed
			// origin. Returning arbitrary origin headers in an access control allow
			// origin header is unsafe and is not required by any use case.
			if o == corsOriginMatchAll {
				returnOrigin = "*"
				break
			}
		}
	}
	w.Header().Set(corsAllowOriginHeader, returnOrigin)

	if r.Method == corsOptionMethod {
		return
	}
	ch.h.ServeHTTP(w, r)
}

// CORS provides Cross-Origin Resource Sharing middleware.
// Example:
//
//  import (
//      "net/http"
//
//      "github.com/gorilla/handlers"
//      "github.com/gorilla/mux"
//  )
//
//  func main() {
//      r := mux.NewRouter()
//      r.HandleFunc("/users", UserEndpoint)
//      r.HandleFunc("/projects", ProjectEndpoint)
//
//      // Apply the CORS middleware to our top-level router, with the defaults.
//      http.ListenAndServe(":8000", handlers.CORS()(r))
//  }
//
func CORS(opts ...CORSOption) func(http.Handler) http.Handler {
	return func(h http.Handler) http.Handler {
		ch := parseCORSOptions(opts...)
		ch.h = h
		return ch
	}
}

func parseCORSOptions(opts ...CORSOption) *cors {
	ch := &cors{
		allowedMethods: defaultCorsMethods,
		allowedHeaders: defaultCorsHeaders,
		allowedOrigins: []string{},
	}

	for _, option := range opts {
		option(ch)
	}

	return ch
}

//
// Functional options for configuring CORS.
//

// AllowedHeaders adds the provided headers to the list of allowed headers in a
// CORS request.
// This is an append operation so the headers Accept, Accept-Language,
// and Content-Language are always allowed.
// Content-Type must be explicitly declared if accepting Content-Types other than
// application/x-www-form-urlencoded, multipart/form-data, or text/plain.
func AllowedHeaders(headers []string) CORSOption {
	return func(ch *cors) error {
		for _, v := range headers {
			normalizedHeader := http.CanonicalHeaderKey(strings.TrimSpace(v))
			if normalizedHeader == "" {
				continue
			}

			if !ch.isMatch(normalizedHeader, ch.allowedHeaders) {
				ch.allowedHeaders = append(ch.allowedHeaders, normalizedHeader)
			}
		}

		return nil
	}
}

// AllowedMethods can be used to explicitly allow methods in the
// Access-Control-Allow-Methods header.
// This is a replacement operation so you must also
// pass GET, HEAD, and POST if you wish to support those methods.
func AllowedMethods(methods []string) CORSOption {
	return func(ch *cors) error {
		ch.allowedMethods = []string{}
		for _, v := range methods {
			normalizedMethod := strings.ToUpper(strings.TrimSpace(v))
			if normalizedMethod == "" {
				continue
			}

			if !ch.isMatch(normalizedMethod, ch.allowedMethods) {
				ch.allowedMethods = append(ch.allowedMethods, normalizedMethod)
			}
		}

		return nil
	}
}

// AllowedOrigins sets the allowed origins for CORS requests, as used in the
// 'Allow-Access-Control-Origin' HTTP header.
// Note: Passing in a []string{"*"} will allow any domain.
func AllowedOrigins(origins []string) CORSOption {
	return func(ch *cors) error {
		for _, v := range origins {
			if v == corsOriginMatchAll {
				ch.allowedOrigins = []string{corsOriginMatchAll}
				return nil
			}
		}

		ch.allowedOrigins = origins
		return nil
	}
}

// AllowedOriginValidator sets a function for evaluating allowed origins in CORS requests, represented by the
// 'Allow-Access-Control-Origin' HTTP header.
func AllowedOriginValidator(fn OriginValidator) CORSOption {
	return func(ch *cors) error {
		ch.allowedOriginValidator = fn
		return nil
	}
}

// ExposeHeaders can be used to specify headers that are available
// and will not be stripped out by the user-agent.
func ExposedHeaders(headers []string) CORSOption {
	return func(ch *cors) error {
		ch.exposedHeaders = []string{}
		for _, v := range headers {
			normalizedHeader := http.CanonicalHeaderKey(strings.TrimSpace(v))
			if normalizedHeader == "" {
				continue
			}

			if !ch.isMatch(normalizedHeader, ch.exposedHeaders) {
				ch.exposedHeaders = append(ch.exposedHeaders, normalizedHeader)
			}
		}

		return nil
	}
}

// MaxAge determines the maximum age (in seconds) between preflight requests. A
// maximum of 10 minutes is allowed. An age above this value will default to 10
// minutes.
func MaxAge(age int) CORSOption {
	return func(ch *cors) error {
		// Maximum of 10 minutes.
		if age > 600 {
			age = 600
		}

		ch.maxAge = age
		return nil
	}
}

// IgnoreOptions causes the CORS middleware to ignore OPTIONS requests, instead
// passing them through to the next handler. This is useful when your application
// or framework has a pre-existing mechanism for responding to OPTIONS requests.
func IgnoreOptions() CORSOption {
	return func(ch *cors) error {
		ch.ignoreOptions = true
		return nil
	}
}

// AllowCredentials can be used to specify that the user agent may pass
// authentication details along with the request.
func AllowCredentials() CORSOption {
	return func(ch *cors) error {
		ch.allowCredentials = true
		return nil
	}
}

func (ch *cors) isOriginAllowed(origin string) bool {
	if origin == "" {
		return false
	}

	if ch.allowedOriginValidator != nil {
		return ch.allowedOriginValidator(origin)
	}

	if len(ch.allowedOrigins) == 0 {
		return true
	}

	for _, allowedOrigin := range ch.allowedOrigins {
		if allowedOrigin == origin || allowedOrigin == corsOriginMatchAll {
			return true
		}
	}

	return false
}

func (ch *cors) isMatch(needle string, haystack []string) bool {
	for _, v := range haystack {
		if v == needle {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>package cqlb

import (
	"fmt"
	"reflect"
	"strings"

	"github.com/gocql/gocql"
	"github.com/jinzhu/inflection"
)

type fieldTag struct {
	Name      string
	OmitEmpty bool
}

type Session struct {
	*gocql.Session
}

func SetSession(s *gocql.Session) *Session {
	return &Session{s}
}

func (s *Session) Insert(v interface{}) error {
	f := fields(v)
	stmt := insertQuery(f)
	return s.Query(stmt, f["values"]).Exec()
}

func insertQuery(f map[string]interface{}) string {
	query := fmt.Sprintf("insert into %s (%s) values(%s)", f["table_name"], f["names"], f["slots"])
	return query
}

func compile(v interface{}, cols []gocql.ColumnInfo) error {

	return nil
}

func tag(f reflect.StructField) *fieldTag {
	ft := &fieldTag{}
	tag := f.Tag.Get("cql")
	opts := strings.Split(tag, ",")
	ft.Name = opts[0]
	if len(opts) > 1 && opts[0] == "omitempty" {
		ft.OmitEmpty = true
	}
	return ft
}

func fields(v interface{}) map[string]interface{} {
	var names string
	var slots string
	var values []interface{}
	result := make(map[string]interface{})
	value := reflect.ValueOf(v)
	indirect := reflect.Indirect(value)
	t := indirect.Type()
	result["table_name"] = inflection.Plural(strings.ToLower(t.Name()))
	for i := 0; i < t.NumField(); i++ {
		var inf interface{}
		f := t.Field(i)
		fv := indirect.Field(i)
		tag := tag(f)
		if fv.IsValid() == false && tag.OmitEmpty == true {
			continue
		}
		fvIndirect := reflect.Indirect(fv)
		inf = fvIndirect.Interface()
		if i != 0 {
			names += ","
			slots += ","
		}
		if tag.Name != "" {
			names += tag.Name
		} else {
			names += strings.ToLower(f.Name)
		}
		slots += "?"
		values = append(values, inf)
	}
	result["names"] = names
	result["values"] = values
	result["slots"] = slots
	return result
}

func contentOfSlice(v reflect.Value) []interface{} {
	slice := make([]interface{}, v.Len())
	for i := 0; i < v.Len(); i++ {
		f := reflect.Indirect(v.Index(i))
		slice[i] = f.Interface()
	}
	return slice
}

func getType(v interface{}) {

}
<commit_msg>Moved insert query to a constant and added ';' at the end.<commit_after>package cqlb

import (
	"fmt"
	"reflect"
	"strings"

	"github.com/gocql/gocql"
	"github.com/jinzhu/inflection"
)

const (
	insertQueryTemplate = "insert into %s (%s) values(%s);"
)

type fieldTag struct {
	Name      string
	OmitEmpty bool
}

type Session struct {
	*gocql.Session
}

func SetSession(s *gocql.Session) *Session {
	return &Session{s}
}

func (s *Session) Insert(v interface{}) error {
	f := fields(v)
	stmt := insertQuery(f)
	return s.Query(stmt, f["values"]).Exec()
}

func insertQuery(f map[string]interface{}) string {
	query := fmt.Sprintf(insertQueryTemplate, f["table_name"], f["names"], f["slots"])
	return query
}

func compile(v interface{}, cols []gocql.ColumnInfo) error {

	return nil
}

func tag(f reflect.StructField) *fieldTag {
	ft := &fieldTag{}
	tag := f.Tag.Get("cql")
	opts := strings.Split(tag, ",")
	ft.Name = opts[0]
	if len(opts) > 1 && opts[0] == "omitempty" {
		ft.OmitEmpty = true
	}
	return ft
}

func fields(v interface{}) map[string]interface{} {
	var names string
	var slots string
	var values []interface{}
	result := make(map[string]interface{})
	value := reflect.ValueOf(v)
	indirect := reflect.Indirect(value)
	t := indirect.Type()
	result["table_name"] = inflection.Plural(strings.ToLower(t.Name()))
	for i := 0; i < t.NumField(); i++ {
		var inf interface{}
		f := t.Field(i)
		fv := indirect.Field(i)
		tag := tag(f)
		if fv.IsValid() == false && tag.OmitEmpty == true {
			continue
		}
		fvIndirect := reflect.Indirect(fv)
		inf = fvIndirect.Interface()
		if i != 0 {
			names += ","
			slots += ","
		}
		if tag.Name != "" {
			names += tag.Name
		} else {
			names += strings.ToLower(f.Name)
		}
		slots += "?"
		values = append(values, inf)
	}
	result["names"] = names
	result["values"] = values
	result["slots"] = slots
	return result
}

func contentOfSlice(v reflect.Value) []interface{} {
	slice := make([]interface{}, v.Len())
	for i := 0; i < v.Len(); i++ {
		f := reflect.Indirect(v.Index(i))
		slice[i] = f.Interface()
	}
	return slice
}

func getType(v interface{}) {

}
<|endoftext|>