<commit_before>// +build windows,amd64

package winapi

import (
	"fmt"
	"os"
	"strings"
	"syscall"
	"unsafe"

	so "github.com/tera-insights/go-win64api/shared"
)

var (
	usrNetLocalGroupAdd        = modNetapi32.NewProc("NetLocalGroupAdd")
	usrNetLocalGroupEnum       = modNetapi32.NewProc("NetLocalGroupEnum")
	usrNetLocalGroupDel        = modNetapi32.NewProc("NetLocalGroupDel")
	usrNetLocalGroupSetMembers = modNetapi32.NewProc("NetLocalGroupSetMembers")
	usrNetLocalGroupGetMembers = modNetapi32.NewProc("NetLocalGroupGetMembers")
)

// LOCALGROUP_INFO_0 represents level 0 information about local Windows groups.
// This struct matches the struct definition in the Windows headers (lmaccess.h).
type LOCALGROUP_INFO_0 struct {
	Lgrpi0_name *uint16 // UTF-16 group name
}

// LOCALGROUP_INFO_1 represents level 1 information about local Windows groups.
// This struct matches the struct definition in the Windows headers (lmaccess.h).
type LOCALGROUP_INFO_1 struct {
	Lgrpi1_name    *uint16 // UTF-16 group name
	Lgrpi1_comment *uint16 // UTF-16 group comment
}

// LocalGroupAdd adds a new local group with the specified name and comment.
func LocalGroupAdd(name, comment string) (bool, error) {
	var parmErr uint32
	var err error
	var gInfo LOCALGROUP_INFO_1

	gInfo.Lgrpi1_name, err = syscall.UTF16PtrFromString(name)
	if err != nil {
		return false, fmt.Errorf("Unable to encode group name to UTF16: %s", err)
	}
	gInfo.Lgrpi1_comment, err = syscall.UTF16PtrFromString(comment)
	if err != nil {
		return false, fmt.Errorf("Unable to encode comment to UTF16: %s", err)
	}

	ret, _, _ := usrNetLocalGroupAdd.Call(
		uintptr(0),                        // server name
		uintptr(uint32(1)),                // information level
		uintptr(unsafe.Pointer(&gInfo)),   // group information
		uintptr(unsafe.Pointer(&parmErr)), // error code out param
	)
	if ret != NET_API_STATUS_NERR_Success {
		return false, fmt.Errorf("Unable to process: status=%d error=%d", ret, parmErr)
	}
	return true, nil
}

// ListLocalGroups enumerates the local groups defined on the system.
func ListLocalGroups() ([]so.LocalGroup, error) {
	var (
		dataPointer  uintptr
		resumeHandle uintptr
		entriesRead  uint32
		entriesTotal uint32
		sizeTest     LOCALGROUP_INFO_1
		retVal       = make([]so.LocalGroup, 0)
	)

	ret, _, _ := usrNetLocalGroupEnum.Call(
		uintptr(0),                                 // servername
		uintptr(uint32(1)),                         // level, LOCALGROUP_INFO_1
		uintptr(unsafe.Pointer(&dataPointer)),      // struct buffer for output data.
		uintptr(uint32(USER_MAX_PREFERRED_LENGTH)), // allow as much memory as required.
		uintptr(unsafe.Pointer(&entriesRead)),
		uintptr(unsafe.Pointer(&entriesTotal)),
		uintptr(unsafe.Pointer(&resumeHandle)),
	)
	if ret != NET_API_STATUS_NERR_Success {
		return nil, fmt.Errorf("error enumerating groups: status=%d", ret)
	} else if dataPointer == uintptr(0) {
		return nil, fmt.Errorf("null pointer while fetching entry")
	}
	defer usrNetApiBufferFree.Call(dataPointer)

	var iter = dataPointer
	for i := uint32(0); i < entriesRead; i++ {
		var data = (*LOCALGROUP_INFO_1)(unsafe.Pointer(iter))

		gd := so.LocalGroup{
			Name:    UTF16toString(data.Lgrpi1_name),
			Comment: UTF16toString(data.Lgrpi1_comment),
		}
		retVal = append(retVal, gd)

		iter = uintptr(unsafe.Pointer(iter + unsafe.Sizeof(sizeTest)))
	}

	return retVal, nil
}

// LocalGroupDel deletes the specified local group.
func LocalGroupDel(name string) (bool, error) {
	namePtr, err := syscall.UTF16PtrFromString(name)
	if err != nil {
		return false, fmt.Errorf("Unable to encode group name to UTF16: %s", err)
	}

	ret, _, _ := usrNetLocalGroupDel.Call(
		uintptr(0), // servername
		uintptr(unsafe.Pointer(namePtr)),
	)
	if ret != NET_API_STATUS_NERR_Success {
		return false, fmt.Errorf("Unable to delete group: status=%d", ret)
	}
	return true, nil
}

func localGroupModMembers(proc *syscall.LazyProc, groupname string, usernames []string) (bool, error) {
	memberInfos := make([]LOCALGROUP_MEMBERS_INFO_3, 0, len(usernames))
	hostname, err := os.Hostname()
	if err != nil {
		return false, fmt.Errorf("Unable to determine hostname: %s", err)
	}
	groupnamePtr, err := syscall.UTF16PtrFromString(groupname)
	if err != nil {
		return false, fmt.Errorf("Unable to encode group name to UTF16: %s", err)
	}

	for _, username := range usernames {
		domainAndUsername := username
		if !strings.ContainsRune(username, '\\') {
			domainAndUsername = fmt.Sprintf(`%s\%s`, hostname, username)
		}
		namePtr, err := syscall.UTF16PtrFromString(domainAndUsername)
		if err != nil {
			return false, fmt.Errorf("Unable to encode username to UTF16: %s", err)
		}
		memberInfos = append(memberInfos, LOCALGROUP_MEMBERS_INFO_3{
			Lgrmi3_domainandname: namePtr,
		})
	}

	if len(memberInfos) == 0 {
		// Add a fake entry just so that the slice isn't empty, so we can take
		// the address of the first entry
		memberInfos = append(memberInfos, LOCALGROUP_MEMBERS_INFO_3{})
	}

	ret, _, _ := proc.Call(
		uintptr(0),                            // servername
		uintptr(unsafe.Pointer(groupnamePtr)), // group name
		uintptr(3),                            // level, LOCALGROUP_MEMBERS_INFO_3
		uintptr(unsafe.Pointer(&memberInfos[0])), // buf
		uintptr(len(usernames)),                  // totalEntries
	)
	if ret != NET_API_STATUS_NERR_Success {
		return false, fmt.Errorf("Failed to modify group members: status=%d", ret)
	}

	return true, nil
}

// LocalGroupSetMembers sets the membership of the group to contain exactly the
// set of users specified in usernames.
func LocalGroupSetMembers(groupname string, usernames []string) (bool, error) {
	return localGroupModMembers(usrNetLocalGroupSetMembers, groupname, usernames)
}

// LocalGroupAddMembers adds the specified members to the group, if they are not
// already members.
func LocalGroupAddMembers(groupname string, usernames []string) (bool, error) {
	return localGroupModMembers(usrNetLocalGroupAddMembers, groupname, usernames)
}

// LocalGroupDelMembers removes the specified members from the local group.
func LocalGroupDelMembers(groupname string, usernames []string) (bool, error) {
	return localGroupModMembers(usrNetLocalGroupDelMembers, groupname, usernames)
}

// LocalGroupGetMembers returns information about the members of the specified
// local group.
func LocalGroupGetMembers(groupname string) ([]so.LocalGroupMember, error) {
	var (
		dataPointer  uintptr
		resumeHandle uintptr
		entriesRead  uint32
		entriesTotal uint32
		sizeTest     LOCALGROUP_MEMBERS_INFO_3
		retVal       []so.LocalGroupMember = make([]so.LocalGroupMember, 0)
	)

	groupnamePtr, err := syscall.UTF16PtrFromString(groupname)
	if err != nil {
		return nil, fmt.Errorf("Unable to encode group name to UTF16: %s", err)
	}

	ret, _, _ := usrNetLocalGroupGetMembers.Call(
		uintptr(0),                                 // servername
		uintptr(unsafe.Pointer(groupnamePtr)),      // group name
		uintptr(3),                                 // level, LOCALGROUP_MEMBERS_INFO_3
		uintptr(unsafe.Pointer(&dataPointer)),      // bufptr
		uintptr(uint32(USER_MAX_PREFERRED_LENGTH)), // prefmaxlen
		uintptr(unsafe.Pointer(&entriesRead)),      // entriesread
		uintptr(unsafe.Pointer(&entriesTotal)),     // totalentries
		uintptr(unsafe.Pointer(&resumeHandle)),     // resumehandle
	)
	if ret != NET_API_STATUS_NERR_Success {
		return nil, fmt.Errorf("error enumerating groups members: status=%d", ret)
	} else if dataPointer == uintptr(0) {
		return nil, fmt.Errorf("null pointer while fetching entry")
	}
	defer usrNetApiBufferFree.Call(dataPointer)

	var iter = dataPointer
	for i := uint32(0); i < entriesRead; i++ {
		var data = (*LOCALGROUP_MEMBERS_INFO_3)(unsafe.Pointer(iter))

		domainAndUsername := UTF16toString(data.Lgrmi3_domainandname)
		split := strings.SplitN(domainAndUsername, "\\", 1)
		var domain, name string
		if len(split) > 1 {
			domain = split[0]
			name = split[1]
		} else {
			// This really shouldn't happen, but just in case...
			name = split[0]
		}

		gd := so.LocalGroupMember{
			Domain:        domain,
			Name:          name,
			DomainAndName: domainAndUsername,
		}
		retVal = append(retVal, gd)

		iter = uintptr(unsafe.Pointer(iter + unsafe.Sizeof(sizeTest)))
	}

	return retVal, nil
}
<commit_msg>groups: return errors from syscalls as Errno<commit_after>// +build windows,amd64

package winapi

import (
	"fmt"
	"os"
	"strings"
	"syscall"
	"unsafe"

	so "github.com/tera-insights/go-win64api/shared"
)

var (
	usrNetLocalGroupAdd        = modNetapi32.NewProc("NetLocalGroupAdd")
	usrNetLocalGroupEnum       = modNetapi32.NewProc("NetLocalGroupEnum")
	usrNetLocalGroupDel        = modNetapi32.NewProc("NetLocalGroupDel")
	usrNetLocalGroupSetMembers = modNetapi32.NewProc("NetLocalGroupSetMembers")
	usrNetLocalGroupGetMembers = modNetapi32.NewProc("NetLocalGroupGetMembers")
)

// Possible errors returned by local group management functions
// Error code enumerations taken from MS-ERREF documentation:
// https://msdn.microsoft.com/en-us/library/cc231196.aspx
const (
	NERR_GroupNotFound syscall.Errno = 2220 // 0x000008AC

	ERROR_ACCESS_DENIED       syscall.Errno = 5    // 0x00000005
	ERROR_MEMBER_NOT_IN_ALIAS syscall.Errno = 1377 // 0x00000561
	ERROR_MEMBER_IN_ALIAS     syscall.Errno = 1378 // 0x00000562
	ERROR_NO_SUCH_MEMBER      syscall.Errno = 1387 // 0x0000056B
	ERROR_INVALID_MEMBER      syscall.Errno = 1388 // 0x0000056C
)

// LOCALGROUP_INFO_0 represents level 0 information about local Windows groups.
// This struct matches the struct definition in the Windows headers (lmaccess.h).
type LOCALGROUP_INFO_0 struct {
	Lgrpi0_name *uint16 // UTF-16 group name
}

// LOCALGROUP_INFO_1 represents level 1 information about local Windows groups.
// This struct matches the struct definition in the Windows headers (lmaccess.h).
type LOCALGROUP_INFO_1 struct {
	Lgrpi1_name    *uint16 // UTF-16 group name
	Lgrpi1_comment *uint16 // UTF-16 group comment
}

// LocalGroupAdd adds a new local group with the specified name and comment.
func LocalGroupAdd(name, comment string) (bool, error) {
	var parmErr uint32
	var err error
	var gInfo LOCALGROUP_INFO_1

	gInfo.Lgrpi1_name, err = syscall.UTF16PtrFromString(name)
	if err != nil {
		return false, fmt.Errorf("Unable to encode group name to UTF16: %s", err)
	}
	gInfo.Lgrpi1_comment, err = syscall.UTF16PtrFromString(comment)
	if err != nil {
		return false, fmt.Errorf("Unable to encode comment to UTF16: %s", err)
	}

	ret, _, _ := usrNetLocalGroupAdd.Call(
		uintptr(0),                        // server name
		uintptr(uint32(1)),                // information level
		uintptr(unsafe.Pointer(&gInfo)),   // group information
		uintptr(unsafe.Pointer(&parmErr)), // error code out param
	)
	if ret != NET_API_STATUS_NERR_Success {
		return false, syscall.Errno(ret)
	}
	return true, nil
}

// ListLocalGroups enumerates the local groups defined on the system.
//
// If an error occurs in the call to the underlying NetLocalGroupEnum function, the
// returned error will be a syscall.Errno containing the error code.
// See: https://docs.microsoft.com/en-us/windows/desktop/api/lmaccess/nf-lmaccess-netlocalgroupenum
func ListLocalGroups() ([]so.LocalGroup, error) {
	var (
		dataPointer  uintptr
		resumeHandle uintptr
		entriesRead  uint32
		entriesTotal uint32
		sizeTest     LOCALGROUP_INFO_1
		retVal       = make([]so.LocalGroup, 0)
	)

	ret, _, _ := usrNetLocalGroupEnum.Call(
		uintptr(0),                                 // servername
		uintptr(uint32(1)),                         // level, LOCALGROUP_INFO_1
		uintptr(unsafe.Pointer(&dataPointer)),      // struct buffer for output data.
		uintptr(uint32(USER_MAX_PREFERRED_LENGTH)), // allow as much memory as required.
		uintptr(unsafe.Pointer(&entriesRead)),
		uintptr(unsafe.Pointer(&entriesTotal)),
		uintptr(unsafe.Pointer(&resumeHandle)),
	)
	if ret != NET_API_STATUS_NERR_Success {
		return nil, syscall.Errno(ret)
	} else if dataPointer == uintptr(0) {
		return nil, fmt.Errorf("null pointer while fetching entry")
	}
	defer usrNetApiBufferFree.Call(dataPointer)

	var iter = dataPointer
	for i := uint32(0); i < entriesRead; i++ {
		var data = (*LOCALGROUP_INFO_1)(unsafe.Pointer(iter))

		gd := so.LocalGroup{
			Name:    UTF16toString(data.Lgrpi1_name),
			Comment: UTF16toString(data.Lgrpi1_comment),
		}
		retVal = append(retVal, gd)

		iter = uintptr(unsafe.Pointer(iter + unsafe.Sizeof(sizeTest)))
	}

	return retVal, nil
}

// LocalGroupDel deletes the specified local group.
//
// If an error occurs in the call to the underlying NetLocalGroupDel function, the
// returned error will be a syscall.Errno containing the error code.
// See: https://docs.microsoft.com/en-us/windows/desktop/api/lmaccess/nf-lmaccess-netlocalgroupdel
func LocalGroupDel(name string) (bool, error) {
	namePtr, err := syscall.UTF16PtrFromString(name)
	if err != nil {
		return false, fmt.Errorf("Unable to encode group name to UTF16: %s", err)
	}

	ret, _, _ := usrNetLocalGroupDel.Call(
		uintptr(0), // servername
		uintptr(unsafe.Pointer(namePtr)),
	)
	if ret != NET_API_STATUS_NERR_Success {
		return false, syscall.Errno(ret)
	}
	return true, nil
}

func localGroupModMembers(proc *syscall.LazyProc, groupname string, usernames []string) (bool, error) {
	memberInfos := make([]LOCALGROUP_MEMBERS_INFO_3, 0, len(usernames))
	hostname, err := os.Hostname()
	if err != nil {
		return false, fmt.Errorf("Unable to determine hostname: %s", err)
	}
	groupnamePtr, err := syscall.UTF16PtrFromString(groupname)
	if err != nil {
		return false, fmt.Errorf("Unable to encode group name to UTF16: %s", err)
	}

	for _, username := range usernames {
		domainAndUsername := username
		if !strings.ContainsRune(username, '\\') {
			domainAndUsername = fmt.Sprintf(`%s\%s`, hostname, username)
		}
		namePtr, err := syscall.UTF16PtrFromString(domainAndUsername)
		if err != nil {
			return false, fmt.Errorf("Unable to encode username to UTF16: %s", err)
		}
		memberInfos = append(memberInfos, LOCALGROUP_MEMBERS_INFO_3{
			Lgrmi3_domainandname: namePtr,
		})
	}

	if len(memberInfos) == 0 {
		// Add a fake entry just so that the slice isn't empty, so we can take
		// the address of the first entry
		memberInfos = append(memberInfos, LOCALGROUP_MEMBERS_INFO_3{})
	}

	ret, _, _ := proc.Call(
		uintptr(0),                               // servername
		uintptr(unsafe.Pointer(groupnamePtr)),    // group name
		uintptr(3),                               // level, LOCALGROUP_MEMBERS_INFO_3
		uintptr(unsafe.Pointer(&memberInfos[0])), // buf
		uintptr(len(usernames)),                  // totalEntries
	)
	if ret != NET_API_STATUS_NERR_Success {
		return false, syscall.Errno(ret)
	}

	return true, nil
}

// LocalGroupSetMembers sets the membership of the group to contain exactly the
// set of users specified in usernames.
//
// If an error occurs in the call to the underlying NetLocalGroupSetMembers function, the
// returned error will be a syscall.Errno containing the error code.
// See: https://docs.microsoft.com/en-us/windows/desktop/api/lmaccess/nf-lmaccess-netlocalgroupsetmembers
func LocalGroupSetMembers(groupname string, usernames []string) (bool, error) {
	return localGroupModMembers(usrNetLocalGroupSetMembers, groupname, usernames)
}

// LocalGroupAddMembers adds the specified members to the group, if they are not
// already members.
//
// If an error occurs in the call to the underlying NetLocalGroupAddMembers function, the
// returned error will be a syscall.Errno containing the error code.
// See: https://docs.microsoft.com/en-us/windows/desktop/api/lmaccess/nf-lmaccess-netlocalgroupaddmembers
func LocalGroupAddMembers(groupname string, usernames []string) (bool, error) {
	return localGroupModMembers(usrNetLocalGroupAddMembers, groupname, usernames)
}

// LocalGroupDelMembers removes the specified members from the local group.
//
// If an error occurs in the call to the underlying NetLocalGroupDelMembers function, the
// returned error will be a syscall.Errno containing the error code.
// See: https://docs.microsoft.com/en-us/windows/desktop/api/lmaccess/nf-lmaccess-netlocalgroupdelmembers
func LocalGroupDelMembers(groupname string, usernames []string) (bool, error) {
	return localGroupModMembers(usrNetLocalGroupDelMembers, groupname, usernames)
}

// LocalGroupGetMembers returns information about the members of the specified
// local group.
//
// If an error occurs in the call to the underlying NetLocalGroupGetMembers function, the
// returned error will be a syscall.Errno containing the error code.
// See: https://docs.microsoft.com/en-us/windows/desktop/api/lmaccess/nf-lmaccess-netlocalgroupgetmembers
func LocalGroupGetMembers(groupname string) ([]so.LocalGroupMember, error) {
	var (
		dataPointer  uintptr
		resumeHandle uintptr
		entriesRead  uint32
		entriesTotal uint32
		sizeTest     LOCALGROUP_MEMBERS_INFO_3
		retVal       []so.LocalGroupMember = make([]so.LocalGroupMember, 0)
	)

	groupnamePtr, err := syscall.UTF16PtrFromString(groupname)
	if err != nil {
		return nil, fmt.Errorf("Unable to encode group name to UTF16: %s", err)
	}

	ret, _, _ := usrNetLocalGroupGetMembers.Call(
		uintptr(0),                                 // servername
		uintptr(unsafe.Pointer(groupnamePtr)),      // group name
		uintptr(3),                                 // level, LOCALGROUP_MEMBERS_INFO_3
		uintptr(unsafe.Pointer(&dataPointer)),      // bufptr
		uintptr(uint32(USER_MAX_PREFERRED_LENGTH)), // prefmaxlen
		uintptr(unsafe.Pointer(&entriesRead)),      // entriesread
		uintptr(unsafe.Pointer(&entriesTotal)),     // totalentries
		uintptr(unsafe.Pointer(&resumeHandle)),     // resumehandle
	)
	if ret != NET_API_STATUS_NERR_Success {
		return nil, syscall.Errno(ret)
	} else if dataPointer == uintptr(0) {
		return nil, fmt.Errorf("null pointer while fetching entry")
	}
	defer usrNetApiBufferFree.Call(dataPointer)

	var iter = dataPointer
	for i := uint32(0); i < entriesRead; i++ {
		var data = (*LOCALGROUP_MEMBERS_INFO_3)(unsafe.Pointer(iter))

		domainAndUsername := UTF16toString(data.Lgrmi3_domainandname)
		split := strings.SplitN(domainAndUsername, "\\", 1)
		var domain, name string
		if len(split) > 1 {
			domain = split[0]
			name = split[1]
		} else {
			// This really shouldn't happen, but just in case...
			name = split[0]
		}

		gd := so.LocalGroupMember{
			Domain:        domain,
			Name:          name,
			DomainAndName: domainAndUsername,
		}
		retVal = append(retVal, gd)

		iter = uintptr(unsafe.Pointer(iter + unsafe.Sizeof(sizeTest)))
	}

	return retVal, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"github.com/amahi/spdy"
	"golang.org/x/net/http2"
	"io/ioutil"
	"jgcrypto/tls"
	"net"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"
)

// in returns true is s is one of the strings in list
func in(list []string, s string) bool {
	for _, t := range list {
		if s == t {
			return true
		}
	}

	return false
}

// tri captures a tri-state. The value of yesno is true only is ran is
// true
type tri struct {
	ran   bool
	yesno bool
}

func (t tri) String() string {
	switch {
	case !t.ran:
		return "-"
	case t.yesno:
		return "t"
	case !t.yesno:
		return "f"
	}

	// Should not be reached ever

	return "!"
}

// site is a web site identified by its DNS name along with the state
// of various tests performed on the site. The states are only filled
// in if test() is called on the site.
type site struct {
	name string        // DNS name of the web site

	resolves       tri // Whether the name resolves
	port443Open    tri // Whether port 443 is open via TCP
	tlsWorks       tri // Whether a TLS connection works
	httpsWorks     tri // Whether an HTTPS request works using HTTP/1.1
	spdyAnnounced  tri // Whether spdy/3.1 is announced using NPN
	http2Announced tri // Whether h2 is announced using NPN
	spdyWorks      tri // Whether a SPDY/3.1 request works
	http2Works     tri // Whether an HTTP/2 request works

	npn            []string // List of protocols advertised by server using NPN
}

// test tests a site to see if it supports various protocols
func (s *site) test(l *os.File) {

	// Check the name resolves, give up if it does not

	s.resolves.ran = true
	_, err := net.LookupHost(s.name)
	if err != nil {
		s.logf(l, "Error resolving name: %s", err)
		s.resolves.yesno = false
		return
	}
	s.resolves.yesno = true

	// See if port 443 is open, give up if it is not

	hostPort := net.JoinHostPort(s.name, "443")

	s.port443Open.ran = true
	c, err := net.DialTimeout("tcp", hostPort, 2 * time.Second)
	if err != nil {
		s.logf(l, "TCP dial to port 443 failed: %s", err)
		s.port443Open.yesno = false
		return
	}
	c.Close()
	s.port443Open.yesno = true

	// See if TLS works and if SPDY/3.1 or HTTP/2 offered. Give up if
	// TLS does not work. If the name doesn't work try adding www.  to
	// se if it's a TLS certificate error.

	s.tlsWorks.ran = true
	config := &tls.Config{}
	config.ServerName = s.name
	tc, err := tls.Dial("tcp", hostPort, config)
	if err != nil {
		s.name = "www." + s.name
		hostPort = net.JoinHostPort(s.name, "443")
		tc, err = tls.Dial("tcp", hostPort, config)
		if err != nil {
			s.logf(l, "Error performing TLS connection: %s", err)
			s.tlsWorks.yesno = false
			return
		}
	}
	s.tlsWorks.yesno = true

	// Retrieve the list of NPN offered protocols and check for
	// spdy/3.1 and h2

	cs := tc.ConnectionState()
	s.npn = cs.OfferedProtocols
	s.spdyAnnounced.ran = true
	s.spdyAnnounced.yesno = in(cs.OfferedProtocols, "spdy/3.1")
	s.http2Announced.ran = true
	s.http2Announced.yesno = in(cs.OfferedProtocols, "h2")
	tc.Close()

	// See if HTTPS works by performing GET /

	s.httpsWorks.ran = true
	resp, err := http.Get("https://" + s.name)
	if err != nil {
		s.logf(l, "HTTP request failed: %s", err)
		return
	}
	if resp != nil && resp.Body != nil {
		_, _ = ioutil.ReadAll(resp.Body)
		resp.Body.Close()
	}
	s.httpsWorks.yesno = err == nil

	// See if SPDY works by doing GET / over SPDY

	if s.spdyAnnounced.yesno {
		s.spdyWorks.ran = true
		conf := &tls.Config{}
		conf.NextProtos = []string{"spdy/3.1"}
		spdyC, err := tls.Dial("tcp", hostPort, conf)
		if err == nil {
			defer spdyC.Close()
			cs = spdyC.ConnectionState()
			if cs.NegotiatedProtocol != "spdy/3.1" {
				s.logf(l, "NegotiatedProtocol not spdy/3.1: %s",
					cs.NegotiatedProtocol)
			} else {
				sc, err := spdy.NewClientConn(spdyC)
				if err == nil {
					defer sc.Close()
					req, _ := http.NewRequest("GET", "https://"+s.name, nil)
					resp, err := sc.Do(req)
					if err != nil {
						s.logf(l, "Failed to do SPDY request: %s", err)
					}
					s.spdyWorks.yesno = err == nil
					if resp != nil && resp.Body != nil {
						_, _ = ioutil.ReadAll(resp.Body)
						resp.Body.Close()
					}
				} else {
					s.logf(l, "Failed create SPDY client connection: %s", err)
				}
			}
		} else {
			s.logf(l, "Failed to dial port 443 for SPDY: %s", err)
		}
	}

	// See if HTTP/2 works by doing GET / using HTTP/2

	if s.http2Announced.yesno {
		s.http2Works.ran = true
		conf := &tls.Config{}
		conf.NextProtos = []string{"h2"}
		h2C, err := tls.Dial("tcp", hostPort, conf)
		if err == nil {
			defer h2C.Close()
			cs = h2C.ConnectionState()
			if cs.NegotiatedProtocol != "h2" {
				s.logf(l, "NegotiatedProtocol not h2: %s",
					cs.NegotiatedProtocol)
			} else {
				h2t := &http2.Transport{}
				h2c, err := h2t.NewClientConn(h2C)
				if err == nil {
					req, _ := http.NewRequest("GET", "https://"+s.name, nil)
					resp, err := h2c.RoundTrip(req)
					if err != nil {
						s.logf(l, "HTTP/2 RoundTrip failed: %s", err)
					}
					s.http2Works.yesno = err == nil
					if resp != nil && resp.Body != nil {
						_, _ = ioutil.ReadAll(resp.Body)
						resp.Body.Close()
					}
				} else {
					s.logf(l, "Failed create HTTP/2 client connection: %s", err)
				}
				h2t.CloseIdleConnections()
			}
		} else {
			s.logf(l, "Failed to dial port 443 for HTTP/2: %s", err)
		}
	}
}

// logf writes to the log file prefixing with the name of the site
// being logged
func (s *site) logf(f *os.File, format string, a ...interface{}) {
	if f != nil {
		fmt.Fprintf(f, fmt.Sprintf(s.name + ": " + format + "\n", a...))
	}
}

// fields returns the list of fields that String() will return for a
// site
func (s *site) fields() string {
	return "name,resolves,port443Open,tlsWorks,httpsWorks,spdyAnnounced,http2Announced,spdyWorks,http2Works,npn"
}

func (s *site) String() string {
	return fmt.Sprintf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s",
		s.name, s.resolves, s.port443Open, s.tlsWorks, s.httpsWorks,
		s.spdyAnnounced, s.http2Announced, s.spdyWorks, s.http2Works,
		strings.Join(s.npn, " "))
}

var wg sync.WaitGroup

func worker(work, result chan *site, l *os.File) {
	for s := range work {
		s.test(l)
		result <- s
	}
	wg.Done()
}

func writer(result chan *site, stop chan struct{}, fields bool) {
	first := true
	for s := range result {
		if fields && first {
			fmt.Printf("%s\n", s.fields())
			first = false
		}

		fmt.Printf("%s\n", s)
	}
	close(stop)
}

func main() {

	// The SPDY library is chatty so discard its log statements

	spdy.SetLog(ioutil.Discard)

	fields := flag.Bool("fields", false,
		"If set outputs a header line containing field names")
	workers := flag.Int("workers", 10, "Number of concurrent workers")
	log := flag.String("log", "", "File to write log information to")
	flag.Parse()

	if *workers < 1 {
		fmt.Printf("-workers must be a positive number\n")
		return
	}

	var l *os.File
	var err error
	if *log != "" {
		if l, err = os.Create(*log); err != nil {
			fmt.Printf("Failed to create log file %s: %s\n", *log, err)
			return
		}
		defer l.Close()
	}

	work := make(chan *site)
	result := make(chan *site)
	stop := make(chan struct{})

	go writer(result, stop, *fields)

	for i := 0; i < *workers; i++ {
		wg.Add(1)
		go worker(work, result, l)
	}

	scan := bufio.NewScanner(os.Stdin)
	for scan.Scan() {
		work <- &site{name: scan.Text()}
	}

	close(work)
	wg.Wait()
	close(result)
	<-stop

	if scan.Err() != nil {
		fmt.Printf("Error reading input: %s\n", scan.Err())
		return
	}
}
<commit_msg>Reset ServerName when trying www. prefix<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"github.com/amahi/spdy"
	"golang.org/x/net/http2"
	"io/ioutil"
	"jgcrypto/tls"
	"net"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"
)

// in returns true is s is one of the strings in list
func in(list []string, s string) bool {
	for _, t := range list {
		if s == t {
			return true
		}
	}

	return false
}

// tri captures a tri-state. The value of yesno is true only is ran is
// true
type tri struct {
	ran   bool
	yesno bool
}

func (t tri) String() string {
	switch {
	case !t.ran:
		return "-"
	case t.yesno:
		return "t"
	case !t.yesno:
		return "f"
	}

	// Should not be reached ever

	return "!"
}

// site is a web site identified by its DNS name along with the state
// of various tests performed on the site. The states are only filled
// in if test() is called on the site.
type site struct {
	name string        // DNS name of the web site

	resolves       tri // Whether the name resolves
	port443Open    tri // Whether port 443 is open via TCP
	tlsWorks       tri // Whether a TLS connection works
	httpsWorks     tri // Whether an HTTPS request works using HTTP/1.1
	spdyAnnounced  tri // Whether spdy/3.1 is announced using NPN
	http2Announced tri // Whether h2 is announced using NPN
	spdyWorks      tri // Whether a SPDY/3.1 request works
	http2Works     tri // Whether an HTTP/2 request works

	npn            []string // List of protocols advertised by server using NPN
}

// test tests a site to see if it supports various protocols
func (s *site) test(l *os.File) {

	// Check the name resolves, give up if it does not

	s.resolves.ran = true
	_, err := net.LookupHost(s.name)
	if err != nil {
		s.logf(l, "Error resolving name: %s", err)
		s.resolves.yesno = false
		return
	}
	s.resolves.yesno = true

	// See if port 443 is open, give up if it is not

	hostPort := net.JoinHostPort(s.name, "443")

	s.port443Open.ran = true
	c, err := net.DialTimeout("tcp", hostPort, 2 * time.Second)
	if err != nil {
		s.logf(l, "TCP dial to port 443 failed: %s", err)
		s.port443Open.yesno = false
		return
	}
	c.Close()
	s.port443Open.yesno = true

	// See if TLS works and if SPDY/3.1 or HTTP/2 offered. Give up if
	// TLS does not work. If the name doesn't work try adding www.  to
	// se if it's a TLS certificate error.

	s.tlsWorks.ran = true
	config := &tls.Config{}
	config.ServerName = s.name
	tc, err := tls.Dial("tcp", hostPort, config)
	if err != nil {
		s.name = "www." + s.name
		config.ServerName = s.name
		hostPort = net.JoinHostPort(s.name, "443")
		tc, err = tls.Dial("tcp", hostPort, config)
		if err != nil {
			s.logf(l, "Error performing TLS connection: %s", err)
			s.tlsWorks.yesno = false
			return
		}
	}
	s.tlsWorks.yesno = true

	// Retrieve the list of NPN offered protocols and check for
	// spdy/3.1 and h2

	cs := tc.ConnectionState()
	s.npn = cs.OfferedProtocols
	s.spdyAnnounced.ran = true
	s.spdyAnnounced.yesno = in(cs.OfferedProtocols, "spdy/3.1")
	s.http2Announced.ran = true
	s.http2Announced.yesno = in(cs.OfferedProtocols, "h2")
	tc.Close()

	// See if HTTPS works by performing GET /

	s.httpsWorks.ran = true
	resp, err := http.Get("https://" + s.name)
	if err != nil {
		s.logf(l, "HTTP request failed: %s", err)
		return
	}
	if resp != nil && resp.Body != nil {
		_, _ = ioutil.ReadAll(resp.Body)
		resp.Body.Close()
	}
	s.httpsWorks.yesno = err == nil

	// See if SPDY works by doing GET / over SPDY

	if s.spdyAnnounced.yesno {
		s.spdyWorks.ran = true
		conf := &tls.Config{}
		conf.NextProtos = []string{"spdy/3.1"}
		spdyC, err := tls.Dial("tcp", hostPort, conf)
		if err == nil {
			defer spdyC.Close()
			cs = spdyC.ConnectionState()
			if cs.NegotiatedProtocol != "spdy/3.1" {
				s.logf(l, "NegotiatedProtocol not spdy/3.1: %s",
					cs.NegotiatedProtocol)
			} else {
				sc, err := spdy.NewClientConn(spdyC)
				if err == nil {
					defer sc.Close()
					req, _ := http.NewRequest("GET", "https://"+s.name, nil)
					resp, err := sc.Do(req)
					if err != nil {
						s.logf(l, "Failed to do SPDY request: %s", err)
					}
					s.spdyWorks.yesno = err == nil
					if resp != nil && resp.Body != nil {
						_, _ = ioutil.ReadAll(resp.Body)
						resp.Body.Close()
					}
				} else {
					s.logf(l, "Failed create SPDY client connection: %s", err)
				}
			}
		} else {
			s.logf(l, "Failed to dial port 443 for SPDY: %s", err)
		}
	}

	// See if HTTP/2 works by doing GET / using HTTP/2

	if s.http2Announced.yesno {
		s.http2Works.ran = true
		conf := &tls.Config{}
		conf.NextProtos = []string{"h2"}
		h2C, err := tls.Dial("tcp", hostPort, conf)
		if err == nil {
			defer h2C.Close()
			cs = h2C.ConnectionState()
			if cs.NegotiatedProtocol != "h2" {
				s.logf(l, "NegotiatedProtocol not h2: %s",
					cs.NegotiatedProtocol)
			} else {
				h2t := &http2.Transport{}
				h2c, err := h2t.NewClientConn(h2C)
				if err == nil {
					req, _ := http.NewRequest("GET", "https://"+s.name, nil)
					resp, err := h2c.RoundTrip(req)
					if err != nil {
						s.logf(l, "HTTP/2 RoundTrip failed: %s", err)
					}
					s.http2Works.yesno = err == nil
					if resp != nil && resp.Body != nil {
						_, _ = ioutil.ReadAll(resp.Body)
						resp.Body.Close()
					}
				} else {
					s.logf(l, "Failed create HTTP/2 client connection: %s", err)
				}
				h2t.CloseIdleConnections()
			}
		} else {
			s.logf(l, "Failed to dial port 443 for HTTP/2: %s", err)
		}
	}
}

// logf writes to the log file prefixing with the name of the site
// being logged
func (s *site) logf(f *os.File, format string, a ...interface{}) {
	if f != nil {
		fmt.Fprintf(f, fmt.Sprintf(s.name + ": " + format + "\n", a...))
	}
}

// fields returns the list of fields that String() will return for a
// site
func (s *site) fields() string {
	return "name,resolves,port443Open,tlsWorks,httpsWorks,spdyAnnounced,http2Announced,spdyWorks,http2Works,npn"
}

func (s *site) String() string {
	return fmt.Sprintf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s",
		s.name, s.resolves, s.port443Open, s.tlsWorks, s.httpsWorks,
		s.spdyAnnounced, s.http2Announced, s.spdyWorks, s.http2Works,
		strings.Join(s.npn, " "))
}

var wg sync.WaitGroup

func worker(work, result chan *site, l *os.File) {
	for s := range work {
		s.test(l)
		result <- s
	}
	wg.Done()
}

func writer(result chan *site, stop chan struct{}, fields bool) {
	first := true
	for s := range result {
		if fields && first {
			fmt.Printf("%s\n", s.fields())
			first = false
		}

		fmt.Printf("%s\n", s)
	}
	close(stop)
}

func main() {

	// The SPDY library is chatty so discard its log statements

	spdy.SetLog(ioutil.Discard)

	fields := flag.Bool("fields", false,
		"If set outputs a header line containing field names")
	workers := flag.Int("workers", 10, "Number of concurrent workers")
	log := flag.String("log", "", "File to write log information to")
	flag.Parse()

	if *workers < 1 {
		fmt.Printf("-workers must be a positive number\n")
		return
	}

	var l *os.File
	var err error
	if *log != "" {
		if l, err = os.Create(*log); err != nil {
			fmt.Printf("Failed to create log file %s: %s\n", *log, err)
			return
		}
		defer l.Close()
	}

	work := make(chan *site)
	result := make(chan *site)
	stop := make(chan struct{})

	go writer(result, stop, *fields)

	for i := 0; i < *workers; i++ {
		wg.Add(1)
		go worker(work, result, l)
	}

	scan := bufio.NewScanner(os.Stdin)
	for scan.Scan() {
		work <- &site{name: scan.Text()}
	}

	close(work)
	wg.Wait()
	close(result)
	<-stop

	if scan.Err() != nil {
		fmt.Printf("Error reading input: %s\n", scan.Err())
		return
	}
}
<|endoftext|>


<commit_before>package h2spec

import (
	"crypto/tls"
	"errors"
	"fmt"
	"github.com/bradfitz/http2"
	"github.com/bradfitz/http2/hpack"
	"net"
	"os"
	"strings"
	"time"
)

type TcpConn struct {
	conn   net.Conn
	dataCh chan []byte
	errCh  chan error
}

type Http2Conn struct {
	conn   net.Conn
	fr     *http2.Framer
	dataCh chan http2.Frame
	errCh  chan error
}

// ReadFrame reads a complete HTTP/2 frame from underlying connection.
// This function blocks until a complete frame is received or timeout
// t is expired.  The returned http2.Frame must not be used after next
// ReadFrame call.
func (h2Conn *Http2Conn) ReadFrame(t time.Duration) (http2.Frame, error) {
	go func() {
		f, err := h2Conn.fr.ReadFrame()
		if err != nil {
			h2Conn.errCh <- err
			return
		}
		h2Conn.dataCh <- f
	}()

	select {
	case f := <-h2Conn.dataCh:
		return f, nil
	case err := <-h2Conn.errCh:
		return nil, err
	case <-time.After(t):
		return nil, errors.New("timeout waiting for frame")
	}
}

type Context struct {
	Port      int
	Host      string
	Tls       bool
	TlsConfig *tls.Config
	Sections  map[string]bool
}

func (ctx *Context) Authority() string {
	return fmt.Sprintf("%s:%d", ctx.Host, ctx.Port)
}

func (ctx *Context) IsTarget(section string) bool {
	if ctx.Sections == nil {
		return true
	}

	_, ok := ctx.Sections[section]
	return ok
}

func Run(ctx *Context) {
	TestHttp2ConnectionPreface(ctx)
	TestFrameSize(ctx)
	TestHeaderCompressionAndDecompression(ctx)
	TestStreamStates(ctx)
	TestErrorHandling(ctx)
	TestData(ctx)
	TestHeaders(ctx)
	TestPriority(ctx)
	TestRstStream(ctx)
	TestSettings(ctx)
	TestPing(ctx)
	TestGoaway(ctx)
	TestWindowUpdate(ctx)
	TestContinuation(ctx)
	TestHTTPRequestResponseExchange(ctx)
}

func connectTls(ctx *Context) (net.Conn, error) {
	if ctx.TlsConfig == nil {
		ctx.TlsConfig = new(tls.Config)
	}
	if ctx.TlsConfig.NextProtos == nil {
		ctx.TlsConfig.NextProtos = append(ctx.TlsConfig.NextProtos, "h2-14", "h2-16")
	}
	conn, err := tls.Dial("tcp", ctx.Authority(), ctx.TlsConfig)
	if err != nil {
		return nil, err
	}

	cs := conn.ConnectionState()
	if !cs.NegotiatedProtocolIsMutual {
		return nil, fmt.Errorf("HTTP/2 protocol was not negotiated")
	}

	return conn, err
}

func CreateTcpConn(ctx *Context) *TcpConn {
	var conn net.Conn
	var err error
	if ctx.Tls {
		conn, err = connectTls(ctx)
	} else {
		conn, err = net.Dial("tcp", ctx.Authority())
	}

	if err != nil {
		fmt.Printf("Unable to connect to the target server: %v\n", err)
		os.Exit(1)
	}

	dataCh := make(chan []byte)
	errCh := make(chan error, 1)

	tcpConn := &TcpConn{
		conn:   conn,
		dataCh: dataCh,
		errCh:  errCh,
	}

	go func() {
		for {
			buf := make([]byte, 512)
			_, err := conn.Read(buf)
			dataCh <- buf
			if err != nil {
				errCh <- err
				return
			}
		}
	}()

	return tcpConn
}

func CreateHttp2Conn(ctx *Context, sn bool) *Http2Conn {
	var conn net.Conn
	var err error
	if ctx.Tls {
		conn, err = connectTls(ctx)
	} else {
		conn, err = net.Dial("tcp", ctx.Authority())
	}

	if err != nil {
		fmt.Printf("Unable to connect to the target server: %v\n", err)
		os.Exit(1)
	}

	fmt.Fprintf(conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")

	if sn {
		done := false
		fr := http2.NewFramer(conn, conn)
		fr.WriteSettings()

		for {
			f, _ := fr.ReadFrame()
			switch f := f.(type) {
			case *http2.SettingsFrame:
				if f.IsAck() {
					done = true
				} else {
					fr.WriteSettingsAck()
				}
			default:
				done = true
			}

			if done {
				break
			}
		}
	}

	fr := http2.NewFramer(conn, conn)
	fr.AllowIllegalWrites = true
	dataCh := make(chan http2.Frame)
	errCh := make(chan error, 1)

	http2Conn := &Http2Conn{
		conn:   conn,
		fr:     fr,
		dataCh: dataCh,
		errCh:  errCh,
	}

	return http2Conn
}

func SetReadTimer(conn net.Conn, sec time.Duration) {
	now := time.Now()
	conn.SetReadDeadline(now.Add(time.Second * sec))
}

func PrintHeader(title string, i int) {
	fmt.Printf("%s%s\n", strings.Repeat("  ", i), title)
}

func PrintFooter() {
	fmt.Println("")
}

func PrintResult(result bool, desc string, msg string, i int) {
	var mark string
	indent := strings.Repeat("  ", i+1)
	if result {
		mark = "✓"
		fmt.Printf("%s\x1b[32m%s\x1b[0m \x1b[90m%s\x1b[0m\n", indent, mark, desc)
	} else {
		mark = "×"
		fmt.Printf("%s\x1b[31m%s %s\x1b[0m\n", indent, mark, desc)
		fmt.Printf("%s\x1b[31m  - %s\x1b[0m\n", indent, msg)
	}
}

func pair(name, value string) hpack.HeaderField {
	return hpack.HeaderField{Name: name, Value: value}
}
<commit_msg>Fix settings negotiation<commit_after>package h2spec

import (
	"crypto/tls"
	"errors"
	"fmt"
	"github.com/bradfitz/http2"
	"github.com/bradfitz/http2/hpack"
	"net"
	"os"
	"strings"
	"time"
)

type TcpConn struct {
	conn   net.Conn
	dataCh chan []byte
	errCh  chan error
}

type Http2Conn struct {
	conn   net.Conn
	fr     *http2.Framer
	dataCh chan http2.Frame
	errCh  chan error
}

// ReadFrame reads a complete HTTP/2 frame from underlying connection.
// This function blocks until a complete frame is received or timeout
// t is expired.  The returned http2.Frame must not be used after next
// ReadFrame call.
func (h2Conn *Http2Conn) ReadFrame(t time.Duration) (http2.Frame, error) {
	go func() {
		f, err := h2Conn.fr.ReadFrame()
		if err != nil {
			h2Conn.errCh <- err
			return
		}
		h2Conn.dataCh <- f
	}()

	select {
	case f := <-h2Conn.dataCh:
		return f, nil
	case err := <-h2Conn.errCh:
		return nil, err
	case <-time.After(t):
		return nil, errors.New("timeout waiting for frame")
	}
}

type Context struct {
	Port      int
	Host      string
	Tls       bool
	TlsConfig *tls.Config
	Sections  map[string]bool
}

func (ctx *Context) Authority() string {
	return fmt.Sprintf("%s:%d", ctx.Host, ctx.Port)
}

func (ctx *Context) IsTarget(section string) bool {
	if ctx.Sections == nil {
		return true
	}

	_, ok := ctx.Sections[section]
	return ok
}

func Run(ctx *Context) {
	TestHttp2ConnectionPreface(ctx)
	TestFrameSize(ctx)
	TestHeaderCompressionAndDecompression(ctx)
	TestStreamStates(ctx)
	TestErrorHandling(ctx)
	TestData(ctx)
	TestHeaders(ctx)
	TestPriority(ctx)
	TestRstStream(ctx)
	TestSettings(ctx)
	TestPing(ctx)
	TestGoaway(ctx)
	TestWindowUpdate(ctx)
	TestContinuation(ctx)
	TestHTTPRequestResponseExchange(ctx)
}

func connectTls(ctx *Context) (net.Conn, error) {
	if ctx.TlsConfig == nil {
		ctx.TlsConfig = new(tls.Config)
	}
	if ctx.TlsConfig.NextProtos == nil {
		ctx.TlsConfig.NextProtos = append(ctx.TlsConfig.NextProtos, "h2-14", "h2-16")
	}
	conn, err := tls.Dial("tcp", ctx.Authority(), ctx.TlsConfig)
	if err != nil {
		return nil, err
	}

	cs := conn.ConnectionState()
	if !cs.NegotiatedProtocolIsMutual {
		return nil, fmt.Errorf("HTTP/2 protocol was not negotiated")
	}

	return conn, err
}

func CreateTcpConn(ctx *Context) *TcpConn {
	var conn net.Conn
	var err error
	if ctx.Tls {
		conn, err = connectTls(ctx)
	} else {
		conn, err = net.Dial("tcp", ctx.Authority())
	}

	if err != nil {
		fmt.Printf("Unable to connect to the target server: %v\n", err)
		os.Exit(1)
	}

	dataCh := make(chan []byte)
	errCh := make(chan error, 1)

	tcpConn := &TcpConn{
		conn:   conn,
		dataCh: dataCh,
		errCh:  errCh,
	}

	go func() {
		for {
			buf := make([]byte, 512)
			_, err := conn.Read(buf)
			dataCh <- buf
			if err != nil {
				errCh <- err
				return
			}
		}
	}()

	return tcpConn
}

func CreateHttp2Conn(ctx *Context, sn bool) *Http2Conn {
	var conn net.Conn
	var err error
	if ctx.Tls {
		conn, err = connectTls(ctx)
	} else {
		conn, err = net.Dial("tcp", ctx.Authority())
	}

	if err != nil {
		fmt.Printf("Unable to connect to the target server: %v\n", err)
		os.Exit(1)
	}

	fmt.Fprintf(conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")

	done := false
	fr := http2.NewFramer(conn, conn)
	fr.WriteSettings()

	for {
		f, _ := fr.ReadFrame()
		switch f := f.(type) {
		case *http2.SettingsFrame:
			if f.IsAck() {
				done = true
			} else {
				if sn {
					fr.WriteSettingsAck()
				}
			}
		default:
			done = true
		}

		if done {
			break
		}
	}

	fr.AllowIllegalWrites = true
	dataCh := make(chan http2.Frame)
	errCh := make(chan error, 1)

	http2Conn := &Http2Conn{
		conn:   conn,
		fr:     fr,
		dataCh: dataCh,
		errCh:  errCh,
	}

	return http2Conn
}

func SetReadTimer(conn net.Conn, sec time.Duration) {
	now := time.Now()
	conn.SetReadDeadline(now.Add(time.Second * sec))
}

func PrintHeader(title string, i int) {
	fmt.Printf("%s%s\n", strings.Repeat("  ", i), title)
}

func PrintFooter() {
	fmt.Println("")
}

func PrintResult(result bool, desc string, msg string, i int) {
	var mark string
	indent := strings.Repeat("  ", i+1)
	if result {
		mark = "✓"
		fmt.Printf("%s\x1b[32m%s\x1b[0m \x1b[90m%s\x1b[0m\n", indent, mark, desc)
	} else {
		mark = "×"
		fmt.Printf("%s\x1b[31m%s %s\x1b[0m\n", indent, mark, desc)
		fmt.Printf("%s\x1b[31m  - %s\x1b[0m\n", indent, msg)
	}
}

func pair(name, value string) hpack.HeaderField {
	return hpack.HeaderField{Name: name, Value: value}
}
<|endoftext|>


<commit_before>// The fuse package provides APIs to implement filesystems in
// userspace, using libfuse on Linux.  Typically, each call of the API
// happens in its own goroutine, so take care to make the file system
// thread-safe.

package fuse

import (
	"os"
)

// Types for users to implement.

// NodeFileSystem is a high level API that resembles the kernel's idea
// of what an FS looks like.  NodeFileSystems can have multiple
// hard-links to one file, for example. It is also suited if the data
// to represent fits in memory: you can construct FsNode at mount
// time, and the filesystem will be ready.
type NodeFileSystem interface {
	OnUnmount()
	OnMount(conn *FileSystemConnector)
	Root() FsNode

	// Used for debug outputs
	String() string
}

type FsNode interface {
	// The following are called by the FileSystemConnector
	Inode() *Inode
	SetInode(node *Inode)

	Lookup(name string, context *Context) (fi *os.FileInfo, node FsNode, code Status)

	// Deletable() should return true if this inode may be
	// discarded from the children list. This will be called from
	// within the treeLock critical section, so you cannot look at
	// other inodes.
	Deletable() bool
	OnForget()

	// Misc.
	Access(mode uint32, context *Context) (code Status)
	Readlink(c *Context) ([]byte, Status)

	// Namespace operations
	Mknod(name string, mode uint32, dev uint32, context *Context) (fi *os.FileInfo, newNode FsNode, code Status)
	Mkdir(name string, mode uint32, context *Context) (fi *os.FileInfo, newNode FsNode, code Status)
	Unlink(name string, context *Context) (code Status)
	Rmdir(name string, context *Context) (code Status)
	Symlink(name string, content string, context *Context) (fi *os.FileInfo, newNode FsNode, code Status)
	Rename(oldName string, newParent FsNode, newName string, context *Context) (code Status)
	Link(name string, existing FsNode, context *Context) (fi *os.FileInfo, newNode FsNode, code Status)

	// Files
	Create(name string, flags uint32, mode uint32, context *Context) (file File, fi *os.FileInfo, newNode FsNode, code Status)
	Open(flags uint32, context *Context) (file File, code Status)
	OpenDir(context *Context) (chan DirEntry, Status)

	// XAttrs
	GetXAttr(attribute string, context *Context) (data []byte, code Status)
	RemoveXAttr(attr string, context *Context) Status
	SetXAttr(attr string, data []byte, flags int, context *Context) Status
	ListXAttr(context *Context) (attrs []string, code Status)

	// Attributes
	GetAttr(file File, context *Context) (fi *os.FileInfo, code Status)
	Chmod(file File, perms uint32, context *Context) (code Status)
	Chown(file File, uid uint32, gid uint32, context *Context) (code Status)
	Truncate(file File, size uint64, context *Context) (code Status)
	Utimens(file File, atime uint64, mtime uint64, context *Context) (code Status)

	StatFs() *StatfsOut
}

// A filesystem API that uses paths rather than inodes.  A minimal
// file system should have at least a functional GetAttr method.
// Typically, each call happens in its own goroutine, so take care to
// make the file system thread-safe.
//
// Include DefaultFileSystem to provide a default null implementation of
// required methods.
type FileSystem interface {
	// Used for pretty printing.
	String() string

	// Attributes.  This function is the main entry point, through
	// which FUSE discovers which files and directories exist.
	//
	// If the filesystem wants to implement hard-links, it should
	// return consistent non-zero FileInfo.Ino data.  Using
	// hardlinks incurs a performance hit.
	GetAttr(name string, context *Context) (*os.FileInfo, Status)

	// These should update the file's ctime too.
	Chmod(name string, mode uint32, context *Context) (code Status)
	Chown(name string, uid uint32, gid uint32, context *Context) (code Status)
	Utimens(name string, AtimeNs uint64, MtimeNs uint64, context *Context) (code Status)

	Truncate(name string, size uint64, context *Context) (code Status)

	Access(name string, mode uint32, context *Context) (code Status)

	// Tree structure
	Link(oldName string, newName string, context *Context) (code Status)
	Mkdir(name string, mode uint32, context *Context) Status
	Mknod(name string, mode uint32, dev uint32, context *Context) Status
	Rename(oldName string, newName string, context *Context) (code Status)
	Rmdir(name string, context *Context) (code Status)
	Unlink(name string, context *Context) (code Status)

	// Extended attributes.
	GetXAttr(name string, attribute string, context *Context) (data []byte, code Status)
	ListXAttr(name string, context *Context) (attributes []string, code Status)
	RemoveXAttr(name string, attr string, context *Context) Status
	SetXAttr(name string, attr string, data []byte, flags int, context *Context) Status

	// Called after mount.
	OnMount(nodeFs *PathNodeFs)
	OnUnmount()

	// File handling.  If opening for writing, the file's mtime
	// should be updated too.
	Open(name string, flags uint32, context *Context) (file File, code Status)
	Create(name string, flags uint32, mode uint32, context *Context) (file File, code Status)

	// Directory handling
	OpenDir(name string, context *Context) (stream chan DirEntry, code Status)

	// Symlinks.
	Symlink(value string, linkName string, context *Context) (code Status)
	Readlink(name string, context *Context) (string, Status)

	StatFs(name string) *StatfsOut
}

// A File object should be returned from FileSystem.Open and
// FileSystem.Create.  Include DefaultFile into the struct to inherit
// a default null implementation.
//
// TODO - should File be thread safe?
// TODO - should we pass a *Context argument?
type File interface {
	// Called upon registering the filehandle in the inode.
	SetInode(*Inode)

	// The String method is for debug printing.
	String() string

	// Wrappers around other File implementations, should return
	// the inner file here.
	InnerFile() File

	Read(*ReadIn, BufferPool) ([]byte, Status)
	Write(*WriteIn, []byte) (written uint32, code Status)
	Flush() Status
	Release()
	Fsync(*FsyncIn) (code Status)

	// The methods below may be called on closed files, due to
	// concurrency.  In that case, you should return EBADF.
	Truncate(size uint64) Status
	GetAttr() (*os.FileInfo, Status)
	Chown(uid uint32, gid uint32) Status
	Chmod(perms uint32) Status
	Utimens(atimeNs uint64, mtimeNs uint64) Status
}

// Wrap a File return in this to set FUSE flags.  Also used internally
// to store open file data.
type WithFlags struct {
	File

	// For debugging.
	Description string

	// Put FOPEN_* flags here.
	FuseFlags uint32

	// O_RDWR, O_TRUNCATE, etc.
	OpenFlags uint32
}

// MountOptions contains time out options for a (Node)FileSystem.  The
// default copied from libfuse and set in NewMountOptions() is
// (1s,1s,0s).
type FileSystemOptions struct {
	EntryTimeout    float64
	AttrTimeout     float64
	NegativeTimeout float64

	// If set, replace all uids with given UID.
	// NewFileSystemOptions() will set this to the daemon's
	// uid/gid.
	*Owner

	// If set, use a more portable, but slower inode number
	// generation scheme.  This will make inode numbers (exported
	// back to callers) stay within int32, which is necessary for
	// making stat() succeed in 32-bit programs.
	PortableInodes bool
}

type MountOptions struct {
	AllowOther bool

	// Options are passed as -o string to fusermount.
	Options []string

	// Default is _DEFAULT_BACKGROUND_TASKS, 12.  This numbers
	// controls the allowed number of requests that relate to
	// async I/O.  Concurrency for synchronous I/O is not limited.
	MaxBackground int
}

// DefaultFileSystem implements a FileSystem that returns ENOSYS for every operation.
type DefaultFileSystem struct{}

// DefaultFile returns ENOSYS for every operation.
type DefaultFile struct{}

// RawFileSystem is an interface close to the FUSE wire protocol.
//
// Unless you really know what you are doing, you should not implement
// this, but rather the FileSystem interface; the details of getting
// interactions with open files, renames, and threading right etc. are
// somewhat tricky and not very interesting.
//
// Include DefaultRawFileSystem to inherit a null implementation.
type RawFileSystem interface {
	Lookup(header *InHeader, name string) (out *EntryOut, status Status)
	Forget(header *InHeader, input *ForgetIn)

	// Attributes.
	GetAttr(header *InHeader, input *GetAttrIn) (out *AttrOut, code Status)
	SetAttr(header *InHeader, input *SetAttrIn) (out *AttrOut, code Status)

	// Modifying structure.
	Mknod(header *InHeader, input *MknodIn, name string) (out *EntryOut, code Status)
	Mkdir(header *InHeader, input *MkdirIn, name string) (out *EntryOut, code Status)
	Unlink(header *InHeader, name string) (code Status)
	Rmdir(header *InHeader, name string) (code Status)
	Rename(header *InHeader, input *RenameIn, oldName string, newName string) (code Status)
	Link(header *InHeader, input *LinkIn, filename string) (out *EntryOut, code Status)

	Symlink(header *InHeader, pointedTo string, linkName string) (out *EntryOut, code Status)
	Readlink(header *InHeader) (out []byte, code Status)
	Access(header *InHeader, input *AccessIn) (code Status)

	// Extended attributes.
	GetXAttr(header *InHeader, attr string) (data []byte, code Status)
	ListXAttr(header *InHeader) (attributes []byte, code Status)
	SetXAttr(header *InHeader, input *SetXAttrIn, attr string, data []byte) Status
	RemoveXAttr(header *InHeader, attr string) (code Status)

	// File handling.
	Create(header *InHeader, input *CreateIn, name string) (flags uint32, handle uint64, out *EntryOut, code Status)
	Open(header *InHeader, input *OpenIn) (flags uint32, handle uint64, status Status)
	Read(*InHeader, *ReadIn, BufferPool) ([]byte, Status)

	Release(header *InHeader, input *ReleaseIn)
	Write(*InHeader, *WriteIn, []byte) (written uint32, code Status)
	Flush(header *InHeader, input *FlushIn) Status
	Fsync(*InHeader, *FsyncIn) (code Status)

	// Directory handling
	OpenDir(header *InHeader, input *OpenIn) (flags uint32, handle uint64, status Status)
	ReadDir(header *InHeader, input *ReadIn) (*DirEntryList, Status)
	ReleaseDir(header *InHeader, input *ReleaseIn)
	FsyncDir(header *InHeader, input *FsyncIn) (code Status)

	//
	Ioctl(header *InHeader, input *IoctlIn) (output *IoctlOut, data []byte, code Status)
	StatFs(header *InHeader) *StatfsOut

	// Provide callbacks for pushing notifications to the kernel.
	Init(params *RawFsInit)
}

// DefaultRawFileSystem returns ENOSYS for every operation.
type DefaultRawFileSystem struct{}

// Talk back to FUSE.
//
// InodeNotify invalidates the information associated with the inode
// (ie. data cache, attributes, etc.)
//
// EntryNotify should be used if the existence status of an entry changes,
// (ie. to notify of creation or deletion of the file).
//
// Somewhat confusingly, InodeNotify for a file that stopped to exist
// will give the correct result for Lstat (ENOENT), but the kernel
// will still issue file Open() on the inode.
type RawFsInit struct {
	InodeNotify func(*NotifyInvalInodeOut) Status
	EntryNotify func(parent uint64, name string) Status
}
<commit_msg>Drop libfuse reference from api.go<commit_after>// The fuse package provides APIs to implement filesystems in
// userspace.  Typically, each call of the API happens in its own
// goroutine, so take care to make the file system thread-safe.

package fuse

import (
	"os"
)

// Types for users to implement.

// NodeFileSystem is a high level API that resembles the kernel's idea
// of what an FS looks like.  NodeFileSystems can have multiple
// hard-links to one file, for example. It is also suited if the data
// to represent fits in memory: you can construct FsNode at mount
// time, and the filesystem will be ready.
type NodeFileSystem interface {
	OnUnmount()
	OnMount(conn *FileSystemConnector)
	Root() FsNode

	// Used for debug outputs
	String() string
}

type FsNode interface {
	// The following are called by the FileSystemConnector
	Inode() *Inode
	SetInode(node *Inode)

	Lookup(name string, context *Context) (fi *os.FileInfo, node FsNode, code Status)

	// Deletable() should return true if this inode may be
	// discarded from the children list. This will be called from
	// within the treeLock critical section, so you cannot look at
	// other inodes.
	Deletable() bool
	OnForget()

	// Misc.
	Access(mode uint32, context *Context) (code Status)
	Readlink(c *Context) ([]byte, Status)

	// Namespace operations
	Mknod(name string, mode uint32, dev uint32, context *Context) (fi *os.FileInfo, newNode FsNode, code Status)
	Mkdir(name string, mode uint32, context *Context) (fi *os.FileInfo, newNode FsNode, code Status)
	Unlink(name string, context *Context) (code Status)
	Rmdir(name string, context *Context) (code Status)
	Symlink(name string, content string, context *Context) (fi *os.FileInfo, newNode FsNode, code Status)
	Rename(oldName string, newParent FsNode, newName string, context *Context) (code Status)
	Link(name string, existing FsNode, context *Context) (fi *os.FileInfo, newNode FsNode, code Status)

	// Files
	Create(name string, flags uint32, mode uint32, context *Context) (file File, fi *os.FileInfo, newNode FsNode, code Status)
	Open(flags uint32, context *Context) (file File, code Status)
	OpenDir(context *Context) (chan DirEntry, Status)

	// XAttrs
	GetXAttr(attribute string, context *Context) (data []byte, code Status)
	RemoveXAttr(attr string, context *Context) Status
	SetXAttr(attr string, data []byte, flags int, context *Context) Status
	ListXAttr(context *Context) (attrs []string, code Status)

	// Attributes
	GetAttr(file File, context *Context) (fi *os.FileInfo, code Status)
	Chmod(file File, perms uint32, context *Context) (code Status)
	Chown(file File, uid uint32, gid uint32, context *Context) (code Status)
	Truncate(file File, size uint64, context *Context) (code Status)
	Utimens(file File, atime uint64, mtime uint64, context *Context) (code Status)

	StatFs() *StatfsOut
}

// A filesystem API that uses paths rather than inodes.  A minimal
// file system should have at least a functional GetAttr method.
// Typically, each call happens in its own goroutine, so take care to
// make the file system thread-safe.
//
// Include DefaultFileSystem to provide a default null implementation of
// required methods.
type FileSystem interface {
	// Used for pretty printing.
	String() string

	// Attributes.  This function is the main entry point, through
	// which FUSE discovers which files and directories exist.
	//
	// If the filesystem wants to implement hard-links, it should
	// return consistent non-zero FileInfo.Ino data.  Using
	// hardlinks incurs a performance hit.
	GetAttr(name string, context *Context) (*os.FileInfo, Status)

	// These should update the file's ctime too.
	Chmod(name string, mode uint32, context *Context) (code Status)
	Chown(name string, uid uint32, gid uint32, context *Context) (code Status)
	Utimens(name string, AtimeNs uint64, MtimeNs uint64, context *Context) (code Status)

	Truncate(name string, size uint64, context *Context) (code Status)

	Access(name string, mode uint32, context *Context) (code Status)

	// Tree structure
	Link(oldName string, newName string, context *Context) (code Status)
	Mkdir(name string, mode uint32, context *Context) Status
	Mknod(name string, mode uint32, dev uint32, context *Context) Status
	Rename(oldName string, newName string, context *Context) (code Status)
	Rmdir(name string, context *Context) (code Status)
	Unlink(name string, context *Context) (code Status)

	// Extended attributes.
	GetXAttr(name string, attribute string, context *Context) (data []byte, code Status)
	ListXAttr(name string, context *Context) (attributes []string, code Status)
	RemoveXAttr(name string, attr string, context *Context) Status
	SetXAttr(name string, attr string, data []byte, flags int, context *Context) Status

	// Called after mount.
	OnMount(nodeFs *PathNodeFs)
	OnUnmount()

	// File handling.  If opening for writing, the file's mtime
	// should be updated too.
	Open(name string, flags uint32, context *Context) (file File, code Status)
	Create(name string, flags uint32, mode uint32, context *Context) (file File, code Status)

	// Directory handling
	OpenDir(name string, context *Context) (stream chan DirEntry, code Status)

	// Symlinks.
	Symlink(value string, linkName string, context *Context) (code Status)
	Readlink(name string, context *Context) (string, Status)

	StatFs(name string) *StatfsOut
}

// A File object should be returned from FileSystem.Open and
// FileSystem.Create.  Include DefaultFile into the struct to inherit
// a default null implementation.
//
// TODO - should File be thread safe?
// TODO - should we pass a *Context argument?
type File interface {
	// Called upon registering the filehandle in the inode.
	SetInode(*Inode)

	// The String method is for debug printing.
	String() string

	// Wrappers around other File implementations, should return
	// the inner file here.
	InnerFile() File

	Read(*ReadIn, BufferPool) ([]byte, Status)
	Write(*WriteIn, []byte) (written uint32, code Status)
	Flush() Status
	Release()
	Fsync(*FsyncIn) (code Status)

	// The methods below may be called on closed files, due to
	// concurrency.  In that case, you should return EBADF.
	Truncate(size uint64) Status
	GetAttr() (*os.FileInfo, Status)
	Chown(uid uint32, gid uint32) Status
	Chmod(perms uint32) Status
	Utimens(atimeNs uint64, mtimeNs uint64) Status
}

// Wrap a File return in this to set FUSE flags.  Also used internally
// to store open file data.
type WithFlags struct {
	File

	// For debugging.
	Description string

	// Put FOPEN_* flags here.
	FuseFlags uint32

	// O_RDWR, O_TRUNCATE, etc.
	OpenFlags uint32
}

// MountOptions contains time out options for a (Node)FileSystem.  The
// default copied from libfuse and set in NewMountOptions() is
// (1s,1s,0s).
type FileSystemOptions struct {
	EntryTimeout    float64
	AttrTimeout     float64
	NegativeTimeout float64

	// If set, replace all uids with given UID.
	// NewFileSystemOptions() will set this to the daemon's
	// uid/gid.
	*Owner

	// If set, use a more portable, but slower inode number
	// generation scheme.  This will make inode numbers (exported
	// back to callers) stay within int32, which is necessary for
	// making stat() succeed in 32-bit programs.
	PortableInodes bool
}

type MountOptions struct {
	AllowOther bool

	// Options are passed as -o string to fusermount.
	Options []string

	// Default is _DEFAULT_BACKGROUND_TASKS, 12.  This numbers
	// controls the allowed number of requests that relate to
	// async I/O.  Concurrency for synchronous I/O is not limited.
	MaxBackground int
}

// DefaultFileSystem implements a FileSystem that returns ENOSYS for every operation.
type DefaultFileSystem struct{}

// DefaultFile returns ENOSYS for every operation.
type DefaultFile struct{}

// RawFileSystem is an interface close to the FUSE wire protocol.
//
// Unless you really know what you are doing, you should not implement
// this, but rather the FileSystem interface; the details of getting
// interactions with open files, renames, and threading right etc. are
// somewhat tricky and not very interesting.
//
// Include DefaultRawFileSystem to inherit a null implementation.
type RawFileSystem interface {
	Lookup(header *InHeader, name string) (out *EntryOut, status Status)
	Forget(header *InHeader, input *ForgetIn)

	// Attributes.
	GetAttr(header *InHeader, input *GetAttrIn) (out *AttrOut, code Status)
	SetAttr(header *InHeader, input *SetAttrIn) (out *AttrOut, code Status)

	// Modifying structure.
	Mknod(header *InHeader, input *MknodIn, name string) (out *EntryOut, code Status)
	Mkdir(header *InHeader, input *MkdirIn, name string) (out *EntryOut, code Status)
	Unlink(header *InHeader, name string) (code Status)
	Rmdir(header *InHeader, name string) (code Status)
	Rename(header *InHeader, input *RenameIn, oldName string, newName string) (code Status)
	Link(header *InHeader, input *LinkIn, filename string) (out *EntryOut, code Status)

	Symlink(header *InHeader, pointedTo string, linkName string) (out *EntryOut, code Status)
	Readlink(header *InHeader) (out []byte, code Status)
	Access(header *InHeader, input *AccessIn) (code Status)

	// Extended attributes.
	GetXAttr(header *InHeader, attr string) (data []byte, code Status)
	ListXAttr(header *InHeader) (attributes []byte, code Status)
	SetXAttr(header *InHeader, input *SetXAttrIn, attr string, data []byte) Status
	RemoveXAttr(header *InHeader, attr string) (code Status)

	// File handling.
	Create(header *InHeader, input *CreateIn, name string) (flags uint32, handle uint64, out *EntryOut, code Status)
	Open(header *InHeader, input *OpenIn) (flags uint32, handle uint64, status Status)
	Read(*InHeader, *ReadIn, BufferPool) ([]byte, Status)

	Release(header *InHeader, input *ReleaseIn)
	Write(*InHeader, *WriteIn, []byte) (written uint32, code Status)
	Flush(header *InHeader, input *FlushIn) Status
	Fsync(*InHeader, *FsyncIn) (code Status)

	// Directory handling
	OpenDir(header *InHeader, input *OpenIn) (flags uint32, handle uint64, status Status)
	ReadDir(header *InHeader, input *ReadIn) (*DirEntryList, Status)
	ReleaseDir(header *InHeader, input *ReleaseIn)
	FsyncDir(header *InHeader, input *FsyncIn) (code Status)

	//
	Ioctl(header *InHeader, input *IoctlIn) (output *IoctlOut, data []byte, code Status)
	StatFs(header *InHeader) *StatfsOut

	// Provide callbacks for pushing notifications to the kernel.
	Init(params *RawFsInit)
}

// DefaultRawFileSystem returns ENOSYS for every operation.
type DefaultRawFileSystem struct{}

// Talk back to FUSE.
//
// InodeNotify invalidates the information associated with the inode
// (ie. data cache, attributes, etc.)
//
// EntryNotify should be used if the existence status of an entry changes,
// (ie. to notify of creation or deletion of the file).
//
// Somewhat confusingly, InodeNotify for a file that stopped to exist
// will give the correct result for Lstat (ENOENT), but the kernel
// will still issue file Open() on the inode.
type RawFsInit struct {
	InodeNotify func(*NotifyInvalInodeOut) Status
	EntryNotify func(parent uint64, name string) Status
}
<|endoftext|>


<commit_before>package v8js

import (
	"fmt"
	"github.com/GeertJohan/go.rice"
	log "github.com/Sirupsen/logrus"
	"github.com/loadimpact/speedboat/runner"
	"github.com/ry/v8worker"
	"github.com/valyala/fasthttp"
	"golang.org/x/net/context"
	"math"
	"os"
	"time"
)

type libFile struct {
	Filename string
	Source   string
}

type Runner struct {
	Filename string
	Source   string
	Client   *fasthttp.Client

	stdlib []libFile
}

type VUContext struct {
	r   *Runner
	ctx context.Context
	ch  chan runner.Result
}

func New(filename, src string) *Runner {
	r := &Runner{
		Filename: filename,
		Source:   src,
		Client: &fasthttp.Client{
			Dial:                fasthttp.Dial,
			MaxIdleConnDuration: time.Duration(0),
			MaxConnsPerHost:     math.MaxInt64,
		},
	}

	// Load the standard library as a rice box; panic if any part of this fails
	// (The only possible cause is a programming/developer error, not user error)
	box := rice.MustFindBox("lib")
	box.Walk("/", func(path string, info os.FileInfo, err error) error {
		if err != nil {
			panic(err)
		}
		if !info.IsDir() {
			r.stdlib = append(r.stdlib, libFile{
				Filename: path,
				Source:   box.MustString(path),
			})
		}
		return nil
	})

	return r
}

func (r *Runner) Run(ctx context.Context, id int64) <-chan runner.Result {
	ch := make(chan runner.Result)

	go func() {
		defer close(ch)

		vu := VUContext{r: r, ctx: ctx, ch: ch}

		w := v8worker.New(vu.Recv, vu.RecvSync)

		for _, f := range r.stdlib {
			if err := w.Load(r.Filename, r.Source); err != nil {
				log.WithError(err).WithField("file", f.Filename).Error("Couldn't load lib")
			}
		}

		src := fmt.Sprintf("function __run__() {%s}; undefined", r.Source)
		if err := w.Load(r.Filename, src); err != nil {
			log.WithError(err).Error("Couldn't load JS")
			return
		}

		for {
			log.Info("-> run")
			w.Send("run")
			log.Info("<- run")

			select {
			case <-ctx.Done():
				return
			default:
			}
		}
	}()

	return ch
}

func (vu *VUContext) Recv(raw string) {
}

func (vu *VUContext) RecvSync(raw string) string {
	return ""
}
<commit_msg>I'm an idiot<commit_after>package v8js

import (
	"fmt"
	"github.com/GeertJohan/go.rice"
	log "github.com/Sirupsen/logrus"
	"github.com/loadimpact/speedboat/runner"
	"github.com/ry/v8worker"
	"github.com/valyala/fasthttp"
	"golang.org/x/net/context"
	"math"
	"os"
	"time"
)

type libFile struct {
	Filename string
	Source   string
}

type Runner struct {
	Filename string
	Source   string
	Client   *fasthttp.Client

	stdlib []libFile
}

type VUContext struct {
	r   *Runner
	ctx context.Context
	ch  chan runner.Result
}

func New(filename, src string) *Runner {
	r := &Runner{
		Filename: filename,
		Source:   src,
		Client: &fasthttp.Client{
			Dial:                fasthttp.Dial,
			MaxIdleConnDuration: time.Duration(0),
			MaxConnsPerHost:     math.MaxInt64,
		},
	}

	// Load the standard library as a rice box; panic if any part of this fails
	// (The only possible cause is a programming/developer error, not user error)
	box := rice.MustFindBox("lib")
	box.Walk("/", func(path string, info os.FileInfo, err error) error {
		if err != nil {
			panic(err)
		}
		if !info.IsDir() {
			r.stdlib = append(r.stdlib, libFile{
				Filename: path,
				Source:   box.MustString(path),
			})
		}
		return nil
	})

	return r
}

func (r *Runner) Run(ctx context.Context, id int64) <-chan runner.Result {
	ch := make(chan runner.Result)

	go func() {
		defer close(ch)

		vu := VUContext{r: r, ctx: ctx, ch: ch}

		w := v8worker.New(vu.Recv, vu.RecvSync)

		for _, f := range r.stdlib {
			if err := w.Load(f.Filename, f.Source); err != nil {
				log.WithError(err).WithField("file", f.Filename).Error("Couldn't load lib")
			}
		}

		src := fmt.Sprintf("function __run__() {%s}; undefined", r.Source)
		if err := w.Load(r.Filename, src); err != nil {
			log.WithError(err).Error("Couldn't load JS")
			return
		}

		for {
			log.Info("-> run")
			w.Send("run")
			log.Info("<- run")

			select {
			case <-ctx.Done():
				return
			default:
			}
		}
	}()

	return ch
}

func (vu *VUContext) Recv(raw string) {
}

func (vu *VUContext) RecvSync(raw string) string {
	return ""
}
<|endoftext|>


<commit_before>// Copyright (c) Liam Stanley <me@liamstanley.io>. All rights reserved. Use
// of this source code is governed by the MIT license that can be found in
// the LICENSE file.

package girc

import (
	"bytes"
	"strings"
)

type ircFmtCode struct {
	aliases []string
	val     string
}

var codes = []*ircFmtCode{
	{aliases: []string{"white"}, val: "\x0300"},
	{aliases: []string{"black"}, val: "\x0301"},
	{aliases: []string{"blue", "navy"}, val: "\x0302"},
	{aliases: []string{"green"}, val: "\x0303"},
	{aliases: []string{"red"}, val: "\x0304"},
	{aliases: []string{"brown", "maroon"}, val: "\x0305"},
	{aliases: []string{"purple"}, val: "\x0306"},
	{aliases: []string{"orange", "olive", "gold"}, val: "\x0307"},
	{aliases: []string{"yellow"}, val: "\x0308"},
	{aliases: []string{"lightgreen", "lime"}, val: "\x0309"},
	{aliases: []string{"teal"}, val: "\x0310"},
	{aliases: []string{"cyan"}, val: "\x0311"},
	{aliases: []string{"lightblue", "royal"}, val: "\x0312"},
	{aliases: []string{"lightpurple", "pink", "fuchsia"}, val: "\x0313"},
	{aliases: []string{"grey", "gray"}, val: "\x0314"},
	{aliases: []string{"lightgrey", "silver"}, val: "\x0315"},
	{aliases: []string{"bold", "b"}, val: "\x02"},
	{aliases: []string{"italic", "i"}, val: "\x1d"},
	{aliases: []string{"reset", "r"}, val: "\x0f"},
	{aliases: []string{"clear", "c"}, val: "\x03"}, // Clears formatting.
	{aliases: []string{"reverse"}, val: "\x16"},
	{aliases: []string{"underline", "ul"}, val: "\x1f"},
	{aliases: []string{"ctcp"}, val: "\x01"}, // CTCP/ACTION delimiter.
}

// Format takes format strings like "{red}" and turns them into the resulting
// ASCII format/color codes for IRC.
//
// For example:
//
//   client.Message("#channel", Format("{red}{bold}Hello World{c}"))
func Format(text string) string {
	for i := 0; i < len(codes); i++ {
		for a := 0; a < len(codes[i].aliases); a++ {
			text = strings.Replace(text, "{"+codes[i].aliases[a]+"}", codes[i].val, -1)
		}

		// makes parsing small strings slightly slower, but helps longer
		// strings.
		var more bool
		for c := 0; c < len(text); c++ {
			if text[c] == 0x7B {
				more = true
				break
			}
		}
		if !more {
			return text
		}
	}

	return text
}

// StripFormat strips all "{fmt}" formatting strings from the input text.
// See Format() for more information.
func StripFormat(text string) string {
	for i := 0; i < len(codes); i++ {
		for a := 0; a < len(codes[i].aliases); a++ {
			text = strings.Replace(text, "{"+codes[i].aliases[a]+"}", "", -1)
		}

		// makes parsing small strings slightly slower, but helps longer
		// strings.
		var more bool
		for c := 0; c < len(text); c++ {
			if text[c] == 0x7B {
				more = true
				break
			}
		}
		if !more {
			return text
		}
	}

	return text
}

// StripRaw tries to strip all ASCII format codes that are used for IRC.
func StripRaw(text string) string {
	for i := 0; i < len(codes); i++ {
		text = strings.Replace(text, codes[i].val, "", -1)
	}

	return text
}

// IsValidChannel validates if channel is an RFC complaint channel or not.
//
// NOTE: If you do not need to validate against servers that support Unicode,
// you may want to ensure that all channel chars are within the range of
// all ASCII printable chars. This function will NOT do that for
// compatibility reasons.
//
//   channel    =  ( "#" / "+" / ( "!" channelid ) / "&" ) chanstring
//                 [ ":" chanstring ]
//   chanstring =  0x01-0x07 / 0x08-0x09 / 0x0B-0x0C / 0x0E-0x1F / 0x21-0x2B
//   chanstring =  / 0x2D-0x39 / 0x3B-0xFF
//                   ; any octet except NUL, BELL, CR, LF, " ", "," and ":"
//   channelid  = 5( 0x41-0x5A / digit )   ; 5( A-Z / 0-9 )
func IsValidChannel(channel string) bool {
	if len(channel) <= 1 || len(channel) > 50 {
		return false
	}

	// #, +, !<channelid>, or &
	// Including "*" in the prefix list, as this is commonly used (e.g. ZNC)
	if bytes.IndexByte([]byte{0x21, 0x23, 0x26, 0x2A, 0x2B}, channel[0]) == -1 {
		return false
	}

	// !<channelid> -- not very commonly supported, but we'll check it anyway.
	// The ID must be 5 chars. This means min-channel size should be:
	//   1 (prefix) + 5 (id) + 1 (+, channel name)
	if channel[0] == 0x21 {
		if len(channel) < 7 {
			return false
		}

		// check for valid ID
		for i := 1; i < 6; i++ {
			if (channel[i] < 0x30 || channel[i] > 0x39) && (channel[i] < 0x41 || channel[i] > 0x5A) {
				return false
			}
		}
	}

	// Check for invalid octets here.
	bad := []byte{0x00, 0x07, 0x0D, 0x0A, 0x20, 0x2C, 0x3A}
	for i := 1; i < len(channel); i++ {
		if bytes.IndexByte(bad, channel[i]) != -1 {
			return false
		}
	}

	return true
}

// IsValidNick validates an IRC nickame. Note that this does not validate
// IRC nickname length.
//
//   nickname =  ( letter / special ) *8( letter / digit / special / "-" )
//   letter   =  0x41-0x5A / 0x61-0x7A
//   digit    =  0x30-0x39
//   special  =  0x5B-0x60 / 0x7B-0x7D
func IsValidNick(nick string) bool {
	if len(nick) <= 0 {
		return false
	}

	// Check the first index. Some characters aren't allowed for the first
	// index of an IRC nickname.
	if nick[0] < 0x41 || nick[0] > 0x7D {
		// a-z, A-Z, and _\[]{}^|
		return false
	}

	for i := 1; i < len(nick); i++ {
		if (nick[i] < 0x41 || nick[i] > 0x7D) && (nick[i] < 0x30 || nick[i] > 0x39) && nick[i] != 0x2D {
			// a-z, A-Z, 0-9, -, and _\[]{}^|
			return false
		}
	}

	return true
}

// IsValidUser validates an IRC ident/username. Note that this does not
// validate IRC ident length.
//
// The validation checks are much like what characters are allowed with an
// IRC nickname (see IsValidNick()), however an ident/username can:
//
// 1. Must either start with alphanumberic char, or "~" then alphanumberic
// char.
//
// 2. Contain a "." (period), for use with "first.last". Though, this may
// not be supported on all networks. Some limit this to only a single period.
//
// Per RFC:
//   user =  1*( %x01-09 / %x0B-0C / %x0E-1F / %x21-3F / %x41-FF )
//           ; any octet except NUL, CR, LF, " " and "@"
func IsValidUser(name string) bool {
	if len(name) <= 0 {
		return false
	}

	// "~" is prepended (commonly) if there was no ident server response.
	if name[0] == 0x7E {
		// Means name only contained "~".
		if len(name) < 2 {
			return false
		}

		name = name[1:]
	}

	// Check to see if the first index is alphanumeric.
	if (name[0] < 0x41 || name[0] > 0x4A) && (name[0] < 0x61 || name[0] > 0x7A) && (name[0] < 0x30 || name[0] > 0x39) {
		return false
	}

	for i := 1; i < len(name); i++ {
		if (name[i] < 0x41 || name[i] > 0x7D) && (name[i] < 0x30 || name[i] > 0x39) && name[i] != 0x2D && name[i] != 0x2E {
			// a-z, A-Z, 0-9, -, and _\[]{}^|
			return false
		}
	}

	return true
}
<commit_msg>add additional noting in regards to channel id lengths and ISUPPORT<commit_after>// Copyright (c) Liam Stanley <me@liamstanley.io>. All rights reserved. Use
// of this source code is governed by the MIT license that can be found in
// the LICENSE file.

package girc

import (
	"bytes"
	"strings"
)

type ircFmtCode struct {
	aliases []string
	val     string
}

var codes = []*ircFmtCode{
	{aliases: []string{"white"}, val: "\x0300"},
	{aliases: []string{"black"}, val: "\x0301"},
	{aliases: []string{"blue", "navy"}, val: "\x0302"},
	{aliases: []string{"green"}, val: "\x0303"},
	{aliases: []string{"red"}, val: "\x0304"},
	{aliases: []string{"brown", "maroon"}, val: "\x0305"},
	{aliases: []string{"purple"}, val: "\x0306"},
	{aliases: []string{"orange", "olive", "gold"}, val: "\x0307"},
	{aliases: []string{"yellow"}, val: "\x0308"},
	{aliases: []string{"lightgreen", "lime"}, val: "\x0309"},
	{aliases: []string{"teal"}, val: "\x0310"},
	{aliases: []string{"cyan"}, val: "\x0311"},
	{aliases: []string{"lightblue", "royal"}, val: "\x0312"},
	{aliases: []string{"lightpurple", "pink", "fuchsia"}, val: "\x0313"},
	{aliases: []string{"grey", "gray"}, val: "\x0314"},
	{aliases: []string{"lightgrey", "silver"}, val: "\x0315"},
	{aliases: []string{"bold", "b"}, val: "\x02"},
	{aliases: []string{"italic", "i"}, val: "\x1d"},
	{aliases: []string{"reset", "r"}, val: "\x0f"},
	{aliases: []string{"clear", "c"}, val: "\x03"}, // Clears formatting.
	{aliases: []string{"reverse"}, val: "\x16"},
	{aliases: []string{"underline", "ul"}, val: "\x1f"},
	{aliases: []string{"ctcp"}, val: "\x01"}, // CTCP/ACTION delimiter.
}

// Format takes format strings like "{red}" and turns them into the resulting
// ASCII format/color codes for IRC.
//
// For example:
//
//   client.Message("#channel", Format("{red}{bold}Hello World{c}"))
func Format(text string) string {
	for i := 0; i < len(codes); i++ {
		for a := 0; a < len(codes[i].aliases); a++ {
			text = strings.Replace(text, "{"+codes[i].aliases[a]+"}", codes[i].val, -1)
		}

		// makes parsing small strings slightly slower, but helps longer
		// strings.
		var more bool
		for c := 0; c < len(text); c++ {
			if text[c] == 0x7B {
				more = true
				break
			}
		}
		if !more {
			return text
		}
	}

	return text
}

// StripFormat strips all "{fmt}" formatting strings from the input text.
// See Format() for more information.
func StripFormat(text string) string {
	for i := 0; i < len(codes); i++ {
		for a := 0; a < len(codes[i].aliases); a++ {
			text = strings.Replace(text, "{"+codes[i].aliases[a]+"}", "", -1)
		}

		// makes parsing small strings slightly slower, but helps longer
		// strings.
		var more bool
		for c := 0; c < len(text); c++ {
			if text[c] == 0x7B {
				more = true
				break
			}
		}
		if !more {
			return text
		}
	}

	return text
}

// StripRaw tries to strip all ASCII format codes that are used for IRC.
func StripRaw(text string) string {
	for i := 0; i < len(codes); i++ {
		text = strings.Replace(text, codes[i].val, "", -1)
	}

	return text
}

// IsValidChannel validates if channel is an RFC complaint channel or not.
//
// NOTE: If you are using this to validate a channel that contains a channel
// ID, (!<channelid>NAME), this only supports the standard 5 character length.
//
// NOTE: If you do not need to validate against servers that support unicode,
// you may want to ensure that all channel chars are within the range of
// all ASCII printable chars. This function will NOT do that for
// compatibility reasons.
//
//   channel    =  ( "#" / "+" / ( "!" channelid ) / "&" ) chanstring
//                 [ ":" chanstring ]
//   chanstring =  0x01-0x07 / 0x08-0x09 / 0x0B-0x0C / 0x0E-0x1F / 0x21-0x2B
//   chanstring =  / 0x2D-0x39 / 0x3B-0xFF
//                   ; any octet except NUL, BELL, CR, LF, " ", "," and ":"
//   channelid  = 5( 0x41-0x5A / digit )   ; 5( A-Z / 0-9 )
func IsValidChannel(channel string) bool {
	if len(channel) <= 1 || len(channel) > 50 {
		return false
	}

	// #, +, !<channelid>, or &
	// Including "*" in the prefix list, as this is commonly used (e.g. ZNC)
	if bytes.IndexByte([]byte{0x21, 0x23, 0x26, 0x2A, 0x2B}, channel[0]) == -1 {
		return false
	}

	// !<channelid> -- not very commonly supported, but we'll check it anyway.
	// The ID must be 5 chars. This means min-channel size should be:
	//   1 (prefix) + 5 (id) + 1 (+, channel name)
	// On some networks, this may be extended with ISUPPORT capabilities,
	// however this is extremely uncommon.
	if channel[0] == 0x21 {
		if len(channel) < 7 {
			return false
		}

		// check for valid ID
		for i := 1; i < 6; i++ {
			if (channel[i] < 0x30 || channel[i] > 0x39) && (channel[i] < 0x41 || channel[i] > 0x5A) {
				return false
			}
		}
	}

	// Check for invalid octets here.
	bad := []byte{0x00, 0x07, 0x0D, 0x0A, 0x20, 0x2C, 0x3A}
	for i := 1; i < len(channel); i++ {
		if bytes.IndexByte(bad, channel[i]) != -1 {
			return false
		}
	}

	return true
}

// IsValidNick validates an IRC nickame. Note that this does not validate
// IRC nickname length.
//
//   nickname =  ( letter / special ) *8( letter / digit / special / "-" )
//   letter   =  0x41-0x5A / 0x61-0x7A
//   digit    =  0x30-0x39
//   special  =  0x5B-0x60 / 0x7B-0x7D
func IsValidNick(nick string) bool {
	if len(nick) <= 0 {
		return false
	}

	// Check the first index. Some characters aren't allowed for the first
	// index of an IRC nickname.
	if nick[0] < 0x41 || nick[0] > 0x7D {
		// a-z, A-Z, and _\[]{}^|
		return false
	}

	for i := 1; i < len(nick); i++ {
		if (nick[i] < 0x41 || nick[i] > 0x7D) && (nick[i] < 0x30 || nick[i] > 0x39) && nick[i] != 0x2D {
			// a-z, A-Z, 0-9, -, and _\[]{}^|
			return false
		}
	}

	return true
}

// IsValidUser validates an IRC ident/username. Note that this does not
// validate IRC ident length.
//
// The validation checks are much like what characters are allowed with an
// IRC nickname (see IsValidNick()), however an ident/username can:
//
// 1. Must either start with alphanumberic char, or "~" then alphanumberic
// char.
//
// 2. Contain a "." (period), for use with "first.last". Though, this may
// not be supported on all networks. Some limit this to only a single period.
//
// Per RFC:
//   user =  1*( %x01-09 / %x0B-0C / %x0E-1F / %x21-3F / %x41-FF )
//           ; any octet except NUL, CR, LF, " " and "@"
func IsValidUser(name string) bool {
	if len(name) <= 0 {
		return false
	}

	// "~" is prepended (commonly) if there was no ident server response.
	if name[0] == 0x7E {
		// Means name only contained "~".
		if len(name) < 2 {
			return false
		}

		name = name[1:]
	}

	// Check to see if the first index is alphanumeric.
	if (name[0] < 0x41 || name[0] > 0x4A) && (name[0] < 0x61 || name[0] > 0x7A) && (name[0] < 0x30 || name[0] > 0x39) {
		return false
	}

	for i := 1; i < len(name); i++ {
		if (name[i] < 0x41 || name[i] > 0x7D) && (name[i] < 0x30 || name[i] > 0x39) && name[i] != 0x2D && name[i] != 0x2E {
			// a-z, A-Z, 0-9, -, and _\[]{}^|
			return false
		}
	}

	return true
}
<|endoftext|>


<commit_before>package luddite

import (
	"net/http"
	"strconv"
)

const (
	HeaderAccept            = "Accept"
	HeaderContentType       = "Content-Type"
	HeaderForwardedFor      = "X-Forwarded-For"
	HeaderLocation          = "Location"
	HeaderRequestId         = "X-Request-Id"
	HeaderSpirentApiVersion = "X-Spirent-Api-Version"
)

func RequestApiVersion(r *http.Request, defaultVersion int) (version int) {
	version = defaultVersion
	if versionStr := r.Header.Get(HeaderSpirentApiVersion); versionStr != "" {
		if i, err := strconv.Atoi(versionStr); err == nil && i > 0 {
			version = i
		}
	}
	return
}
<commit_msg>Added helper func.<commit_after>package luddite

import (
	"net/http"
	"strconv"
)

const (
	HeaderAccept            = "Accept"
	HeaderAuthorization     = "Authorization"
	HeaderContentType       = "Content-Type"
	HeaderForwardedFor      = "X-Forwarded-For"
	HeaderLocation          = "Location"
	HeaderRequestId         = "X-Request-Id"
	HeaderSpirentApiVersion = "X-Spirent-Api-Version"
)

func RequestBearerToken(r *http.Request) (token string) {
	if authStr := r.Header.Get(HeaderAuthorization); authStr != "" && authStr[:7] == "Bearer " {
		token = authStr[7:]
	}
	return
}

func RequestApiVersion(r *http.Request, defaultVersion int) (version int) {
	version = defaultVersion
	if versionStr := r.Header.Get(HeaderSpirentApiVersion); versionStr != "" {
		if i, err := strconv.Atoi(versionStr); err == nil && i > 0 {
			version = i
		}
	}
	return
}
<|endoftext|>


<commit_before>package response

import (
	"encoding/json"
	"html/template"
	"net/http"
	"os"
	"path/filepath"

	"github.com/volatile/core"
	"github.com/volatile/core/httputil"
)

const viewsDir = "views"

var views *template.Template

func init() {
	if _, err := os.Stat(viewsDir); err != nil {
		return
	}

	views = template.New("views")

	// Built-in views funcs
	views.Funcs(template.FuncMap{
		"html":  viewsFuncHTML,
		"nl2br": viewsFuncNL2BR,
	})

	core.BeforeRun(func() {
		if err := filepath.Walk(viewsDir, viewsWalk); err != nil {
			panic("response: " + err.Error())
		}
	})
}

// walk is the path/filepath.WalkFunc used to walk viewsDir in order to initialize views.
// It will try to parse all files it encounters and recurse into subdirectories.
func viewsWalk(path string, f os.FileInfo, err error) error {
	if err != nil {
		return err
	}

	if f.IsDir() {
		return nil
	}

	_, err = views.ParseFiles(path)

	return err
}

// FuncMap is the type of the map defining the mapping from names to functions.
// Each function must have either a single return value, or two return values of which the second has type error.
// In that case, if the second (error) argument evaluates to non-nil during execution, execution terminates and Execute returns that error.
// FuncMap has the same base type as FuncMap in "text/template", copied here so clients need not import "text/template".
type FuncMap map[string]interface{}

// ViewsFuncs adds a function that will be available to all templates.
func ViewsFuncs(funcMap FuncMap) {
	if views == nil {
		panic(`response: views can't be used without a "views" directory`)
	}
	views.Funcs(template.FuncMap(funcMap))
}

// Status responds with the given status code.
func Status(c *core.Context, v int) {
	http.Error(c.ResponseWriter, http.StatusText(v), v)
}

// String responds with the given string.
func String(c *core.Context, s string) {
	StringStatus(c, http.StatusOK, s)
}

// StringStatus responds with the given string and status code.
func StringStatus(c *core.Context, code int, s string) {
	httputil.SetDetectedContentType(c.ResponseWriter, []byte(s))
	c.ResponseWriter.WriteHeader(code)
	c.ResponseWriter.Write([]byte(s))
}

// Bytes responds with the given slice of byte.
func Bytes(c *core.Context, b []byte) {
	BytesStatus(c, http.StatusOK, b)
}

// BytesStatus responds with the given slice of byte and status code.
func BytesStatus(c *core.Context, code int, b []byte) {
	httputil.SetDetectedContentType(c.ResponseWriter, b)
	c.ResponseWriter.WriteHeader(code)
	c.ResponseWriter.Write(b)
}

// JSON set the correct header and responds with the marshalled content.
func JSON(c *core.Context, v interface{}) {
	JSONStatus(c, http.StatusOK, v)
}

// JSONStatus set the correct header and responds with the marshalled content and status code.
func JSONStatus(c *core.Context, code int, v interface{}) {
	b, err := json.Marshal(v)
	if err != nil {
		panic(err)
	}

	c.ResponseWriter.Header().Set("Content-Type", "application/json")
	c.ResponseWriter.WriteHeader(code)
	c.ResponseWriter.Write(b)
}

// View pass the data to the template associated to name, and responds with it.
func View(c *core.Context, name string, data map[string]interface{}) {
	if views == nil {
		panic(`response: views can't be used without a "views" directory`)
	}

	if data == nil {
		data = make(map[string]interface{})
	}
	data["c"] = c

	c.ResponseWriter.Header().Set("Content-Type", "text/html; charset=utf-8")
	if err := views.ExecuteTemplate(c.ResponseWriter, name, data); err != nil {
		panic(err)
	}
}
<commit_msg>Add ViewStatus<commit_after>package response

import (
	"encoding/json"
	"html/template"
	"net/http"
	"os"
	"path/filepath"

	"github.com/volatile/core"
	"github.com/volatile/core/httputil"
)

const viewsDir = "views"

var views *template.Template

func init() {
	if _, err := os.Stat(viewsDir); err != nil {
		return
	}

	views = template.New("views")

	// Built-in views funcs
	views.Funcs(template.FuncMap{
		"html":  viewsFuncHTML,
		"nl2br": viewsFuncNL2BR,
	})

	core.BeforeRun(func() {
		if err := filepath.Walk(viewsDir, viewsWalk); err != nil {
			panic("response: " + err.Error())
		}
	})
}

// walk is the path/filepath.WalkFunc used to walk viewsDir in order to initialize views.
// It will try to parse all files it encounters and recurse into subdirectories.
func viewsWalk(path string, f os.FileInfo, err error) error {
	if err != nil {
		return err
	}

	if f.IsDir() {
		return nil
	}

	_, err = views.ParseFiles(path)
	return err
}

// FuncMap is the type of the map defining the mapping from names to functions.
// Each function must have either a single return value, or two return values of which the second has type error.
// In that case, if the second (error) argument evaluates to non-nil during execution, execution terminates and Execute returns that error.
// FuncMap has the same base type as FuncMap in "text/template", copied here so clients need not import "text/template".
type FuncMap map[string]interface{}

// ViewsFuncs adds a function that will be available to all templates.
func ViewsFuncs(funcMap FuncMap) {
	if views == nil {
		panic(`response: views can't be used without a "views" directory`)
	}
	views.Funcs(template.FuncMap(funcMap))
}

// Status responds with the status code.
func Status(c *core.Context, code int) {
	http.Error(c.ResponseWriter, http.StatusText(code), code)
}

// String responds with the string s.
func String(c *core.Context, s string) {
	StringStatus(c, http.StatusOK, s)
}

// StringStatus responds with the status code and the string s.
func StringStatus(c *core.Context, code int, s string) {
	httputil.SetDetectedContentType(c.ResponseWriter, []byte(s))
	c.ResponseWriter.WriteHeader(code)
	c.ResponseWriter.Write([]byte(s))
}

// Bytes responds with the slice of bytes b.
func Bytes(c *core.Context, b []byte) {
	BytesStatus(c, http.StatusOK, b)
}

// BytesStatus responds with the status code and the slice of bytes b.
func BytesStatus(c *core.Context, code int, b []byte) {
	httputil.SetDetectedContentType(c.ResponseWriter, b)
	c.ResponseWriter.WriteHeader(code)
	c.ResponseWriter.Write(b)
}

// JSON responds with the JSON marshalled v.
func JSON(c *core.Context, v interface{}) {
	JSONStatus(c, http.StatusOK, v)
}

// JSONStatus responds with the status code and the JSON marshalled v.
func JSONStatus(c *core.Context, code int, v interface{}) {
	b, err := json.Marshal(v)
	if err != nil {
		panic(err)
	}

	c.ResponseWriter.Header().Set("Content-Type", "application/json")
	c.ResponseWriter.WriteHeader(code)
	c.ResponseWriter.Write(b)
}

// View responds with the view associated to name.
func View(c *core.Context, name string, data map[string]interface{}) {
	ViewStatus(c, http.StatusOK, name, data)
}

// ViewStatus responds with the status code and the view associated to name.
func ViewStatus(c *core.Context, code int, name string, data map[string]interface{}) {
	if views == nil {
		panic(`response: views can't be used without a "views" directory`)
	}

	if data == nil {
		data = make(map[string]interface{})
	}
	data["c"] = c

	c.ResponseWriter.Header().Set("Content-Type", "text/html; charsets=utf-8")
	c.ResponseWriter.WriteHeader(code)
	if err := views.ExecuteTemplate(c.ResponseWriter, name, data); err != nil {
		panic(err)
	}
}
<|endoftext|>