<commit_before>package meshutil

import (
	"encoding/binary"
	"bufio"
	"bytes"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
)

// An intermediate representation for 3d vectors
type Vector3 struct {
	x, y, z float64
}

type FaceVert struct {
	positionIndex, texcoIndex, normalIndex int
}

type Polygon struct {
	verts []FaceVert
}

type Mesh struct {
	Name string
	Positions, Texcos, Normals []Vector3
	Polygons []Polygon
}

//
type vertex struct {
	position, normal Vector3
	color0, color1 Vector3
	texco0, texco1 Vector3
	tangent, binormal Vector3
}

type triangle struct {
	vertices [3]vertex
}

type triangleMesh struct {
	vertexAttribNames []VertexAttribName
	triangles []triangle
}

func (m *Mesh) toTriangleMesh() *triangleMesh {
	trimesh := new(triangleMesh)
	has_position := len(m.Positions) > 0
	if has_position {
		trimesh.vertexAttribNames = append(trimesh.vertexAttribNames, Position)
	}
	has_normal := len(m.Normals) > 0
	if has_position {
		trimesh.vertexAttribNames = append(trimesh.vertexAttribNames, Normal)
	}
	has_texco := len(m.Texcos) > 0
	if has_texco {
		trimesh.vertexAttribNames = append(trimesh.vertexAttribNames, Texco0)
	}

	for _, p := range m.Polygons {
		var poly []vertex
		for _, f := range p.verts {
			var newVert vertex
			if has_position {
				newVert.position = m.Positions[f.positionIndex]
			}
			if has_normal {
				newVert.normal = m.Normals[f.normalIndex]
			}
			if has_texco {
				newVert.texco0 = m.Texcos[f.texcoIndex]
			}
			poly = append(poly, newVert)
		}

		switch (len(poly)) {
		case 4:
			var t, u triangle
			t.vertices[0] = poly[0]
			t.vertices[1] = poly[1]
			t.vertices[2] = poly[2]
			u.vertices[0] = poly[0]
			u.vertices[1] = poly[2]
			u.vertices[2] = poly[3]
			trimesh.triangles = append(trimesh.triangles, t, u)
		case 3:
			var t triangle
			t.vertices[0] = poly[0]
			t.vertices[1] = poly[1]
			t.vertices[2] = poly[2]
			trimesh.triangles = append(trimesh.triangles, t)
		}
	}

	return trimesh
}

func contains(vertices []vertex, vertex vertex) (bool, int) {
    for i, v := range vertices {
	    if v == vertex { return true, i }
    }
    return false, len(vertices)
}

func (m *triangleMesh) makeBuffers() (vertices []vertex, indices []int) {
	for _, tri := range m.triangles {
		for _, vert := range tri.vertices {
			alreadyIn, idx := contains(vertices, vert)
			if !alreadyIn {
				vertices = append(vertices, vert)
			}
			indices = append(indices, idx)
		}
	}

	return
}

type glHeader struct {
	name [16]byte
	vertAttribCount, vertAttribOffset uint32
	surfDescCount, surfDescOffset uint32
	vertCount, vertDataOffset, vertDataSize uint32
	indCount, indDataOffset, indDataSize uint32
	aabbCenter, aabbExtent [3]float32
}

type VertexAttribName uint32
const (
	Position VertexAttribName = iota
        Normal
        Color
	Texco0
	Texco1
	TangentDet
)

type VertexAttribType uint32
const (
	Float32 VertexAttribType = iota
	Int32
	Int16
	Uint16
	Int8
	Uint8
)

/*
type VertexAttrib struct {
	Name VertexAttribName
	Count int
}
*/

type VertexAttribFormat struct {
	Type VertexAttribType
	ByteSize int
	MaxValue uint // used for normalized attributes
}

var attribFormat = [...]VertexAttribFormat{
	{Float32, 4, uint(^uint32(0))},
	{Int32, 4, uint(^uint32(0) >> 1) - 1},
	{Int16, 2, uint(^uint16(0) >> 1) - 1},
	{Uint16, 2, uint(^uint16(0)) - 1},
	{Int8, 1, uint(^uint8(0) >> 1) -1},
	{Uint8, 1, uint(^uint8(0)) - 1},
}

// TODO: rename to VertexAttribDesc
type VertexAttribDesc struct {
	Name VertexAttribName
	Count int
	Type VertexAttribType
	Normalized bool
}

var attribsGL3 = [...]VertexAttribDesc{
	{Position, 3, Float32, false},
	{Normal, 3, Int16, true},
	{Color, 3, Uint8, true},
	{Texco0, 2, Float32, false},
}

// struct describing what composes a vertex
type glVertexAttrib struct {
	Index, Count, Type, Normalized uint32
	Stride, Offset uint32
}

func (v *Vector3) format(format VertexAttribDesc) []byte {
	buf := new(bytes.Buffer)
	switch (format.Type) {
	case Float32:
		p := [3]float32{ float32(v.x), float32(v.y), float32(v.z), }
		binary.Write(buf, binary.LittleEndian, p)
	case Int16:
		var l float64
		if format.Normalized { l = 4 } else { l = 1 }
		p := [3]int16{ int16(v.x/l), int16(v.y/l), int16(v.z/l) }
		binary.Write(buf, binary.LittleEndian, p)
	default:
		panic("oops")
	}
	return buf.Bytes()
}

func (m *Mesh) WriteOpenGL(filename string) {
	file, _ := os.Create(filename)
	defer file.Close()

	trimesh := m.toTriangleMesh()

	fmt.Println("Vertex attributes: ", trimesh.vertexAttribNames)
	offset := 0
	var glAttribs []glVertexAttrib
	for _, va := range trimesh.vertexAttribNames {
		desc := attribsGL3[va]
		fmt.Printf("%+v\n", attribsGL3[va])
		normalized := 0
		if desc.Normalized {
			normalized = 1
		}
		glDesc := glVertexAttrib{
			Index: uint32(desc.Name),
			Count: uint32(desc.Count),
			Type: uint32(desc.Type),
			Normalized: uint32(normalized),
			Offset: uint32(offset),
		}
		offset += binary.Size(glDesc)
		glAttribs = append(glAttribs, glDesc)
	}

	vadata := new(bytes.Buffer)
	for _, va := range glAttribs {
		// Stride was not computable at first
		va.Stride = uint32(offset)
		fmt.Printf("%+v\n", va)
		binary.Write(vadata, binary.LittleEndian, va)
	}

	vertices, indices := trimesh.makeBuffers()
	vdata := new(bytes.Buffer)
	for _, v := range vertices {
		binary.Write(vdata, binary.LittleEndian, v.position.format(attribsGL3[Position]))
		binary.Write(vdata, binary.LittleEndian, v.normal.format(attribsGL3[Normal]))
	}

	idata := new(bytes.Buffer)
	for _, i := range indices {
		binary.Write(idata, binary.LittleEndian, uint16(i))
	}

	var header glHeader
	headerSize := binary.Size(header)
	copy(header.name[:len(header.name)-1], m.Name)
	header.vertAttribCount = uint32(len(trimesh.vertexAttribNames))
	header.vertAttribOffset = uint32(headerSize)
	header.surfDescCount = 0
	header.surfDescOffset = header.vertAttribOffset + 0
	header.vertCount = uint32(len(vertices))
	header.vertDataOffset = uint32(headerSize)
	header.vertDataSize = uint32(vdata.Len())
	header.indCount = uint32(len(indices))
	header.indDataOffset = uint32(headerSize + vdata.Len())
	header.indDataSize = uint32(idata.Len())

	fmt.Printf("%+v\n", header)

	hdata := new(bytes.Buffer)
	binary.Write(hdata, binary.LittleEndian, header)

	file.Write(hdata.Bytes())
	file.Write(vadata.Bytes())
	file.Write(vdata.Bytes())
	file.Write(idata.Bytes())
}

func ParseObj(filename string) Mesh {
	file, _ := os.Open(filename)
	defer file.Close()

	mesh := Mesh{ Name: "untitled" }
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := strings.Split(scanner.Text(), " ")
		ident, val := line[0], line[1:]
		switch (ident) {
		case "v", "vn":
			x, _ := strconv.ParseFloat(val[0], 64)
			y, _ := strconv.ParseFloat(val[1], 64)
			z, _ := strconv.ParseFloat(val[2], 64)
			v := Vector3{ x, y, z }
			if (ident == "v") {
				mesh.Positions = append(mesh.Positions, v)
			} else {
				mesh.Normals = append(mesh.Normals, v)
			}
		case "vt":
			s, _ := strconv.ParseFloat(val[0], 64)
			t, _ := strconv.ParseFloat(val[1], 64)
			v := Vector3{ s, t, 0 }
			mesh.Texcos = append(mesh.Texcos, v)
		case "f":
			var p Polygon
			for _, s := range val {
				var pos, texco, norm int64
				idx := strings.Split(s, "/")
				pos, _ = strconv.ParseInt(idx[0], 0, 0)
				if len(idx) > 2 {
					texco, _ = strconv.ParseInt(idx[1], 0, 0)
				}
				norm, _ = strconv.ParseInt(idx[len(idx)-1], 0, 0)

				// compensate for indices from obj file starting at 1
				v := FaceVert{ int(pos)-1, int(texco)-1, int(norm)-1 }
				p.verts = append(p.verts, v)
			}
			mesh.Polygons = append(mesh.Polygons, p)
		default:
			fmt.Println(ident + " not parsed yet")
		}
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}

	return mesh
}
<commit_msg>Minor changes<commit_after>package meshutil

import (
	"encoding/binary"
	"bufio"
	"bytes"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
)

// An intermediate representation for 3d vectors
type Vector3 struct {
	x, y, z float64
}

type FaceVert struct {
	positionIndex, texcoIndex, normalIndex int
}

type Polygon struct {
	verts []FaceVert
}

type Mesh struct {
	Name string
	Positions, Texcos, Normals []Vector3
	Polygons []Polygon
}

//
type vertex struct {
	position, normal Vector3
	color0, color1 Vector3
	texco0, texco1 Vector3
	tangent, binormal Vector3
}

type triangle struct {
	vertices [3]vertex
}

type triangleMesh struct {
	vertexAttribNames []VertexAttribName
	triangles []triangle
}

func (m *Mesh) toTriangleMesh() *triangleMesh {
	trimesh := new(triangleMesh)
	has_position := len(m.Positions) > 0
	if has_position {
		trimesh.vertexAttribNames = append(trimesh.vertexAttribNames, Position)
	}
	has_normal := len(m.Normals) > 0
	if has_position {
		trimesh.vertexAttribNames = append(trimesh.vertexAttribNames, Normal)
	}
	has_texco := len(m.Texcos) > 0
	if has_texco {
		trimesh.vertexAttribNames = append(trimesh.vertexAttribNames, Texco0)
	}

	for _, p := range m.Polygons {
		var poly []vertex
		for _, f := range p.verts {
			var newVert vertex
			if has_position {
				newVert.position = m.Positions[f.positionIndex]
			}
			if has_normal {
				newVert.normal = m.Normals[f.normalIndex]
			}
			if has_texco {
				newVert.texco0 = m.Texcos[f.texcoIndex]
			}
			poly = append(poly, newVert)
		}

		switch (len(poly)) {
		case 4:
			var t, u triangle
			t.vertices[0] = poly[0]
			t.vertices[1] = poly[1]
			t.vertices[2] = poly[2]
			u.vertices[0] = poly[0]
			u.vertices[1] = poly[2]
			u.vertices[2] = poly[3]
			trimesh.triangles = append(trimesh.triangles, t, u)
		case 3:
			var t triangle
			t.vertices[0] = poly[0]
			t.vertices[1] = poly[1]
			t.vertices[2] = poly[2]
			trimesh.triangles = append(trimesh.triangles, t)
		}
	}

	return trimesh
}

func contains(vertices []vertex, vertex vertex) (bool, int) {
    for i, v := range vertices {
	    if v == vertex { return true, i }
    }
    return false, len(vertices)
}

func (m *triangleMesh) makeBuffers() (vertices []vertex, indices []int) {
	for _, tri := range m.triangles {
		for _, vert := range tri.vertices {
			alreadyIn, idx := contains(vertices, vert)
			if !alreadyIn {
				vertices = append(vertices, vert)
			}
			indices = append(indices, idx)
		}
	}

	return
}

type glHeader struct {
	name [16]byte
	vertAttribCount, vertAttribOffset uint32
	surfDescCount, surfDescOffset uint32
	vertCount, vertDataOffset, vertDataSize uint32
	indCount, indDataOffset, indDataSize uint32
	aabbCenter, aabbExtent [3]float32
}

type VertexAttribName uint32
const (
	Position VertexAttribName = iota
        Normal
        Color
	Texco0
	Texco1
	TangentDet
)

type VertexAttribType uint32
const (
	Float32 VertexAttribType = iota
	Int32
	Int16
	Uint16
	Int8
	Uint8
)

/*
type VertexAttrib struct {
	Name VertexAttribName
	Count int
}
*/

type VertexAttribFormat struct {
	Type VertexAttribType
	ByteSize int
	MaxValue uint // used for normalized attributes
}

var attribFormat = [...]VertexAttribFormat{
	{Float32, 4, uint(^uint32(0))},
	{Int32, 4, uint(^uint32(0) >> 1) - 1},
	{Int16, 2, uint(^uint16(0) >> 1) - 1},
	{Uint16, 2, uint(^uint16(0)) - 1},
	{Int8, 1, uint(^uint8(0) >> 1) -1},
	{Uint8, 1, uint(^uint8(0)) - 1},
}

// TODO: rename to VertexAttribDesc
type VertexAttribDesc struct {
	Name VertexAttribName
	Count int
	Type VertexAttribType
	Normalized bool
}

var attribsGL3 = [...]VertexAttribDesc{
	{Position, 3, Float32, false},
	{Normal, 3, Int16, true},
	{Color, 3, Uint8, true},
	{Texco0, 2, Float32, false},
}

// struct describing what composes a vertex
type BinaryVertexAttrib struct {
	Index, Count, Type, Normalized uint32
	Stride, Offset uint32
}

func (v *Vector3) format(format VertexAttribDesc) []byte {
	buf := new(bytes.Buffer)
	switch (format.Type) {
	case Float32:
		p := [3]float32{ float32(v.x), float32(v.y), float32(v.z), }
		binary.Write(buf, binary.LittleEndian, p)
	case Int16:
		var l float64
		if format.Normalized { l = 4 } else { l = 1 }
		p := [3]int16{ int16(v.x/l), int16(v.y/l), int16(v.z/l) }
		binary.Write(buf, binary.LittleEndian, p)
	default:
		panic("oops")
	}
	return buf.Bytes()
}

func (m *Mesh) WriteOpenGL(filename string) {
	file, _ := os.Create(filename)
	defer file.Close()

	trimesh := m.toTriangleMesh()

	fmt.Println("Vertex attributes: ", trimesh.vertexAttribNames)
	offset := 0
	var binAttribArray []BinaryVertexAttrib
	for _, va := range trimesh.vertexAttribNames {
		desc := attribsGL3[va]
		fmt.Printf("%+v\n", attribsGL3[va])
		normalized := 0
		if desc.Normalized {
			normalized = 1
		}
		binAttrib := BinaryVertexAttrib{
			Index: uint32(desc.Name),
			Count: uint32(desc.Count),
			Type: uint32(desc.Type),
			Normalized: uint32(normalized),
			Offset: uint32(offset),
		}
		binAttribArray = append(binAttribArray, binAttrib)
		offset += binary.Size(binAttrib)
	}

	vadata := new(bytes.Buffer)
	for _, va := range binAttribArray {
		// Stride was not computable at first
		va.Stride = uint32(offset)
		fmt.Printf("%+v\n", va)
		binary.Write(vadata, binary.LittleEndian, va)
	}

	vertices, indices := trimesh.makeBuffers()
	vdata := new(bytes.Buffer)
	for _, v := range vertices {
		binary.Write(vdata, binary.LittleEndian, v.position.format(attribsGL3[Position]))
		binary.Write(vdata, binary.LittleEndian, v.normal.format(attribsGL3[Normal]))
	}

	idata := new(bytes.Buffer)
	for _, i := range indices {
		binary.Write(idata, binary.LittleEndian, uint16(i))
	}

	var header glHeader
	headerSize := binary.Size(header)
	copy(header.name[:len(header.name)-1], m.Name)
	header.vertAttribCount = uint32(len(trimesh.vertexAttribNames))
	header.vertAttribOffset = uint32(headerSize)
	header.surfDescCount = 0
	header.surfDescOffset = header.vertAttribOffset + 0
	header.vertCount = uint32(len(vertices))
	header.vertDataOffset = uint32(headerSize)
	header.vertDataSize = uint32(vdata.Len())
	header.indCount = uint32(len(indices))
	header.indDataOffset = uint32(headerSize + vdata.Len())
	header.indDataSize = uint32(idata.Len())

	fmt.Printf("%+v\n", header)

	hdata := new(bytes.Buffer)
	binary.Write(hdata, binary.LittleEndian, header)

	file.Write(hdata.Bytes())
	file.Write(vadata.Bytes())
	file.Write(vdata.Bytes())
	file.Write(idata.Bytes())
}

func ParseObj(filename string) Mesh {
	file, _ := os.Open(filename)
	defer file.Close()

	mesh := Mesh{ Name: "untitled" }
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := strings.Split(scanner.Text(), " ")
		ident, val := line[0], line[1:]
		switch (ident) {
		case "v", "vn":
			x, _ := strconv.ParseFloat(val[0], 64)
			y, _ := strconv.ParseFloat(val[1], 64)
			z, _ := strconv.ParseFloat(val[2], 64)
			v := Vector3{ x, y, z }
			if (ident == "v") {
				mesh.Positions = append(mesh.Positions, v)
			} else {
				mesh.Normals = append(mesh.Normals, v)
			}
		case "vt":
			s, _ := strconv.ParseFloat(val[0], 64)
			t, _ := strconv.ParseFloat(val[1], 64)
			v := Vector3{ s, t, 0 }
			mesh.Texcos = append(mesh.Texcos, v)
		case "f":
			var p Polygon
			for _, s := range val {
				var pos, texco, norm int64
				idx := strings.Split(s, "/")
				pos, _ = strconv.ParseInt(idx[0], 0, 0)
				if len(idx) > 2 {
					texco, _ = strconv.ParseInt(idx[1], 0, 0)
				}
				norm, _ = strconv.ParseInt(idx[len(idx)-1], 0, 0)

				// compensate for indices from obj file starting at 1
				v := FaceVert{ int(pos)-1, int(texco)-1, int(norm)-1 }
				p.verts = append(p.verts, v)
			}
			mesh.Polygons = append(mesh.Polygons, p)
		default:
			fmt.Println(ident + " not parsed yet")
		}
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}

	return mesh
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/bitly/go-nsq"
	"github.com/bitly/nsq/util"
	"github.com/bmizerany/assert"
	"github.com/mreiferson/go-snappystream"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"testing"
	"time"
)

func TestStats(t *testing.T) {
	log.SetOutput(ioutil.Discard)
	defer log.SetOutput(os.Stdout)

	options := NewNSQDOptions()
	tcpAddr, _, nsqd := mustStartNSQD(options)
	defer nsqd.Exit()

	topicName := "test_stats" + strconv.Itoa(int(time.Now().Unix()))
	topic := nsqd.GetTopic(topicName)
	msg := nsq.NewMessage(<-nsqd.idChan, []byte("test body"))
	topic.PutMessage(msg)

	conn, err := mustConnectNSQD(tcpAddr)
	assert.Equal(t, err, nil)

	identify(t, conn)
	sub(t, conn, topicName, "ch")

	stats := nsqd.getStats()
	assert.Equal(t, len(stats), 1)
	assert.Equal(t, len(stats[0].Channels), 1)
	assert.Equal(t, len(stats[0].Channels[0].Clients), 1)
	log.Printf("stats: %+v", stats)
}

func TestClientAttributes(t *testing.T) {
	log.SetOutput(ioutil.Discard)
	defer log.SetOutput(os.Stdout)

	userAgent := "Test User Agent"

	*verbose = true
	options := NewNSQDOptions()
	options.SnappyEnabled = true
	tcpAddr, httpAddr, nsqd := mustStartNSQD(options)
	defer nsqd.Exit()

	conn, err := mustConnectNSQD(tcpAddr)
	assert.Equal(t, err, nil)

	data := identifyFeatureNegotiation(t, conn, map[string]interface{}{"snappy": true, "user_agent": userAgent})
	resp := struct {
		Snappy    bool   `json:"snappy"`
		UserAgent string `json:"user_agent"`
	}{}
	err = json.Unmarshal(data, &resp)
	assert.Equal(t, err, nil)
	assert.Equal(t, resp.Snappy, true)

	r := snappystream.NewReader(conn, snappystream.SkipVerifyChecksum)
	w := snappystream.NewWriter(conn)

	topicName := "test_client_attributes" + strconv.Itoa(int(time.Now().Unix()))
	sub(t, readWriter{r, w}, topicName, "ch")

	testUrl := fmt.Sprintf("http://127.0.0.1:%d/stats?format=json", httpAddr.Port)

	statsData, err := util.ApiRequest(testUrl)
	if err != nil {
		t.Fatalf(err.Error())
	}
	client := statsData.Get("topics").GetIndex(0).Get("channels").GetIndex(0).Get("clients").GetIndex(0)
	assert.Equal(t, client.Get("user_agent").MustString(), userAgent)
	assert.Equal(t, client.Get("snappy").MustBool(), true)
}
<commit_msg>nsqd: fix flakey TestClientAttributes<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/bitly/go-nsq"
	"github.com/bitly/nsq/util"
	"github.com/bmizerany/assert"
	"github.com/mreiferson/go-snappystream"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"testing"
	"time"
)

func TestStats(t *testing.T) {
	log.SetOutput(ioutil.Discard)
	defer log.SetOutput(os.Stdout)

	options := NewNSQDOptions()
	tcpAddr, _, nsqd := mustStartNSQD(options)
	defer nsqd.Exit()

	topicName := "test_stats" + strconv.Itoa(int(time.Now().Unix()))
	topic := nsqd.GetTopic(topicName)
	msg := nsq.NewMessage(<-nsqd.idChan, []byte("test body"))
	topic.PutMessage(msg)

	conn, err := mustConnectNSQD(tcpAddr)
	assert.Equal(t, err, nil)

	identify(t, conn)
	sub(t, conn, topicName, "ch")

	stats := nsqd.getStats()
	assert.Equal(t, len(stats), 1)
	assert.Equal(t, len(stats[0].Channels), 1)
	assert.Equal(t, len(stats[0].Channels[0].Clients), 1)
	log.Printf("stats: %+v", stats)
}

func TestClientAttributes(t *testing.T) {
	log.SetOutput(ioutil.Discard)
	defer log.SetOutput(os.Stdout)

	userAgent := "Test User Agent"

	*verbose = true
	options := NewNSQDOptions()
	options.SnappyEnabled = true
	tcpAddr, httpAddr, nsqd := mustStartNSQD(options)
	defer nsqd.Exit()

	conn, err := mustConnectNSQD(tcpAddr)
	assert.Equal(t, err, nil)

	data := identifyFeatureNegotiation(t, conn, map[string]interface{}{"snappy": true, "user_agent": userAgent})
	resp := struct {
		Snappy    bool   `json:"snappy"`
		UserAgent string `json:"user_agent"`
	}{}
	err = json.Unmarshal(data, &resp)
	assert.Equal(t, err, nil)
	assert.Equal(t, resp.Snappy, true)

	r := snappystream.NewReader(conn, snappystream.SkipVerifyChecksum)
	w := snappystream.NewWriter(conn)

	topicName := "test_client_attributes" + strconv.Itoa(int(time.Now().Unix()))
	sub(t, readWriter{r, w}, topicName, "ch")

	// need to give nsqd a chance to sub the client
	time.Sleep(50 * time.Millisecond)

	testUrl := fmt.Sprintf("http://127.0.0.1:%d/stats?format=json", httpAddr.Port)

	statsData, err := util.ApiRequest(testUrl)
	if err != nil {
		t.Fatalf(err.Error())
	}
	client := statsData.Get("topics").GetIndex(0).Get("channels").GetIndex(0).Get("clients").GetIndex(0)
	assert.Equal(t, client.Get("user_agent").MustString(), userAgent)
	assert.Equal(t, client.Get("snappy").MustBool(), true)
}
<|endoftext|>


<commit_before>// Copyright 2016 Tamás Gulácsi. All rights reserved.
// Use of this source code is governed by The MIT License
// found in the accompanying LICENSE file.

package num

import "strings"

//go:generate go-fuzz-build gopkg.in/rana/ora.v3/num

// Fuzz:
// go-fuzz -bin=./num-fuzz.zip -workdir=/tmp/fuzz
func Fuzz(p []byte) int {
	pS := string(p)
	var q [22]byte
	n := OCINum(q[:0])
	if err := n.SetString(pS); err != nil {
		return 0
	}
	s := n.String()
	if s != strings.TrimSpace(pS) {
		return 1
	}
	return 0
}
<commit_msg>use gofuzz tag<commit_after>// +build gofuzz

// Copyright 2016 Tamás Gulácsi. All rights reserved.
// Use of this source code is governed by The MIT License
// found in the accompanying LICENSE file.

package num

import "strings"

//go:generate go-fuzz-build gopkg.in/rana/ora.v3/num

// Fuzz:
// go-fuzz -bin=./num-fuzz.zip -workdir=/tmp/fuzz
func Fuzz(p []byte) int {
	pS := string(p)
	var q [22]byte
	n := OCINum(q[:0])
	if err := n.SetString(pS); err != nil {
		return -1
	}
	s := n.String()
	if s != strings.TrimSpace(pS) {
		return 1
	}
	return 0
}
<|endoftext|>


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

package v1

import "regexp"

// ImageLayoutVersion is the version of ImageLayout
const ImageLayoutVersion = "1.0.0"

// ImageLayout is the structure in the "oci-layout" file, found in the root
// of an OCI Image-layout directory.
type ImageLayout struct {
	Version string `json:"imageLayoutVersion"`
}

var (
	// RefsRegexp matches requirement of image-layout 'refs' charset.
	RefsRegexp = regexp.MustCompile(`^[a-zA-Z0-9-._]+$`)
)
<commit_msg>specs-go/v1/layout: Remove RefsRegexp<commit_after>// Copyright 2016 The Linux Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package v1

// ImageLayoutVersion is the version of ImageLayout
const ImageLayoutVersion = "1.0.0"

// ImageLayout is the structure in the "oci-layout" file, found in the root
// of an OCI Image-layout directory.
type ImageLayout struct {
	Version string `json:"imageLayoutVersion"`
}
<|endoftext|>


<commit_before>/**
 * @file scoreboard.go
 * @author Mikhail Klementyev jollheef<AT>riseup.net
 * @license GNU AGPLv3
 * @date November, 2015
 * @brief web scoreboard
 *
 * Contain web ui and several helpers for convert results to table
 */

package scoreboard

import (
	"database/sql"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"time"

	"github.com/jollheef/henhouse/game"
	"golang.org/x/net/websocket"
)

const (
	contestStateNotAvailable = "state n/a"
	contestNotStarted        = "not started"
	contestRunning           = "running"
	contestCompleted         = "completed"
)

var (
	gameShim      *game.Game
	contestStatus string
)

var (
	// InfoTimeout timeout between update info through websocket
	InfoTimeout = time.Second
	// ScoreboardTimeout timeout between update scoreboard through websocket
	ScoreboardTimeout = time.Second
	// TasksTimeout timeout between update tasks through websocket
	TasksTimeout = time.Second
	// FlagTimeout timeout between send flags
	FlagTimeout = time.Second
	// ScoreboardRecalcTimeout timeout between update scoreboard
	ScoreboardRecalcTimeout = time.Second
)

func durationToHMS(d time.Duration) string {

	sec := int(d.Seconds())

	var h, m, s int

	h = sec / 60 / 60
	m = (sec / 60) % 60
	s = sec % 60

	return fmt.Sprintf("%02d:%02d:%02d", h, m, s)
}

func getInfo() string {

	var left time.Duration
	var btnType string

	now := time.Now()

	if now.Before(gameShim.Start) {

		contestStatus = contestNotStarted
		left = gameShim.Start.Sub(now)
		btnType = "stop"

	} else if now.Before(gameShim.End) {

		contestStatus = contestRunning
		left = gameShim.End.Sub(now)
		btnType = "run"

	} else {
		contestStatus = contestCompleted
		left = 0
		btnType = "stop"
	}

	info := fmt.Sprintf(`<span id="game_status-%s">contest %s</span>`,
		btnType, contestStatus)

	if left != 0 {
		info += fmt.Sprintf(`<span id="timer">Left %s</span>`,
			durationToHMS(left))
	}

	return info
}

func infoHandler(ws *websocket.Conn) {

	defer ws.Close()
	for {
		_, err := fmt.Fprint(ws, getInfo())
		if err != nil {
			//log.Println("Socket closed:", err)
			return
		}

		time.Sleep(InfoTimeout)
	}
}

func scoreboardHandler(ws *websocket.Conn) {

	defer ws.Close()

	teamID := getTeamID(ws.Request())

	currentResult := scoreboardHTML(teamID)

	fmt.Fprint(ws, currentResult)

	sendedResult := currentResult

	lastUpdate := time.Now()

	for {
		currentResult := scoreboardHTML(teamID)

		if sendedResult != currentResult ||
			time.Now().After(lastUpdate.Add(time.Minute)) {

			sendedResult = currentResult
			lastUpdate = time.Now()

			_, err := fmt.Fprint(ws, currentResult)
			if err != nil {
				//log.Println("Socket closed:", err)
				return
			}
		}

		time.Sleep(ScoreboardTimeout)
	}
}

func scoreboardHTML(teamID int) (result string) {

	result = "
" +
		"#	" + "Team	" +
		"Score	" +
		"
"

	result += "
"

	scores, err := gameShim.Scoreboard()
	if err != nil {
		log.Println("Get scoreboard fail:", err)
		return
	}

	for n, teamScore := range scores {
		if teamScore.ID == teamID {
			result += `<tr class="self-team">`
		} else {
			result += `<tr>`
		}

		result += fmt.Sprintf(
			`<td class="team_index">%d</td>`+
				`<td class="team_name">%s</td>`+
				`<td class="team_score">%d</td></tr>`,
			n+1, teamScore.Name, teamScore.Score)

	}

	result += "
"

	return
}

func scoreboardUpdater(game *game.Game, updateTimeout time.Duration) {

	for {
		time.Sleep(updateTimeout)

		err := game.RecalcScoreboard()
		if err != nil {
			log.Println("Recalc scoreboard fail:", err)
		}
	}
}

func tasksHTML(teamID int) (result string) {

	cats, err := gameShim.Tasks()
	if err != nil {
		log.Println("Get tasks fail:", err)
	}

	for _, cat := range cats {
		result += categoryToHTML(teamID, cat)
	}

	return
}

func tasksHandler(ws *websocket.Conn) {

	defer ws.Close()

	teamID := getTeamID(ws.Request())

	currentTasks := tasksHTML(teamID)

	fmt.Fprint(ws, currentTasks)

	sendedTasks := currentTasks

	lastUpdate := time.Now()

	for {
		currentTasks := tasksHTML(teamID)

		if sendedTasks != currentTasks ||
			time.Now().After(lastUpdate.Add(time.Minute)) {

			sendedTasks = currentTasks
			lastUpdate = time.Now()

			_, err := fmt.Fprint(ws, currentTasks)
			if err != nil {
				//log.Println("Socket closed:", err)
				return
			}
		}

		time.Sleep(TasksTimeout)
	}
}

func taskHandler(w http.ResponseWriter, r *http.Request) {

	id, err := strconv.Atoi(r.URL.Query().Get("id"))
	if err != nil {
		log.Println("Atoi fail:", err)
		http.Redirect(w, r, "/", 307)
		return
	}

	cats, err := gameShim.Tasks()
	if err != nil {
		log.Println("Get tasks fail:", err)
		http.Redirect(w, r, "/", 307)
		return
	}

	task := game.TaskInfo{ID: id, Opened: false}

	for _, c := range cats {
		for _, t := range c.TasksInfo {
			if t.ID == id {
				task = t
				break
			}
		}
	}

	if !task.Opened {
		// Try to see closed task -> gtfo
		http.Redirect(w, r, "/", 307)
		return
	}

	teamID := getTeamID(r)

	flagSubmitFormat := `<br>` +
		`<form class="input-group" action="/flag?id=%d" method="post">` +
		`<input class="form-control float-left" name="flag" value="" placeholder="Flag">` +
		`<span class="input-group-btn">` +
		`<button class="btn btn-submit">Submit</button>` +
		`</span>` +
		`</form>`

	var submitForm string
	if taskSolvedBy(task, teamID) {
		submitForm = "Already solved"
	} else {
		submitForm = fmt.Sprintf(flagSubmitFormat, task.ID)
	}

	fmt.Fprintf(w, `<!DOCTYPE html>

    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="shortcut icon" href="images/favicon.png" type="image/png">
    <title>Juniors CTF</title>

    <link rel="stylesheet" href="css/style.css" class="--apng-checked">

Scoreboard

Tasks

News

Sponsors

%s

        %s

	%s

          %s

`, task.Name, task.Desc, task.Author, submitForm)
}

func flagHandler(w http.ResponseWriter, r *http.Request) {

	if r.Method != "POST" {
		http.Redirect(w, r, "/", 307)
		return
	}

	taskID, err := strconv.Atoi(r.URL.Query().Get("id"))
	if err != nil {
		log.Println("Atoi fail:", err)
		http.Redirect(w, r, "/", 307)
		return
	}

	flag := r.FormValue("flag")

	teamID := getTeamID(r)

	solved, err := gameShim.Solve(teamID, taskID, flag)
	if err != nil {
		solved = false
	}

	var solvedMsg string
	if solved {
		solvedMsg = `<div class="flag_status solved">Solved</div>`
	} else {
		solvedMsg = `<div class="flag_status invalid">Invalid flag</div>`
	}

	log.Printf("Team ID: %d, Task ID: %d, Flag: %s, Result: %s\n",
		teamID, taskID, flag, solvedMsg)

	time.Sleep(FlagTimeout)

	fmt.Fprintf(w, `<!DOCTYPE html>

    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="shortcut icon" href="images/favicon.png" type="image/png">
    <title>Juniors CTF</title>

    <link rel="stylesheet" href="css/style.css" class="--apng-checked">

Scoreboard

Tasks

News

Sponsors

%s

`, solvedMsg)
}

func handleStaticFile(pattern, file string) {
	http.HandleFunc(pattern,
		func(w http.ResponseWriter, r *http.Request) {
			http.ServeFile(w, r, file)
		})
}

func handleStaticFileSimple(file, wwwPath string) {
	handleStaticFile(file, wwwPath+file)
}

// Scoreboard implements web scoreboard
func Scoreboard(database *sql.DB, game *game.Game, wwwPath,
	addr string) (err error) {

	contestStatus = contestStateNotAvailable
	gameShim = game

	go scoreboardUpdater(game, ScoreboardRecalcTimeout)

	// Static files
	handleStaticFileSimple("/css/style.css", wwwPath)
	handleStaticFileSimple("/js/scoreboard.js", wwwPath)
	handleStaticFileSimple("/js/tasks.js", wwwPath)
	handleStaticFileSimple("/news.html", wwwPath)
	handleStaticFileSimple("/sponsors.html", wwwPath)
	handleStaticFileSimple("/images/bg.jpg", wwwPath)
	handleStaticFileSimple("/images/favicon.ico", wwwPath)
	handleStaticFileSimple("/images/favicon.png", wwwPath)
	handleStaticFileSimple("/images/401.jpg", wwwPath)
	handleStaticFileSimple("/images/juniors_ctf_txt.png", wwwPath)
	handleStaticFileSimple("/auth.html", wwwPath)

	// Get
	http.Handle("/", authorized(database, http.HandlerFunc(staticScoreboard)))
	http.Handle("/index.html", authorized(database, http.HandlerFunc(staticScoreboard)))
	http.Handle("/tasks.html", authorized(database, http.HandlerFunc(staticTasks)))
	http.Handle("/logout", authorized(database, http.HandlerFunc(logoutHandler)))

	// Websocket
	http.Handle("/scoreboard", authorized(database, websocket.Handler(scoreboardHandler)))
	http.Handle("/info", authorized(database, websocket.Handler(infoHandler)))
	http.Handle("/tasks", authorized(database, websocket.Handler(tasksHandler)))

	// Post
	http.Handle("/task", authorized(database, http.HandlerFunc(taskHandler)))
	http.Handle("/flag", authorized(database, http.HandlerFunc(flagHandler)))

	http.HandleFunc("/auth.php", http.HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			authHandler(database, w, r)
		}))

	log.Println("Launching scoreboard at", addr)

	return http.ListenAndServe(addr, nil)
}
<commit_msg>Change text alignment<commit_after>/**
 * @file scoreboard.go
 * @author Mikhail Klementyev jollheef<AT>riseup.net
 * @license GNU AGPLv3
 * @date November, 2015
 * @brief web scoreboard
 *
 * Contain web ui and several helpers for convert results to table
 */

package scoreboard

import (
	"database/sql"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"time"

	"github.com/jollheef/henhouse/game"
	"golang.org/x/net/websocket"
)

const (
	contestStateNotAvailable = "state n/a"
	contestNotStarted        = "not started"
	contestRunning           = "running"
	contestCompleted         = "completed"
)

var (
	gameShim      *game.Game
	contestStatus string
)

var (
	// InfoTimeout timeout between update info through websocket
	InfoTimeout = time.Second
	// ScoreboardTimeout timeout between update scoreboard through websocket
	ScoreboardTimeout = time.Second
	// TasksTimeout timeout between update tasks through websocket
	TasksTimeout = time.Second
	// FlagTimeout timeout between send flags
	FlagTimeout = time.Second
	// ScoreboardRecalcTimeout timeout between update scoreboard
	ScoreboardRecalcTimeout = time.Second
)

func durationToHMS(d time.Duration) string {

	sec := int(d.Seconds())

	var h, m, s int

	h = sec / 60 / 60
	m = (sec / 60) % 60
	s = sec % 60

	return fmt.Sprintf("%02d:%02d:%02d", h, m, s)
}

func getInfo() string {

	var left time.Duration
	var btnType string

	now := time.Now()

	if now.Before(gameShim.Start) {

		contestStatus = contestNotStarted
		left = gameShim.Start.Sub(now)
		btnType = "stop"

	} else if now.Before(gameShim.End) {

		contestStatus = contestRunning
		left = gameShim.End.Sub(now)
		btnType = "run"

	} else {
		contestStatus = contestCompleted
		left = 0
		btnType = "stop"
	}

	info := fmt.Sprintf(`<span id="game_status-%s">contest %s</span>`,
		btnType, contestStatus)

	if left != 0 {
		info += fmt.Sprintf(`<span id="timer">Left %s</span>`,
			durationToHMS(left))
	}

	return info
}

func infoHandler(ws *websocket.Conn) {

	defer ws.Close()
	for {
		_, err := fmt.Fprint(ws, getInfo())
		if err != nil {
			//log.Println("Socket closed:", err)
			return
		}

		time.Sleep(InfoTimeout)
	}
}

func scoreboardHandler(ws *websocket.Conn) {

	defer ws.Close()

	teamID := getTeamID(ws.Request())

	currentResult := scoreboardHTML(teamID)

	fmt.Fprint(ws, currentResult)

	sendedResult := currentResult

	lastUpdate := time.Now()

	for {
		currentResult := scoreboardHTML(teamID)

		if sendedResult != currentResult ||
			time.Now().After(lastUpdate.Add(time.Minute)) {

			sendedResult = currentResult
			lastUpdate = time.Now()

			_, err := fmt.Fprint(ws, currentResult)
			if err != nil {
				//log.Println("Socket closed:", err)
				return
			}
		}

		time.Sleep(ScoreboardTimeout)
	}
}

func scoreboardHTML(teamID int) (result string) {

	result = "
" +
		"#	" + "Team	" +
		"Score	" +
		"
"

	result += "
"

	scores, err := gameShim.Scoreboard()
	if err != nil {
		log.Println("Get scoreboard fail:", err)
		return
	}

	for n, teamScore := range scores {
		if teamScore.ID == teamID {
			result += `<tr class="self-team">`
		} else {
			result += `<tr>`
		}

		result += fmt.Sprintf(
			`<td class="team_index">%d</td>`+
				`<td class="team_name">%s</td>`+
				`<td class="team_score">%d</td></tr>`,
			n+1, teamScore.Name, teamScore.Score)

	}

	result += "
"

	return
}

func scoreboardUpdater(game *game.Game, updateTimeout time.Duration) {

	for {
		time.Sleep(updateTimeout)

		err := game.RecalcScoreboard()
		if err != nil {
			log.Println("Recalc scoreboard fail:", err)
		}
	}
}

func tasksHTML(teamID int) (result string) {

	cats, err := gameShim.Tasks()
	if err != nil {
		log.Println("Get tasks fail:", err)
	}

	for _, cat := range cats {
		result += categoryToHTML(teamID, cat)
	}

	return
}

func tasksHandler(ws *websocket.Conn) {

	defer ws.Close()

	teamID := getTeamID(ws.Request())

	currentTasks := tasksHTML(teamID)

	fmt.Fprint(ws, currentTasks)

	sendedTasks := currentTasks

	lastUpdate := time.Now()

	for {
		currentTasks := tasksHTML(teamID)

		if sendedTasks != currentTasks ||
			time.Now().After(lastUpdate.Add(time.Minute)) {

			sendedTasks = currentTasks
			lastUpdate = time.Now()

			_, err := fmt.Fprint(ws, currentTasks)
			if err != nil {
				//log.Println("Socket closed:", err)
				return
			}
		}

		time.Sleep(TasksTimeout)
	}
}

func taskHandler(w http.ResponseWriter, r *http.Request) {

	id, err := strconv.Atoi(r.URL.Query().Get("id"))
	if err != nil {
		log.Println("Atoi fail:", err)
		http.Redirect(w, r, "/", 307)
		return
	}

	cats, err := gameShim.Tasks()
	if err != nil {
		log.Println("Get tasks fail:", err)
		http.Redirect(w, r, "/", 307)
		return
	}

	task := game.TaskInfo{ID: id, Opened: false}

	for _, c := range cats {
		for _, t := range c.TasksInfo {
			if t.ID == id {
				task = t
				break
			}
		}
	}

	if !task.Opened {
		// Try to see closed task -> gtfo
		http.Redirect(w, r, "/", 307)
		return
	}

	teamID := getTeamID(r)

	flagSubmitFormat := `<br>` +
		`<form class="input-group" action="/flag?id=%d" method="post">` +
		`<input class="form-control float-left" name="flag" value="" placeholder="Flag">` +
		`<span class="input-group-btn">` +
		`<button class="btn btn-submit">Submit</button>` +
		`</span>` +
		`</form>`

	var submitForm string
	if taskSolvedBy(task, teamID) {
		submitForm = "Already solved"
	} else {
		submitForm = fmt.Sprintf(flagSubmitFormat, task.ID)
	}

	fmt.Fprintf(w, `<!DOCTYPE html>

    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="shortcut icon" href="images/favicon.png" type="image/png">
    <title>Juniors CTF</title>

    <link rel="stylesheet" href="css/style.css" class="--apng-checked">

Scoreboard

Tasks

News

Sponsors

%s

          %s

          %s

            %s

`, task.Name, task.Desc, task.Author, submitForm)
}

func flagHandler(w http.ResponseWriter, r *http.Request) {

	if r.Method != "POST" {
		http.Redirect(w, r, "/", 307)
		return
	}

	taskID, err := strconv.Atoi(r.URL.Query().Get("id"))
	if err != nil {
		log.Println("Atoi fail:", err)
		http.Redirect(w, r, "/", 307)
		return
	}

	flag := r.FormValue("flag")

	teamID := getTeamID(r)

	solved, err := gameShim.Solve(teamID, taskID, flag)
	if err != nil {
		solved = false
	}

	var solvedMsg string
	if solved {
		solvedMsg = `<div class="flag_status solved">Solved</div>`
	} else {
		solvedMsg = `<div class="flag_status invalid">Invalid flag</div>`
	}

	log.Printf("Team ID: %d, Task ID: %d, Flag: %s, Result: %s\n",
		teamID, taskID, flag, solvedMsg)

	time.Sleep(FlagTimeout)

	fmt.Fprintf(w, `<!DOCTYPE html>

    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="shortcut icon" href="images/favicon.png" type="image/png">
    <title>Juniors CTF</title>

    <link rel="stylesheet" href="css/style.css" class="--apng-checked">

Scoreboard

Tasks

News

Sponsors

%s

`, solvedMsg)
}

func handleStaticFile(pattern, file string) {
	http.HandleFunc(pattern,
		func(w http.ResponseWriter, r *http.Request) {
			http.ServeFile(w, r, file)
		})
}

func handleStaticFileSimple(file, wwwPath string) {
	handleStaticFile(file, wwwPath+file)
}

// Scoreboard implements web scoreboard
func Scoreboard(database *sql.DB, game *game.Game, wwwPath,
	addr string) (err error) {

	contestStatus = contestStateNotAvailable
	gameShim = game

	go scoreboardUpdater(game, ScoreboardRecalcTimeout)

	// Static files
	handleStaticFileSimple("/css/style.css", wwwPath)
	handleStaticFileSimple("/js/scoreboard.js", wwwPath)
	handleStaticFileSimple("/js/tasks.js", wwwPath)
	handleStaticFileSimple("/news.html", wwwPath)
	handleStaticFileSimple("/sponsors.html", wwwPath)
	handleStaticFileSimple("/images/bg.jpg", wwwPath)
	handleStaticFileSimple("/images/favicon.ico", wwwPath)
	handleStaticFileSimple("/images/favicon.png", wwwPath)
	handleStaticFileSimple("/images/401.jpg", wwwPath)
	handleStaticFileSimple("/images/juniors_ctf_txt.png", wwwPath)
	handleStaticFileSimple("/auth.html", wwwPath)

	// Get
	http.Handle("/", authorized(database, http.HandlerFunc(staticScoreboard)))
	http.Handle("/index.html", authorized(database, http.HandlerFunc(staticScoreboard)))
	http.Handle("/tasks.html", authorized(database, http.HandlerFunc(staticTasks)))
	http.Handle("/logout", authorized(database, http.HandlerFunc(logoutHandler)))

	// Websocket
	http.Handle("/scoreboard", authorized(database, websocket.Handler(scoreboardHandler)))
	http.Handle("/info", authorized(database, websocket.Handler(infoHandler)))
	http.Handle("/tasks", authorized(database, websocket.Handler(tasksHandler)))

	// Post
	http.Handle("/task", authorized(database, http.HandlerFunc(taskHandler)))
	http.Handle("/flag", authorized(database, http.HandlerFunc(flagHandler)))

	http.HandleFunc("/auth.php", http.HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			authHandler(database, w, r)
		}))

	log.Println("Launching scoreboard at", addr)

	return http.ListenAndServe(addr, nil)
}
<|endoftext|>


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

package sqlgen

import (
	"reflect"
	"testing"
)

type Person struct {
	Id   int
	Name string
	Age  int
}

func TestFieldNames(t *testing.T) {
	var p Person
	expected := []string{"Name", "Age"}
	got := fieldNames(reflect.TypeOf(p))
	if !reflect.DeepEqual(expected, got) {
		t.Errorf("Expected %q. Got %q.", expected, got)
	}
}

func TestGenerateSelectFromStruct(t *testing.T) {
	var p Person
	expected := "select id, name, age from person"
	got := Select(p)
	if expected != got {
		t.Errorf(`SELECT generation for %q. Was expecting "%s", got %s.`, reflect.TypeOf(p), expected, got)
	}
}

func TestGenerateInsertFromStruct(t *testing.T) {
	var p Person
	expected := "insert into person (id, name, age) values (?, ?, ?)"
	got := Insert(p)
	if expected != got {
		t.Errorf(`INSERT generation for %q. Was expecting "%s", got %s.`, reflect.TypeOf(p), expected, got)
	}
}

func TestGenerateUpdateFromStruct(t *testing.T) {
	p := Person{Id: 1, Name: "Umi", Age: 6}
	expected := `update person name="Umi", age=6 where id=1`
	got := Update(p, []string{"name", "age"}, []string{"id"})

	if expected != got {
		t.Errorf(`UPDATE generation for %q. Was expecting "%s", got %s.`, reflect.TypeOf(p), expected, got)
	}
}
<commit_msg>Testing fieldValues function<commit_after>// Copyright 2012 Cobrateam members. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sqlgen

import (
	"reflect"
	"testing"
)

type Person struct {
	Id   int
	Name string
	Age  int
}

func TestFieldNames(t *testing.T) {
	var p Person
	expected := []string{"Id", "Name", "Age"}
	got := fieldNames(reflect.TypeOf(p))
	if !reflect.DeepEqual(expected, got) {
		t.Errorf("Expected %q. Got %q.", expected, got)
	}
}

func TestFieldValues(t *testing.T) {
	p := Person{Id: 1, Name: "Umi", Age: 6}
	expected := []string{"id=1", `name="Umi"`, "age=6"}
	got := fieldValues(reflect.ValueOf(p), []string{"id", "name", "age"})

	if !reflect.DeepEqual(expected, got) {
		t.Errorf("Expected %q. Got %q.", expected, got)
	}
}

func TestGenerateSelectFromStruct(t *testing.T) {
	var p Person
	expected := "select id, name, age from person"
	got := Select(p)
	if expected != got {
		t.Errorf(`SELECT generation for %q. Was expecting "%s", got %s.`, reflect.TypeOf(p), expected, got)
	}
}

func TestGenerateInsertFromStruct(t *testing.T) {
	var p Person
	expected := "insert into person (id, name, age) values (?, ?, ?)"
	got := Insert(p)
	if expected != got {
		t.Errorf(`INSERT generation for %q. Was expecting "%s", got %s.`, reflect.TypeOf(p), expected, got)
	}
}

func TestGenerateUpdateFromStruct(t *testing.T) {
	p := Person{Id: 1, Name: "Umi", Age: 6}
	expected := `update person name="Umi", age=6 where id=1`
	got := Update(p, []string{"name", "age"}, []string{"id"})

	if expected != got {
		t.Errorf(`UPDATE generation for %q. Was expecting "%s", got %s.`, reflect.TypeOf(p), expected, got)
	}
}
<|endoftext|>


<commit_before>package schema

import (
	"encoding/json"
	"io/ioutil"
	"os"
)

type StandardField struct {
	Type        string `json:"type"`
	Description string `json:"description"`
	Format      string `json:"format,omitempty"`
}

type Core struct {
	Type                 string `json:"type"`
	AdditionalProperties bool   `json:"additionalProperties"`
	Properties           struct {
		Name            StandardField `json:"name"`
		Title           StandardField `json:"title"`
		Image           StandardField `json:"image"`
		Email           StandardField `json:"email"`
		Phone           StandardField `json:"phone"`
		URL             StandardField `json:"url"`
		Summary         StandardField `json:"summary"`
		CurrentLocation struct {
			Type        string `json:"type"`
			Format      string `json:"format"`
			Description string `json:"description"`
			Properties  struct {
				Lat struct {
					Type string `json:"type"`
				} `json:"lat"`
				Long struct {
					Type string `json:"type"`
				} `json:"long"`
			} `json:"properties"`
		} `json:"currentLocation"`
		PermanentLocation struct {
			Type        string `json:"type"`
			Format      string `json:"format"`
			Description string `json:"description"`
			Properties  struct {
				Lat struct {
					Type string `json:"type"`
				} `json:"lat"`
				Long struct {
					Type string `json:"type"`
				} `json:"long"`
			} `json:"properties"`
		} `json:"permanentLocation"`
	} `json:"properties"`
}

type Work struct {
	Type            string `json:"type"`
	AdditionalItems bool   `json:"additionalItems"`
	Items           struct {
		Type                 string `json:"type"`
		AdditionalProperties bool   `json:"additionalProperties"`
		Properties           struct {
			Name        StandardField `json:"name"`
			Description StandardField `json:"description"`
			Position    StandardField `json:"position"`
			Location    struct {
				Type        string `json:"type"`
				Format      string `json:"format"`
				Description string `json:"description"`
				Properties  struct {
					Lat struct {
						Type string `json:"type"`
					} `json:"lat"`
					Long struct {
						Type string `json:"type"`
					} `json:"long"`
				} `json:"properties"`
			} `json:"location"`
			URL        StandardField `json:"url"`
			StartDate  StandardField `json:"startDate"`
			EndDate    StandardField `json:"endDate"`
			Summary    StandardField `json:"summary"`
			Highlights struct {
				Type            string        `json:"type"`
				Description     string        `json:"description"`
				AdditionalItems bool          `json:"additionalItems"`
				Items           StandardField `json:"items"`
			} `json:"highlights"`
		} `json:"properties"`
	} `json:"items"`
}

type Education struct {
	Type            string `json:"type"`
	AdditionalItems bool   `json:"additionalItems"`
	Items           struct {
		Type                 string `json:"type"`
		AdditionalProperties bool   `json:"additionalProperties"`
		Properties           struct {
			Institution StandardField `json:"institution"`
			Location    struct {
				Type        string `json:"type"`
				Format      string `json:"format"`
				Description string `json:"description"`
				Properties  struct {
					Lat struct {
						Type string `json:"type"`
					} `json:"lat"`
					Long struct {
						Type string `json:"type"`
					} `json:"long"`
				} `json:"properties"`
			} `json:"location"`
			Area      StandardField `json:"area"`
			StudyType StandardField `json:"studyType"`
			StartDate StandardField `json:"startDate"`
			EndDate   StandardField `json:"endDate"`
			Score     struct {
				Type                 string `json:"type"`
				AdditionalProperties bool   `json:"additionalProperties"`
				Properties           struct {
					Scoretype  StandardField `json:"scoretype"`
					Scorevalue StandardField `json:"scorevalue"`
				} `json:"properties"`
			} `json:"score"`
			Courses struct {
				Type            string        `json:"type"`
				Description     string        `json:"description"`
				AdditionalItems bool          `json:"additionalItems"`
				Items           StandardField `json:"items"`
			} `json:"courses"`
			Honors struct {
				Type            string        `json:"type"`
				Description     string        `json:"description"`
				AdditionalItems bool          `json:"additionalItems"`
				Items           StandardField `json:"items"`
			} `json:"honors"`
		} `json:"properties"`
	} `json:"items"`
}

type Volunteer struct {
	Type            string `json:"type"`
	AdditionalItems bool   `json:"additionalItems"`
	Items           struct {
		Type                 string `json:"type"`
		AdditionalProperties bool   `json:"additionalProperties"`
		Properties           struct {
			Organization StandardField `json:"organization"`
			Position     StandardField `json:"position"`
			Location     struct {
				Type        string `json:"type"`
				Format      string `json:"format"`
				Description string `json:"description"`
				Properties  struct {
					Lat struct {
						Type string `json:"type"`
					} `json:"lat"`
					Long struct {
						Type string `json:"type"`
					} `json:"long"`
				} `json:"properties"`
			} `json:"location"`
			URL        StandardField `json:"url"`
			StartDate  StandardField `json:"startDate"`
			EndDate    StandardField `json:"endDate"`
			Summary    StandardField `json:"summary"`
			Highlights struct {
				Type            string        `json:"type"`
				Description     string        `json:"description"`
				AdditionalItems bool          `json:"additionalItems"`
				Items           StandardField `json:"items"`
			} `json:"highlights"`
		} `json:"properties"`
	} `json:"items"`
}

type Publications struct {
	Type            string `json:"type"`
	Description     string `json:"description"`
	AdditionalItems bool   `json:"additionalItems"`
	Items           struct {
		Type                 string `json:"type"`
		AdditionalProperties bool   `json:"additionalProperties"`
		Properties           struct {
			Name        StandardField `json:"name"`
			Publisher   StandardField `json:"publisher"`
			ReleaseDate StandardField `json:"releaseDate"`
			Resources   struct {
				Type            string        `json:"type"`
				Description     string        `json:"description"`
				AdditionalItems bool          `json:"additionalItems"`
				URL             StandardField `json:"url"`
				Label           StandardField `json:"label"`
			} `json:"resources"`
			URL     StandardField `json:"url"`
			Summary StandardField `json:"summary"`
		} `json:"properties"`
	} `json:"items"`
}

type Legal struct {
	Type            string `json:"type"`
	Description     string `json:"description"`
	AdditionalItems bool   `json:"additionalItems"`
	Items           struct {
		Type                 string `json:"type"`
		AdditionalProperties bool   `json:"additionalProperties"`
		Properties           struct {
			Name            StandardField `json:"name"`
			LegalType       StandardField `json:"legalType"`
			Description     StandardField `json:"description"`
			ApplicationDate StandardField `json:"applicationDate"`
			GrantDate       StandardField `json:"grantDate"`
			EndDate         StandardField `json:"endDate"`
			Resources       struct {
				Type            string        `json:"type"`
				Description     string        `json:"description"`
				AdditionalItems bool          `json:"additionalItems"`
				URL             StandardField `json:"url"`
				Label           StandardField `json:"label"`
			} `json:"resources"`
			IDNumber StandardField `json:"idNumber"`
		} `json:"properties"`
	} `json:"items"`
}

type Skills struct {
	Type            string `json:"type"`
	Description     string `json:"description"`
	AdditionalItems bool   `json:"additionalItems"`
	Items           struct {
		Type                 string `json:"type"`
		AdditionalProperties bool   `json:"additionalProperties"`
		Properties           struct {
			Name    StandardField `json:"name"`
			Keyword struct {
				Type            string `json:"type"`
				Description     string `json:"description"`
				AdditionalItems bool   `json:"additionalItems"`
				Items           struct {
					Type                 string `json:"type"`
					AdditionalProperties bool   `json:"additionalProperties"`
					Properties           struct {
						Name  StandardField `json:"name"`
						Score StandardField `json:"score"`
					} `json:"properties"`
				} `json:"items"`
			} `json:"keyword"`
		} `json:"properties"`
	} `json:"items"`
}

type Awards struct {
	Type            string `json:"type"`
	Description     string `json:"description"`
	AdditionalItems bool   `json:"additionalItems"`
	Items           struct {
		Type                 string `json:"type"`
		AdditionalProperties bool   `json:"additionalProperties"`
		Properties           struct {
			Title   StandardField `json:"title"`
			Date    StandardField `json:"date"`
			Awarder StandardField `json:"awarder"`
			Summary StandardField `json:"summary"`
		} `json:"properties"`
	} `json:"items"`
}

type Projects struct {
	Type            string `json:"type"`
	Description     string `json:"description"`
	AdditionalItems bool   `json:"additionalItems"`
	Items           struct {
		Type                 string `json:"type"`
		AdditionalProperties bool   `json:"additionalProperties"`
		Properties           struct {
			Name     StandardField `json:"name"`
			Location struct {
				Type        string `json:"type"`
				Format      string `json:"format"`
				Description string `json:"description"`
				Properties  struct {
					Lat struct {
						Type string `json:"type"`
					} `json:"lat"`
					Long struct {
						Type string `json:"type"`
					} `json:"long"`
				} `json:"properties"`
			} `json:"location"`
			Description StandardField `json:"description"`
			Highlights  struct {
				Type            string        `json:"type"`
				Description     string        `json:"description"`
				AdditionalItems bool          `json:"additionalItems"`
				Items           StandardField `json:"items"`
			} `json:"highlights"`
			Keywords struct {
				Type            string        `json:"type"`
				Description     string        `json:"description"`
				AdditionalItems bool          `json:"additionalItems"`
				Items           StandardField `json:"items"`
			} `json:"keywords"`
			StartDate StandardField `json:"startDate"`
			EndDate   StandardField `json:"endDate"`
			Resources struct {
				Type            string        `json:"type"`
				Description     string        `json:"description"`
				AdditionalItems bool          `json:"additionalItems"`
				URL             StandardField `json:"url"`
				Label           StandardField `json:"label"`
			} `json:"resources"`
			URL   StandardField `json:"url"`
			Roles struct {
				Type            string        `json:"type"`
				Description     string        `json:"description"`
				AdditionalItems bool          `json:"additionalItems"`
				Items           StandardField `json:"items"`
			} `json:"roles"`
			Entity StandardField `json:"entity"`
			Type   StandardField `json:"type"`
		} `json:"properties"`
	} `json:"items"`
}

type Certificates struct {
	Type            string `json:"type"`
	AdditionalItems bool   `json:"additionalItems"`
	Items           struct {
		Type                 string `json:"type"`
		AdditionalProperties bool   `json:"additionalProperties"`
		Properties           struct {
			Code          StandardField `json:"code"`
			Name          StandardField `json:"name"`
			Website       StandardField `json:"website"`
			Verification  StandardField `json:"verification"`
			GrantDate     StandardField `json:"grantDate"`
			Score         StandardField `json:"score"`
			EndDate       StandardField `json:"endDate"`
			DoesNotExpire struct {
				Type   string `json:"type"`
				Format string `json:"format"`
			} `json:"doesNotExpire"`
		} `json:"properties"`
	} `json:"items"`
}

type References struct {
	Type            string `json:"type"`
	Description     string `json:"description"`
	AdditionalItems bool   `json:"additionalItems"`
	Items           struct {
		Type                 string `json:"type"`
		AdditionalProperties bool   `json:"additionalProperties"`
		Properties           struct {
			Name      StandardField `json:"name"`
			Company   StandardField `json:"company"`
			Position  StandardField `json:"position"`
			Reference StandardField `json:"reference"`
		} `json:"properties"`
	} `json:"items"`
}

type Languages struct {
	Type            string `json:"type"`
	Description     string `json:"description"`
	AdditionalItems bool   `json:"additionalItems"`
	Items           struct {
		Type                 string `json:"type"`
		AdditionalProperties bool   `json:"additionalProperties"`
		Properties           struct {
			Language StandardField `json:"language"`
			Score    StandardField `json:"score"`
		} `json:"properties"`
	} `json:"items"`
}

type Interests struct {
	Type            string `json:"type"`
	AdditionalItems bool   `json:"additionalItems"`
	Items           struct {
		Type                 string `json:"type"`
		AdditionalProperties bool   `json:"additionalProperties"`
		Properties           struct {
			Name     StandardField `json:"name"`
			Keywords struct {
				Type            string        `json:"type"`
				Description     string        `json:"description"`
				AdditionalItems bool          `json:"additionalItems"`
				Items           StandardField `json:"items"`
			} `json:"keywords"`
		} `json:"properties"`
	} `json:"items"`
}

type Meta struct {
	Type                 string `json:"type"`
	Description          string `json:"description"`
	AdditionalProperties bool   `json:"additionalProperties"`
	Properties           struct {
		Canonical    StandardField `json:"canonical"`
		Version      StandardField `json:"version"`
		LastModified StandardField `json:"lastModified"`
	} `json:"properties"`
}

type Schema struct {
	Schema               string `json:"$schema"`
	Title                string `json:"title"`
	Type                 string `json:"type"`
	AdditionalProperties bool   `json:"additionalProperties"`
	Properties           struct {
		Core         Core         `json:"core"`
		Work         Work         `json:"work"`
		Education    Education    `json:"education"`
		Volunteer    Volunteer    `json:"volunteer"`
		Publications Publications `json:"publications"`
		Legal        Legal        `json:"legal"`
		Skills       Skills       `json:"skills"`
		Awards       Awards       `json:"awards"`
		Projects     Projects     `json:"projects"`
		Certificates Certificates `json:"certificates"`
		References   References   `json:"references"`
		Languages    Languages    `json:"languages"`
		Interests    Interests    `json:"interests"`
		Meta         Meta         `json:"meta"`
	} `json:"properties"`
}

// GetSchema fills the Schema{} struct with correct values and returns it
func GetSchema() (Schema, error) {
	valuesFile, err := os.Open("./schema.json")
	if err != nil {
		return Schema{}, err
	}
	defer valuesFile.Close()

	values, err := ioutil.ReadAll(valuesFile)
	if err != nil {
		return Schema{}, err
	}
	schema := Schema{}
	err = json.Unmarshal(values, &schema)
	return schema, nil
}
<commit_msg>Extract Location and Highlights into single structs<commit_after>package schema

import (
	"encoding/json"
	"io/ioutil"
	"os"
)

// Schema fields

type StandardField struct {
	Type        string `json:"type"`
	Description string `json:"description"`
	Format      string `json:"format,omitempty"`
}

type Location struct {
	Type        string `json:"type"`
	Format      string `json:"format"`
	Description string `json:"description"`
	Properties  struct {
		Lat struct {
			Type string `json:"type"`
		} `json:"lat"`
		Long struct {
			Type string `json:"type"`
		} `json:"long"`
	} `json:"properties"`
}

type Highlights struct {
	Type            string        `json:"type"`
	Description     string        `json:"description"`
	AdditionalItems bool          `json:"additionalItems"`
	Items           StandardField `json:"items"`
}

// Main properties
type Core struct {
	Type                 string `json:"type"`
	AdditionalProperties bool   `json:"additionalProperties"`
	Properties           struct {
		Name              StandardField `json:"name"`
		Title             StandardField `json:"title"`
		Image             StandardField `json:"image"`
		Email             StandardField `json:"email"`
		Phone             StandardField `json:"phone"`
		URL               StandardField `json:"url"`
		Summary           StandardField `json:"summary"`
		CurrentLocation   Location      `json:"currentLocation"`
		PermanentLocation Location      `json:"permanentLocation"`
	} `json:"properties"`
}

type Work struct {
	Type            string `json:"type"`
	AdditionalItems bool   `json:"additionalItems"`
	Items           struct {
		Type                 string `json:"type"`
		AdditionalProperties bool   `json:"additionalProperties"`
		Properties           struct {
			Name        StandardField `json:"name"`
			Description StandardField `json:"description"`
			Position    StandardField `json:"position"`
			Location    Location      `json:"location"`
			URL         StandardField `json:"url"`
			StartDate   StandardField `json:"startDate"`
			EndDate     StandardField `json:"endDate"`
			Summary     StandardField `json:"summary"`
			Highlights  Highlights    `json:"highlights"`
		} `json:"properties"`
	} `json:"items"`
}

type Education struct {
	Type            string `json:"type"`
	AdditionalItems bool   `json:"additionalItems"`
	Items           struct {
		Type                 string `json:"type"`
		AdditionalProperties bool   `json:"additionalProperties"`
		Properties           struct {
			Institution StandardField `json:"institution"`
			Location    Location      `json:"location"`
			Area        StandardField `json:"area"`
			StudyType   StandardField `json:"studyType"`
			StartDate   StandardField `json:"startDate"`
			EndDate     StandardField `json:"endDate"`
			Score       struct {
				Type                 string `json:"type"`
				AdditionalProperties bool   `json:"additionalProperties"`
				Properties           struct {
					Scoretype  StandardField `json:"scoretype"`
					Scorevalue StandardField `json:"scorevalue"`
				} `json:"properties"`
			} `json:"score"`
			Courses struct {
				Type            string        `json:"type"`
				Description     string        `json:"description"`
				AdditionalItems bool          `json:"additionalItems"`
				Items           StandardField `json:"items"`
			} `json:"courses"`
			Honors struct {
				Type            string        `json:"type"`
				Description     string        `json:"description"`
				AdditionalItems bool          `json:"additionalItems"`
				Items           StandardField `json:"items"`
			} `json:"honors"`
		} `json:"properties"`
	} `json:"items"`
}

type Volunteer struct {
	Type            string `json:"type"`
	AdditionalItems bool   `json:"additionalItems"`
	Items           struct {
		Type                 string `json:"type"`
		AdditionalProperties bool   `json:"additionalProperties"`
		Properties           struct {
			Organization StandardField `json:"organization"`
			Position     StandardField `json:"position"`
			Location     Location      `json:"location"`
			URL          StandardField `json:"url"`
			StartDate    StandardField `json:"startDate"`
			EndDate      StandardField `json:"endDate"`
			Summary      StandardField `json:"summary"`
			Highlights   Highlights    `json:"highlights"`
		} `json:"properties"`
	} `json:"items"`
}

type Publications struct {
	Type            string `json:"type"`
	Description     string `json:"description"`
	AdditionalItems bool   `json:"additionalItems"`
	Items           struct {
		Type                 string `json:"type"`
		AdditionalProperties bool   `json:"additionalProperties"`
		Properties           struct {
			Name        StandardField `json:"name"`
			Publisher   StandardField `json:"publisher"`
			ReleaseDate StandardField `json:"releaseDate"`
			Resources   struct {
				Type            string        `json:"type"`
				Description     string        `json:"description"`
				AdditionalItems bool          `json:"additionalItems"`
				URL             StandardField `json:"url"`
				Label           StandardField `json:"label"`
			} `json:"resources"`
			URL     StandardField `json:"url"`
			Summary StandardField `json:"summary"`
		} `json:"properties"`
	} `json:"items"`
}

type Legal struct {
	Type            string `json:"type"`
	Description     string `json:"description"`
	AdditionalItems bool   `json:"additionalItems"`
	Items           struct {
		Type                 string `json:"type"`
		AdditionalProperties bool   `json:"additionalProperties"`
		Properties           struct {
			Name            StandardField `json:"name"`
			LegalType       StandardField `json:"legalType"`
			Description     StandardField `json:"description"`
			ApplicationDate StandardField `json:"applicationDate"`
			GrantDate       StandardField `json:"grantDate"`
			EndDate         StandardField `json:"endDate"`
			Resources       struct {
				Type            string        `json:"type"`
				Description     string        `json:"description"`
				AdditionalItems bool          `json:"additionalItems"`
				URL             StandardField `json:"url"`
				Label           StandardField `json:"label"`
			} `json:"resources"`
			IDNumber StandardField `json:"idNumber"`
		} `json:"properties"`
	} `json:"items"`
}

type Skills struct {
	Type            string `json:"type"`
	Description     string `json:"description"`
	AdditionalItems bool   `json:"additionalItems"`
	Items           struct {
		Type                 string `json:"type"`
		AdditionalProperties bool   `json:"additionalProperties"`
		Properties           struct {
			Name    StandardField `json:"name"`
			Keyword struct {
				Type            string `json:"type"`
				Description     string `json:"description"`
				AdditionalItems bool   `json:"additionalItems"`
				Items           struct {
					Type                 string `json:"type"`
					AdditionalProperties bool   `json:"additionalProperties"`
					Properties           struct {
						Name  StandardField `json:"name"`
						Score StandardField `json:"score"`
					} `json:"properties"`
				} `json:"items"`
			} `json:"keyword"`
		} `json:"properties"`
	} `json:"items"`
}

type Awards struct {
	Type            string `json:"type"`
	Description     string `json:"description"`
	AdditionalItems bool   `json:"additionalItems"`
	Items           struct {
		Type                 string `json:"type"`
		AdditionalProperties bool   `json:"additionalProperties"`
		Properties           struct {
			Title   StandardField `json:"title"`
			Date    StandardField `json:"date"`
			Awarder StandardField `json:"awarder"`
			Summary StandardField `json:"summary"`
		} `json:"properties"`
	} `json:"items"`
}

type Projects struct {
	Type            string `json:"type"`
	Description     string `json:"description"`
	AdditionalItems bool   `json:"additionalItems"`
	Items           struct {
		Type                 string `json:"type"`
		AdditionalProperties bool   `json:"additionalProperties"`
		Properties           struct {
			Name        StandardField `json:"name"`
			Location    Location      `json:"location"`
			Description StandardField `json:"description"`
			Highlights  Highlights    `json:"highlights"`
			Keywords    struct {
				Type            string        `json:"type"`
				Description     string        `json:"description"`
				AdditionalItems bool          `json:"additionalItems"`
				Items           StandardField `json:"items"`
			} `json:"keywords"`
			StartDate StandardField `json:"startDate"`
			EndDate   StandardField `json:"endDate"`
			Resources struct {
				Type            string        `json:"type"`
				Description     string        `json:"description"`
				AdditionalItems bool          `json:"additionalItems"`
				URL             StandardField `json:"url"`
				Label           StandardField `json:"label"`
			} `json:"resources"`
			URL   StandardField `json:"url"`
			Roles struct {
				Type            string        `json:"type"`
				Description     string        `json:"description"`
				AdditionalItems bool          `json:"additionalItems"`
				Items           StandardField `json:"items"`
			} `json:"roles"`
			Entity StandardField `json:"entity"`
			Type   StandardField `json:"type"`
		} `json:"properties"`
	} `json:"items"`
}

type Certificates struct {
	Type            string `json:"type"`
	AdditionalItems bool   `json:"additionalItems"`
	Items           struct {
		Type                 string `json:"type"`
		AdditionalProperties bool   `json:"additionalProperties"`
		Properties           struct {
			Code          StandardField `json:"code"`
			Name          StandardField `json:"name"`
			Website       StandardField `json:"website"`
			Verification  StandardField `json:"verification"`
			GrantDate     StandardField `json:"grantDate"`
			Score         StandardField `json:"score"`
			EndDate       StandardField `json:"endDate"`
			DoesNotExpire struct {
				Type   string `json:"type"`
				Format string `json:"format"`
			} `json:"doesNotExpire"`
		} `json:"properties"`
	} `json:"items"`
}

type References struct {
	Type            string `json:"type"`
	Description     string `json:"description"`
	AdditionalItems bool   `json:"additionalItems"`
	Items           struct {
		Type                 string `json:"type"`
		AdditionalProperties bool   `json:"additionalProperties"`
		Properties           struct {
			Name      StandardField `json:"name"`
			Company   StandardField `json:"company"`
			Position  StandardField `json:"position"`
			Reference StandardField `json:"reference"`
		} `json:"properties"`
	} `json:"items"`
}

type Languages struct {
	Type            string `json:"type"`
	Description     string `json:"description"`
	AdditionalItems bool   `json:"additionalItems"`
	Items           struct {
		Type                 string `json:"type"`
		AdditionalProperties bool   `json:"additionalProperties"`
		Properties           struct {
			Language StandardField `json:"language"`
			Score    StandardField `json:"score"`
		} `json:"properties"`
	} `json:"items"`
}

type Interests struct {
	Type            string `json:"type"`
	AdditionalItems bool   `json:"additionalItems"`
	Items           struct {
		Type                 string `json:"type"`
		AdditionalProperties bool   `json:"additionalProperties"`
		Properties           struct {
			Name     StandardField `json:"name"`
			Keywords struct {
				Type            string        `json:"type"`
				Description     string        `json:"description"`
				AdditionalItems bool          `json:"additionalItems"`
				Items           StandardField `json:"items"`
			} `json:"keywords"`
		} `json:"properties"`
	} `json:"items"`
}

type Meta struct {
	Type                 string `json:"type"`
	Description          string `json:"description"`
	AdditionalProperties bool   `json:"additionalProperties"`
	Properties           struct {
		Canonical    StandardField `json:"canonical"`
		Version      StandardField `json:"version"`
		LastModified StandardField `json:"lastModified"`
	} `json:"properties"`
}

type Schema struct {
	Schema               string `json:"$schema"`
	Title                string `json:"title"`
	Type                 string `json:"type"`
	AdditionalProperties bool   `json:"additionalProperties"`
	Properties           struct {
		Core         Core         `json:"core"`
		Work         Work         `json:"work"`
		Education    Education    `json:"education"`
		Volunteer    Volunteer    `json:"volunteer"`
		Publications Publications `json:"publications"`
		Legal        Legal        `json:"legal"`
		Skills       Skills       `json:"skills"`
		Awards       Awards       `json:"awards"`
		Projects     Projects     `json:"projects"`
		Certificates Certificates `json:"certificates"`
		References   References   `json:"references"`
		Languages    Languages    `json:"languages"`
		Interests    Interests    `json:"interests"`
		Meta         Meta         `json:"meta"`
	} `json:"properties"`
}

// GetSchema fills the Schema{} struct with correct values and returns it
func GetSchema() (Schema, error) {
	valuesFile, err := os.Open("./schema.json")
	if err != nil {
		return Schema{}, err
	}
	defer valuesFile.Close()

	values, err := ioutil.ReadAll(valuesFile)
	if err != nil {
		return Schema{}, err
	}
	schema := Schema{}
	err = json.Unmarshal(values, &schema)
	return schema, nil
}
<|endoftext|>


<commit_before>package webserver

import (
	"crypto/rand"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"time"

	"github.com/golang/glog"
	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"github.com/gorilla/websocket"
	"github.com/monopole/mdrip/base"
	"github.com/monopole/mdrip/loader"
	"github.com/monopole/mdrip/model"
	"github.com/monopole/mdrip/program"
	"github.com/monopole/mdrip/tmux"
	"github.com/monopole/mdrip/util"
	"github.com/monopole/mdrip/webapp"
)

type myConn struct {
	conn    *websocket.Conn
	lastUse time.Time
}

func (c myConn) Write(bytes []byte) (n int, err error) {
	glog.Info("Attempting socket write.")
	c.lastUse = time.Now()
	err = c.conn.WriteMessage(websocket.TextMessage, bytes)
	if err != nil {
		glog.Error("bad socket write:", err)
		return 0, err
	}
	glog.Info("Socket seemed to work.")
	return len(bytes), nil
}

type Server struct {
	loader           *loader.Loader
	tutorial         model.Tutorial
	store            sessions.Store
	upgrader         websocket.Upgrader
	connections      map[webapp.TypeSessId]*myConn
	connReaperQuitCh chan bool
}

const (
	cookieName = "mdrip"
	keySessId  = "sessId"
)

// var keyAuth = securecookie.GenerateRandomKey(16)
var keyAuth = []byte("static-visible-secret")
var keyEncrypt = []byte(nil)

func NewServer(l *loader.Loader) (*Server, error) {
	s := sessions.NewCookieStore(keyAuth, keyEncrypt)
	s.Options = &sessions.Options{
		Domain:   "localhost",
		Path:     "/",
		MaxAge:   3600 * 8, // 8 hours
		HttpOnly: true,
	}
	result := &Server{
		l,
		nil,
		s,
		websocket.Upgrader{},
		make(map[webapp.TypeSessId]*myConn),
		make(chan bool),
	}
	go result.reapConnections()
	return result, nil
}

func getSessionId(s *sessions.Session) webapp.TypeSessId {
	if c, ok := s.Values[keySessId].(string); ok {
		return webapp.TypeSessId(c)
	}
	return ""
}

func assureSessionId(s *sessions.Session) webapp.TypeSessId {
	c := getSessionId(s)
	if c == "" {
		c = makeSessionId()
		s.Values[keySessId] = string(c)
	}
	return c
}

func makeSessionId() webapp.TypeSessId {
	b := make([]byte, 5)
	if _, err := rand.Read(b); err != nil {
		panic(err)
	}
	return webapp.TypeSessId(fmt.Sprintf("%X", b))
}

func getSessionIdParam(n string, r *http.Request) (webapp.TypeSessId, error) {
	v := r.URL.Query().Get(n)
	if v == "" {
		return "", errors.New("no session Id")
	}
	return webapp.TypeSessId(v), nil
}

// Pull session Id out of request, create a socket connection,
// store connection in a map.  The block runner will attempt to
// find the connection and write to it, else fall back to its
// other behaviors.
func (ws *Server) openWebSocket(w http.ResponseWriter, r *http.Request) {
	sessId, err := getSessionIdParam("id", r)
	if err != nil {
		glog.Errorf("no session Id: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if c := ws.connections[sessId]; c != nil {
		glog.Info("Wut? session found: ", sessId)
		// Possibly the other side shutdown and restarted.
		// Close and make new one.
		c.conn.Close()
		delete(ws.connections, sessId)
	}
	c, err := ws.upgrader.Upgrade(w, r, nil)
	if err != nil {
		glog.Info("upgrade:", err)
		return
	}
	glog.Info("established websocket")
	go func() {
		_, message, err := c.ReadMessage()
		if err == nil {
			glog.Info("handshake: ", string(message))
		} else {
			glog.Info("websocket err:", err)
		}
	}()
	ws.connections[sessId] = &myConn{c, time.Now()}
}

func write500(w http.ResponseWriter, e error) {
	http.Error(w, e.Error(), http.StatusInternalServerError)
}

func (ws *Server) reload(w http.ResponseWriter, r *http.Request) {
	value := mux.Vars(r)["gitclone"]
	if len(value) < 1 {
		value = r.URL.Query().Get("q")
	}
	var t model.Tutorial
	var err error
	if len(value) > 0 {
		// Load data from new source.
		ds, err := base.NewDataSource([]string{value})
		if err != nil {
			http.Error(w,
				fmt.Sprintf("Bad value %s", value), http.StatusBadRequest)
			return
		}
		l := loader.NewLoader(ds)
		t, err = l.Load()
		if err != nil {
			http.Error(w,
				fmt.Sprintf("Unable to load from %s: %v", ds, err),
				http.StatusBadRequest)
			return
		}
		ws.loader = l
	} else {
		// reload from same source, presumably changed.
		t, err = ws.loader.Load()
		if err != nil {
			write500(w, err)
			return
		}
	}
	ws.tutorial = t
	http.Redirect(w, r, "/", http.StatusSeeOther)
}

func (ws *Server) showControlPage(w http.ResponseWriter, r *http.Request) {
	session, err := ws.store.Get(r, cookieName)
	if err != nil {
		write500(w, err)
		return
	}
	app := webapp.NewWebApp(assureSessionId(session), r.Host, ws.tutorial)
	err = session.Save(r, w)
	if err != nil {
		write500(w, err)
		return
	}
	if err := app.Render(w); err != nil {
		write500(w, err)
		return
	}
}

func (ws *Server) showDebugPage(w http.ResponseWriter, r *http.Request) {
	ws.tutorial.Accept(model.NewTutorialTxtPrinter(w))
	p := program.NewProgramFromTutorial(base.WildCardLabel, ws.tutorial)
	fmt.Fprintf(w, "\n\nfile count %d\n\n", len(p.Lessons()))
	for i, lesson := range p.Lessons() {
		fmt.Fprintf(w, "file %d: %s\n", i, lesson.Path())
		for j, b := range lesson.Blocks() {
			fmt.Fprintf(w, "  block %d, content: %s\n",
				j, util.SampleString(b.Code().String(), 50))
		}
	}
}

// Returns a writer one can write a code block to for execution.
// First tries to find a session socket.  Failing that, try to find
// a locally running instance of tmux.  Failing that, returns a
// writer that discards the code.
func (ws *Server) getCodeRunner(sessId webapp.TypeSessId) io.Writer {
	c := ws.connections[sessId]
	if c != nil {
		glog.Infof("Socket found for ID %v", sessId)
		return c
	}
	t := tmux.NewTmux(tmux.Path)
	if t.IsUp() {
		glog.Info("No socket, writing to local tmux.")
		return t
	}
	glog.Info("No sockets, tmux not up, discarding code.")
	return ioutil.Discard
}

func (ws *Server) attemptTmuxWrite(b *program.BlockPgm) error {
	t := tmux.NewTmux(tmux.Path)
	if !t.IsUp() {
		return errors.New("No local tmux to write to.")
	}
	_, err := t.Write(b.Code().Bytes())
	return err
}

func inRange(w http.ResponseWriter, name string, arg, n int) bool {
	if arg >= 0 || arg < n {
		return true
	}
	http.Error(w,
		fmt.Sprintf("%s %d out of range 0-%d",
			name, arg, n-1), http.StatusBadRequest)
	return false
}

func (ws *Server) makeBlockRunner() func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		session, err := ws.store.Get(r, cookieName)
		if err != nil {
			write500(w, err)
			return
		}
		sessId := assureSessionId(session)
		indexFile := getIntParam("fid", r, -1)
		glog.Info("fid = ", indexFile)
		indexBlock := getIntParam("bid", r, -1)
		glog.Info("bid = ", indexBlock)

		p := program.NewProgramFromTutorial(base.WildCardLabel, ws.tutorial)
		if !inRange(w, "fid", indexFile, len(p.Lessons())) {
			return
		}
		lesson := p.Lessons()[indexFile]
		if !inRange(w, "bid", indexBlock, len(lesson.Blocks())) {
			return
		}
		block := lesson.Blocks()[indexBlock]

		err = nil
		c := ws.connections[sessId]
		if c == nil {
			glog.Infof("no socket for session %v", sessId)
		} else {
			_, err := c.Write(block.Code().Bytes())
			if err != nil {
				glog.Infof("socket write failed: %v", err)
				delete(ws.connections, sessId)
			}
		}
		if c == nil || err != nil {
			err = ws.attemptTmuxWrite(block)
			if err != nil {
				glog.Infof("tmux write failed: %v", err)
				// nothing more to try
			}
		}

		session.Values["file"] = strconv.Itoa(indexFile)
		session.Values["block"] = strconv.Itoa(indexBlock)
		err = session.Save(r, w)
		if err != nil {
			glog.Errorf("Unable to save session: %v", err)
		}
		fmt.Fprintln(w, "Ok")
	}
}

func (ws *Server) favicon(w http.ResponseWriter, r *http.Request) {
	util.Lissajous(w, 7, 3, 1)
}

func (ws *Server) image(w http.ResponseWriter, r *http.Request) {
	session, _ := ws.store.Get(r, cookieName)
	session.Save(r, w)
	util.Lissajous(w,
		getIntParam("s", r, 300),
		getIntParam("c", r, 30),
		getIntParam("n", r, 100))
}

func getIntParam(n string, r *http.Request, d int) int {
	v, err := strconv.Atoi(r.URL.Query().Get(n))
	if err != nil {
		return d
	}
	return v
}

func (ws *Server) quit(w http.ResponseWriter, r *http.Request) {
	close(ws.connReaperQuitCh)
	os.Exit(0)
}

const (
	maxConnectionIdleTime    = 10 * time.Minute
	connectionScanWaitPeriod = 5 * time.Minute
)

// Look for and close idle websockets.
func (ws *Server) closeStaleConnections() {
	for s, c := range ws.connections {
		if time.Since(c.lastUse) > maxConnectionIdleTime {
			glog.Info(
				"Time since last use in session %v exceeds %v; closing.",
				s, maxConnectionIdleTime)
			c.conn.Close()
			delete(ws.connections, s)
		}
	}
}

// reapConnections periodically scans websockets for idleness.
// It also closes everything and quits scanning if quit signal received.
func (ws *Server) reapConnections() {
	for {
		ws.closeStaleConnections()
		select {
		case <-time.After(connectionScanWaitPeriod):
		case <-ws.connReaperQuitCh:
			glog.Info("Received quit, reaping all connections.")
			for s, c := range ws.connections {
				c.conn.Close()
				delete(ws.connections, s)
			}
			return
		}
	}
}

// Serve offers an http service.
func (ws *Server) Serve(hostAndPort string) {
	r := mux.NewRouter()
	r.HandleFunc("/r", ws.reload)
	r.HandleFunc("/r/", ws.reload)
	r.HandleFunc("/r/{gitclone:.*}", ws.reload)
	r.HandleFunc("/runblock", ws.makeBlockRunner())
	r.HandleFunc("/debug", ws.showDebugPage)
	r.HandleFunc("/ws", ws.openWebSocket)
	r.HandleFunc("/favicon.ico", ws.favicon)
	r.HandleFunc("/image", ws.image)
	r.HandleFunc("/q", ws.quit)
	r.HandleFunc("/", ws.showControlPage)
	ws.tutorial, _ = ws.loader.Load()
	glog.Info("Serving at " + hostAndPort)
	glog.Fatal(http.ListenAndServe(hostAndPort, r))
}
<commit_msg>moresession<commit_after>package webserver

import (
	"crypto/rand"
	"errors"
	"fmt"
	"net/http"
	"os"
	"strconv"
	"time"

	"github.com/golang/glog"
	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"github.com/gorilla/websocket"
	"github.com/monopole/mdrip/base"
	"github.com/monopole/mdrip/loader"
	"github.com/monopole/mdrip/model"
	"github.com/monopole/mdrip/program"
	"github.com/monopole/mdrip/tmux"
	"github.com/monopole/mdrip/util"
	"github.com/monopole/mdrip/webapp"
)

type myConn struct {
	conn    *websocket.Conn
	lastUse time.Time
}

func (c *myConn) Write(bytes []byte) (n int, err error) {
	glog.Info("Attempting socket write.")
	c.lastUse = time.Now()
	err = c.conn.WriteMessage(websocket.TextMessage, bytes)
	if err != nil {
		glog.Error("bad socket write:", err)
		return 0, err
	}
	glog.Info("Socket seemed to work.")
	return len(bytes), nil
}

type Server struct {
	loader           *loader.Loader
	tutorial         model.Tutorial
	store            sessions.Store
	upgrader         websocket.Upgrader
	connections      map[webapp.TypeSessId]*myConn
	connReaperQuitCh chan bool
}

const (
	cookieName = "mdrip"
	keySessId  = "sessId"
)

// var keyAuth = securecookie.GenerateRandomKey(16)
var keyAuth = []byte("static-visible-secret")
var keyEncrypt = []byte(nil)

func NewServer(l *loader.Loader) (*Server, error) {
	s := sessions.NewCookieStore(keyAuth, keyEncrypt)
	s.Options = &sessions.Options{
		Domain:   "localhost",
		Path:     "/",
		MaxAge:   3600 * 8, // 8 hours
		HttpOnly: true,
	}
	result := &Server{
		l,
		nil,
		s,
		websocket.Upgrader{},
		make(map[webapp.TypeSessId]*myConn),
		make(chan bool),
	}
	go result.reapConnections()
	return result, nil
}

func getSessionId(s *sessions.Session) webapp.TypeSessId {
	if c, ok := s.Values[keySessId].(string); ok {
		return webapp.TypeSessId(c)
	}
	return ""
}

func assureSessionId(s *sessions.Session) webapp.TypeSessId {
	c := getSessionId(s)
	if c == "" {
		c = makeSessionId()
		s.Values[keySessId] = string(c)
	}
	return c
}

func makeSessionId() webapp.TypeSessId {
	b := make([]byte, 5)
	if _, err := rand.Read(b); err != nil {
		panic(err)
	}
	return webapp.TypeSessId(fmt.Sprintf("%X", b))
}

func getSessionIdParam(n string, r *http.Request) (webapp.TypeSessId, error) {
	v := r.URL.Query().Get(n)
	if v == "" {
		return "", errors.New("no session Id")
	}
	return webapp.TypeSessId(v), nil
}

// Pull session Id out of request, create a socket connection,
// store connection in a map.  The block runner will attempt to
// find the connection and write to it, else fall back to its
// other behaviors.
func (ws *Server) openWebSocket(w http.ResponseWriter, r *http.Request) {
	sessId, err := getSessionIdParam("id", r)
	if err != nil {
		glog.Errorf("no session Id: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	existingConn := ws.connections[sessId]
	var c *websocket.Conn
	if existingConn != nil {
		glog.Infof("Reusing live session %v found when asking for new session.", sessId)
		// Possibly the other side shutdown and restarted.
		// Could close and make new one,
		//  c.conn.Close()
		//  delete(ws.connections, sessId)
		// but try to reuse
		c = existingConn.conn
	} else {
		glog.Infof("Attempting to upgrade session %v to a websocket.", sessId)
		c, err = ws.upgrader.Upgrade(w, r, nil)
	}
	if err != nil {
		glog.Errorf("unable to upgrade for session %v: %v", sessId, err)
		write500(w, err)
		return
	}
	glog.Infof("established websocket for session %v", sessId)
	go func() {
		_, message, err := c.ReadMessage()
		if err == nil {
			glog.Info("handshake: ", string(message))
		} else {
			glog.Info("websocket err: ", err)
		}
	}()
	ws.connections[sessId] = &myConn{c, time.Now()}
}

func write500(w http.ResponseWriter, e error) {
	http.Error(w, e.Error(), http.StatusInternalServerError)
}

func (ws *Server) reload(w http.ResponseWriter, r *http.Request) {
	session, err := ws.store.Get(r, cookieName)
	if err != nil {
		write500(w, err)
		return
	}
	value := mux.Vars(r)["gitclone"]
	if len(value) < 1 {
		value = r.URL.Query().Get("q")
	}
	var t model.Tutorial
	if len(value) > 0 {
		// Load data from new source.
		ds, err := base.NewDataSource([]string{value})
		if err != nil {
			http.Error(w,
				fmt.Sprintf("Bad value %s", value), http.StatusBadRequest)
			return
		}
		l := loader.NewLoader(ds)
		t, err = l.Load()
		if err != nil {
			http.Error(w,
				fmt.Sprintf("Unable to load from %s: %v", ds, err),
				http.StatusBadRequest)
			return
		}
		ws.loader = l
	} else {
		// reload from same source, presumably changed.
		t, err = ws.loader.Load()
		if err != nil {
			write500(w, err)
			return
		}
	}
	err = session.Save(r, w)
	if err != nil {
		glog.Errorf("Unable to save session: %v", err)
	}

	ws.tutorial = t
	http.Redirect(w, r, "/", http.StatusSeeOther)
}

func (ws *Server) showControlPage(w http.ResponseWriter, r *http.Request) {
	session, err := ws.store.Get(r, cookieName)
	if err != nil {
		write500(w, err)
		return
	}
	app := webapp.NewWebApp(assureSessionId(session), r.Host, ws.tutorial)
	err = session.Save(r, w)
	if err != nil {
		write500(w, err)
		return
	}
	if err := app.Render(w); err != nil {
		write500(w, err)
		return
	}
}

func (ws *Server) showDebugPage(w http.ResponseWriter, r *http.Request) {
	session, err := ws.store.Get(r, cookieName)
	if err != nil {
		write500(w, err)
		return
	}
	err = session.Save(r, w)
	ws.tutorial.Accept(model.NewTutorialTxtPrinter(w))
	p := program.NewProgramFromTutorial(base.WildCardLabel, ws.tutorial)
	fmt.Fprintf(w, "\n\nfile count %d\n\n", len(p.Lessons()))
	for i, lesson := range p.Lessons() {
		fmt.Fprintf(w, "file %d: %s\n", i, lesson.Path())
		for j, b := range lesson.Blocks() {
			fmt.Fprintf(w, "  block %d, content: %s\n",
				j, util.SampleString(b.Code().String(), 50))
		}
	}
}

func (ws *Server) attemptTmuxWrite(b *program.BlockPgm) error {
	t := tmux.NewTmux(tmux.Path)
	if !t.IsUp() {
		return errors.New("No local tmux to write to.")
	}
	_, err := t.Write(b.Code().Bytes())
	return err
}

func inRange(w http.ResponseWriter, name string, arg, n int) bool {
	if arg >= 0 || arg < n {
		return true
	}
	http.Error(w,
		fmt.Sprintf("%s %d out of range 0-%d",
			name, arg, n-1), http.StatusBadRequest)
	return false
}

func (ws *Server) makeBlockRunner() func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		session, err := ws.store.Get(r, cookieName)
		if err != nil {
			write500(w, err)
			return
		}
		sessId := assureSessionId(session)
		glog.Info("sessId = ", sessId)
		indexFile := getIntParam("fid", r, -1)
		glog.Info("fid = ", indexFile)
		indexBlock := getIntParam("bid", r, -1)
		glog.Info("bid = ", indexBlock)

		p := program.NewProgramFromTutorial(base.WildCardLabel, ws.tutorial)
		if !inRange(w, "fid", indexFile, len(p.Lessons())) {
			return
		}
		lesson := p.Lessons()[indexFile]
		if !inRange(w, "bid", indexBlock, len(lesson.Blocks())) {
			return
		}
		block := lesson.Blocks()[indexBlock]

		err = nil
		c := ws.connections[sessId]
		if c == nil {
			glog.Infof("no socket for session %v", sessId)
		} else {
			_, err := c.Write(block.Code().Bytes())
			if err != nil {
				glog.Infof("socket write failed: %v", err)
				delete(ws.connections, sessId)
			}
		}
		if c == nil || err != nil {
			err = ws.attemptTmuxWrite(block)
			if err != nil {
				glog.Infof("tmux write failed: %v", err)
				// nothing more to try
			}
		}

		session.Values["file"] = strconv.Itoa(indexFile)
		session.Values["block"] = strconv.Itoa(indexBlock)
		err = session.Save(r, w)
		if err != nil {
			glog.Errorf("Unable to save session: %v", err)
		}
		fmt.Fprintln(w, "Ok")
	}
}

func (ws *Server) favicon(w http.ResponseWriter, r *http.Request) {
	util.Lissajous(w, 7, 3, 1)
}

func (ws *Server) image(w http.ResponseWriter, r *http.Request) {
	session, _ := ws.store.Get(r, cookieName)
	session.Save(r, w)
	util.Lissajous(w,
		getIntParam("s", r, 300),
		getIntParam("c", r, 30),
		getIntParam("n", r, 100))
}

func getIntParam(n string, r *http.Request, d int) int {
	v, err := strconv.Atoi(r.URL.Query().Get(n))
	if err != nil {
		return d
	}
	return v
}

func (ws *Server) quit(w http.ResponseWriter, r *http.Request) {
	close(ws.connReaperQuitCh)
	os.Exit(0)
}

const (
	maxConnectionIdleTime    = 10 * time.Minute
	connectionScanWaitPeriod = 5 * time.Minute
)

// Look for and close idle websockets.
func (ws *Server) closeStaleConnections() {
	for s, c := range ws.connections {
		if time.Since(c.lastUse) > maxConnectionIdleTime {
			glog.Infof(
				"Time since last use in session %v exceeds %v; closing.",
				s, maxConnectionIdleTime)
			c.conn.Close()
			delete(ws.connections, s)
		}
	}
}

// reapConnections periodically scans websockets for idleness.
// It also closes everything and quits scanning if quit signal received.
func (ws *Server) reapConnections() {
	for {
		ws.closeStaleConnections()
		select {
		case <-time.After(connectionScanWaitPeriod):
		case <-ws.connReaperQuitCh:
			glog.Info("Received quit, reaping all connections.")
			for s, c := range ws.connections {
				c.conn.Close()
				delete(ws.connections, s)
			}
			return
		}
	}
}

// Serve offers an http service.
func (ws *Server) Serve(hostAndPort string) {
	r := mux.NewRouter()
	r.HandleFunc("/r", ws.reload)
	r.HandleFunc("/r/", ws.reload)
	r.HandleFunc("/r/{gitclone:.*}", ws.reload)
	r.HandleFunc("/runblock", ws.makeBlockRunner())
	r.HandleFunc("/debug", ws.showDebugPage)
	r.HandleFunc("/ws", ws.openWebSocket)
	r.HandleFunc("/favicon.ico", ws.favicon)
	r.HandleFunc("/image", ws.image)
	r.HandleFunc("/q", ws.quit)
	r.HandleFunc("/", ws.showControlPage)
	ws.tutorial, _ = ws.loader.Load()
	glog.Info("Serving at " + hostAndPort)
	glog.Fatal(http.ListenAndServe(hostAndPort, r))
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"

	log "github.com/Sirupsen/logrus"
	"github.com/gin-gonic/gin"
)

// ProxyRegister represents a register proxy
type ProxyRegister struct {
	Engine  *gin.Engine
	proxies []Proxy
}

func (p *ProxyRegister) registerMany(proxies []Proxy, breaker *ExtendedCircuitBreakerMeta, beforeHandlers []gin.HandlerFunc, afterHandlers []gin.HandlerFunc) {
	for _, proxy := range proxies {
		p.Register(proxy, breaker, beforeHandlers, afterHandlers)
	}
}

// Register register a new proxy
func (p *ProxyRegister) Register(proxy Proxy, breaker *ExtendedCircuitBreakerMeta, beforeHandlers []gin.HandlerFunc, afterHandlers []gin.HandlerFunc) {
	var handlers []gin.HandlerFunc

	defaultHandler := []gin.HandlerFunc{p.ToHandler(proxy, breaker)}
	handlers = append(defaultHandler, handlers...)

	if len(beforeHandlers) > 0 {
		handlers = append(beforeHandlers, handlers...)
	}

	if len(afterHandlers) > 0 {
		handlers = append(handlers, afterHandlers...)
	}

	if false == p.Exists(proxy) {
		log.WithFields(log.Fields{
			"listen_path": proxy.ListenPath,
		}).Info("Registering a proxy")

		p.Engine.Any(proxy.ListenPath, handlers...)
		p.proxies = append(p.proxies, proxy)
	}
}

// Exists checks if a proxy is already registered in the manager
func (p *ProxyRegister) Exists(proxy Proxy) bool {
	for _, route := range p.proxies {
		if route.ListenPath == proxy.ListenPath {
			return true
		}
	}

	return false
}

// ToHandler turns a proxy configuration into a handler
func (p *ProxyRegister) ToHandler(proxy Proxy, breaker *ExtendedCircuitBreakerMeta) gin.HandlerFunc {
	return func(c *gin.Context) {
		transport := &transport{http.DefaultTransport, breaker, c}
		reverseProxy := NewSingleHostReverseProxy(proxy, transport)
		reverseProxy.ServeHTTP(c.Writer, c.Request)
	}
}
<commit_msg>Taking into consideration the methods<commit_after>package main

import (
	"net/http"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/gin-gonic/gin"
)

const (
	methodAll = "ALL"
)

// ProxyRegister represents a register proxy
type ProxyRegister struct {
	Engine  *gin.Engine
	proxies []Proxy
}

func (p *ProxyRegister) registerMany(proxies []Proxy, breaker *ExtendedCircuitBreakerMeta, beforeHandlers []gin.HandlerFunc, afterHandlers []gin.HandlerFunc) {
	for _, proxy := range proxies {
		p.Register(proxy, breaker, beforeHandlers, afterHandlers)
	}
}

// Register register a new proxy
func (p *ProxyRegister) Register(proxy Proxy, breaker *ExtendedCircuitBreakerMeta, beforeHandlers []gin.HandlerFunc, afterHandlers []gin.HandlerFunc) {
	var handlers []gin.HandlerFunc

	defaultHandler := []gin.HandlerFunc{p.ToHandler(proxy, breaker)}
	handlers = append(defaultHandler, handlers...)

	if len(beforeHandlers) > 0 {
		handlers = append(beforeHandlers, handlers...)
	}

	if len(afterHandlers) > 0 {
		handlers = append(handlers, afterHandlers...)
	}

	if false == p.Exists(proxy) {
		log.WithFields(log.Fields{
			"listen_path": proxy.ListenPath,
		}).Info("Registering a proxy")

		for _, method := range proxy.Methods {
			if strings.ToUpper(method) == methodAll {
				p.Engine.Any(proxy.ListenPath, handlers...)
			}

			p.Engine.Handle(strings.ToUpper(method), proxy.ListenPath, handlers...)
		}

		p.proxies = append(p.proxies, proxy)
	}
}

// Exists checks if a proxy is already registered in the manager
func (p *ProxyRegister) Exists(proxy Proxy) bool {
	for _, route := range p.proxies {
		if route.ListenPath == proxy.ListenPath {
			return true
		}
	}

	return false
}

// ToHandler turns a proxy configuration into a handler
func (p *ProxyRegister) ToHandler(proxy Proxy, breaker *ExtendedCircuitBreakerMeta) gin.HandlerFunc {
	return func(c *gin.Context) {
		transport := &transport{http.DefaultTransport, breaker, c}
		reverseProxy := NewSingleHostReverseProxy(proxy, transport)
		reverseProxy.ServeHTTP(c.Writer, c.Request)
	}
}
<|endoftext|>