<commit_before>// Copyright 2015 Matthew Collins
//
// 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 resource

import (
	"archive/zip"
	"bytes"
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/thinkofdeath/steven/resource/internal"
)

const (
	ResourcesVersion = "1.8.4"
	vanillaURL       = "https://s3.amazonaws.com/Minecraft.Download/versions/%[1]s/%[1]s.jar"
)

var (
	lock           sync.RWMutex
	packs          []*pack
	errMissingFile = errors.New("file not found")
)

type pack struct {
	name string

	files map[string]opener
}

type opener func() (io.ReadCloser, error)

// Open searches through all open resource packs for the requested file.
// If a file exists but fails to open that error will be returned instead
// of the standard 'file not found' but only if another file couldn't be
// found to be used in its place.
func Open(plugin, name string) (io.ReadCloser, error) {
	lock.RLock()
	defer lock.RUnlock()
	var lastErr error
	for i := len(packs) - 1; i >= 0; i-- {
		pck := packs[i]
		if f, ok := pck.files[fmt.Sprintf("assets/%s/%s", plugin, name)]; ok {
			r, err := f()
			if err != nil {
				lastErr = err
				continue
			}
			return r, nil
		}
	}
	if lastErr == nil {
		return nil, errMissingFile
	}
	return nil, lastErr
}

// OpenAll searches through all open resource packs for the requested file.
// If a file exists but fails to open that error will be returned instead
// of the standard 'file not found' but only if another file couldn't be
// found to be used in its place. Unlike Open this will return all matching
// files
func OpenAll(plugin, name string) ([]io.ReadCloser, error) {
	lock.RLock()
	defer lock.RUnlock()
	var lastErr error
	var out []io.ReadCloser
	for i := len(packs) - 1; i >= 0; i-- {
		pck := packs[i]
		if f, ok := pck.files[fmt.Sprintf("assets/%s/%s", plugin, name)]; ok {
			r, err := f()
			if err != nil {
				lastErr = err
				continue
			}
			out = append(out, r)
		}
	}
	if lastErr == nil && len(out) == 0 {
		return nil, errMissingFile
	}
	return out, lastErr
}

// Search searches for files that belong to the passed plugin and exist
// the passed path with the passed extension. This searches all open packs.
func Search(plugin, path, ext string) []string {
	lock.RLock()
	defer lock.RUnlock()
	found := map[string]bool{}
	var lst []string
	search := fmt.Sprintf("assets/%s/%s", plugin, path)
	base := fmt.Sprintf("assets/%s/", plugin)
	for _, pck := range packs {
		for k := range pck.files {
			if !found[k] && strings.HasPrefix(k, search) && strings.HasSuffix(k, ext) {
				found[k] = true
				lst = append(lst, k[len(base):])
			}
		}
	}
	return lst
}

func IsActive(name string) bool {
	lock.RLock()
	defer lock.RUnlock()
	for _, pck := range packs {
		if pck.name == name {
			return true
		}
	}
	return false
}

func RemovePack(name string) {
	lock.RLock()
	defer lock.RUnlock()
	for i, pck := range packs {
		if pck.name == name {
			packs = append(packs[:i], packs[i+1:]...)
			return
		}
	}
}

// TODO(Think) Ideally this package has a way to start the download instead of
// being an init thing. Also should have a way to get progress information.

func init() {
	defLocation := fmt.Sprintf("./resources-%s", ResourcesVersion)
	_, err := os.Stat(defLocation)
	if os.IsNotExist(err) {
		downloadDefault(defLocation)
	}
	if err := fromDir(defLocation); err != nil {
		panic(err)
	}

	fromInternal()
}

type dummyCloser struct {
	*bytes.Reader
}

func (dummyCloser) Close() error { return nil }

func fromInternal() {
	lock.Lock()
	defer lock.Unlock()
	p := &pack{
		name:  "$internal",
		files: map[string]opener{},
	}
	for _, name := range internal.AssetNames() {
		p.files[name] = func() (io.ReadCloser, error) {
			data, err := internal.Asset(name)
			return dummyCloser{bytes.NewReader(data)}, err
		}
	}
	packs = append(packs, p)
}

func fromDir(d string) error {
	lock.Lock()
	defer lock.Unlock()
	p := &pack{
		name:  d,
		files: map[string]opener{},
	}

	err := filepath.Walk(d, func(path string, info os.FileInfo, err error) error {
		if err != nil || info.IsDir() {
			return err
		}
		rel, err := filepath.Rel(d, path)
		if err != nil {
			return err
		}
		rel = strings.Replace(rel, string(filepath.Separator), "/", -1)
		p.files[rel] = func() (io.ReadCloser, error) {
			return os.Open(path)
		}
		return nil
	})
	if err != nil {
		return err
	}

	packs = append(packs, p)
	return nil
}

func LoadZip(name string) error {
	f, err := os.Open(name)
	if err != nil {
		return err
	}
	return fromFile(f, name)
}

func fromFile(f *os.File, name string) error {
	lock.Lock()
	defer lock.Unlock()
	s, err := f.Stat()
	if err != nil {
		return err
	}
	z, err := zip.NewReader(f, s.Size())
	if err != nil {
		return err
	}
	p := &pack{
		name:  name,
		files: map[string]opener{},
	}
	for _, f := range z.File {
		f := f
		p.files[f.Name] = f.Open
	}
	packs = append(packs, p)
	return nil
}

func downloadDefault(target string) {
	fmt.Printf("Obtaining vanilla resources for %s, please wait...\n", ResourcesVersion)
	resp, err := http.Get(fmt.Sprintf(vanillaURL, ResourcesVersion))
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	os.MkdirAll("./", 0777)
	f, err := os.Create(target + ".tmp")
	if err != nil {
		panic(err)
	}
	defer os.Remove(target + ".tmp")
	defer f.Close()
	size, err := io.Copy(f, resp.Body)
	if err != nil {
		panic(err)
	}

	f.Seek(0, 0) // Go back to the start
	fr, err := zip.NewReader(f, size)

	os.MkdirAll(target, 0777)

	// Copy the assets (not the classes) in the new zip
	for _, f := range fr.File {
		if !strings.HasPrefix(f.Name, "assets/") {
			continue
		}
		func() {
			path := filepath.Join(target, f.Name)
			os.MkdirAll(filepath.Dir(path), 0777)
			w, err := os.Create(path)
			if err != nil {
				panic(err)
			}
			defer w.Close()
			r, err := f.Open()
			if err != nil {
				panic(err)
			}
			defer r.Close()
			_, err = io.Copy(w, r)
			if err != nil {
				panic(err)
			}
		}()
	}
}
<commit_msg>resource: fix the wrong internal resource being loaded<commit_after>// Copyright 2015 Matthew Collins
//
// 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 resource

import (
	"archive/zip"
	"bytes"
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/thinkofdeath/steven/resource/internal"
)

const (
	ResourcesVersion = "1.8.4"
	vanillaURL       = "https://s3.amazonaws.com/Minecraft.Download/versions/%[1]s/%[1]s.jar"
)

var (
	lock           sync.RWMutex
	packs          []*pack
	errMissingFile = errors.New("file not found")
)

type pack struct {
	name string

	files map[string]opener
}

type opener func() (io.ReadCloser, error)

// Open searches through all open resource packs for the requested file.
// If a file exists but fails to open that error will be returned instead
// of the standard 'file not found' but only if another file couldn't be
// found to be used in its place.
func Open(plugin, name string) (io.ReadCloser, error) {
	lock.RLock()
	defer lock.RUnlock()
	var lastErr error
	for i := len(packs) - 1; i >= 0; i-- {
		pck := packs[i]
		if f, ok := pck.files[fmt.Sprintf("assets/%s/%s", plugin, name)]; ok {
			r, err := f()
			if err != nil {
				lastErr = err
				continue
			}
			return r, nil
		}
	}
	if lastErr == nil {
		return nil, errMissingFile
	}
	return nil, lastErr
}

// OpenAll searches through all open resource packs for the requested file.
// If a file exists but fails to open that error will be returned instead
// of the standard 'file not found' but only if another file couldn't be
// found to be used in its place. Unlike Open this will return all matching
// files
func OpenAll(plugin, name string) ([]io.ReadCloser, error) {
	lock.RLock()
	defer lock.RUnlock()
	var lastErr error
	var out []io.ReadCloser
	for i := len(packs) - 1; i >= 0; i-- {
		pck := packs[i]
		if f, ok := pck.files[fmt.Sprintf("assets/%s/%s", plugin, name)]; ok {
			r, err := f()
			if err != nil {
				lastErr = err
				continue
			}
			out = append(out, r)
		}
	}
	if lastErr == nil && len(out) == 0 {
		return nil, errMissingFile
	}
	return out, lastErr
}

// Search searches for files that belong to the passed plugin and exist
// the passed path with the passed extension. This searches all open packs.
func Search(plugin, path, ext string) []string {
	lock.RLock()
	defer lock.RUnlock()
	found := map[string]bool{}
	var lst []string
	search := fmt.Sprintf("assets/%s/%s", plugin, path)
	base := fmt.Sprintf("assets/%s/", plugin)
	for _, pck := range packs {
		for k := range pck.files {
			if !found[k] && strings.HasPrefix(k, search) && strings.HasSuffix(k, ext) {
				found[k] = true
				lst = append(lst, k[len(base):])
			}
		}
	}
	return lst
}

func IsActive(name string) bool {
	lock.RLock()
	defer lock.RUnlock()
	for _, pck := range packs {
		if pck.name == name {
			return true
		}
	}
	return false
}

func RemovePack(name string) {
	lock.RLock()
	defer lock.RUnlock()
	for i, pck := range packs {
		if pck.name == name {
			packs = append(packs[:i], packs[i+1:]...)
			return
		}
	}
}

// TODO(Think) Ideally this package has a way to start the download instead of
// being an init thing. Also should have a way to get progress information.

func init() {
	defLocation := fmt.Sprintf("./resources-%s", ResourcesVersion)
	_, err := os.Stat(defLocation)
	if os.IsNotExist(err) {
		downloadDefault(defLocation)
	}
	if err := fromDir(defLocation); err != nil {
		panic(err)
	}

	fromInternal()
}

type dummyCloser struct {
	*bytes.Reader
}

func (dummyCloser) Close() error { return nil }

func fromInternal() {
	lock.Lock()
	defer lock.Unlock()
	p := &pack{
		name:  "$internal",
		files: map[string]opener{},
	}
	for _, name := range internal.AssetNames() {
		name := name
		p.files[name] = func() (io.ReadCloser, error) {
			data, err := internal.Asset(name)
			return dummyCloser{bytes.NewReader(data)}, err
		}
	}
	packs = append(packs, p)
}

func fromDir(d string) error {
	lock.Lock()
	defer lock.Unlock()
	p := &pack{
		name:  d,
		files: map[string]opener{},
	}

	err := filepath.Walk(d, func(path string, info os.FileInfo, err error) error {
		if err != nil || info.IsDir() {
			return err
		}
		rel, err := filepath.Rel(d, path)
		if err != nil {
			return err
		}
		rel = strings.Replace(rel, string(filepath.Separator), "/", -1)
		p.files[rel] = func() (io.ReadCloser, error) {
			return os.Open(path)
		}
		return nil
	})
	if err != nil {
		return err
	}

	packs = append(packs, p)
	return nil
}

func LoadZip(name string) error {
	f, err := os.Open(name)
	if err != nil {
		return err
	}
	return fromFile(f, name)
}

func fromFile(f *os.File, name string) error {
	lock.Lock()
	defer lock.Unlock()
	s, err := f.Stat()
	if err != nil {
		return err
	}
	z, err := zip.NewReader(f, s.Size())
	if err != nil {
		return err
	}
	p := &pack{
		name:  name,
		files: map[string]opener{},
	}
	for _, f := range z.File {
		f := f
		p.files[f.Name] = f.Open
	}
	packs = append(packs, p)
	return nil
}

func downloadDefault(target string) {
	fmt.Printf("Obtaining vanilla resources for %s, please wait...\n", ResourcesVersion)
	resp, err := http.Get(fmt.Sprintf(vanillaURL, ResourcesVersion))
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	os.MkdirAll("./", 0777)
	f, err := os.Create(target + ".tmp")
	if err != nil {
		panic(err)
	}
	defer os.Remove(target + ".tmp")
	defer f.Close()
	size, err := io.Copy(f, resp.Body)
	if err != nil {
		panic(err)
	}

	f.Seek(0, 0) // Go back to the start
	fr, err := zip.NewReader(f, size)

	os.MkdirAll(target, 0777)

	// Copy the assets (not the classes) in the new zip
	for _, f := range fr.File {
		if !strings.HasPrefix(f.Name, "assets/") {
			continue
		}
		func() {
			path := filepath.Join(target, f.Name)
			os.MkdirAll(filepath.Dir(path), 0777)
			w, err := os.Create(path)
			if err != nil {
				panic(err)
			}
			defer w.Close()
			r, err := f.Open()
			if err != nil {
				panic(err)
			}
			defer r.Close()
			_, err = io.Copy(w, r)
			if err != nil {
				panic(err)
			}
		}()
	}
}
<|endoftext|>


<commit_before>package aws

import (
	"github.com/hashicorp/terraform/terraform"
)

type ResourceProvider struct {
}

func (p *ResourceProvider) Configure(map[string]interface{}) error {
	return nil
}

func (p *ResourceProvider) Diff(
	s *terraform.ResourceState,
	c map[string]interface{}) (*terraform.ResourceDiff, error) {
	return nil, nil
}

func (p *ResourceProvider) Resources() []terraform.ResourceType {
	return nil
}
<commit_msg>providers/aws: some mock data<commit_after>package aws

import (
	"github.com/hashicorp/terraform/terraform"
)

type ResourceProvider struct {
}

func (p *ResourceProvider) Configure(map[string]interface{}) error {
	return nil
}

func (p *ResourceProvider) Diff(
	s *terraform.ResourceState,
	c map[string]interface{}) (*terraform.ResourceDiff, error) {
	return &terraform.ResourceDiff{
		Attributes: map[string]*terraform.ResourceAttrDiff{
			"id": &terraform.ResourceAttrDiff{
				Old:         "",
				NewComputed: true,
				RequiresNew: true,
			},
			"created": &terraform.ResourceAttrDiff{
				Old: "false",
				New: "true",
			},
		},
	}, nil
}

func (p *ResourceProvider) Resources() []terraform.ResourceType {
	return []terraform.ResourceType{
		terraform.ResourceType{
			Name: "aws_instance",
		},
	}
}
<|endoftext|>


<commit_before>// Response package provides the core functionality of handling
// the client connection, chunked response and other features
package response
import (
	"net"
	"log"
	"net/http"
	"bufio"
	"fmt"
	"os"
	"crypto/md5"
	"io"
	"strconv"
	"encoding/json"
	"encoding/hex"
	"time"
	header "github.com/DronRathore/goexpress/header"
	cookie "github.com/DronRathore/goexpress/cookie"
	utils "github.com/DronRathore/go-mimes"
)

type NextFunc func(NextFunc)

// Response Structure extends basic http.ResponseWriter interface
// It encapsulates Header and Cookie class for direct access
type Response struct{
	response http.ResponseWriter
	Header *header.Header
	Cookie *cookie.Cookie
	Locals map[string]interface{}
	writer *bufio.ReadWriter
	connection net.Conn
	ended bool
}
// Intialise the Response Struct, requires the Hijacked buffer,
// connection and Response interface
func (res *Response) Init(rs http.ResponseWriter, r *http.Request, w *bufio.ReadWriter, con net.Conn) *Response{
	res.response = rs
	res.writer = w
	res.connection = con
	res.Header = &header.Header{}
	res.Header.Init(rs, r, w)
	res.Cookie = &cookie.Cookie{}
	res.Cookie.Init(res, r)
	res.Locals = make(map[string]interface{})
	res.ended = false
	return res
}

// This function is for internal Use by Cookie Struct
func (res *Response) AddCookie(key string, value string){
	res.Header.AppendCookie(key, value)
}

// Writes a string content to the buffer and immediately flushes the same
func (res *Response) Write(content string) *Response{
	if res.Header.BasicSent() == false && res.Header.CanSendHeader() == true {
		res.Cookie.Finish()
		if sent := res.Header.FlushHeaders(); sent == false {
			log.Print("Failed to push headers")
		}
	}
	var bytes = []byte(content)
	res.WriteBytes(bytes)
	return res
}

// Writes an array of bytes to the socket
func (res *Response) WriteBytes(bytes []byte) *Response {
	var chunkSize = fmt.Sprintf("%x", len(bytes))
	res.writer.WriteString(chunkSize + "\r\n")
	res.writer.Write(bytes)
	res.writer.WriteString("\r\n")
	res.writer.Flush()
	return res
}

func (res *Response) sendContent(status int, content_type string, content []byte) {
	if res.Header.BasicSent() == false {
		res.Header.SetStatus(status)
	}
	if res.Header.CanSendHeader() == true {
		res.Header.Set("Content-Type", content_type)
		res.Cookie.Finish()
		if sent := res.Header.FlushHeaders(); sent == false {
			log.Print("Failed to write headers")
		}
	}
	var chunkSize = fmt.Sprintf("%x", len(content))
	res.writer.WriteString(chunkSize + "\r\n")
	res.writer.Write(content)
	res.writer.WriteString("\r\n")
	res.writer.Writer.Flush()
	res.End()
}
// Reads a file in buffer and writes it to the socket
// It also checks with the existing E-Tags list
// so as to provide caching.
func (res *Response) SendFile(url string, noCache bool) bool {
	if len(url) == 0 {
		// no need to panic ?
		return false
	}
	file, err := os.OpenFile(url, os.O_RDONLY, 0644)
	if err != nil {
		// panic and return false
		log.Print("Cannot open ", url, err)
		return false
	}
	stat, err := file.Stat()
	if err != nil {
		log.Print("Couldn't get fstat of ", url)
		return false
	}
	if stat.IsDir() == true {
		// cannot send dir, abort
		return false
	}
	var modTime = stat.ModTime().Unix()
	var currTime = (time.Now()).Format(time.RFC1123)
	var etag = res.Header.GetRequestHeader("If-None-Match")

	if noCache == false {
		hasher := md5.New()
		io.WriteString(hasher, strconv.FormatInt(modTime, 10))
		hash := hex.EncodeToString(hasher.Sum(nil))
		var miss bool = true
		// do we have an etag
		if len(etag) == 1 {
			if etag[0] == hash {
				// its a hit!
				if res.Header.CanSendHeader() == true {
					var ext = utils.GetMimeType(url)
					res.Header.Set("Content-Type", ext)
					res.Header.SetStatus(304)
					res.Header.Set("Cache-Control", "max-age=300000")
					miss = false
				} else {
					log.Print("Cannot write header after being sent")
				}
			} // a miss
		}

		if miss == true {
			if res.Header.CanSendHeader() == true {
				res.Header.Set("Etag", hash)
				var ext = utils.GetMimeType(url)
				if ext == "" {
					res.Header.Set("Content-Type", "none")
				} else {
					res.Header.Set("Content-Type", ext)
				}
			} else {
				log.Print("Cannot write header after being flushed")
			}
		}

		if res.Header.CanSendHeader() == true {
			res.Header.Set("Date", currTime)
			res.Cookie.Finish()
			res.Header.FlushHeaders()
		}
		if miss == false {
			// empty response for cache hit
			res.End()
			return true
		}
	}

	var offset int64 = 0
	// async read and write
	var reader NextFunc
	var channel = make(chan bool)
	reader = func(reader NextFunc){
		go func(channel chan bool){
			var data = make([]byte, 1500)
			n, err := file.ReadAt(data, offset)
			if err != nil {
				if err == io.EOF {
					res.WriteBytes(data[:n-1])
					res.End()
					channel<-true
					return
				}
				log.Print("Error while reading ", url, err)
				res.End()
				channel<-true
				return
			} else {
				if n == 0 {
					res.End()
					channel<-true
					return
				}
				res.WriteBytes(data)
				offset = offset + int64(n)
				reader(reader)
				return
			}
		}(channel)
	}
	reader(reader)
	<-channel
	return true
}

// Ends a response and drops the connection with client
func (res *Response) End(){
	res.writer.WriteString("0\r\n\r\n")
	res.writer.Flush()
	err := res.connection.Close()
	res.ended = true
	if err != nil {
		log.Print("Couldn't close the connection, already lost?")
	}
}

// Redirects a request, takes the url as the Location
func (res *Response) Redirect(url string) *Response{
	res.Header.SetStatus(301)
	res.Header.Set("Location", url)
	res.Header.FlushHeaders()
	res.ended = true
	res.End()
	return res
}

// An internal package use function to check the state of connection
func (res *Response) HasEnded() bool{
	return res.ended
}

// A helper for middlewares to get the original http.ResponseWriter
func (res *Response) GetRaw () http.ResponseWriter{
	return res.response
}

// A helper for middlewares to get the original net.Conn
func (res *Response) GetConnection () net.Conn {
	return res.connection
}

// A helper for middlewares to get the original Request buffer
func (res *Response) GetBuffer () *bufio.ReadWriter {
	return res.writer
}

// Send Error, takes HTTP status and a string content
func (res *Response) Error (status int, str string) {
	res.sendContent(status, "text/html", []byte(str))
}

// Send JSON response, takes interface as input
func (res *Response) JSON(content interface{}){
	output, err := json.Marshal(content)
	if err != nil {
		res.sendContent(500, "application/json", []byte(""))
	} else {
		res.sendContent(200, "application/json", output)
	}
}
<commit_msg>send 404 instead of just an error<commit_after>// Response package provides the core functionality of handling
// the client connection, chunked response and other features
package response
import (
	"net"
	"log"
	"net/http"
	"bufio"
	"fmt"
	"os"
	"crypto/md5"
	"io"
	"strconv"
	"encoding/json"
	"encoding/hex"
	"time"
	header "github.com/DronRathore/goexpress/header"
	cookie "github.com/DronRathore/goexpress/cookie"
	utils "github.com/DronRathore/go-mimes"
)

type NextFunc func(NextFunc)

// Response Structure extends basic http.ResponseWriter interface
// It encapsulates Header and Cookie class for direct access
type Response struct{
	response http.ResponseWriter
	Header *header.Header
	Cookie *cookie.Cookie
	Locals map[string]interface{}
	writer *bufio.ReadWriter
	connection net.Conn
	ended bool
}
// Intialise the Response Struct, requires the Hijacked buffer,
// connection and Response interface
func (res *Response) Init(rs http.ResponseWriter, r *http.Request, w *bufio.ReadWriter, con net.Conn) *Response{
	res.response = rs
	res.writer = w
	res.connection = con
	res.Header = &header.Header{}
	res.Header.Init(rs, r, w)
	res.Cookie = &cookie.Cookie{}
	res.Cookie.Init(res, r)
	res.Locals = make(map[string]interface{})
	res.ended = false
	return res
}

// This function is for internal Use by Cookie Struct
func (res *Response) AddCookie(key string, value string){
	res.Header.AppendCookie(key, value)
}

// Writes a string content to the buffer and immediately flushes the same
func (res *Response) Write(content string) *Response{
	if res.Header.BasicSent() == false && res.Header.CanSendHeader() == true {
		res.Cookie.Finish()
		if sent := res.Header.FlushHeaders(); sent == false {
			log.Print("Failed to push headers")
		}
	}
	var bytes = []byte(content)
	res.WriteBytes(bytes)
	return res
}

// Writes an array of bytes to the socket
func (res *Response) WriteBytes(bytes []byte) *Response {
	var chunkSize = fmt.Sprintf("%x", len(bytes))
	res.writer.WriteString(chunkSize + "\r\n")
	res.writer.Write(bytes)
	res.writer.WriteString("\r\n")
	res.writer.Flush()
	return res
}

func (res *Response) sendContent(status int, content_type string, content []byte) {
	if res.Header.BasicSent() == false {
		res.Header.SetStatus(status)
	}
	if res.Header.CanSendHeader() == true {
		res.Header.Set("Content-Type", content_type)
		res.Cookie.Finish()
		if sent := res.Header.FlushHeaders(); sent == false {
			log.Print("Failed to write headers")
		}
	}
	var chunkSize = fmt.Sprintf("%x", len(content))
	res.writer.WriteString(chunkSize + "\r\n")
	res.writer.Write(content)
	res.writer.WriteString("\r\n")
	res.writer.Writer.Flush()
	res.End()
}
// Reads a file in buffer and writes it to the socket
// It also checks with the existing E-Tags list
// so as to provide caching.
func (res *Response) SendFile(url string, noCache bool) bool {
	if len(url) == 0 {
		// no need to panic ?
		return false
	}
	file, err := os.OpenFile(url, os.O_RDONLY, 0644)
	if err != nil {
		// panic and return false
		log.Print("File not found ", url, err)
		res.Header.SetStatus(404)
		res.Header.FlushHeaders()
		res.End()
		return false
	}
	stat, err := file.Stat()
	if err != nil {
		log.Print("Couldn't get fstat of ", url)
		return false
	}
	if stat.IsDir() == true {
		// cannot send dir, abort
		return false
	}
	var modTime = stat.ModTime().Unix()
	var currTime = (time.Now()).Format(time.RFC1123)
	var etag = res.Header.GetRequestHeader("If-None-Match")

	if noCache == false {
		hasher := md5.New()
		io.WriteString(hasher, strconv.FormatInt(modTime, 10))
		hash := hex.EncodeToString(hasher.Sum(nil))
		var miss bool = true
		// do we have an etag
		if len(etag) == 1 {
			if etag[0] == hash {
				// its a hit!
				if res.Header.CanSendHeader() == true {
					var ext = utils.GetMimeType(url)
					res.Header.Set("Content-Type", ext)
					res.Header.SetStatus(304)
					res.Header.Set("Cache-Control", "max-age=300000")
					miss = false
				} else {
					res.End()
					log.Print("Cannot write header after being sent")
					return false
				}
			} // a miss
		}

		if miss == true {
			if res.Header.CanSendHeader() == true {
				res.Header.Set("Etag", hash)
				var ext = utils.GetMimeType(url)
				if ext == "" {
					res.Header.Set("Content-Type", "none")
				} else {
					res.Header.Set("Content-Type", ext)
				}
			} else {
				res.End()
				log.Print("Cannot write header after being flushed")
				return false
			}
		}

		if res.Header.CanSendHeader() == true {
			res.Header.Set("Date", currTime)
			res.Cookie.Finish()
			res.Header.FlushHeaders()
		} else {
			res.End()
			log.Print("Cannot write header after being flushed")
			return false
		}
		if miss == false {
			// empty response for cache hit
			res.End()
			return true
		}
	}

	var offset int64 = 0
	// async read and write
	var reader NextFunc
	var channel = make(chan bool)
	reader = func(reader NextFunc){
		go func(channel chan bool){
			var data = make([]byte, 1500)
			n, err := file.ReadAt(data, offset)
			if err != nil {
				if err == io.EOF {
					res.WriteBytes(data[:n-1])
					res.End()
					channel<-true
					return
				}
				log.Print("Error while reading ", url, err)
				res.End()
				channel<-true
				return
			} else {
				if n == 0 {
					res.End()
					channel<-true
					return
				}
				res.WriteBytes(data)
				offset = offset + int64(n)
				reader(reader)
				return
			}
		}(channel)
	}
	reader(reader)
	<-channel
	return true
}

// Ends a response and drops the connection with client
func (res *Response) End(){
	res.writer.WriteString("0\r\n\r\n")
	res.writer.Flush()
	err := res.connection.Close()
	res.ended = true
	if err != nil {
		log.Print("Couldn't close the connection, already lost?")
	}
}

// Redirects a request, takes the url as the Location
func (res *Response) Redirect(url string) *Response{
	res.Header.SetStatus(301)
	res.Header.Set("Location", url)
	res.Header.FlushHeaders()
	res.ended = true
	res.End()
	return res
}

// An internal package use function to check the state of connection
func (res *Response) HasEnded() bool{
	return res.ended
}

// A helper for middlewares to get the original http.ResponseWriter
func (res *Response) GetRaw () http.ResponseWriter{
	return res.response
}

// A helper for middlewares to get the original net.Conn
func (res *Response) GetConnection () net.Conn {
	return res.connection
}

// A helper for middlewares to get the original Request buffer
func (res *Response) GetBuffer () *bufio.ReadWriter {
	return res.writer
}

// Send Error, takes HTTP status and a string content
func (res *Response) Error (status int, str string) {
	res.sendContent(status, "text/html", []byte(str))
}

// Send JSON response, takes interface as input
func (res *Response) JSON(content interface{}){
	output, err := json.Marshal(content)
	if err != nil {
		res.sendContent(500, "application/json", []byte(""))
	} else {
		res.sendContent(200, "application/json", output)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"regexp"
	"testing"
)

type RunTest struct {
	json    string
	regexps []string
}

func Test_Dot(t *testing.T) {
	allMatch := []string{
		"(?s)digraph docker {.*}",
		`(?m) base \[style=invisible\]`,
	}
	allRegex := compileRegexps(t, allMatch)

	dotTests := []RunTest{
		RunTest{
			json: `[{ "VirtualSize": 662553464, "Size": 662553464, "RepoTags": [ "<none>:<none>" ], "ParentId": "", "Id": "4c1208b690c68af3476b437e7bc2bcc460f062bda2094d2d8f21a7e70368d358", "Created": 1386114144 }]`,
			regexps: []string{
				`base -> "4c1208b690c6"`,
			},
		},
	}

	for _, dotTest := range dotTests {
		im, _ := parseJSON([]byte(dotTest.json))
		result := jsonToDot(im)

		for _, regexp := range allRegex {
			if !regexp.MatchString(result) {
				t.Fatalf("images dot content '%s' did not match regexp '%s'", result, regexp)
			}
		}

		for _, regexp := range compileRegexps(t, dotTest.regexps) {
			if !regexp.MatchString(result) {
				t.Fatalf("images dot content '%s' did not match regexp '%s'", result, regexp)
			}
		}
	}
}

func compileRegexps(t *testing.T, regexpStrings []string) []*regexp.Regexp {

	compiledRegexps := []*regexp.Regexp{}
	for _, regexpString := range regexpStrings {
		regexp, err := regexp.Compile(regexpString)
		if err != nil {
			t.Errorf("Error in regex string '%s': %s", regexpString, err)
		}
		compiledRegexps = append(compiledRegexps, regexp)
	}

	return compiledRegexps
}
<commit_msg>more complete test for graphviz output<commit_after>package main

import (
	"regexp"
	"testing"
)

type RunTest struct {
	json    string
	regexps []string
}

func Test_Dot(t *testing.T) {
	allMatch := []string{
		"(?s)digraph docker {.*}",
		`(?m) base \[style=invisible\]`,
	}
	allRegex := compileRegexps(t, allMatch)

	dotTests := []RunTest{
		RunTest{
			json: `[{ "VirtualSize": 662553464, "Size": 662553464, "RepoTags": [ "<none>:<none>" ], "ParentId": "", "Id": "4c1208b690c68af3476b437e7bc2bcc460f062bda2094d2d8f21a7e70368d358", "Created": 1386114144 }]`,
			regexps: []string{
				`base -> "4c1208b690c6"`,
			},
		},
		RunTest{
			json: `[{ "VirtualSize": 662553464, "Size": 0, "RepoTags": [ "foo:latest" ], "ParentId": "735f5db5626147582d2ae3f2c87be8e5e697c088574c5faaf8d4d1bccab99470", "Id": "c87be8e5e697c735f5db5626147582d2ae3f2088574c5faaf8d4d1bccab99470", "Created": 1386142123 },{ "VirtualSize": 662553464, "Size": 0, "RepoTags": [ "<none>:<none>" ], "ParentId": "4c1208b690c68af3476b437e7bc2bcc460f062bda2094d2d8f21a7e70368d358", "Id": "735f5db5626147582d2ae3f2c87be8e5e697c088574c5faaf8d4d1bccab99470", "Created": 1386142123 },{ "VirtualSize": 662553464, "Size": 662553464, "RepoTags": [ "<none>:<none>" ], "ParentId": "", "Id": "4c1208b690c68af3476b437e7bc2bcc460f062bda2094d2d8f21a7e70368d358", "Created": 1386114144 }]`,
			regexps: []string{
				`base -> "4c1208b690c6"`,
				`"4c1208b690c6" -> "735f5db56261"`,
				`"c87be8e5e697" \[label="c87be8e5e697\\nfoo:latest"`,
			},
		},
	}

	for _, dotTest := range dotTests {
		im, _ := parseJSON([]byte(dotTest.json))
		result := jsonToDot(im)

		for _, regexp := range allRegex {
			if !regexp.MatchString(result) {
				t.Fatalf("images dot content '%s' did not match regexp '%s'", result, regexp)
			}
		}

		for _, regexp := range compileRegexps(t, dotTest.regexps) {
			if !regexp.MatchString(result) {
				t.Fatalf("images dot content '%s' did not match regexp '%s'", result, regexp)
			}
		}
	}
}

func compileRegexps(t *testing.T, regexpStrings []string) []*regexp.Regexp {

	compiledRegexps := []*regexp.Regexp{}
	for _, regexpString := range regexpStrings {
		regexp, err := regexp.Compile(regexpString)
		if err != nil {
			t.Errorf("Error in regex string '%s': %s", regexpString, err)
		}
		compiledRegexps = append(compiledRegexps, regexp)
	}

	return compiledRegexps
}
<|endoftext|>


<commit_before>package main

import (
	"regexp"
	"testing"
)

type DotTest struct {
	json    string
	regexps []string
}

type TreeTest struct {
	json       string
	startImage string
	noTrunc    bool
	regexps    []string
}

func Test_BadJSON(t *testing.T) {
	_, err := parseJSON([]byte(` "VirtualSize": 662553464, "Size": 662553464, "RepoTags": [ "<none>:<none>" ], "ParentId": "", "Id": "4c1208b690c68af3476b437e7bc2bcc460f062bda2094d2d8f21a7e70368d358", "Created": 1386114144 }]`))

	if err == nil {
		t.Error("invalid json did not cause an error")
	}
}

func Test_Dot(t *testing.T) {
	allMatch := []string{
		"(?s)digraph docker {.*}",
		`(?m) base \[style=invisible\]`,
	}
	allRegex := compileRegexps(t, allMatch)

	dotTests := []DotTest{
		DotTest{
			json: `[{ "VirtualSize": 662553464, "Size": 662553464, "RepoTags": [ "<none>:<none>" ], "ParentId": "", "Id": "4c1208b690c68af3476b437e7bc2bcc460f062bda2094d2d8f21a7e70368d358", "Created": 1386114144 }]`,
			regexps: []string{
				`base -> "4c1208b690c6"`,
			},
		},
		DotTest{
			json: `[{ "VirtualSize": 662553464, "Size": 0, "RepoTags": [ "foo:latest" ], "ParentId": "735f5db5626147582d2ae3f2c87be8e5e697c088574c5faaf8d4d1bccab99470", "Id": "c87be8e5e697c735f5db5626147582d2ae3f2088574c5faaf8d4d1bccab99470", "Created": 1386142123 },{ "VirtualSize": 662553464, "Size": 0, "RepoTags": [ "<none>:<none>" ], "ParentId": "4c1208b690c68af3476b437e7bc2bcc460f062bda2094d2d8f21a7e70368d358", "Id": "735f5db5626147582d2ae3f2c87be8e5e697c088574c5faaf8d4d1bccab99470", "Created": 1386142123 },{ "VirtualSize": 662553464, "Size": 662553464, "RepoTags": [ "<none>:<none>" ], "ParentId": "", "Id": "4c1208b690c68af3476b437e7bc2bcc460f062bda2094d2d8f21a7e70368d358", "Created": 1386114144 }]`,
			regexps: []string{
				`base -> "4c1208b690c6"`,
				`"4c1208b690c6" -> "735f5db56261"`,
				`"c87be8e5e697" \[label="c87be8e5e697\\nfoo:latest"`,
			},
		},
	}

	for _, dotTest := range dotTests {
		im, _ := parseJSON([]byte(dotTest.json))
		result := jsonToDot(im)

		for _, regexp := range allRegex {
			if !regexp.MatchString(result) {
				t.Fatalf("images dot content '%s' did not match regexp '%s'", result, regexp)
			}
		}

		for _, regexp := range compileRegexps(t, dotTest.regexps) {
			if !regexp.MatchString(result) {
				t.Fatalf("images dot content '%s' did not match regexp '%s'", result, regexp)
			}
		}
	}
}

func Test_Tree(t *testing.T) {
	treeTests := []TreeTest{
		TreeTest{
			json:       `[{ "VirtualSize": 662553464, "Size": 0, "RepoTags": [ "foo:latest" ], "ParentId": "735f5db5626147582d2ae3f2c87be8e5e697c088574c5faaf8d4d1bccab99470", "Id": "c87be8e5e697c735f5db5626147582d2ae3f2088574c5faaf8d4d1bccab99470", "Created": 1386142123 },{ "VirtualSize": 662553464, "Size": 0, "RepoTags": [ "<none>:<none>" ], "ParentId": "4c1208b690c68af3476b437e7bc2bcc460f062bda2094d2d8f21a7e70368d358", "Id": "735f5db5626147582d2ae3f2c87be8e5e697c088574c5faaf8d4d1bccab99470", "Created": 1386142123 },{ "VirtualSize": 662553464, "Size": 662553464, "RepoTags": [ "<none>:<none>" ], "ParentId": "", "Id": "4c1208b690c68af3476b437e7bc2bcc460f062bda2094d2d8f21a7e70368d358", "Created": 1386114144 }]`,
			startImage: "",
			noTrunc:    false,
			regexps: []string{
				`(?m)└─4c1208b690c6`,
				`(?m)  └─735f5db56261`,
				`(?m)    └─c87be8e5e697`,
			},
		},
	}

	for _, treeTest := range treeTests {
		im, _ := parseJSON([]byte(treeTest.json))
		result := jsonToTree(im, treeTest.startImage, treeTest.noTrunc)

		for _, regexp := range compileRegexps(t, treeTest.regexps) {
			if !regexp.MatchString(result) {
				t.Fatalf("images tree content '%s' did not match regexp '%s'", result, regexp)
			}
		}
	}
}

func compileRegexps(t *testing.T, regexpStrings []string) []*regexp.Regexp {

	compiledRegexps := []*regexp.Regexp{}
	for _, regexpString := range regexpStrings {
		regexp, err := regexp.Compile(regexpString)
		if err != nil {
			t.Errorf("Error in regex string '%s': %s", regexpString, err)
		}
		compiledRegexps = append(compiledRegexps, regexp)
	}

	return compiledRegexps
}
<commit_msg>full coverage on image tree code<commit_after>package main

import (
	"regexp"
	"testing"
)

type DotTest struct {
	json    string
	regexps []string
}

type TreeTest struct {
	json       string
	startImage string
	noTrunc    bool
	regexps    []string
}

func Test_BadJSON(t *testing.T) {
	_, err := parseJSON([]byte(` "VirtualSize": 662553464, "Size": 662553464, "RepoTags": [ "<none>:<none>" ], "ParentId": "", "Id": "4c1208b690c68af3476b437e7bc2bcc460f062bda2094d2d8f21a7e70368d358", "Created": 1386114144 }]`))

	if err == nil {
		t.Error("invalid json did not cause an error")
	}
}

func Test_Dot(t *testing.T) {
	allMatch := []string{
		"(?s)digraph docker {.*}",
		`(?m) base \[style=invisible\]`,
	}
	allRegex := compileRegexps(t, allMatch)

	dotTests := []DotTest{
		DotTest{
			json: `[{ "VirtualSize": 662553464, "Size": 662553464, "RepoTags": [ "<none>:<none>" ], "ParentId": "", "Id": "4c1208b690c68af3476b437e7bc2bcc460f062bda2094d2d8f21a7e70368d358", "Created": 1386114144 }]`,
			regexps: []string{
				`base -> "4c1208b690c6"`,
			},
		},
		DotTest{
			json: `[{ "VirtualSize": 662553464, "Size": 0, "RepoTags": [ "foo:latest" ], "ParentId": "735f5db5626147582d2ae3f2c87be8e5e697c088574c5faaf8d4d1bccab99470", "Id": "c87be8e5e697c735f5db5626147582d2ae3f2088574c5faaf8d4d1bccab99470", "Created": 1386142123 },{ "VirtualSize": 662553464, "Size": 0, "RepoTags": [ "<none>:<none>" ], "ParentId": "4c1208b690c68af3476b437e7bc2bcc460f062bda2094d2d8f21a7e70368d358", "Id": "735f5db5626147582d2ae3f2c87be8e5e697c088574c5faaf8d4d1bccab99470", "Created": 1386142123 },{ "VirtualSize": 662553464, "Size": 662553464, "RepoTags": [ "<none>:<none>" ], "ParentId": "", "Id": "4c1208b690c68af3476b437e7bc2bcc460f062bda2094d2d8f21a7e70368d358", "Created": 1386114144 }]`,
			regexps: []string{
				`base -> "4c1208b690c6"`,
				`"4c1208b690c6" -> "735f5db56261"`,
				`"c87be8e5e697" \[label="c87be8e5e697\\nfoo:latest"`,
			},
		},
	}

	for _, dotTest := range dotTests {
		im, _ := parseJSON([]byte(dotTest.json))
		result := jsonToDot(im)

		for _, regexp := range allRegex {
			if !regexp.MatchString(result) {
				t.Fatalf("images dot content '%s' did not match regexp '%s'", result, regexp)
			}
		}

		for _, regexp := range compileRegexps(t, dotTest.regexps) {
			if !regexp.MatchString(result) {
				t.Fatalf("images dot content '%s' did not match regexp '%s'", result, regexp)
			}
		}
	}
}

func Test_Tree(t *testing.T) {
	treeJSON := `[ { "VirtualSize": 662553464, "Size": 0, "RepoTags": [ "foo:latest" ], "ParentId": "735f5db5626147582d2ae3f2c87be8e5e697c088574c5faaf8d4d1bccab99470", "Id": "c87be8e5e697c735f5db5626147582d2ae3f2088574c5faaf8d4d1bccab99470", "Created": 1386142123 }, { "VirtualSize": 682553464, "Size": 0, "RepoTags": [ "<none>:<none>" ], "ParentId": "4c1208b690c68af3476b437e7bc2bcc460f062bda2094d2d8f21a7e70368d358", "Id": "626147582d2ae3735f5db5f2c87be8e5e697c088574c5faaf8d4d1bccab99470", "Created": 1386142123 }, { "VirtualSize": 712553464, "Size": 0, "RepoTags": [ "base:latest" ], "ParentId": "626147582d2ae3735f5db5f2c87be8e5e697c088574c5faaf8d4d1bccab99470", "Id": "574c5faaf8d4d1bccab994626147582d2ae3735f5db5f2c87be8e5e697c08870", "Created": 1386142123 }, { "VirtualSize": 752553464, "Size": 0, "RepoTags": [ "<none>:<none>" ], "ParentId": "574c5faaf8d4d1bccab994626147582d2ae3735f5db5f2c87be8e5e697c08870", "Id": "aaf8d4d1bccab994574c5f626147582d2ae3735f5db5f2c87be8e5e697c08870", "Created": 1386142123 }, { "VirtualSize": 662553464, "Size": 0, "RepoTags": [ "<none>:<none>" ], "ParentId": "4c1208b690c68af3476b437e7bc2bcc460f062bda2094d2d8f21a7e70368d358", "Id": "735f5db5626147582d2ae3f2c87be8e5e697c088574c5faaf8d4d1bccab99470", "Created": 1386142123 }, { "VirtualSize": 662553464, "Size": 662553464, "RepoTags": [ "<none>:<none>" ], "ParentId": "", "Id": "4c1208b690c68af3476b437e7bc2bcc460f062bda2094d2d8f21a7e70368d358", "Created": 1386114144 } ]`

	treeTests := []TreeTest{
		TreeTest{
			json:       treeJSON,
			startImage: "",
			noTrunc:    false,
			regexps: []string{
				`(?m)└─4c1208b690c6`,
				`(?m)  └─735f5db56261`,
				`(?m)    └─c87be8e5e697`,
			},
		},
		TreeTest{
			json:       treeJSON,
			startImage: "626147582d2a",
			noTrunc:    false,
			regexps: []string{
				`(?m)└─626147582d2a`,
				`(?m)  └─574c5faaf8d4`,
				`(?m)    └─aaf8d4d1bcca`,
			},
		},
		TreeTest{
			json:       treeJSON,
			startImage: "base:latest",
			noTrunc:    true,
			regexps: []string{
				`(?m)└─574c5faaf8d4d1bccab994626147582d2ae3735f5db5f2c87be8e5e697c08870`,
				`(?m)  └─aaf8d4d1bccab994574c5f626147582d2ae3735f5db5f2c87be8e5e697c08870`,
			},
		},
	}

	for _, treeTest := range treeTests {
		im, _ := parseJSON([]byte(treeTest.json))
		result := jsonToTree(im, treeTest.startImage, treeTest.noTrunc)

		for _, regexp := range compileRegexps(t, treeTest.regexps) {
			if !regexp.MatchString(result) {
				t.Fatalf("images tree content '%s' did not match regexp '%s'", result, regexp)
			}
		}
	}
}

func compileRegexps(t *testing.T, regexpStrings []string) []*regexp.Regexp {

	compiledRegexps := []*regexp.Regexp{}
	for _, regexpString := range regexpStrings {
		regexp, err := regexp.Compile(regexpString)
		if err != nil {
			t.Errorf("Error in regex string '%s': %s", regexpString, err)
		}
		compiledRegexps = append(compiledRegexps, regexp)
	}

	return compiledRegexps
}
<|endoftext|>


<commit_before>package servers

import (
	"github.com/hawkingrei/g53/utils"
	"github.com/miekg/dns"
	"testing"
	"time"
)

func TestDNSError(t *testing.T) {
	const TestAddr = "127.0.0.1:9954"

	config := utils.NewConfig()
	config.DnsAddr = TestAddr
	config.Nameservers = []string{"123.123.123.123:53","123.321.123.321:53"}

	server := NewDNSServer(config)
	go server.Start()

	// Allow some time for server to start
	time.Sleep(250 * time.Millisecond)

	var inputs = []struct {
		query    string
		expected int
		qType    string
		rcode    int
	}{
		{"google.com.", -1, "A", dns.RcodeRefused},
	}
	c := new(dns.Client)
	c.Timeout = 15 * time.Second
	c.DialTimeout = 15 * time.Second
	c.ReadTimeout = 15 * time.Second
	c.WriteTimeout = 15 * time.Second
	c.UDPSize = uint16(4096)
	for _, input := range inputs {
		qType := dns.StringToType[input.qType]

		m := new(dns.Msg)
		m.SetEdns0(4096, true)
		m.SetQuestion(input.query, qType)
		in, _, err := c.Exchange(m, TestAddr)
		//t.Log(r)
		if err != nil {
			t.Error("Error response from the server", err)
			break
		}
		if in.Rcode != input.rcode {
			t.Error(input, "Rcode expected:",
				dns.RcodeToString[input.rcode],
				" got:", dns.RcodeToString[in.Rcode])
		}
	}
	server.Stop()
	time.Sleep(250 * time.Millisecond)
}

func TestDNSResponse(t *testing.T) {
	const TestAddr = "127.0.0.1:9955"

	config := utils.NewConfig()
	config.DnsAddr = TestAddr

	server := NewDNSServer(config)
	go server.Start()

	// Allow some time for server to start
	time.Sleep(250 * time.Millisecond)

	//server.AddService("www.duitang.net", Service{RecordType: "CNAME", TTL: 600 , Value: "www.cctv.com",Aliases: "www.duitang.net"})
	server.AddService(utils.Service{RecordType: "A", TTL: 600, Value: "127.0.0.1", Aliases: "a.duitang.net"})
	server.AddService(utils.Service{RecordType: "CNAME", TTL: 600, Value: "wiki.duitang.com", Aliases: "b.duitang.net"})
	server.AddService(utils.Service{RecordType: "MX", TTL: 600, Value: "wiki.duitang.com", Aliases: "b.duitang.net"})
	//server.AddService("b.duitang.net", Service{RecordType:"MX",TTL:60,Value:"mxbiz1.qq.com.",Aliases:"b.duitang.net"})
	//server.AddService("foo", Service{Name: "foo", Image: "bar", IPs: []net.IP{net.ParseIP("127.0.0.1")}})
	//server.AddService("baz", Service{Name: "baz", Image: "bar", IPs: []net.IP{net.ParseIP("127.0.0.1")}, TTL: -1})
	//server.AddService("biz", Service{Name: "hey", Image: "", IPs: []net.IP{net.ParseIP("127.0.0.4")}})
	//server.AddService("joe", Service{Name: "joe", Image: "", IPs: []net.IP{net.ParseIP("127.0.0.5")}, Aliases: []string{"lala.docker", "super-alias", "alias.domain"}})

	var inputs = []struct {
		query    string
		expected int
		qType    string
		rcode    int
	}{
		{"hawkingreirrrr.com.", 0, "A", dns.RcodeNameError},
		{"google.com.", -1, "A", dns.RcodeSuccess},
		{"google.com.", -1, "AAAA", dns.RcodeSuccess}, // baidu has AAAA records
		{"google.com.", -1, "MX", dns.RcodeSuccess},
		{"wiki.duitang.net.", -1, "CNAME", dns.RcodeSuccess},
		{"a.duitang.net.", -1, "A", dns.RcodeSuccess},
		{"a.duitang.net.", -1, "A", dns.RcodeSuccess},
		{"b.duitang.net.", -1, "CNAME", dns.RcodeSuccess},
		{"foo.docker.", 0, "A", dns.RcodeNameError},
	}

	c := new(dns.Client)
	c.Timeout = 15 * time.Second
	c.DialTimeout = 15 * time.Second
	c.ReadTimeout = 15 * time.Second
	c.WriteTimeout = 15 * time.Second
	c.UDPSize = uint16(4096)
	for _, input := range inputs {
		t.Log("Query", input.query, input.qType)
		qType := dns.StringToType[input.qType]

		m := new(dns.Msg)
		m.SetEdns0(4096, true)
		m.SetQuestion(input.query, qType)
		r, _, err := c.Exchange(m, TestAddr)

		if err != nil {
			t.Error("Error response from the server", err)
			break
		}

		if input.expected > 0 && len(r.Answer) != input.expected {
			t.Error(input, "Expected:", input.expected,
				" Got:", len(r.Answer))
		}

		if input.expected < 0 && len(r.Answer) == 0 {
			t.Error(input, "Expected at least one record but got none")
		}

		if r.Rcode != input.rcode {
			t.Error(input, "Rcode expected:",
				dns.RcodeToString[input.rcode],
				" got:", dns.RcodeToString[r.Rcode])
		}

		for _, a := range r.Answer {
			rrType := dns.Type(a.Header().Rrtype).String()
			if input.qType != rrType {
				t.Error("Did not receive ", input.qType, " resource record")
			} else {
				t.Log("Received expected response RR type", rrType, "code", dns.RcodeToString[input.rcode])
			}
		}

	}

	server.Stop()
	time.Sleep(250 * time.Millisecond)
}

func TestServiceManagement(t *testing.T) {
	list := ServiceListProvider(NewDNSServer(utils.NewConfig()))

	if len(list.GetAllServices()) != 0 {
		t.Error("Initial service count should be 0.")
	}

	A := utils.Service{Aliases: "bar.duitang.com.", RecordType: "A", TTL: 3600, Value: "127.0.0.1"}
	list.AddService(A)

	if len(list.GetAllServices()) != 1 {
		t.Error("Service count should be 1.")
	}

	s1, err := list.GetService(A)
	if err != nil {
		t.Error("GetService error", err)
	}

	if s1.Aliases != "bar.duitang.com." {
		t.Error("Expected: bar got:", s1.Aliases)
	}
	/*
		_, err = list.GetService("boo.duitang.com.")
		if err == nil {
			t.Error("Request to boo should have failed")
		}

		list.AddService(utils.Service{Aliases: "boo.duitang.com.", TTL: 3600, RecordType: "A", Value: "127.0.0.1"})

		all := list.GetAllServices()

		delete(all, "bar.duitang.com.")
		s2 := all["boo.duitang.com."]
		s2.Aliases = "zoo.duitang.com."

		if len(list.GetAllServices()) != 2 {
			t.Error("Local map change should not remove items")
		}

		if s1, _ = list.GetService("boo.duitang.com."); s1.Aliases != "boo.duitang.com." {
			t.Error("Local map change should not change items")
		}

		err = list.RemoveService("barr.duitang.com.")
		if err == nil {
			t.Error("Removing bar.duitang.com. should fail")
		}

		err = list.RemoveService("boo.duitang.com.")
		if err != nil {
			t.Error("Removing boo.duitang.com. failed", err)
		}

		if len(list.GetAllServices()) != 1 {
			t.Log(len(list.GetAllServices()))
			t.Error("Item count after remove should be 1")
		}

		list.AddService("416261e74515b7dd1dbd55f35e8625b063044f6ddf74907269e07e9f142bc0df", Service{Aliases: "mysql.duitang.net.", RecordType: "A", Value: "127.0.0.1"})

		if s1, _ = list.GetService("416261"); s1.Aliases != "mysql.duitang.net." {
			t.Error("Container can't be found by prefix")
		}

		err = list.RemoveService("416261")
		if err != nil {
			t.Error("Removing 416261 failed", err)
		}

		if len(list.GetAllServices()) != 1 {
			t.Error("Item count after remove should be 1")
		}
	*/

}
<commit_msg>style: auto go fmt<commit_after>package servers

import (
	"github.com/hawkingrei/g53/utils"
	"github.com/miekg/dns"
	"testing"
	"time"
)

func TestDNSError(t *testing.T) {
	const TestAddr = "127.0.0.1:9954"

	config := utils.NewConfig()
	config.DnsAddr = TestAddr
	config.Nameservers = []string{"123.123.123.123:53", "123.321.123.321:53"}

	server := NewDNSServer(config)
	go server.Start()

	// Allow some time for server to start
	time.Sleep(250 * time.Millisecond)

	var inputs = []struct {
		query    string
		expected int
		qType    string
		rcode    int
	}{
		{"google.com.", -1, "A", dns.RcodeRefused},
	}
	c := new(dns.Client)
	c.Timeout = 15 * time.Second
	c.DialTimeout = 15 * time.Second
	c.ReadTimeout = 15 * time.Second
	c.WriteTimeout = 15 * time.Second
	c.UDPSize = uint16(4096)
	for _, input := range inputs {
		qType := dns.StringToType[input.qType]

		m := new(dns.Msg)
		m.SetEdns0(4096, true)
		m.SetQuestion(input.query, qType)
		in, _, err := c.Exchange(m, TestAddr)
		//t.Log(r)
		if err != nil {
			t.Error("Error response from the server", err)
			break
		}
		if in.Rcode != input.rcode {
			t.Error(input, "Rcode expected:",
				dns.RcodeToString[input.rcode],
				" got:", dns.RcodeToString[in.Rcode])
		}
	}
	server.Stop()
	time.Sleep(250 * time.Millisecond)
}

func TestDNSResponse(t *testing.T) {
	const TestAddr = "127.0.0.1:9955"

	config := utils.NewConfig()
	config.DnsAddr = TestAddr

	server := NewDNSServer(config)
	go server.Start()

	// Allow some time for server to start
	time.Sleep(250 * time.Millisecond)

	//server.AddService("www.duitang.net", Service{RecordType: "CNAME", TTL: 600 , Value: "www.cctv.com",Aliases: "www.duitang.net"})
	server.AddService(utils.Service{RecordType: "A", TTL: 600, Value: "127.0.0.1", Aliases: "a.duitang.net"})
	server.AddService(utils.Service{RecordType: "CNAME", TTL: 600, Value: "wiki.duitang.com", Aliases: "b.duitang.net"})
	server.AddService(utils.Service{RecordType: "MX", TTL: 600, Value: "wiki.duitang.com", Aliases: "b.duitang.net"})
	//server.AddService("b.duitang.net", Service{RecordType:"MX",TTL:60,Value:"mxbiz1.qq.com.",Aliases:"b.duitang.net"})
	//server.AddService("foo", Service{Name: "foo", Image: "bar", IPs: []net.IP{net.ParseIP("127.0.0.1")}})
	//server.AddService("baz", Service{Name: "baz", Image: "bar", IPs: []net.IP{net.ParseIP("127.0.0.1")}, TTL: -1})
	//server.AddService("biz", Service{Name: "hey", Image: "", IPs: []net.IP{net.ParseIP("127.0.0.4")}})
	//server.AddService("joe", Service{Name: "joe", Image: "", IPs: []net.IP{net.ParseIP("127.0.0.5")}, Aliases: []string{"lala.docker", "super-alias", "alias.domain"}})

	var inputs = []struct {
		query    string
		expected int
		qType    string
		rcode    int
	}{
		{"hawkingreirrrr.com.", 0, "A", dns.RcodeNameError},
		{"google.com.", -1, "A", dns.RcodeSuccess},
		{"google.com.", -1, "AAAA", dns.RcodeSuccess}, // baidu has AAAA records
		{"google.com.", -1, "MX", dns.RcodeSuccess},
		{"wiki.duitang.net.", -1, "CNAME", dns.RcodeSuccess},
		{"a.duitang.net.", -1, "A", dns.RcodeSuccess},
		{"a.duitang.net.", -1, "A", dns.RcodeSuccess},
		{"b.duitang.net.", -1, "CNAME", dns.RcodeSuccess},
		{"foo.docker.", 0, "A", dns.RcodeNameError},
	}

	c := new(dns.Client)
	c.Timeout = 15 * time.Second
	c.DialTimeout = 15 * time.Second
	c.ReadTimeout = 15 * time.Second
	c.WriteTimeout = 15 * time.Second
	c.UDPSize = uint16(4096)
	for _, input := range inputs {
		t.Log("Query", input.query, input.qType)
		qType := dns.StringToType[input.qType]

		m := new(dns.Msg)
		m.SetEdns0(4096, true)
		m.SetQuestion(input.query, qType)
		r, _, err := c.Exchange(m, TestAddr)

		if err != nil {
			t.Error("Error response from the server", err)
			break
		}

		if input.expected > 0 && len(r.Answer) != input.expected {
			t.Error(input, "Expected:", input.expected,
				" Got:", len(r.Answer))
		}

		if input.expected < 0 && len(r.Answer) == 0 {
			t.Error(input, "Expected at least one record but got none")
		}

		if r.Rcode != input.rcode {
			t.Error(input, "Rcode expected:",
				dns.RcodeToString[input.rcode],
				" got:", dns.RcodeToString[r.Rcode])
		}

		for _, a := range r.Answer {
			rrType := dns.Type(a.Header().Rrtype).String()
			if input.qType != rrType {
				t.Error("Did not receive ", input.qType, " resource record")
			} else {
				t.Log("Received expected response RR type", rrType, "code", dns.RcodeToString[input.rcode])
			}
		}

	}

	server.Stop()
	time.Sleep(250 * time.Millisecond)
}

func TestServiceManagement(t *testing.T) {
	list := ServiceListProvider(NewDNSServer(utils.NewConfig()))

	if len(list.GetAllServices()) != 0 {
		t.Error("Initial service count should be 0.")
	}

	A := utils.Service{Aliases: "bar.duitang.com.", RecordType: "A", TTL: 3600, Value: "127.0.0.1"}
	list.AddService(A)

	if len(list.GetAllServices()) != 1 {
		t.Error("Service count should be 1.")
	}

	s1, err := list.GetService(A)
	if err != nil {
		t.Error("GetService error", err)
	}

	if s1.Aliases != "bar.duitang.com." {
		t.Error("Expected: bar got:", s1.Aliases)
	}
	/*
		_, err = list.GetService("boo.duitang.com.")
		if err == nil {
			t.Error("Request to boo should have failed")
		}

		list.AddService(utils.Service{Aliases: "boo.duitang.com.", TTL: 3600, RecordType: "A", Value: "127.0.0.1"})

		all := list.GetAllServices()

		delete(all, "bar.duitang.com.")
		s2 := all["boo.duitang.com."]
		s2.Aliases = "zoo.duitang.com."

		if len(list.GetAllServices()) != 2 {
			t.Error("Local map change should not remove items")
		}

		if s1, _ = list.GetService("boo.duitang.com."); s1.Aliases != "boo.duitang.com." {
			t.Error("Local map change should not change items")
		}

		err = list.RemoveService("barr.duitang.com.")
		if err == nil {
			t.Error("Removing bar.duitang.com. should fail")
		}

		err = list.RemoveService("boo.duitang.com.")
		if err != nil {
			t.Error("Removing boo.duitang.com. failed", err)
		}

		if len(list.GetAllServices()) != 1 {
			t.Log(len(list.GetAllServices()))
			t.Error("Item count after remove should be 1")
		}

		list.AddService("416261e74515b7dd1dbd55f35e8625b063044f6ddf74907269e07e9f142bc0df", Service{Aliases: "mysql.duitang.net.", RecordType: "A", Value: "127.0.0.1"})

		if s1, _ = list.GetService("416261"); s1.Aliases != "mysql.duitang.net." {
			t.Error("Container can't be found by prefix")
		}

		err = list.RemoveService("416261")
		if err != nil {
			t.Error("Removing 416261 failed", err)
		}

		if len(list.GetAllServices()) != 1 {
			t.Error("Item count after remove should be 1")
		}
	*/

}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"log"
	"net"
	"regexp"
	"sort"
	"strconv"
	"strings"
)

// GetIP determines the IP address of the container
func GetIP(specList []string) (string, error) {

	if specList == nil || len(specList) == 0 {
		// Use a sane default
		specList = []string{"eth0:inet"}
	}

	specs, err := parseInterfaceSpecs(specList)
	if err != nil {
		return "", err
	}

	interfaces, interfacesErr := net.Interfaces()

	if interfacesErr != nil {
		return "", interfacesErr
	}

	interfaceIPs, interfaceIPsErr := getinterfaceIPs(interfaces)

	/* We had an error and there were no interfaces returned, this is clearly
	 * an error state. */
	if interfaceIPsErr != nil && len(interfaceIPs) < 1 {
		return "", interfaceIPsErr
	}
	/* We had error(s) and there were interfaces returned, this is potentially
	 * recoverable. Let's pass on the parsed interfaces and log the error
	 * state. */
	if interfaceIPsErr != nil && len(interfaceIPs) > 0 {
		log.Printf("We had a problem reading information about some network "+
			"interfaces. If everything works, it is safe to ignore this "+
			"message. Details:\n%s\n", interfaceIPsErr)
	}

	return findIPWithSpecs(specs, interfaceIPs)
}

// findIPWithSpecs will use the given interface specification list and will
// find the first IP in the interfaceIPs that matches a spec
func findIPWithSpecs(specs []interfaceSpec, interfaceIPs []interfaceIP) (string, error) {
	// Find the interface matching the name given
	for _, spec := range specs {
		index := 0
		iface := ""
		for _, iip := range interfaceIPs {
			// Since the interfaces are ordered by name
			// a change in interface name can safely reset the index
			if iface != iip.Name {
				index = 0
				iface = iip.Name
			} else {
				index++
			}
			if spec.Match(index, iip) {
				return iip.IPString(), nil
			}
		}
	}

	// Interface not found, return error
	return "", fmt.Errorf("None of the interface specifications were able to match\nSpecifications: %s\nInterfaces IPs: %s",
		specs, interfaceIPs)
}

type interfaceSpec struct {
	Spec     string
	IPv6     bool
	Name     string
	Network  *net.IPNet
	Index    int
	HasIndex bool
}

func (spec interfaceSpec) String() string {
	return spec.Spec
}

func (spec interfaceSpec) Match(index int, iip interfaceIP) bool {
	// Specific Interface eth1, eth0[1], eth0:inet6, inet, inet6
	if spec.Name == iip.Name || spec.Name == "*" {
		// Has index and matches
		if spec.HasIndex {
			return (spec.Index == index)
		}
		// Don't match loopback address for wildcard spec
		if spec.Name == "*" && iip.IP.IsLoopback() {
			return false
		}
		return spec.IPv6 != iip.IsIPv4()
	}
	// CIDR
	if spec.Network != nil && spec.Network.Contains(iip.IP) {
		return true
	}

	return false
}

func parseInterfaceSpecs(interfaces []string) ([]interfaceSpec, error) {
	var errors []string
	var specs []interfaceSpec
	for _, iface := range interfaces {
		spec, err := parseInterfaceSpec(iface)
		if err != nil {
			errors = append(errors, err.Error())
			continue
		}
		specs = append(specs, spec)
	}
	if len(errors) > 0 {
		err := fmt.Errorf(strings.Join(errors, "\n"))
		log.Println(err.Error())
		return specs, err
	}
	return specs, nil
}

var (
	ifaceSpec = regexp.MustCompile(`^(?P<Name>\w+)(?:(?:\[(?P<Index>\d+)\])|(?::(?P<Version>inet6?)))?$`)
)

func parseInterfaceSpec(spec string) (interfaceSpec, error) {
	if spec == "inet" {
		return interfaceSpec{IPv6: false, Name: "*"}, nil
	}
	if spec == "inet6" {
		return interfaceSpec{IPv6: true, Name: "*"}, nil
	}

	if match := ifaceSpec.FindStringSubmatch(spec); match != nil {
		name := match[1]
		index := match[2]
		inet := match[3]
		if index != "" {
			i, err := strconv.Atoi(index)
			if err != nil {
				return interfaceSpec{Spec: spec}, fmt.Errorf("Unable to parse index %s in %s", index, spec)
			}
			return interfaceSpec{Spec: spec, Name: name, Index: i, HasIndex: true}, nil
		}
		if inet != "" {
			if inet == "inet" {
				return interfaceSpec{Spec: spec, Name: name, IPv6: false}, nil
			}
			return interfaceSpec{Spec: spec, Name: name, IPv6: true}, nil
		}
		return interfaceSpec{Spec: spec, Name: name, IPv6: false}, nil
	}
	if _, net, err := net.ParseCIDR(spec); err == nil {
		return interfaceSpec{Spec: spec, Network: net}, nil
	}
	return interfaceSpec{Spec: spec}, fmt.Errorf("Unable to parse interface spec: %s", spec)
}

type interfaceIP struct {
	Name string
	IP   net.IP
}

func (iip interfaceIP) To16() net.IP {
	return iip.IP.To16()
}

func (iip interfaceIP) To4() net.IP {
	return iip.IP.To4()
}

func (iip interfaceIP) IsIPv4() bool {
	return iip.To4() != nil
}

func (iip interfaceIP) IPString() string {
	if v4 := iip.To4(); v4 != nil {
		return v4.String()
	}
	return iip.IP.String()
}

func (iip interfaceIP) String() string {
	return fmt.Sprintf("%s:%s", iip.Name, iip.IPString())
}

// Queries the network interfaces on the running machine and returns a list
// of IPs for each interface. Currently, this only returns IPv4 addresses.
func getinterfaceIPs(interfaces []net.Interface) ([]interfaceIP, error) {
	var ifaceIPs []interfaceIP
	var errors []string

	for _, intf := range interfaces {
		ipAddrs, addrErr := intf.Addrs()

		if addrErr != nil {
			errors = append(errors, addrErr.Error())
			continue
		}

		for _, ipAddr := range ipAddrs {
			// Addresses some times come in the form "192.168.100.1/24 2001:DB8::/48"
			// so they must be split on whitespace
			for _, splitIP := range strings.Split(ipAddr.String(), " ") {
				ip, _, err := net.ParseCIDR(splitIP)
				if err != nil {
					errors = append(errors, err.Error())
					continue
				}
				intfIP := interfaceIP{Name: intf.Name, IP: ip}
				ifaceIPs = append(ifaceIPs, intfIP)
			}
		}
	}

	// Stable Sort the interface IPs so that selecting the correct IP in GetIP
	// can be consistent
	sort.Stable(ByInterfaceThenIP(ifaceIPs))

	/* If we had any errors parsing interfaces, we accumulate them all and
	 * then return them so that the caller can decide what they want to do. */
	if len(errors) > 0 {
		err := fmt.Errorf(strings.Join(errors, "\n"))
		log.Println(err.Error())
		return ifaceIPs, err
	}

	return ifaceIPs, nil
}

// ByInterfaceThenIP implements the Sort with the following properties:
// 1. Sort interfaces alphabetically
// 2. Sort IPs by bytes (normalized to 16 byte form)
type ByInterfaceThenIP []interfaceIP

func (se ByInterfaceThenIP) Len() int      { return len(se) }
func (se ByInterfaceThenIP) Swap(i, j int) { se[i], se[j] = se[j], se[i] }
func (se ByInterfaceThenIP) Less(i, j int) bool {
	iip1, iip2 := se[i], se[j]
	if cmp := strings.Compare(iip1.Name, iip2.Name); cmp != 0 {
		return cmp < 0
	}
	return bytes.Compare(iip1.To16(), iip2.To16()) < 0
}
<commit_msg>Remove outdated comment<commit_after>package main

import (
	"bytes"
	"fmt"
	"log"
	"net"
	"regexp"
	"sort"
	"strconv"
	"strings"
)

// GetIP determines the IP address of the container
func GetIP(specList []string) (string, error) {

	if specList == nil || len(specList) == 0 {
		// Use a sane default
		specList = []string{"eth0:inet"}
	}

	specs, err := parseInterfaceSpecs(specList)
	if err != nil {
		return "", err
	}

	interfaces, interfacesErr := net.Interfaces()

	if interfacesErr != nil {
		return "", interfacesErr
	}

	interfaceIPs, interfaceIPsErr := getinterfaceIPs(interfaces)

	/* We had an error and there were no interfaces returned, this is clearly
	 * an error state. */
	if interfaceIPsErr != nil && len(interfaceIPs) < 1 {
		return "", interfaceIPsErr
	}
	/* We had error(s) and there were interfaces returned, this is potentially
	 * recoverable. Let's pass on the parsed interfaces and log the error
	 * state. */
	if interfaceIPsErr != nil && len(interfaceIPs) > 0 {
		log.Printf("We had a problem reading information about some network "+
			"interfaces. If everything works, it is safe to ignore this "+
			"message. Details:\n%s\n", interfaceIPsErr)
	}

	return findIPWithSpecs(specs, interfaceIPs)
}

// findIPWithSpecs will use the given interface specification list and will
// find the first IP in the interfaceIPs that matches a spec
func findIPWithSpecs(specs []interfaceSpec, interfaceIPs []interfaceIP) (string, error) {
	// Find the interface matching the name given
	for _, spec := range specs {
		index := 0
		iface := ""
		for _, iip := range interfaceIPs {
			// Since the interfaces are ordered by name
			// a change in interface name can safely reset the index
			if iface != iip.Name {
				index = 0
				iface = iip.Name
			} else {
				index++
			}
			if spec.Match(index, iip) {
				return iip.IPString(), nil
			}
		}
	}

	// Interface not found, return error
	return "", fmt.Errorf("None of the interface specifications were able to match\nSpecifications: %s\nInterfaces IPs: %s",
		specs, interfaceIPs)
}

type interfaceSpec struct {
	Spec     string
	IPv6     bool
	Name     string
	Network  *net.IPNet
	Index    int
	HasIndex bool
}

func (spec interfaceSpec) String() string {
	return spec.Spec
}

func (spec interfaceSpec) Match(index int, iip interfaceIP) bool {
	// Specific Interface eth1, eth0[1], eth0:inet6, inet, inet6
	if spec.Name == iip.Name || spec.Name == "*" {
		// Has index and matches
		if spec.HasIndex {
			return (spec.Index == index)
		}
		// Don't match loopback address for wildcard spec
		if spec.Name == "*" && iip.IP.IsLoopback() {
			return false
		}
		return spec.IPv6 != iip.IsIPv4()
	}
	// CIDR
	if spec.Network != nil && spec.Network.Contains(iip.IP) {
		return true
	}

	return false
}

func parseInterfaceSpecs(interfaces []string) ([]interfaceSpec, error) {
	var errors []string
	var specs []interfaceSpec
	for _, iface := range interfaces {
		spec, err := parseInterfaceSpec(iface)
		if err != nil {
			errors = append(errors, err.Error())
			continue
		}
		specs = append(specs, spec)
	}
	if len(errors) > 0 {
		err := fmt.Errorf(strings.Join(errors, "\n"))
		log.Println(err.Error())
		return specs, err
	}
	return specs, nil
}

var (
	ifaceSpec = regexp.MustCompile(`^(?P<Name>\w+)(?:(?:\[(?P<Index>\d+)\])|(?::(?P<Version>inet6?)))?$`)
)

func parseInterfaceSpec(spec string) (interfaceSpec, error) {
	if spec == "inet" {
		return interfaceSpec{IPv6: false, Name: "*"}, nil
	}
	if spec == "inet6" {
		return interfaceSpec{IPv6: true, Name: "*"}, nil
	}

	if match := ifaceSpec.FindStringSubmatch(spec); match != nil {
		name := match[1]
		index := match[2]
		inet := match[3]
		if index != "" {
			i, err := strconv.Atoi(index)
			if err != nil {
				return interfaceSpec{Spec: spec}, fmt.Errorf("Unable to parse index %s in %s", index, spec)
			}
			return interfaceSpec{Spec: spec, Name: name, Index: i, HasIndex: true}, nil
		}
		if inet != "" {
			if inet == "inet" {
				return interfaceSpec{Spec: spec, Name: name, IPv6: false}, nil
			}
			return interfaceSpec{Spec: spec, Name: name, IPv6: true}, nil
		}
		return interfaceSpec{Spec: spec, Name: name, IPv6: false}, nil
	}
	if _, net, err := net.ParseCIDR(spec); err == nil {
		return interfaceSpec{Spec: spec, Network: net}, nil
	}
	return interfaceSpec{Spec: spec}, fmt.Errorf("Unable to parse interface spec: %s", spec)
}

type interfaceIP struct {
	Name string
	IP   net.IP
}

func (iip interfaceIP) To16() net.IP {
	return iip.IP.To16()
}

func (iip interfaceIP) To4() net.IP {
	return iip.IP.To4()
}

func (iip interfaceIP) IsIPv4() bool {
	return iip.To4() != nil
}

func (iip interfaceIP) IPString() string {
	if v4 := iip.To4(); v4 != nil {
		return v4.String()
	}
	return iip.IP.String()
}

func (iip interfaceIP) String() string {
	return fmt.Sprintf("%s:%s", iip.Name, iip.IPString())
}

// Queries the network interfaces on the running machine and returns a list
// of IPs for each interface.
func getinterfaceIPs(interfaces []net.Interface) ([]interfaceIP, error) {
	var ifaceIPs []interfaceIP
	var errors []string

	for _, intf := range interfaces {
		ipAddrs, addrErr := intf.Addrs()

		if addrErr != nil {
			errors = append(errors, addrErr.Error())
			continue
		}

		for _, ipAddr := range ipAddrs {
			// Addresses some times come in the form "192.168.100.1/24 2001:DB8::/48"
			// so they must be split on whitespace
			for _, splitIP := range strings.Split(ipAddr.String(), " ") {
				ip, _, err := net.ParseCIDR(splitIP)
				if err != nil {
					errors = append(errors, err.Error())
					continue
				}
				intfIP := interfaceIP{Name: intf.Name, IP: ip}
				ifaceIPs = append(ifaceIPs, intfIP)
			}
		}
	}

	// Stable Sort the interface IPs so that selecting the correct IP in GetIP
	// can be consistent
	sort.Stable(ByInterfaceThenIP(ifaceIPs))

	/* If we had any errors parsing interfaces, we accumulate them all and
	 * then return them so that the caller can decide what they want to do. */
	if len(errors) > 0 {
		err := fmt.Errorf(strings.Join(errors, "\n"))
		log.Println(err.Error())
		return ifaceIPs, err
	}

	return ifaceIPs, nil
}

// ByInterfaceThenIP implements the Sort with the following properties:
// 1. Sort interfaces alphabetically
// 2. Sort IPs by bytes (normalized to 16 byte form)
type ByInterfaceThenIP []interfaceIP

func (se ByInterfaceThenIP) Len() int      { return len(se) }
func (se ByInterfaceThenIP) Swap(i, j int) { se[i], se[j] = se[j], se[i] }
func (se ByInterfaceThenIP) Less(i, j int) bool {
	iip1, iip2 := se[i], se[j]
	if cmp := strings.Compare(iip1.Name, iip2.Name); cmp != 0 {
		return cmp < 0
	}
	return bytes.Compare(iip1.To16(), iip2.To16()) < 0
}
<|endoftext|>


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

package ggstat

import (
	"math"

	"github.com/aclements/go-gg/generic/slice"
	"github.com/aclements/go-gg/table"
	"github.com/aclements/go-moremath/stats"
	"github.com/aclements/go-moremath/vec"
)

// TODO: Default to first (and second) column for X (and Y)?

// Density constructs a probability density estimate from a set of
// samples using kernel density estimation.
//
// X is the only required field. All other fields have reasonable
// default zero values.
//
// The result of Density has three columns in addition to constant
// columns from the input:
//
// - Column X is the points at which the density estimate is sampled.
//
// - Column "probability density" is the density estimate.
//
// - Column "cumulative density" is the cumulative density estimate.
type Density struct {
	// X is the name of the column to use for samples.
	X string

	// W is the optional name of the column to use for sample
	// weights. It may be "" to uniformly weight samples.
	W string

	// N is the number of points to sample the KDE at. If N is 0,
	// a reasonable default is used.
	//
	// TODO: This is particularly sensitive to the scale
	// transform.
	N int

	// Widen controls the domain of the returned density estimate.
	// If Widen is < 0, the domain is the range of the data.
	// Otherwise, the domain will be expanded by Widen*Bandwidth
	// (which may be the computed bandwidth). If Widen is 0, it is
	// replaced with a default value of 3.
	//
	// TODO: This is different from ECDF and LeastSquares. Make
	// them consistent.
	Widen float64

	// SplitGroups indicates that each group in the table should
	// have separate bounds based on the data in that group alone.
	// The default, false, indicates that the density function
	// should use the bounds of all of the data combined. This
	// makes it possible to stack KDEs and easier to compare KDEs
	// across groups.
	SplitGroups bool

	// Kernel is the kernel to use for the KDE.
	Kernel stats.KDEKernel

	// Bandwidth is the bandwidth to use for the KDE.
	//
	// If this is zero, the bandwidth is computed from the data
	// using a default bandwidth estimator (currently
	// stats.BandwidthScott).
	Bandwidth float64

	// BoundaryMethod is the boundary correction method to use for
	// the KDE. The default value is BoundaryReflect; however, the
	// default bounds are effectively +/-inf, which is equivalent
	// to performing no boundary correction.
	BoundaryMethod stats.KDEBoundaryMethod

	// [BoundaryMin, BoundaryMax) specify a bounded support for
	// the KDE. If both are 0 (their default values), they are
	// treated as +/-inf.
	//
	// To specify a half-bounded support, set Min to math.Inf(-1)
	// or Max to math.Inf(1).
	BoundaryMin float64
	BoundaryMax float64
}

func (d Density) F(g table.Grouping) table.Grouping {
	kde := stats.KDE{
		Kernel:         d.Kernel,
		Bandwidth:      d.Bandwidth,
		BoundaryMethod: d.BoundaryMethod,
		BoundaryMin:    d.BoundaryMin,
		BoundaryMax:    d.BoundaryMax,
	}
	if d.N == 0 {
		d.N = 200
	}
	if d.Widen == 0 {
		d.Widen = 3
	}
	dname, cname := "probability density", "cumulative density"

	// Gather samples.
	samples := map[table.GroupID]stats.Sample{}
	for _, gid := range g.Tables() {
		t := g.Table(gid)
		var sample stats.Sample
		slice.Convert(&sample.Xs, t.MustColumn(d.X))
		if d.W != "" {
			slice.Convert(&sample.Weights, t.MustColumn(d.W))
		}
		samples[gid] = sample
	}

	min, max := math.NaN(), math.NaN()
	if !d.SplitGroups {
		// Compute combined bounds.
		for _, sample := range samples {
			smin, smax := sample.Bounds()
			if math.IsNaN(smin) {
				continue
			}

			bandwidth := d.Bandwidth
			if d.Bandwidth == 0 {
				bandwidth = stats.BandwidthScott(sample)
			}

			smin, smax = smin-d.Widen*bandwidth, smax+d.Widen*bandwidth
			if smin < min || math.IsNaN(min) {
				min = smin
			}
			if smax > max || math.IsNaN(max) {
				max = smax
			}
		}
	}

	return table.MapTables(g, func(gid table.GroupID, t *table.Table) *table.Table {
		kde.Sample = samples[gid]

		if kde.Sample.Weight() == 0 {
			return new(table.Builder).Add(d.X, []float64{}).Add(dname, []float64{}).Add(cname, []float64{}).Done()
		}

		if d.Bandwidth == 0 {
			kde.Bandwidth = stats.BandwidthScott(kde.Sample)
		}

		if d.SplitGroups {
			// Compute group bounds.
			min, max = kde.Sample.Bounds()
			min, max = min-d.Widen*kde.Bandwidth, max+d.Widen*kde.Bandwidth
		}

		ss := vec.Linspace(min, max, d.N)
		nt := new(table.Builder).Add(d.X, ss)
		nt.Add(dname, vec.Map(kde.PDF, ss))
		nt.Add(cname, vec.Map(kde.CDF, ss))
		preserveConsts(nt, t)
		return nt.Done()
	})
}
<commit_msg>gg: preserve original type in Density<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ggstat

import (
	"math"
	"reflect"

	"github.com/aclements/go-gg/generic/slice"
	"github.com/aclements/go-gg/table"
	"github.com/aclements/go-moremath/stats"
	"github.com/aclements/go-moremath/vec"
)

// TODO: Default to first (and second) column for X (and Y)?

// Density constructs a probability density estimate from a set of
// samples using kernel density estimation.
//
// X is the only required field. All other fields have reasonable
// default zero values.
//
// The result of Density has three columns in addition to constant
// columns from the input:
//
// - Column X is the points at which the density estimate is sampled.
//
// - Column "probability density" is the density estimate.
//
// - Column "cumulative density" is the cumulative density estimate.
type Density struct {
	// X is the name of the column to use for samples.
	X string

	// W is the optional name of the column to use for sample
	// weights. It may be "" to uniformly weight samples.
	W string

	// N is the number of points to sample the KDE at. If N is 0,
	// a reasonable default is used.
	//
	// TODO: This is particularly sensitive to the scale
	// transform.
	N int

	// Widen controls the domain of the returned density estimate.
	// If Widen is < 0, the domain is the range of the data.
	// Otherwise, the domain will be expanded by Widen*Bandwidth
	// (which may be the computed bandwidth). If Widen is 0, it is
	// replaced with a default value of 3.
	//
	// TODO: This is different from ECDF and LeastSquares. Make
	// them consistent.
	Widen float64

	// SplitGroups indicates that each group in the table should
	// have separate bounds based on the data in that group alone.
	// The default, false, indicates that the density function
	// should use the bounds of all of the data combined. This
	// makes it possible to stack KDEs and easier to compare KDEs
	// across groups.
	SplitGroups bool

	// Kernel is the kernel to use for the KDE.
	Kernel stats.KDEKernel

	// Bandwidth is the bandwidth to use for the KDE.
	//
	// If this is zero, the bandwidth is computed from the data
	// using a default bandwidth estimator (currently
	// stats.BandwidthScott).
	Bandwidth float64

	// BoundaryMethod is the boundary correction method to use for
	// the KDE. The default value is BoundaryReflect; however, the
	// default bounds are effectively +/-inf, which is equivalent
	// to performing no boundary correction.
	BoundaryMethod stats.KDEBoundaryMethod

	// [BoundaryMin, BoundaryMax) specify a bounded support for
	// the KDE. If both are 0 (their default values), they are
	// treated as +/-inf.
	//
	// To specify a half-bounded support, set Min to math.Inf(-1)
	// or Max to math.Inf(1).
	BoundaryMin float64
	BoundaryMax float64
}

func (d Density) F(g table.Grouping) table.Grouping {
	kde := stats.KDE{
		Kernel:         d.Kernel,
		Bandwidth:      d.Bandwidth,
		BoundaryMethod: d.BoundaryMethod,
		BoundaryMin:    d.BoundaryMin,
		BoundaryMax:    d.BoundaryMax,
	}
	if d.N == 0 {
		d.N = 200
	}
	if d.Widen == 0 {
		d.Widen = 3
	}
	dname, cname := "probability density", "cumulative density"

	// Gather samples.
	samples := map[table.GroupID]stats.Sample{}
	for _, gid := range g.Tables() {
		t := g.Table(gid)
		var sample stats.Sample
		slice.Convert(&sample.Xs, t.MustColumn(d.X))
		if d.W != "" {
			slice.Convert(&sample.Weights, t.MustColumn(d.W))
		}
		samples[gid] = sample
	}

	min, max := math.NaN(), math.NaN()
	if !d.SplitGroups {
		// Compute combined bounds.
		for _, sample := range samples {
			smin, smax := sample.Bounds()
			if math.IsNaN(smin) {
				continue
			}

			bandwidth := d.Bandwidth
			if d.Bandwidth == 0 {
				bandwidth = stats.BandwidthScott(sample)
			}

			smin, smax = smin-d.Widen*bandwidth, smax+d.Widen*bandwidth
			if smin < min || math.IsNaN(min) {
				min = smin
			}
			if smax > max || math.IsNaN(max) {
				max = smax
			}
		}
	}

	return table.MapTables(g, func(gid table.GroupID, t *table.Table) *table.Table {
		kde.Sample = samples[gid]

		if kde.Sample.Weight() == 0 {
			return new(table.Builder).Add(d.X, []float64{}).Add(dname, []float64{}).Add(cname, []float64{}).Done()
		}

		if d.Bandwidth == 0 {
			kde.Bandwidth = stats.BandwidthScott(kde.Sample)
		}

		if d.SplitGroups {
			// Compute group bounds.
			min, max = kde.Sample.Bounds()
			min, max = min-d.Widen*kde.Bandwidth, max+d.Widen*kde.Bandwidth
		}

		// TODO: Unify this with LOESS and LeastSquares and make them all convert to the original data type.
		ctype := table.ColType(t, d.X)
		ss := vec.Linspace(min, max, d.N)
		vs := reflect.New(ctype)
		slice.Convert(vs.Interface(), ss)
		var vsf []float64
		slice.Convert(&vsf, vs.Elem().Interface())
		nt := new(table.Builder).Add(d.X, vs.Elem().Interface())
		nt.Add(dname, vec.Map(kde.PDF, vsf))
		nt.Add(cname, vec.Map(kde.CDF, vsf))
		preserveConsts(nt, t)
		return nt.Done()
	})
}
<|endoftext|>


<commit_before>package service

import (
	"flag"
	"github.com/ayufan/golang-kardianos-service"
	"log"
	"strings"
)

func NewServiceRunnerBuilder(serviceName string, runHandler RunHandler) ServiceRunnerBuilder {
	if strings.TrimSpace(serviceName) == "" {
		panic("The service name cannot be blank in NewServiceRunnerBuilder")
	}

	return &builder{
		ServiceName:        serviceName,
		ServiceDisplayName: serviceName, //default to name
		ServiceDescription: serviceName, //default to name
		ServiceUserName:    "",
		RunHandler:         runHandler,
	}
}

type ServiceRunnerBuilder interface {
	WithServiceDisplayName(serviceDisplayName string) ServiceRunnerBuilder
	WithServiceDescription(serviceDescription string) ServiceRunnerBuilder

	WithAdditionalArguments(args ...string) ServiceRunnerBuilder

	WithServiceUserName(serviceUserName string) ServiceRunnerBuilder
	WithServiceUserName_AsCurrentUser() ServiceRunnerBuilder

	WithOnStopHandler(h OnStopHandler) ServiceRunnerBuilder

	Run()
}

type builder struct {
	ServiceName        string
	ServiceDisplayName string
	ServiceDescription string

	AdditionalArguments []string

	ServiceUserName string

	RunHandler    RunHandler
	OnStopHandler OnStopHandler
}

func (b *builder) WithServiceDisplayName(serviceDisplayName string) ServiceRunnerBuilder {
	b.ServiceDisplayName = serviceDisplayName
	return b
}

func (b *builder) WithServiceDescription(serviceDescription string) ServiceRunnerBuilder {
	b.ServiceDescription = serviceDescription
	return b
}

func (b *builder) WithAdditionalArguments(args ...string) ServiceRunnerBuilder {
	b.AdditionalArguments = args
	return b
}

func (b *builder) WithServiceUserName(serviceUserName string) ServiceRunnerBuilder {
	b.ServiceUserName = serviceUserName
	return b
}

func (b *builder) WithServiceUserName_AsCurrentUser() ServiceRunnerBuilder {
	return b.WithServiceUserName(getCurrentUserName())
}

func (b *builder) WithOnStopHandler(h OnStopHandler) ServiceRunnerBuilder {
	b.OnStopHandler = h
	return b
}

func (b *builder) Run() {
	svcFlag := flag.String("service", "", "Control the system service.")
	flag.Parse()

	svcConfig := &service.Config{
		Name:        b.ServiceName,
		DisplayName: b.ServiceDisplayName,
		Description: b.ServiceDescription,
		Arguments:   b.AdditionalArguments,
		UserName:    b.ServiceUserName,
	}

	prg := &program{}
	s, err := service.New(prg, svcConfig)
	if err != nil {
		log.Fatal(err)
	}
	errs := make(chan error, 5)
	logger, err := s.Logger(errs)
	if err != nil {
		log.Fatal(err)
	}

	prg.Logger = logger
	prg.RunHandler = b.RunHandler
	prg.OnStopHandler = b.OnStopHandler

	go func() {
		for {
			err := <-errs
			if err != nil {
				log.Print(err)
			}
		}
	}()

	if len(*svcFlag) != 0 {
		err := service.Control(s, *svcFlag)
		if err != nil {
			log.Printf("Valid actions: %q\n", service.ControlAction)
			log.Fatal(err)
		}
		return
	}

	err = s.Run()
	if err != nil {
		logger.Error(err)
	}
}
<commit_msg>Exposed the `ServiceFlag` publicly and also check to not parse a second time if already called `flag.Parse()`.<commit_after>package service

import (
	"flag"
	"github.com/ayufan/golang-kardianos-service"
	"log"
	"strings"
)

var ServiceFlag = flag.String("service", "", "Control the system service.")

func NewServiceRunnerBuilder(serviceName string, runHandler RunHandler) ServiceRunnerBuilder {
	if strings.TrimSpace(serviceName) == "" {
		panic("The service name cannot be blank in NewServiceRunnerBuilder")
	}

	return &builder{
		ServiceName:        serviceName,
		ServiceDisplayName: serviceName, //default to name
		ServiceDescription: serviceName, //default to name
		ServiceUserName:    "",
		RunHandler:         runHandler,
	}
}

type ServiceRunnerBuilder interface {
	WithServiceDisplayName(serviceDisplayName string) ServiceRunnerBuilder
	WithServiceDescription(serviceDescription string) ServiceRunnerBuilder

	WithAdditionalArguments(args ...string) ServiceRunnerBuilder

	WithServiceUserName(serviceUserName string) ServiceRunnerBuilder
	WithServiceUserName_AsCurrentUser() ServiceRunnerBuilder

	WithOnStopHandler(h OnStopHandler) ServiceRunnerBuilder

	Run()
}

type builder struct {
	ServiceName        string
	ServiceDisplayName string
	ServiceDescription string

	AdditionalArguments []string

	ServiceUserName string

	RunHandler    RunHandler
	OnStopHandler OnStopHandler
}

func (b *builder) WithServiceDisplayName(serviceDisplayName string) ServiceRunnerBuilder {
	b.ServiceDisplayName = serviceDisplayName
	return b
}

func (b *builder) WithServiceDescription(serviceDescription string) ServiceRunnerBuilder {
	b.ServiceDescription = serviceDescription
	return b
}

func (b *builder) WithAdditionalArguments(args ...string) ServiceRunnerBuilder {
	b.AdditionalArguments = args
	return b
}

func (b *builder) WithServiceUserName(serviceUserName string) ServiceRunnerBuilder {
	b.ServiceUserName = serviceUserName
	return b
}

func (b *builder) WithServiceUserName_AsCurrentUser() ServiceRunnerBuilder {
	return b.WithServiceUserName(getCurrentUserName())
}

func (b *builder) WithOnStopHandler(h OnStopHandler) ServiceRunnerBuilder {
	b.OnStopHandler = h
	return b
}

func (b *builder) Run() {
	if !flag.Parsed() {
		flag.Parse()
	}

	svcConfig := &service.Config{
		Name:        b.ServiceName,
		DisplayName: b.ServiceDisplayName,
		Description: b.ServiceDescription,
		Arguments:   b.AdditionalArguments,
		UserName:    b.ServiceUserName,
	}

	prg := &program{}
	s, err := service.New(prg, svcConfig)
	if err != nil {
		log.Fatal(err)
	}
	errs := make(chan error, 5)
	logger, err := s.Logger(errs)
	if err != nil {
		log.Fatal(err)
	}

	prg.Logger = logger
	prg.RunHandler = b.RunHandler
	prg.OnStopHandler = b.OnStopHandler

	go func() {
		for {
			err := <-errs
			if err != nil {
				log.Print(err)
			}
		}
	}()

	if len(*ServiceFlag) != 0 {
		err := service.Control(s, *ServiceFlag)
		if err != nil {
			log.Printf("Valid actions: %q\n", service.ControlAction)
			log.Fatal(err)
		}
		return
	}

	err = s.Run()
	if err != nil {
		logger.Error(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"encoding/json"
	"github.com/bmizerany/pq"
	"github.com/gorilla/mux"
	"log"
	"net/http"
	"strconv"
	"time"
)

func main() {
	log.Print("starting ChicagoWorksforYou.com API server")

	router := mux.NewRouter()
	router.HandleFunc("/health_check", HealthCheckHandler)
	router.HandleFunc("/services.json", ServicesHandler)
	router.HandleFunc("/wards/{id}/requests.json", WardRequestsHandler)
	router.HandleFunc("/wards/{id}/counts.json", WardCountsHandler)
	http.ListenAndServe(":5000", router)
}

func WardCountsHandler(response http.ResponseWriter, request *http.Request) {
	// for a given ward, return the number of service requests opened
	// grouped by day, then by service request type

        // sample output
        // $ curl "http://localhost:5000/wards/10/counts.json?service_code=4fd3b167e750846744000005&count=5"
        // {
        //   "2013-06-06": 2,
        //   "2013-06-07": 4,
        //   "2013-06-09": 5,
        //   "2013-06-10": 6,
        //   "2013-06-12": 23
        // }
        //
	vars := mux.Vars(request)
	ward_id := vars["id"]
	params := request.URL.Query()

	db, err := sql.Open("postgres", "dbname=cwfy sslmode=disable")
	if err != nil {
		log.Fatal("Cannot open database connection", err)
	}
	defer db.Close()

	// determine date range. default is last 7 days.
	days, _ := strconv.Atoi(params["count"][0])

	end, _	:= time.Parse("2006-01-02", params["end_date"][0])
	end = end.AddDate(0,0,1) // inc to the following day
	start 	:= end.AddDate(0, 0, -days)

	log.Printf("fetching counts for ward %s code %s for past %d days", ward_id, params["service_code"][0], days)
	log.Printf("date range is %s to %s", start, end)

	rows, err := db.Query("SELECT COUNT(*), DATE(requested_datetime) as requested_date FROM service_requests WHERE ward = $1 " +
			"AND duplicate IS NULL AND service_code = $2 AND requested_datetime >= $3::date AND requested_datetime <= $4::date " +
			"GROUP BY DATE(requested_datetime) ORDER BY requested_date;", string(ward_id), params["service_code"][0], start, end)
	if err != nil {
		log.Fatal("error fetching data for WardCountsHandler", err)
	}

	type WardCount struct {
		Requested_date time.Time
		Count          int
	}

	var counts []WardCount
	for rows.Next() {
		wc := WardCount{}
		if err := rows.Scan(&wc.Count, &wc.Requested_date); err != nil {
			log.Print("error reading row of ward count", err)
		}

		// trunc the requested time to just date
		counts = append(counts, wc)
	}

	resp := make(map[string]int)

	for _, c := range counts {
		key := c.Requested_date.Format("2006-01-02")
		log.Print("key: ", key)
		resp[key] = c.Count
	}

	jsn, _ := json.MarshalIndent(resp, "", "  ")
	response.Write(jsn)
}

func WardRequestsHandler(response http.ResponseWriter, request *http.Request) {
	// for a given ward, return recent service requests

	vars := mux.Vars(request)
	ward_id := vars["id"]

	log.Print("fetch requests for ward ", ward_id)

	// open database
	db, err := sql.Open("postgres", "dbname=cwfy sslmode=disable")
	if err != nil {
		log.Fatal("Cannot open database connection", err)
	}
	defer db.Close()

	rows, err := db.Query("SELECT lat,long,ward,police_district,service_request_id,status,service_name,service_code,agency_responsible,address,channel,media_url,requested_datetime,updated_datetime,created_at,updated_at,duplicate,parent_service_request_id,id FROM service_requests WHERE duplicate IS NULL AND ward = $1 ORDER BY updated_at DESC LIMIT 100;", ward_id)

	if err != nil {
		log.Fatal("error fetching data for WardRequestsHandler", err)
	}

	type Open311RequestRow struct {
		Lat, Long                                                                                float64
		Ward, Police_district, Id                                                                                int
		Service_request_id, Status, Service_name, Service_code, Agency_responsible, Address, Channel, Media_url, Duplicate, Parent_service_request_id sql.NullString
		Requested_datetime, Updated_datetime, Created_at, Updated_at                                                                                pq.NullTime // FIXME: should these be proper time objects?
		Extended_attributes                                                                                map[string]interface{}
	}

	var result []Open311RequestRow

	for rows.Next() {
		var row Open311RequestRow
		if err := rows.Scan(&row.Lat, &row.Long, &row.Ward, &row.Police_district,
			&row.Service_request_id, &row.Status, &row.Service_name,
			&row.Service_code, &row.Agency_responsible, &row.Address,
			&row.Channel, &row.Media_url, &row.Requested_datetime,
			&row.Updated_datetime, &row.Created_at, &row.Updated_at,
			&row.Duplicate, &row.Parent_service_request_id,
			&row.Id); err != nil {
			log.Fatal("error reading row", err)
		}

		result = append(result, row)
	}

	jsn, _ := json.MarshalIndent(result, "", "  ")
	response.Write(jsn)
}

func ServicesHandler(response http.ResponseWriter, request *http.Request) {
	// return counts of requests, grouped by service name
	//
	// Sample output:
	//
	// [
	//   {
	//     "Count": 1139,
	//     "Service_code": "4fd3b167e750846744000005",
	//     "Service_name": "Graffiti Removal"
	//   },
	//   {
	//     "Count": 25,
	//     "Service_code": "4fd6e4ece750840569000019",
	//     "Service_name": "Restaurant Complaint"
	//   },
	//
	//  ... snip ...
	//
	// ]

	type ServicesCount struct {
		Count        int
		Service_code string
		Service_name string
	}

	var services []ServicesCount

	// open database
	db, err := sql.Open("postgres", "dbname=cwfy sslmode=disable")
	if err != nil {
		log.Fatal("Cannot open database connection", err)
	}
	defer db.Close()

	rows, err := db.Query("SELECT COUNT(*), service_code, service_name FROM service_requests WHERE duplicate IS NULL GROUP BY service_code,service_name;")

	if err != nil {
		log.Fatal("error fetching data for ServicesHandler", err)
	}

	for rows.Next() {
		var count int
		var service_code, service_name string

		if err := rows.Scan(&count, &service_code, &service_name); err != nil {
			log.Fatal("error reading row", err)
		}

		row := ServicesCount{Count: count, Service_code: service_code, Service_name: service_name}
		services = append(services, row)
	}

	jsn, _ := json.MarshalIndent(services, "", "  ")
	response.Write(jsn)
}

func HealthCheckHandler(response http.ResponseWriter, request *http.Request) {
	response.Header().Add("Content-type", "application/json")

	type HealthCheck struct {
		Count    int
		Database bool
		Healthy  bool
	}

	// open database
	db, err := sql.Open("postgres", "dbname=cwfy sslmode=disable")
	if err != nil {
		log.Fatal("Cannot open database connection", err)
	}
	defer db.Close()

	health_check := HealthCheck{}

	health_check.Database = db.Ping() == nil

	rows, _ := db.Query("SELECT COUNT(*) FROM service_requests;")
	for rows.Next() {
		if err := rows.Scan(&health_check.Count); err != nil {
			log.Fatal("error fetching count", err)
		}
	}

	// calculate overall health
	health_check.Healthy = health_check.Count > 0 && health_check.Database

	log.Printf("health_check: %+v", health_check)
	if !health_check.Healthy {
		log.Printf("health_check failed")
	}
	jsn, _ := json.Marshal(health_check)
	response.Write(jsn)
}
<commit_msg>gofmt and added comments<commit_after>package main

import (
	"database/sql"
	"encoding/json"
	"github.com/bmizerany/pq"
	"github.com/gorilla/mux"
	"log"
	"net/http"
	"strconv"
	"time"
)

func main() {
	log.Print("starting ChicagoWorksforYou.com API server")

	router := mux.NewRouter()
	router.HandleFunc("/health_check", HealthCheckHandler)
	router.HandleFunc("/services.json", ServicesHandler)
	router.HandleFunc("/wards/{id}/requests.json", WardRequestsHandler)
	router.HandleFunc("/wards/{id}/counts.json", WardCountsHandler)
	http.ListenAndServe(":5000", router)
}

func WardCountsHandler(response http.ResponseWriter, request *http.Request) {
	// for a given ward, return the number of service requests opened
	// grouped by day, then by service request type
	//
	// Parameters:
	//
	// 	count: 		the number of days of data to return
	// 	end_date: 	date that +count+ is based from.
	// 	service_code: 	the code used by the City of Chicago to categorize service requests
	//
	// Sample API output
	//
	// Note that the end date is June 12, and the results include the end_date.
	// $ curl "http://localhost:5000/wards/10/counts.json?service_code=4fd3b167e750846744000005&count=5&end_date=2013-06-12"
	// {
	//   "2013-06-06": 2,
	//   "2013-06-07": 4,
	//   "2013-06-09": 5,
	//   "2013-06-10": 6,
	//   "2013-06-12": 23
	// }
	//
	vars := mux.Vars(request)
	ward_id := vars["id"]
	params := request.URL.Query()

	db, err := sql.Open("postgres", "dbname=cwfy sslmode=disable")
	if err != nil {
		log.Fatal("Cannot open database connection", err)
	}
	defer db.Close()

	// determine date range. default is last 7 days.
	days, _ := strconv.Atoi(params["count"][0])

	end, _ := time.Parse("2006-01-02", params["end_date"][0])
	end = end.AddDate(0, 0, 1) // inc to the following day
	start := end.AddDate(0, 0, -days)

	log.Printf("fetching counts for ward %s code %s for past %d days", ward_id, params["service_code"][0], days)
	log.Printf("date range is %s to %s", start, end)

	rows, err := db.Query("SELECT COUNT(*), DATE(requested_datetime) as requested_date FROM service_requests WHERE ward = $1 "+
		"AND duplicate IS NULL AND service_code = $2 AND requested_datetime >= $3::date AND requested_datetime <= $4::date "+
		"GROUP BY DATE(requested_datetime) ORDER BY requested_date;", string(ward_id), params["service_code"][0], start, end)
	if err != nil {
		log.Fatal("error fetching data for WardCountsHandler", err)
	}

	type WardCount struct {
		Requested_date time.Time
		Count          int
	}

	var counts []WardCount
	for rows.Next() {
		wc := WardCount{}
		if err := rows.Scan(&wc.Count, &wc.Requested_date); err != nil {
			log.Print("error reading row of ward count", err)
		}

		// trunc the requested time to just date
		counts = append(counts, wc)
	}

	resp := make(map[string]int)

	for _, c := range counts {
		key := c.Requested_date.Format("2006-01-02")
		log.Print("key: ", key)
		resp[key] = c.Count
	}

	jsn, _ := json.MarshalIndent(resp, "", "  ")
	response.Write(jsn)
}

func WardRequestsHandler(response http.ResponseWriter, request *http.Request) {
	// for a given ward, return recent service requests

	vars := mux.Vars(request)
	ward_id := vars["id"]

	log.Print("fetch requests for ward ", ward_id)

	// open database
	db, err := sql.Open("postgres", "dbname=cwfy sslmode=disable")
	if err != nil {
		log.Fatal("Cannot open database connection", err)
	}
	defer db.Close()

	rows, err := db.Query("SELECT lat,long,ward,police_district,service_request_id,status,service_name,service_code,agency_responsible,address,channel,media_url,requested_datetime,updated_datetime,created_at,updated_at,duplicate,parent_service_request_id,id FROM service_requests WHERE duplicate IS NULL AND ward = $1 ORDER BY updated_at DESC LIMIT 100;", ward_id)

	if err != nil {
		log.Fatal("error fetching data for WardRequestsHandler", err)
	}

	type Open311RequestRow struct {
		Lat, Long                                                                                float64
		Ward, Police_district, Id                                                                                int
		Service_request_id, Status, Service_name, Service_code, Agency_responsible, Address, Channel, Media_url, Duplicate, Parent_service_request_id sql.NullString
		Requested_datetime, Updated_datetime, Created_at, Updated_at                                                                                pq.NullTime // FIXME: should these be proper time objects?
		Extended_attributes                                                                                map[string]interface{}
	}

	var result []Open311RequestRow

	for rows.Next() {
		var row Open311RequestRow
		if err := rows.Scan(&row.Lat, &row.Long, &row.Ward, &row.Police_district,
			&row.Service_request_id, &row.Status, &row.Service_name,
			&row.Service_code, &row.Agency_responsible, &row.Address,
			&row.Channel, &row.Media_url, &row.Requested_datetime,
			&row.Updated_datetime, &row.Created_at, &row.Updated_at,
			&row.Duplicate, &row.Parent_service_request_id,
			&row.Id); err != nil {
			log.Fatal("error reading row", err)
		}

		result = append(result, row)
	}

	jsn, _ := json.MarshalIndent(result, "", "  ")
	response.Write(jsn)
}

func ServicesHandler(response http.ResponseWriter, request *http.Request) {
	// return counts of requests, grouped by service name
	//
	// Sample output:
	//
	// [
	//   {
	//     "Count": 1139,
	//     "Service_code": "4fd3b167e750846744000005",
	//     "Service_name": "Graffiti Removal"
	//   },
	//   {
	//     "Count": 25,
	//     "Service_code": "4fd6e4ece750840569000019",
	//     "Service_name": "Restaurant Complaint"
	//   },
	//
	//  ... snip ...
	//
	// ]

	type ServicesCount struct {
		Count        int
		Service_code string
		Service_name string
	}

	var services []ServicesCount

	// open database
	db, err := sql.Open("postgres", "dbname=cwfy sslmode=disable")
	if err != nil {
		log.Fatal("Cannot open database connection", err)
	}
	defer db.Close()

	rows, err := db.Query("SELECT COUNT(*), service_code, service_name FROM service_requests WHERE duplicate IS NULL GROUP BY service_code,service_name;")

	if err != nil {
		log.Fatal("error fetching data for ServicesHandler", err)
	}

	for rows.Next() {
		var count int
		var service_code, service_name string

		if err := rows.Scan(&count, &service_code, &service_name); err != nil {
			log.Fatal("error reading row", err)
		}

		row := ServicesCount{Count: count, Service_code: service_code, Service_name: service_name}
		services = append(services, row)
	}

	jsn, _ := json.MarshalIndent(services, "", "  ")
	response.Write(jsn)
}

func HealthCheckHandler(response http.ResponseWriter, request *http.Request) {
	response.Header().Add("Content-type", "application/json")

	type HealthCheck struct {
		Count    int
		Database bool
		Healthy  bool
	}

	// open database
	db, err := sql.Open("postgres", "dbname=cwfy sslmode=disable")
	if err != nil {
		log.Fatal("Cannot open database connection", err)
	}
	defer db.Close()

	health_check := HealthCheck{}

	health_check.Database = db.Ping() == nil

	rows, _ := db.Query("SELECT COUNT(*) FROM service_requests;")
	for rows.Next() {
		if err := rows.Scan(&health_check.Count); err != nil {
			log.Fatal("error fetching count", err)
		}
	}

	// calculate overall health
	health_check.Healthy = health_check.Count > 0 && health_check.Database

	log.Printf("health_check: %+v", health_check)
	if !health_check.Healthy {
		log.Printf("health_check failed")
	}
	jsn, _ := json.Marshal(health_check)
	response.Write(jsn)
}
<|endoftext|>