<commit_before>package s3api

import (
	"crypto/md5"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/chrislusf/seaweedfs/weed/s3api/s3err"

	"github.com/gorilla/mux"

	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	weed_server "github.com/chrislusf/seaweedfs/weed/server"
	"github.com/chrislusf/seaweedfs/weed/util"
)

var (
	client *http.Client
)

func init() {
	client = &http.Client{Transport: &http.Transport{
		MaxIdleConnsPerHost: 1024,
	}}
}

func (s3a *S3ApiServer) PutObjectHandler(w http.ResponseWriter, r *http.Request) {

	// http://docs.aws.amazon.com/AmazonS3/latest/dev/UploadingObjects.html

	bucket, object := getBucketAndObject(r)

	_, err := validateContentMd5(r.Header)
	if err != nil {
		writeErrorResponse(w, s3err.ErrInvalidDigest, r.URL)
		return
	}

	dataReader := r.Body
	if s3a.iam.isEnabled() {
		rAuthType := getRequestAuthType(r)
		var s3ErrCode s3err.ErrorCode
		switch rAuthType {
		case authTypeStreamingSigned:
			dataReader, s3ErrCode = s3a.iam.newSignV4ChunkedReader(r)
		case authTypeSignedV2, authTypePresignedV2:
			_, s3ErrCode = s3a.iam.isReqAuthenticatedV2(r)
		case authTypePresigned, authTypeSigned:
			_, s3ErrCode = s3a.iam.reqSignatureV4Verify(r)
		}
		if s3ErrCode != s3err.ErrNone {
			writeErrorResponse(w, s3ErrCode, r.URL)
			return
		}
	}
	defer dataReader.Close()

	if strings.HasSuffix(object, "/") {
		if err := s3a.mkdir(s3a.option.BucketsPath, bucket+object, nil); err != nil {
			writeErrorResponse(w, s3err.ErrInternalError, r.URL)
			return
		}
	} else {
		uploadUrl := fmt.Sprintf("http://%s%s/%s%s", s3a.option.Filer, s3a.option.BucketsPath, bucket, object)

		etag, errCode := s3a.putToFiler(r, uploadUrl, dataReader)

		if errCode != s3err.ErrNone {
			writeErrorResponse(w, errCode, r.URL)
			return
		}

		setEtag(w, etag)
	}

	writeSuccessResponseEmpty(w)
}

func (s3a *S3ApiServer) GetObjectHandler(w http.ResponseWriter, r *http.Request) {

	bucket, object := getBucketAndObject(r)

	if strings.HasSuffix(r.URL.Path, "/") {
		writeErrorResponse(w, s3err.ErrNotImplemented, r.URL)
		return
	}

	destUrl := fmt.Sprintf("http://%s%s/%s%s",
		s3a.option.Filer, s3a.option.BucketsPath, bucket, object)

	s3a.proxyToFiler(w, r, destUrl, passThroughResponse)

}

func (s3a *S3ApiServer) HeadObjectHandler(w http.ResponseWriter, r *http.Request) {

	bucket, object := getBucketAndObject(r)

	destUrl := fmt.Sprintf("http://%s%s/%s%s",
		s3a.option.Filer, s3a.option.BucketsPath, bucket, object)

	s3a.proxyToFiler(w, r, destUrl, passThroughResponse)

}

func (s3a *S3ApiServer) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) {

	bucket, object := getBucketAndObject(r)

	response, _ := s3a.listFilerEntries(bucket, object, 1, "", "/")
	if len(response.Contents) != 0 && strings.HasSuffix(object, "/") {
		w.WriteHeader(http.StatusNoContent)
		return
	}

	destUrl := fmt.Sprintf("http://%s%s/%s%s?recursive=true",
		s3a.option.Filer, s3a.option.BucketsPath, bucket, object)

	s3a.proxyToFiler(w, r, destUrl, func(proxyResponse *http.Response, w http.ResponseWriter) {
		for k, v := range proxyResponse.Header {
			w.Header()[k] = v
		}
		w.WriteHeader(http.StatusNoContent)
	})
}

// / ObjectIdentifier carries key name for the object to delete.
type ObjectIdentifier struct {
	ObjectName string `xml:"Key"`
}

// DeleteObjectsRequest - xml carrying the object key names which needs to be deleted.
type DeleteObjectsRequest struct {
	// Element to enable quiet mode for the request
	Quiet bool
	// List of objects to be deleted
	Objects []ObjectIdentifier `xml:"Object"`
}

// DeleteError structure.
type DeleteError struct {
	Code    string
	Message string
	Key     string
}

// DeleteObjectsResponse container for multiple object deletes.
type DeleteObjectsResponse struct {
	XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ DeleteResult" json:"-"`

	// Collection of all deleted objects
	DeletedObjects []ObjectIdentifier `xml:"Deleted,omitempty"`

	// Collection of errors deleting certain objects.
	Errors []DeleteError `xml:"Error,omitempty"`
}

// DeleteMultipleObjectsHandler - Delete multiple objects
func (s3a *S3ApiServer) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) {

	bucket, _ := getBucketAndObject(r)

	deleteXMLBytes, err := ioutil.ReadAll(r.Body)
	if err != nil {
		writeErrorResponse(w, s3err.ErrInternalError, r.URL)
		return
	}

	deleteObjects := &DeleteObjectsRequest{}
	if err := xml.Unmarshal(deleteXMLBytes, deleteObjects); err != nil {
		writeErrorResponse(w, s3err.ErrMalformedXML, r.URL)
		return
	}

	var deletedObjects []ObjectIdentifier
	var deleteErrors []DeleteError

	s3a.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		for _, object := range deleteObjects.Objects {
			response, _ := s3a.listFilerEntries(bucket, object.ObjectName, 1, "", "/")
			if len(response.Contents) != 0 && strings.HasSuffix(object.ObjectName, "/") {
				continue
			}

			lastSeparator := strings.LastIndex(object.ObjectName, "/")
			parentDirectoryPath, entryName, isDeleteData, isRecursive := "/", object.ObjectName, true, true
			if lastSeparator > 0 && lastSeparator+1 < len(object.ObjectName) {
				entryName = object.ObjectName[lastSeparator+1:]
				parentDirectoryPath = "/" + object.ObjectName[:lastSeparator]
			}
			parentDirectoryPath = fmt.Sprintf("%s/%s%s", s3a.option.BucketsPath, bucket, parentDirectoryPath)

			err := doDeleteEntry(client, parentDirectoryPath, entryName, isDeleteData, isRecursive)
			if err == nil {
				deletedObjects = append(deletedObjects, object)
			} else {
				deleteErrors = append(deleteErrors, DeleteError{
					Code:    "",
					Message: err.Error(),
					Key:     object.ObjectName,
				})
			}
		}
		return nil
	})

	deleteResp := DeleteObjectsResponse{}
	if !deleteObjects.Quiet {
		deleteResp.DeletedObjects = deletedObjects
	}
	deleteResp.Errors = deleteErrors

	writeSuccessResponseXML(w, encodeResponse(deleteResp))

}

var passThroughHeaders = []string{
	"response-cache-control",
	"response-content-disposition",
	"response-content-encoding",
	"response-content-language",
	"response-content-type",
	"response-expires",
}

func (s3a *S3ApiServer) proxyToFiler(w http.ResponseWriter, r *http.Request, destUrl string, responseFn func(proxyResponse *http.Response, w http.ResponseWriter)) {

	glog.V(2).Infof("s3 proxying %s to %s", r.Method, destUrl)

	proxyReq, err := http.NewRequest(r.Method, destUrl, r.Body)

	if err != nil {
		glog.Errorf("NewRequest %s: %v", destUrl, err)
		writeErrorResponse(w, s3err.ErrInternalError, r.URL)
		return
	}

	proxyReq.Header.Set("Host", s3a.option.Filer)
	proxyReq.Header.Set("X-Forwarded-For", r.RemoteAddr)

	for header, values := range r.Header {
		// handle s3 related headers
		passed := false
		for _, h := range passThroughHeaders {
			if strings.ToLower(header) == h && len(values) > 0 {
				proxyReq.Header.Add(header[len("response-"):], values[0])
				passed = true
				break
			}
		}
		if passed {
			continue
		}
		// handle other headers
		for _, value := range values {
			proxyReq.Header.Add(header, value)
		}
	}

	resp, postErr := client.Do(proxyReq)

	if (resp.ContentLength == -1 || resp.StatusCode == 404) && !strings.HasSuffix(destUrl, "/") {
		writeErrorResponse(w, s3err.ErrNoSuchKey, r.URL)
		return
	}

	if postErr != nil {
		glog.Errorf("post to filer: %v", postErr)
		writeErrorResponse(w, s3err.ErrInternalError, r.URL)
		return
	}
	defer util.CloseResponse(resp)

	responseFn(resp, w)

}

func passThroughResponse(proxyResponse *http.Response, w http.ResponseWriter) {
	for k, v := range proxyResponse.Header {
		w.Header()[k] = v
	}
	w.WriteHeader(proxyResponse.StatusCode)
	io.Copy(w, proxyResponse.Body)
}

func (s3a *S3ApiServer) putToFiler(r *http.Request, uploadUrl string, dataReader io.Reader) (etag string, code s3err.ErrorCode) {

	hash := md5.New()
	var body = io.TeeReader(dataReader, hash)

	proxyReq, err := http.NewRequest("PUT", uploadUrl, body)

	if err != nil {
		glog.Errorf("NewRequest %s: %v", uploadUrl, err)
		return "", s3err.ErrInternalError
	}

	proxyReq.Header.Set("Host", s3a.option.Filer)
	proxyReq.Header.Set("X-Forwarded-For", r.RemoteAddr)

	for header, values := range r.Header {
		for _, value := range values {
			proxyReq.Header.Add(header, value)
		}
	}

	resp, postErr := client.Do(proxyReq)

	if postErr != nil {
		glog.Errorf("post to filer: %v", postErr)
		return "", s3err.ErrInternalError
	}
	defer resp.Body.Close()

	etag = fmt.Sprintf("%x", hash.Sum(nil))

	resp_body, ra_err := ioutil.ReadAll(resp.Body)
	if ra_err != nil {
		glog.Errorf("upload to filer response read %d: %v", resp.StatusCode, ra_err)
		return etag, s3err.ErrInternalError
	}
	var ret weed_server.FilerPostResult
	unmarshal_err := json.Unmarshal(resp_body, &ret)
	if unmarshal_err != nil {
		glog.Errorf("failing to read upload to %s : %v", uploadUrl, string(resp_body))
		return "", s3err.ErrInternalError
	}
	if ret.Error != "" {
		glog.Errorf("upload to filer error: %v", ret.Error)
		return "", filerErrorToS3Error(ret.Error)
	}

	return etag, s3err.ErrNone
}

func setEtag(w http.ResponseWriter, etag string) {
	if etag != "" {
		if strings.HasPrefix(etag, "\"") {
			w.Header().Set("ETag", etag)
		} else {
			w.Header().Set("ETag", "\""+etag+"\"")
		}
	}
}

func getBucketAndObject(r *http.Request) (bucket, object string) {
	vars := mux.Vars(r)
	bucket = vars["bucket"]
	object = vars["object"]
	if !strings.HasPrefix(object, "/") {
		object = "/" + object
	}

	return
}

func filerErrorToS3Error(errString string) s3err.ErrorCode {
	if strings.HasPrefix(errString, "existing ") && strings.HasSuffix(errString, "is a directory") {
		return s3err.ErrExistingObjectIsDirectory
	}
	return s3err.ErrInternalError
}
<commit_msg>s3: avoid nil resp when having error<commit_after>package s3api

import (
	"crypto/md5"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/chrislusf/seaweedfs/weed/s3api/s3err"

	"github.com/gorilla/mux"

	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	weed_server "github.com/chrislusf/seaweedfs/weed/server"
	"github.com/chrislusf/seaweedfs/weed/util"
)

var (
	client *http.Client
)

func init() {
	client = &http.Client{Transport: &http.Transport{
		MaxIdleConnsPerHost: 1024,
	}}
}

func (s3a *S3ApiServer) PutObjectHandler(w http.ResponseWriter, r *http.Request) {

	// http://docs.aws.amazon.com/AmazonS3/latest/dev/UploadingObjects.html

	bucket, object := getBucketAndObject(r)

	_, err := validateContentMd5(r.Header)
	if err != nil {
		writeErrorResponse(w, s3err.ErrInvalidDigest, r.URL)
		return
	}

	dataReader := r.Body
	if s3a.iam.isEnabled() {
		rAuthType := getRequestAuthType(r)
		var s3ErrCode s3err.ErrorCode
		switch rAuthType {
		case authTypeStreamingSigned:
			dataReader, s3ErrCode = s3a.iam.newSignV4ChunkedReader(r)
		case authTypeSignedV2, authTypePresignedV2:
			_, s3ErrCode = s3a.iam.isReqAuthenticatedV2(r)
		case authTypePresigned, authTypeSigned:
			_, s3ErrCode = s3a.iam.reqSignatureV4Verify(r)
		}
		if s3ErrCode != s3err.ErrNone {
			writeErrorResponse(w, s3ErrCode, r.URL)
			return
		}
	}
	defer dataReader.Close()

	if strings.HasSuffix(object, "/") {
		if err := s3a.mkdir(s3a.option.BucketsPath, bucket+object, nil); err != nil {
			writeErrorResponse(w, s3err.ErrInternalError, r.URL)
			return
		}
	} else {
		uploadUrl := fmt.Sprintf("http://%s%s/%s%s", s3a.option.Filer, s3a.option.BucketsPath, bucket, object)

		etag, errCode := s3a.putToFiler(r, uploadUrl, dataReader)

		if errCode != s3err.ErrNone {
			writeErrorResponse(w, errCode, r.URL)
			return
		}

		setEtag(w, etag)
	}

	writeSuccessResponseEmpty(w)
}

func (s3a *S3ApiServer) GetObjectHandler(w http.ResponseWriter, r *http.Request) {

	bucket, object := getBucketAndObject(r)

	if strings.HasSuffix(r.URL.Path, "/") {
		writeErrorResponse(w, s3err.ErrNotImplemented, r.URL)
		return
	}

	destUrl := fmt.Sprintf("http://%s%s/%s%s",
		s3a.option.Filer, s3a.option.BucketsPath, bucket, object)

	s3a.proxyToFiler(w, r, destUrl, passThroughResponse)

}

func (s3a *S3ApiServer) HeadObjectHandler(w http.ResponseWriter, r *http.Request) {

	bucket, object := getBucketAndObject(r)

	destUrl := fmt.Sprintf("http://%s%s/%s%s",
		s3a.option.Filer, s3a.option.BucketsPath, bucket, object)

	s3a.proxyToFiler(w, r, destUrl, passThroughResponse)

}

func (s3a *S3ApiServer) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) {

	bucket, object := getBucketAndObject(r)

	response, _ := s3a.listFilerEntries(bucket, object, 1, "", "/")
	if len(response.Contents) != 0 && strings.HasSuffix(object, "/") {
		w.WriteHeader(http.StatusNoContent)
		return
	}

	destUrl := fmt.Sprintf("http://%s%s/%s%s?recursive=true",
		s3a.option.Filer, s3a.option.BucketsPath, bucket, object)

	s3a.proxyToFiler(w, r, destUrl, func(proxyResponse *http.Response, w http.ResponseWriter) {
		for k, v := range proxyResponse.Header {
			w.Header()[k] = v
		}
		w.WriteHeader(http.StatusNoContent)
	})
}

// / ObjectIdentifier carries key name for the object to delete.
type ObjectIdentifier struct {
	ObjectName string `xml:"Key"`
}

// DeleteObjectsRequest - xml carrying the object key names which needs to be deleted.
type DeleteObjectsRequest struct {
	// Element to enable quiet mode for the request
	Quiet bool
	// List of objects to be deleted
	Objects []ObjectIdentifier `xml:"Object"`
}

// DeleteError structure.
type DeleteError struct {
	Code    string
	Message string
	Key     string
}

// DeleteObjectsResponse container for multiple object deletes.
type DeleteObjectsResponse struct {
	XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ DeleteResult" json:"-"`

	// Collection of all deleted objects
	DeletedObjects []ObjectIdentifier `xml:"Deleted,omitempty"`

	// Collection of errors deleting certain objects.
	Errors []DeleteError `xml:"Error,omitempty"`
}

// DeleteMultipleObjectsHandler - Delete multiple objects
func (s3a *S3ApiServer) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) {

	bucket, _ := getBucketAndObject(r)

	deleteXMLBytes, err := ioutil.ReadAll(r.Body)
	if err != nil {
		writeErrorResponse(w, s3err.ErrInternalError, r.URL)
		return
	}

	deleteObjects := &DeleteObjectsRequest{}
	if err := xml.Unmarshal(deleteXMLBytes, deleteObjects); err != nil {
		writeErrorResponse(w, s3err.ErrMalformedXML, r.URL)
		return
	}

	var deletedObjects []ObjectIdentifier
	var deleteErrors []DeleteError

	s3a.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		for _, object := range deleteObjects.Objects {
			response, _ := s3a.listFilerEntries(bucket, object.ObjectName, 1, "", "/")
			if len(response.Contents) != 0 && strings.HasSuffix(object.ObjectName, "/") {
				continue
			}

			lastSeparator := strings.LastIndex(object.ObjectName, "/")
			parentDirectoryPath, entryName, isDeleteData, isRecursive := "/", object.ObjectName, true, true
			if lastSeparator > 0 && lastSeparator+1 < len(object.ObjectName) {
				entryName = object.ObjectName[lastSeparator+1:]
				parentDirectoryPath = "/" + object.ObjectName[:lastSeparator]
			}
			parentDirectoryPath = fmt.Sprintf("%s/%s%s", s3a.option.BucketsPath, bucket, parentDirectoryPath)

			err := doDeleteEntry(client, parentDirectoryPath, entryName, isDeleteData, isRecursive)
			if err == nil {
				deletedObjects = append(deletedObjects, object)
			} else {
				deleteErrors = append(deleteErrors, DeleteError{
					Code:    "",
					Message: err.Error(),
					Key:     object.ObjectName,
				})
			}
		}
		return nil
	})

	deleteResp := DeleteObjectsResponse{}
	if !deleteObjects.Quiet {
		deleteResp.DeletedObjects = deletedObjects
	}
	deleteResp.Errors = deleteErrors

	writeSuccessResponseXML(w, encodeResponse(deleteResp))

}

var passThroughHeaders = []string{
	"response-cache-control",
	"response-content-disposition",
	"response-content-encoding",
	"response-content-language",
	"response-content-type",
	"response-expires",
}

func (s3a *S3ApiServer) proxyToFiler(w http.ResponseWriter, r *http.Request, destUrl string, responseFn func(proxyResponse *http.Response, w http.ResponseWriter)) {

	glog.V(2).Infof("s3 proxying %s to %s", r.Method, destUrl)

	proxyReq, err := http.NewRequest(r.Method, destUrl, r.Body)

	if err != nil {
		glog.Errorf("NewRequest %s: %v", destUrl, err)
		writeErrorResponse(w, s3err.ErrInternalError, r.URL)
		return
	}

	proxyReq.Header.Set("Host", s3a.option.Filer)
	proxyReq.Header.Set("X-Forwarded-For", r.RemoteAddr)

	for header, values := range r.Header {
		// handle s3 related headers
		passed := false
		for _, h := range passThroughHeaders {
			if strings.ToLower(header) == h && len(values) > 0 {
				proxyReq.Header.Add(header[len("response-"):], values[0])
				passed = true
				break
			}
		}
		if passed {
			continue
		}
		// handle other headers
		for _, value := range values {
			proxyReq.Header.Add(header, value)
		}
	}

	resp, postErr := client.Do(proxyReq)

	if postErr != nil {
		glog.Errorf("post to filer: %v", postErr)
		writeErrorResponse(w, s3err.ErrInternalError, r.URL)
		return
	}
	defer util.CloseResponse(resp)

	if (resp.ContentLength == -1 || resp.StatusCode == 404) && !strings.HasSuffix(destUrl, "/") {
		writeErrorResponse(w, s3err.ErrNoSuchKey, r.URL)
		return
	}

	responseFn(resp, w)

}

func passThroughResponse(proxyResponse *http.Response, w http.ResponseWriter) {
	for k, v := range proxyResponse.Header {
		w.Header()[k] = v
	}
	w.WriteHeader(proxyResponse.StatusCode)
	io.Copy(w, proxyResponse.Body)
}

func (s3a *S3ApiServer) putToFiler(r *http.Request, uploadUrl string, dataReader io.Reader) (etag string, code s3err.ErrorCode) {

	hash := md5.New()
	var body = io.TeeReader(dataReader, hash)

	proxyReq, err := http.NewRequest("PUT", uploadUrl, body)

	if err != nil {
		glog.Errorf("NewRequest %s: %v", uploadUrl, err)
		return "", s3err.ErrInternalError
	}

	proxyReq.Header.Set("Host", s3a.option.Filer)
	proxyReq.Header.Set("X-Forwarded-For", r.RemoteAddr)

	for header, values := range r.Header {
		for _, value := range values {
			proxyReq.Header.Add(header, value)
		}
	}

	resp, postErr := client.Do(proxyReq)

	if postErr != nil {
		glog.Errorf("post to filer: %v", postErr)
		return "", s3err.ErrInternalError
	}
	defer resp.Body.Close()

	etag = fmt.Sprintf("%x", hash.Sum(nil))

	resp_body, ra_err := ioutil.ReadAll(resp.Body)
	if ra_err != nil {
		glog.Errorf("upload to filer response read %d: %v", resp.StatusCode, ra_err)
		return etag, s3err.ErrInternalError
	}
	var ret weed_server.FilerPostResult
	unmarshal_err := json.Unmarshal(resp_body, &ret)
	if unmarshal_err != nil {
		glog.Errorf("failing to read upload to %s : %v", uploadUrl, string(resp_body))
		return "", s3err.ErrInternalError
	}
	if ret.Error != "" {
		glog.Errorf("upload to filer error: %v", ret.Error)
		return "", filerErrorToS3Error(ret.Error)
	}

	return etag, s3err.ErrNone
}

func setEtag(w http.ResponseWriter, etag string) {
	if etag != "" {
		if strings.HasPrefix(etag, "\"") {
			w.Header().Set("ETag", etag)
		} else {
			w.Header().Set("ETag", "\""+etag+"\"")
		}
	}
}

func getBucketAndObject(r *http.Request) (bucket, object string) {
	vars := mux.Vars(r)
	bucket = vars["bucket"]
	object = vars["object"]
	if !strings.HasPrefix(object, "/") {
		object = "/" + object
	}

	return
}

func filerErrorToS3Error(errString string) s3err.ErrorCode {
	if strings.HasPrefix(errString, "existing ") && strings.HasSuffix(errString, "is a directory") {
		return s3err.ErrExistingObjectIsDirectory
	}
	return s3err.ErrInternalError
}
<|endoftext|>


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

// This test runs too slowly under the race detector.
// +build !race

package nsync_test

import (
	"fmt"
	"math/rand"
	"testing"
	"time"

	"v.io/x/lib/nsync"
)

// ---------------------------

// A cvStressData represents the data used by the threads of TestCVWaitStress.
type cvStressData struct {
	mu       nsync.Mu // protects fields below
	count    uint64   // incremented by the various threads
	timeouts uint64   // incremented on each timeout

	refs uint // reference count: one per test thread, decremented when it exits

	countIsIMod4 [4]nsync.CV // element i signalled when count==i mod 4
	refsIsZero   nsync.CV    // signalled when refs==0
}

// The delay in cvStressIncLoop() is uniformly distributed from 0 to
// cvMaxDelayMicros-1 microseconds.
const cvMaxDelayMicros = 1000                                // maximum delay
const cvMeanDelayMicros = cvMaxDelayMicros / 2               // mean delay
const cvExpectedTimeoutsPerSec = 1000000 / cvMeanDelayMicros // number of timeouts expected per second

// cvStressIncLoop() acquires s.mu, then increments s.count n times, each time
// waiting until condition is true.  A random delay between 0us and 999us is
// used for each wait; if the timeout expires, s.timeouts is incremented, and
// the wait is retried.  s.refs is decremented before the routine returns.
func cvStressIncLoop(s *cvStressData, countImod4 uint64, n uint64) {
	s.mu.Lock()
	s.mu.AssertHeld()
	for i := uint64(0); i != n; i++ {
		s.mu.AssertHeld()
		for (s.count & 3) != countImod4 {
			var absDeadline time.Time = time.Now().Add(time.Duration(rand.Int31n(cvMaxDelayMicros)) * time.Microsecond)
			for s.countIsIMod4[countImod4].WaitWithDeadline(&s.mu, absDeadline, nil) != nsync.OK && (s.count&3) != countImod4 {
				s.mu.AssertHeld()
				s.timeouts++
				s.mu.AssertHeld()
				absDeadline = time.Now().Add(time.Duration(rand.Int31n(cvMaxDelayMicros)) * time.Microsecond)
			}
		}
		s.mu.AssertHeld()
		s.count++
		s.countIsIMod4[s.count&3].Signal()
	}
	s.refs--
	if s.refs == 0 {
		s.refsIsZero.Signal()
	}
	s.mu.AssertHeld()
	s.mu.Unlock()
}

// TestCVTimeoutStress() tests many threads using a single lock, using
// WaitConditions and timeouts.
//
// It creates a cvStressData s, and then creates several threads using
// cvStressIncLoop() trying to increment s.count from 1 to 2 mod 4, from 2 to 3
// mod 4, and from 3 to 0 mod 4, using random delays.  It sleeps a few seconds, ensuring
// many random timeouts by threads in cvStressIncLoop, because there is no thread
// incrementing s.count from 0 (which is 0 mod 4).  It then creates several
// threads using cvStressIncLoop() trying to increment s.count from 0 to 1 mod 4.
// This allows all the threads to run to completion, since there are equal
// numbers for each condition.
// Finally, it waits for all threads to exit.
func TestCVTimeoutStress(t *testing.T) {
	const loopCount = 50000
	const threadsPerValue = 5
	var s cvStressData

	s.mu.Lock()
	s.mu.AssertHeld()
	// Create threads trying to increment from 1, 2, and 3 mod 4.
	// They will continually hit their timeouts because s.count==0
	for i := 0; i != threadsPerValue; i++ {
		s.mu.AssertHeld()
		s.refs++
		go cvStressIncLoop(&s, 1, loopCount)
		s.refs++
		go cvStressIncLoop(&s, 2, loopCount)
		s.refs++
		go cvStressIncLoop(&s, 3, loopCount)
	}
	s.mu.AssertHeld()
	s.mu.Unlock()

	// Sleep a few seconds to cause many timeouts.
	start := time.Now()
	const sleepSeconds = 3
	time.Sleep(sleepSeconds * time.Second)

	s.mu.Lock()
	s.mu.AssertHeld()

	// Check that approximately the right number of timeouts have occurred.
	// The 3 below is the three classes of thread produced before the Sleep().
	// The factor of 1/4 is to allow for randomness and slow test machines.
	expectedTimeouts := uint64(threadsPerValue * 3 * sleepSeconds * cvExpectedTimeoutsPerSec / 4)
	timeoutsSeen := s.timeouts
	if timeoutsSeen < expectedTimeouts {
		t.Errorf("expected more than %d timeouts, got %d", expectedTimeouts, timeoutsSeen)
	}

	// Now create the threads that increment from 0 mod 4.   s.count will then be incremented.
	for i := 0; i != threadsPerValue; i++ {
		s.mu.AssertHeld()
		s.refs++
		go cvStressIncLoop(&s, 0, loopCount)
	}

	// Wait for threads to exit.
	s.mu.AssertHeld()
	for s.refs != 0 {
		s.refsIsZero.Wait(&s.mu)
	}
	s.mu.AssertHeld()
	if s.refs != 0 {
		t.Fatalf(fmt.Sprintf("s.refs == %d; expected 0 at end of TestCVWaitStress", s.refs))
	}

	s.mu.AssertHeld()
	s.mu.Unlock()
	timeTaken := time.Now().Sub(start)

	// Check that s.count has the right value.
	expectedCount := uint64(loopCount * threadsPerValue * 4)
	if s.count != expectedCount {
		t.Errorf("expected to increment s.count to %d, got %d", expectedCount, s.count)
	}

	// Some timeouts shoud have happened while the counts were being incremented.
	expectedTimeouts = timeoutsSeen + 1000
	if timeTaken > 4*time.Second {
		overTime := timeTaken - (4 * time.Second)
		expectedTimeouts -= 2000 * uint64(overTime/time.Second)
		t.Logf("%v of overtime, adjusting expeting timeouts accordingly", overTime)
	}
	if s.timeouts < expectedTimeouts {
		t.Errorf("expected more than %d timeouts, got %d", expectedTimeouts, s.timeouts)
	}
	if s.timeouts == 0 {
		t.Errorf("expected a non-zero number of timeouts")
	}
	t.Logf("timeouts: %v, expected: %v, time taken: %v", s.timeouts, expectedTimeouts, timeTaken)
}
<commit_msg>x/lib/nsync: tweak flaky test.. (#21)<commit_after>// Copyright 2016 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This test runs too slowly under the race detector.
// +build !race

package nsync_test

import (
	"fmt"
	"math/rand"
	"testing"
	"time"

	"v.io/x/lib/nsync"
)

// ---------------------------

// A cvStressData represents the data used by the threads of TestCVWaitStress.
type cvStressData struct {
	mu       nsync.Mu // protects fields below
	count    uint64   // incremented by the various threads
	timeouts uint64   // incremented on each timeout

	refs uint // reference count: one per test thread, decremented when it exits

	countIsIMod4 [4]nsync.CV // element i signalled when count==i mod 4
	refsIsZero   nsync.CV    // signalled when refs==0
}

// The delay in cvStressIncLoop() is uniformly distributed from 0 to
// cvMaxDelayMicros-1 microseconds.
const cvMaxDelayMicros = 1000                                // maximum delay
const cvMeanDelayMicros = cvMaxDelayMicros / 2               // mean delay
const cvExpectedTimeoutsPerSec = 1000000 / cvMeanDelayMicros // number of timeouts expected per second

// cvStressIncLoop() acquires s.mu, then increments s.count n times, each time
// waiting until condition is true.  A random delay between 0us and 999us is
// used for each wait; if the timeout expires, s.timeouts is incremented, and
// the wait is retried.  s.refs is decremented before the routine returns.
func cvStressIncLoop(s *cvStressData, countImod4 uint64, n uint64) {
	s.mu.Lock()
	s.mu.AssertHeld()
	for i := uint64(0); i != n; i++ {
		s.mu.AssertHeld()
		for (s.count & 3) != countImod4 {
			var absDeadline time.Time = time.Now().Add(time.Duration(rand.Int31n(cvMaxDelayMicros)) * time.Microsecond)
			for s.countIsIMod4[countImod4].WaitWithDeadline(&s.mu, absDeadline, nil) != nsync.OK && (s.count&3) != countImod4 {
				s.mu.AssertHeld()
				s.timeouts++
				s.mu.AssertHeld()
				absDeadline = time.Now().Add(time.Duration(rand.Int31n(cvMaxDelayMicros)) * time.Microsecond)
			}
		}
		s.mu.AssertHeld()
		s.count++
		s.countIsIMod4[s.count&3].Signal()
	}
	s.refs--
	if s.refs == 0 {
		s.refsIsZero.Signal()
	}
	s.mu.AssertHeld()
	s.mu.Unlock()
}

// TestCVTimeoutStress() tests many threads using a single lock, using
// WaitConditions and timeouts.
//
// It creates a cvStressData s, and then creates several threads using
// cvStressIncLoop() trying to increment s.count from 1 to 2 mod 4, from 2 to 3
// mod 4, and from 3 to 0 mod 4, using random delays.  It sleeps a few seconds, ensuring
// many random timeouts by threads in cvStressIncLoop, because there is no thread
// incrementing s.count from 0 (which is 0 mod 4).  It then creates several
// threads using cvStressIncLoop() trying to increment s.count from 0 to 1 mod 4.
// This allows all the threads to run to completion, since there are equal
// numbers for each condition.
// Finally, it waits for all threads to exit.
func TestCVTimeoutStress(t *testing.T) {
	const loopCount = 50000
	const threadsPerValue = 5
	var s cvStressData

	s.mu.Lock()
	s.mu.AssertHeld()
	// Create threads trying to increment from 1, 2, and 3 mod 4.
	// They will continually hit their timeouts because s.count==0
	for i := 0; i != threadsPerValue; i++ {
		s.mu.AssertHeld()
		s.refs++
		go cvStressIncLoop(&s, 1, loopCount)
		s.refs++
		go cvStressIncLoop(&s, 2, loopCount)
		s.refs++
		go cvStressIncLoop(&s, 3, loopCount)
	}
	s.mu.AssertHeld()
	s.mu.Unlock()

	// Sleep a few seconds to cause many timeouts.
	const sleepSeconds = 3
	time.Sleep(sleepSeconds * time.Second)

	// Start the clock after the sleep above.
	start := time.Now()

	s.mu.Lock()
	s.mu.AssertHeld()

	// Check that approximately the right number of timeouts have occurred.
	// The 3 below is the three classes of thread produced before the Sleep().
	// The factor of 1/4 is to allow for randomness and slow test machines.
	expectedTimeouts := uint64(threadsPerValue * 3 * sleepSeconds * cvExpectedTimeoutsPerSec / 4)
	timeoutsSeen := s.timeouts
	if timeoutsSeen < expectedTimeouts {
		// Note, that this can potentially fail if the test is run on a
		// very loaded machine.
		t.Errorf("expected more than %d timeouts, got %d", expectedTimeouts, timeoutsSeen)
	}

	// Now create the threads that increment from 0 mod 4.   s.count will then be incremented.
	for i := 0; i != threadsPerValue; i++ {
		s.mu.AssertHeld()
		s.refs++
		go cvStressIncLoop(&s, 0, loopCount)
	}

	// Wait for threads to exit.
	s.mu.AssertHeld()
	for s.refs != 0 {
		s.refsIsZero.Wait(&s.mu)
	}
	s.mu.AssertHeld()
	if s.refs != 0 {
		t.Fatalf(fmt.Sprintf("s.refs == %d; expected 0 at end of TestCVWaitStress", s.refs))
	}

	s.mu.AssertHeld()
	s.mu.Unlock()
	timeTaken := time.Now().Sub(start)

	// Check that s.count has the right value.
	expectedCount := uint64(loopCount * threadsPerValue * 4)
	if s.count != expectedCount {
		t.Errorf("expected to increment s.count to %d, got %d", expectedCount, s.count)
	}

	// Some timeouts shoud have happened while the counts were being incremented.
	expectedTimeouts = timeoutsSeen + 1000
	t.Logf("timeouts: %v, expected: %v, time taken: %v, seen: %v", s.timeouts, expectedTimeouts, timeTaken, timeoutsSeen)
	if timeTaken > 4*time.Second {
		// Looks like a slow test, let's just be content
		// with a small number of timeouts.
		expectedTimeouts = 1000
		t.Logf("slow test, adjusting expected timeouts accordingly: %v", expectedTimeouts)
	}
	if s.timeouts < expectedTimeouts {
		t.Errorf("expected more than %d timeouts, got %d", expectedTimeouts, s.timeouts)
	}
	if s.timeouts == 0 {
		t.Errorf("expected a non-zero number of timeouts")
	}
}
<|endoftext|>


<commit_before>package x86

import (
	"github.com/lunixbochs/ghostrace/ghost/sys/num"
	uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn"

	"github.com/lunixbochs/usercorn/go/kernel/common"
	"github.com/lunixbochs/usercorn/go/kernel/linux"
	"github.com/lunixbochs/usercorn/go/models"
)

var LinuxRegs = []int{uc.X86_REG_EBX, uc.X86_REG_ECX, uc.X86_REG_EDX, uc.X86_REG_ESI, uc.X86_REG_EDI, uc.X86_REG_EBP}

func LinuxKernels(u models.Usercorn) []interface{} {
	return []interface{}{linux.NewKernel(u)}
}

func LinuxSyscall(u models.Usercorn) {
	// TODO: handle errors or something
	eax, _ := u.RegRead(uc.X86_REG_EAX)
	name, _ := num.Linux_x86[int(eax)]
	ret, _ := u.Syscall(int(eax), name, common.RegArgs(u, LinuxRegs))
	u.RegWrite(uc.X86_REG_EAX, ret)
}

func LinuxInterrupt(u models.Usercorn, intno uint32) {
	if intno == 0x80 {
		LinuxSyscall(u)
	}
}

func init() {
	Arch.RegisterOS(&models.OS{
		Name:      "linux",
		Kernels:   LinuxKernels,
		Init:      linux.StackInit,
		Interrupt: LinuxInterrupt,
	})
}
<commit_msg>add socketcall() for x86-linux<commit_after>package x86

import (
	"github.com/lunixbochs/ghostrace/ghost/sys/num"
	uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn"

	co "github.com/lunixbochs/usercorn/go/kernel/common"
	"github.com/lunixbochs/usercorn/go/kernel/linux"
	"github.com/lunixbochs/usercorn/go/kernel/posix"
	"github.com/lunixbochs/usercorn/go/models"
)

var LinuxRegs = []int{uc.X86_REG_EBX, uc.X86_REG_ECX, uc.X86_REG_EDX, uc.X86_REG_ESI, uc.X86_REG_EDI, uc.X86_REG_EBP}

type LinuxKernel struct {
	linux.LinuxKernel
}

var socketCallMap = map[int]string{
	1:  "socket",
	2:  "bind",
	3:  "connect",
	4:  "listen",
	5:  "accept",
	6:  "getsockname",
	7:  "getpeername",
	8:  "socketpair",
	9:  "send",
	10: "recv",
	11: "sendto",
	12: "recvfrom",
	13: "shutdown",
	14: "setsockopt",
	15: "getsockopt",
	16: "sendmsg",
	17: "recvmsg",
	18: "accept4",
}

func (k *LinuxKernel) Socketcall(index int, params co.Buf) uint64 {
	if name, ok := socketCallMap[index]; ok {
		if sys := k.UsercornSyscall(name); sys != nil {
			rawArgs := make([]uint32, len(sys.In))
			if err := params.Unpack(rawArgs); err != nil {
				return posix.UINT64_MAX
			}
			args := make([]uint64, len(rawArgs))
			for i, v := range rawArgs {
				args[i] = uint64(v)
			}
			return sys.Call(args)
		}
	}
	return posix.UINT64_MAX // FIXME
}

func LinuxKernels(u models.Usercorn) []interface{} {
	kernel := &LinuxKernel{*linux.DefaultKernel()}
	kernel.UsercornInit(kernel, u)
	return []interface{}{kernel}
}

func LinuxSyscall(u models.Usercorn) {
	// TODO: handle errors or something
	eax, _ := u.RegRead(uc.X86_REG_EAX)
	name, _ := num.Linux_x86[int(eax)]
	ret, _ := u.Syscall(int(eax), name, co.RegArgs(u, LinuxRegs))
	u.RegWrite(uc.X86_REG_EAX, ret)
}

func LinuxInterrupt(u models.Usercorn, intno uint32) {
	if intno == 0x80 {
		LinuxSyscall(u)
	}
}

func init() {
	Arch.RegisterOS(&models.OS{
		Name:      "linux",
		Kernels:   LinuxKernels,
		Init:      linux.StackInit,
		Interrupt: LinuxInterrupt,
	})
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/sony/v8eval/go/v8eval"
)

func Add(x, y int) int {
	var v8 = v8eval.NewV8()
	v8.Eval("var add = (x, y) => x + y;", nil)

	var sum int
	v8.Call("add", []int{x, y}, &sum)
	return sum
}

func main() {
	v8eval.Initialize()
	fmt.Println(Add(1, 2))
	v8eval.Dispose()
}
<commit_msg>Add a comment<commit_after>package main

import (
	"fmt"

	"github.com/sony/v8eval/go/v8eval"
)

// Add returns the sum of the given integers.
func Add(x, y int) int {
	var v8 = v8eval.NewV8()
	v8.Eval("var add = (x, y) => x + y;", nil)

	var sum int
	v8.Call("add", []int{x, y}, &sum)
	return sum
}

func main() {
	v8eval.Initialize()
	fmt.Println(Add(1, 2))
	v8eval.Dispose()
}
<|endoftext|>


<commit_before>package gusher

import (
	"gopkg.in/igm/sockjs-go.v2/sockjs"
)

type channel struct {
	sessions map[string]sockjs.Session
}

func newChannel() *channel {
	return &channel{sessions: make(map[string]sockjs.Session)}
}

func (ch *channel) publish(payload string) {
	for _, session := range ch.sessions {
		session.Send(payload)
	}
}

func (ch *channel) subscribe(session sockjs.Session) {
	ch.sessions[session.ID()] = session
}

func (ch *channel) unsubscribe(session sockjs.Session) {
	id := session.ID()
	_, ok := ch.sessions[id]
	if ok {
		delete(ch.sessions, id)
	}
}
<commit_msg>added mutex locks to channel funcs<commit_after>package gusher

import (
	"gopkg.in/igm/sockjs-go.v2/sockjs"
	"sync"
)

type channel struct {
	mutex    sync.Mutex
	sessions map[string]sockjs.Session
}

func newChannel() *channel {
	return &channel{sessions: make(map[string]sockjs.Session)}
}

func (ch *channel) publish(payload string) {
	//what happens when a session is added while looping?
	for _, session := range ch.sessions {
		session.Send(payload)
	}
}

func (ch *channel) subscribe(session sockjs.Session) {
	ch.mutex.Lock()
	ch.sessions[session.ID()] = session
	ch.mutex.Unlock()
}

func (ch *channel) unsubscribe(session sockjs.Session) {
	id := session.ID()
	_, ok := ch.sessions[id]
	if ok {
		ch.mutex.Lock()
		delete(ch.sessions, id)
		ch.mutex.Unlock()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os/exec"
	"time"

	pbb "github.com/brotherlogic/buildserver/proto"
	pbfc "github.com/brotherlogic/filecopier/proto"
	pb "github.com/brotherlogic/gobuildslave/proto"
	"github.com/golang/protobuf/proto"
	"golang.org/x/net/context"
)

const (
	pendWait = time.Minute
)

func (s *Server) runTransition(ctx context.Context, job *pb.JobAssignment) {
	startState := job.State
	switch job.State {
	case pb.State_ACKNOWLEDGED:
		key := s.scheduleBuild(ctx, job)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("SCHED: %v @ %v", key, time.Now())
		s.stateMutex.Unlock()
		if !job.Job.Bootstrap {
			if key != "" {
				job.Server = s.Registry.Identifier
				job.State = pb.State_BUILT
				job.RunningVersion = key
			}
		} else {
			job.CommandKey = key
			job.State = pb.State_BUILDING
			job.Server = s.Registry.Identifier
		}
	case pb.State_BUILDING:
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("BUILD(%v): %v", job.CommandKey, s.scheduler.getState(job.CommandKey))
		s.stateMutex.Unlock()
		if s.taskComplete(job.CommandKey) {
			job.State = pb.State_BUILT
		}
	case pb.State_BUILT:
		output, _ := s.scheduler.getOutput(job.CommandKey)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("BUILT(%v): (%v): %v", job.CommandKey, len(output), output)
		s.stateMutex.Unlock()
		if job.Job.Bootstrap && len(output) > 0 {
			if job.BuildFail == 5 {
				s.deliverCrashReport(ctx, job, output)
				job.BuildFail = 0
			}
			job.BuildFail++
			job.State = pb.State_DIED
		} else {
			job.BuildFail = 0
			key := s.scheduleRun(job.Job)
			job.CommandKey = key
			job.StartTime = time.Now().Unix()
			job.State = pb.State_PENDING
			if _, ok := s.pendingMap[time.Now().Weekday()]; !ok {
				s.pendingMap[time.Now().Weekday()] = make(map[string]int)
			}
			s.pendingMap[time.Now().Weekday()][job.Job.Name]++
		}
	case pb.State_PENDING:
		s.stateMutex.Lock()
		out, _ := s.scheduler.getOutput(job.CommandKey)
		s.stateMap[job.Job.Name] = fmt.Sprintf("OUTPUT = %v", out)
		s.stateMutex.Unlock()
		if time.Now().Add(-time.Minute).Unix() > job.StartTime {
			job.State = pb.State_RUNNING
		}
	case pb.State_RUNNING:
		output, errout := s.scheduler.getOutput(job.CommandKey)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("ROUTPUT = %v, %v", output, s.scheduler.getStatus(job.CommandKey))
		job.Status = s.scheduler.getStatus(job.CommandKey)
		s.stateMutex.Unlock()
		if len(job.CommandKey) > 0 && s.taskComplete(job.CommandKey) {
			s.stateMutex.Lock()
			s.stateMap[job.Job.Name] = fmt.Sprintf("COMPLETE = %v", output)
			s.stateMutex.Unlock()
			s.deliverCrashReport(ctx, job, output+fmt.Sprintf("%v", errout))
			job.State = pb.State_DIED
		}

		err := s.discover.discover(job.Job.Name, s.Registry.Identifier)
		if err != nil {
			if job.DiscoverCount > 30 {
				output2, errout2 := s.scheduler.getErrOutput(job.CommandKey)
				s.RaiseIssue(ctx, "Cannot Discover Running Server", fmt.Sprintf("%v on %v is not discoverable, despite running (%v) the output says %v (%v), %v, %v", job.Job.Name, s.Registry.Identifier, err, output, errout, output2, errout2), false)
			}
			job.DiscoverCount++
		} else {
			job.DiscoverCount = 0
		}

		// Restart this job if we need to
		if !job.Job.Bootstrap {
			if time.Now().Sub(time.Unix(job.LastVersionPull, 0)) > time.Minute*5 {
				version, err := s.getVersion(ctx, job.Job)
				job.LastVersionPull = time.Now().Unix()

				if err == nil && version.Version != job.RunningVersion {
					s.stateMutex.Lock()
					s.stateMap[job.Job.Name] = fmt.Sprintf("VERSION_MISMATCH = %v,%v", version, job.RunningVersion)
					s.stateMutex.Unlock()
					s.scheduler.killJob(job.CommandKey)
				}
			}
		}
	case pb.State_BRINK_OF_DEATH:
		if s.version.confirm(ctx, job.Job.Name) {
			s.scheduler.killJob(job.CommandKey)
			job.State = pb.State_ACKNOWLEDGED
		}
	case pb.State_DIED:
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("DIED %v", job.CommandKey)
		s.stateMutex.Unlock()
		s.scheduler.removeJob(job.CommandKey)
		job.State = pb.State_ACKNOWLEDGED
	}

	if job.State != startState {
		job.LastTransitionTime = time.Now().Unix()
	}
}

type translator interface {
	build(job *pb.Job) *exec.Cmd
	run(job *pb.Job) *exec.Cmd
}

type checker interface {
	isAlive(ctx context.Context, job *pb.JobAssignment) bool
}

func (s *Server) getVersion(ctx context.Context, job *pb.Job) (*pbb.Version, error) {
	versions, err := s.builder.build(ctx, job)
	if err != nil {
		return &pbb.Version{}, err
	}

	if len(versions) == 0 {
		return &pbb.Version{}, nil
	}

	return versions[0], nil

}

func updateJob(err error, job *pb.JobAssignment, resp *pbfc.CopyResponse) {
	if err == nil {
		job.QueuePos = resp.IndexInQueue
	}

}

// scheduleBuild builds out the job, returning the current version
func (s *Server) scheduleBuild(ctx context.Context, job *pb.JobAssignment) string {
	if job.Job.Bootstrap {
		c := s.translator.build(job.Job)
		return s.scheduler.Schedule(&rCommand{command: c, base: job.Job.Name})
	}

	versions, err := s.builder.build(ctx, job.Job)

	s.lastCopyStatus = fmt.Sprintf("%v", err)
	if len(versions) == 0 {
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("No Versions: %v", err)
		s.stateMutex.Unlock()
		return ""
	}

	t := time.Now()

	//Only copy if the latest version is different to the local version
	s.versionsMutex.Lock()
	v, ok := s.versions[job.Job.Name]
	s.versionsMutex.Unlock()
	if !ok || v.Version != versions[0].Version {
		s.copies++

		resp, err := s.builder.copy(ctx, versions[0])
		s.lastCopyTime = time.Now().Sub(t)
		s.lastCopyStatus = fmt.Sprintf("%v", err)
		if err != nil || resp.Status != pbfc.CopyStatus_COMPLETE || len(resp.Error) > 0 {
			updateJob(err, job, resp)
			s.stateMutex.Lock()
			s.stateMap[job.Job.Name] = fmt.Sprintf("Copy fail (%v) -> %v", time.Now().Sub(t), err)
			s.stateMutex.Unlock()
			return ""
		}
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("Copied version %v", versions[0].Version)
		s.stateMutex.Unlock()

		//Save the version file alongside the binary
		data, _ := proto.Marshal(versions[0])
		ioutil.WriteFile("/home/simon/gobuild/bin/"+job.Job.Name+".version", data, 0644)
	} else {
		s.skippedCopies++
	}

	s.versionsMutex.Lock()
	s.versions[job.Job.Name] = versions[0]
	s.versionsMutex.Unlock()
	return versions[0].Version
}

func (s *Server) scheduleRun(job *pb.Job) string {
	c := s.translator.run(job)
	return s.scheduler.Schedule(&rCommand{command: c, base: job.Name})
}

func (s *Server) taskComplete(key string) bool {
	return s.scheduler.schedulerComplete(key)
}
<commit_msg>Explicit crash report<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os/exec"
	"time"

	pbb "github.com/brotherlogic/buildserver/proto"
	pbfc "github.com/brotherlogic/filecopier/proto"
	pb "github.com/brotherlogic/gobuildslave/proto"
	"github.com/golang/protobuf/proto"
	"golang.org/x/net/context"
)

const (
	pendWait = time.Minute
)

func (s *Server) runTransition(ctx context.Context, job *pb.JobAssignment) {
	startState := job.State
	switch job.State {
	case pb.State_ACKNOWLEDGED:
		key := s.scheduleBuild(ctx, job)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("SCHED: %v @ %v", key, time.Now())
		s.stateMutex.Unlock()
		if !job.Job.Bootstrap {
			if key != "" {
				job.Server = s.Registry.Identifier
				job.State = pb.State_BUILT
				job.RunningVersion = key
			}
		} else {
			job.CommandKey = key
			job.State = pb.State_BUILDING
			job.Server = s.Registry.Identifier
		}
	case pb.State_BUILDING:
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("BUILD(%v): %v", job.CommandKey, s.scheduler.getState(job.CommandKey))
		s.stateMutex.Unlock()
		if s.taskComplete(job.CommandKey) {
			job.State = pb.State_BUILT
		}
	case pb.State_BUILT:
		output, _ := s.scheduler.getOutput(job.CommandKey)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("BUILT(%v): (%v): %v", job.CommandKey, len(output), output)
		s.stateMutex.Unlock()
		if job.Job.Bootstrap && len(output) > 0 {
			if job.BuildFail == 5 {
				s.deliverCrashReport(ctx, job, output)
				job.BuildFail = 0
			}
			job.BuildFail++
			job.State = pb.State_DIED
		} else {
			job.BuildFail = 0
			key := s.scheduleRun(job.Job)
			job.CommandKey = key
			job.StartTime = time.Now().Unix()
			job.State = pb.State_PENDING
			if _, ok := s.pendingMap[time.Now().Weekday()]; !ok {
				s.pendingMap[time.Now().Weekday()] = make(map[string]int)
			}
			s.pendingMap[time.Now().Weekday()][job.Job.Name]++
		}
	case pb.State_PENDING:
		s.stateMutex.Lock()
		out, _ := s.scheduler.getOutput(job.CommandKey)
		s.stateMap[job.Job.Name] = fmt.Sprintf("OUTPUT = %v", out)
		s.stateMutex.Unlock()
		if time.Now().Add(-time.Minute).Unix() > job.StartTime {
			job.State = pb.State_RUNNING
		}
	case pb.State_RUNNING:
		output, errout := s.scheduler.getOutput(job.CommandKey)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("ROUTPUT = %v, %v", output, s.scheduler.getStatus(job.CommandKey))
		job.Status = s.scheduler.getStatus(job.CommandKey)
		s.stateMutex.Unlock()
		if len(job.CommandKey) > 0 && s.taskComplete(job.CommandKey) {
			s.stateMutex.Lock()
			s.stateMap[job.Job.Name] = fmt.Sprintf("COMPLETE = %v", output)
			s.stateMutex.Unlock()
			s.deliverCrashReport(ctx, job, "CRASH: "+output+fmt.Sprintf("%v", errout))
			job.State = pb.State_DIED
		}

		err := s.discover.discover(job.Job.Name, s.Registry.Identifier)
		if err != nil {
			if job.DiscoverCount > 30 {
				output2, errout2 := s.scheduler.getErrOutput(job.CommandKey)
				s.RaiseIssue(ctx, "Cannot Discover Running Server", fmt.Sprintf("%v on %v is not discoverable, despite running (%v) the output says %v (%v), %v, %v", job.Job.Name, s.Registry.Identifier, err, output, errout, output2, errout2), false)
			}
			job.DiscoverCount++
		} else {
			job.DiscoverCount = 0
		}

		// Restart this job if we need to
		if !job.Job.Bootstrap {
			if time.Now().Sub(time.Unix(job.LastVersionPull, 0)) > time.Minute*5 {
				version, err := s.getVersion(ctx, job.Job)
				job.LastVersionPull = time.Now().Unix()

				if err == nil && version.Version != job.RunningVersion {
					s.stateMutex.Lock()
					s.stateMap[job.Job.Name] = fmt.Sprintf("VERSION_MISMATCH = %v,%v", version, job.RunningVersion)
					s.stateMutex.Unlock()
					s.scheduler.killJob(job.CommandKey)
				}
			}
		}
	case pb.State_BRINK_OF_DEATH:
		if s.version.confirm(ctx, job.Job.Name) {
			s.scheduler.killJob(job.CommandKey)
			job.State = pb.State_ACKNOWLEDGED
		}
	case pb.State_DIED:
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("DIED %v", job.CommandKey)
		s.stateMutex.Unlock()
		s.scheduler.removeJob(job.CommandKey)
		job.State = pb.State_ACKNOWLEDGED
	}

	if job.State != startState {
		job.LastTransitionTime = time.Now().Unix()
	}
}

type translator interface {
	build(job *pb.Job) *exec.Cmd
	run(job *pb.Job) *exec.Cmd
}

type checker interface {
	isAlive(ctx context.Context, job *pb.JobAssignment) bool
}

func (s *Server) getVersion(ctx context.Context, job *pb.Job) (*pbb.Version, error) {
	versions, err := s.builder.build(ctx, job)
	if err != nil {
		return &pbb.Version{}, err
	}

	if len(versions) == 0 {
		return &pbb.Version{}, nil
	}

	return versions[0], nil

}

func updateJob(err error, job *pb.JobAssignment, resp *pbfc.CopyResponse) {
	if err == nil {
		job.QueuePos = resp.IndexInQueue
	}

}

// scheduleBuild builds out the job, returning the current version
func (s *Server) scheduleBuild(ctx context.Context, job *pb.JobAssignment) string {
	if job.Job.Bootstrap {
		c := s.translator.build(job.Job)
		return s.scheduler.Schedule(&rCommand{command: c, base: job.Job.Name})
	}

	versions, err := s.builder.build(ctx, job.Job)

	s.lastCopyStatus = fmt.Sprintf("%v", err)
	if len(versions) == 0 {
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("No Versions: %v", err)
		s.stateMutex.Unlock()
		return ""
	}

	t := time.Now()

	//Only copy if the latest version is different to the local version
	s.versionsMutex.Lock()
	v, ok := s.versions[job.Job.Name]
	s.versionsMutex.Unlock()
	if !ok || v.Version != versions[0].Version {
		s.copies++

		resp, err := s.builder.copy(ctx, versions[0])
		s.lastCopyTime = time.Now().Sub(t)
		s.lastCopyStatus = fmt.Sprintf("%v", err)
		if err != nil || resp.Status != pbfc.CopyStatus_COMPLETE || len(resp.Error) > 0 {
			updateJob(err, job, resp)
			s.stateMutex.Lock()
			s.stateMap[job.Job.Name] = fmt.Sprintf("Copy fail (%v) -> %v", time.Now().Sub(t), err)
			s.stateMutex.Unlock()
			return ""
		}
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("Copied version %v", versions[0].Version)
		s.stateMutex.Unlock()

		//Save the version file alongside the binary
		data, _ := proto.Marshal(versions[0])
		ioutil.WriteFile("/home/simon/gobuild/bin/"+job.Job.Name+".version", data, 0644)
	} else {
		s.skippedCopies++
	}

	s.versionsMutex.Lock()
	s.versions[job.Job.Name] = versions[0]
	s.versionsMutex.Unlock()
	return versions[0].Version
}

func (s *Server) scheduleRun(job *pb.Job) string {
	c := s.translator.run(job)
	return s.scheduler.Schedule(&rCommand{command: c, base: job.Name})
}

func (s *Server) taskComplete(key string) bool {
	return s.scheduler.schedulerComplete(key)
}
<|endoftext|>


<commit_before>package gpx_test

import (
	"bytes"
	"fmt"
	"os"
	"time"

	"github.com/twpayne/go-gpx"
)

func ExampleRead() {
	r := bytes.NewBufferString(`
		<gpx version="1.0" creator="ExpertGPS 1.1 - http://www.topografix.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/0" xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd">
		  <wpt lat="42.438878" lon="-71.119277">
			<ele>44.586548</ele>
			<time>2001-11-28T21:05:28Z</time>
			<name>5066</name>
			<desc>5066</desc>
			<sym>Crossing</sym>
			<type>Crossing</type>
		  </wpt>
		</gpx>
	`)
	t, err := gpx.Read(r)
	if err != nil {
		fmt.Printf("err == %v", err)
		return
	}
	fmt.Printf("t.Wpt[0] == %+v", t.Wpt[0])
	// Output:
	// t.Wpt[0] == &{Lat:42.438878 Lon:-71.119277 Ele:44.586548 Time:2001-11-28 21:05:28 +0000 UTC MagVar:0 GeoidHeight:0 Name:5066 Cmt: Desc:5066 Src: Link:[] Sym:Crossing Type:Crossing Fix: Sat:0 HDOP:0 VDOP:0 PDOP:0 AgeOfGPSData:0 DGPSID:[] Extensions:<nil>}
}

func ExampleGPX_WriteIndent() {
	g := &gpx.GPX{
		Version: "1.0",
		Creator: "ExpertGPS 1.1 - http://www.topografix.com",
		Wpt: []*gpx.WptType{
			{
				Lat:  42.438878,
				Lon:  -71.119277,
				Ele:  44.586548,
				Time: time.Date(2001, 11, 28, 21, 5, 28, 0, time.UTC),
				Name: "5066",
				Desc: "5066",
				Sym:  "Crossing",
				Type: "Crossing",
			},
		},
	}
	if err := g.WriteIndent(os.Stdout, "", "  "); err != nil {
		fmt.Printf("err == %v", err)
	}
	// Output:
	// <gpx version="1.0" creator="ExpertGPS 1.1 - http://www.topografix.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/0" xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd">
	//   <wpt lat="42.438878" lon="-71.119277">
	//     <ele>44.586548</ele>
	//     <time>2001-11-28T21:05:28Z</time>
	//     <name>5066</name>
	//     <desc>5066</desc>
	//     <sym>Crossing</sym>
	//     <type>Crossing</type>
	//   </wpt>
	// </gpx>
}
<commit_msg>Fix test<commit_after>package gpx_test

import (
	"bytes"
	"fmt"
	"os"
	"time"

	"github.com/twpayne/go-gpx"
)

func ExampleRead() {
	r := bytes.NewBufferString(`
		<gpx version="1.0" creator="ExpertGPS 1.1 - http://www.topografix.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/0" xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd">
		  <wpt lat="42.438878" lon="-71.119277">
			<ele>44.586548</ele>
			<speed>9.16</speed>
			<time>2001-11-28T21:05:28Z</time>
			<name>5066</name>
			<desc>5066</desc>
			<sym>Crossing</sym>
			<type>Crossing</type>
		  </wpt>
		</gpx>
	`)
	t, err := gpx.Read(r)
	if err != nil {
		fmt.Printf("err == %v", err)
		return
	}
	fmt.Printf("t.Wpt[0] == %+v", t.Wpt[0])
	// Output:
	// t.Wpt[0] == &{Lat:42.438878 Lon:-71.119277 Ele:44.586548 Speed:9.16 Course:0 Time:2001-11-28 21:05:28 +0000 UTC MagVar:0 GeoidHeight:0 Name:5066 Cmt: Desc:5066 Src: Link:[] Sym:Crossing Type:Crossing Fix: Sat:0 HDOP:0 VDOP:0 PDOP:0 AgeOfGPSData:0 DGPSID:[] Extensions:<nil>}
}

func ExampleGPX_WriteIndent() {
	g := &gpx.GPX{
		Version: "1.0",
		Creator: "ExpertGPS 1.1 - http://www.topografix.com",
		Wpt: []*gpx.WptType{
			{
				Lat:  42.438878,
				Lon:  -71.119277,
				Ele:  44.586548,
				Time: time.Date(2001, 11, 28, 21, 5, 28, 0, time.UTC),
				Name: "5066",
				Desc: "5066",
				Sym:  "Crossing",
				Type: "Crossing",
			},
		},
	}
	if err := g.WriteIndent(os.Stdout, "", "  "); err != nil {
		fmt.Printf("err == %v", err)
	}
	// Output:
	// <gpx version="1.0" creator="ExpertGPS 1.1 - http://www.topografix.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/0" xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd">
	//   <wpt lat="42.438878" lon="-71.119277">
	//     <ele>44.586548</ele>
	//     <time>2001-11-28T21:05:28Z</time>
	//     <name>5066</name>
	//     <desc>5066</desc>
	//     <sym>Crossing</sym>
	//     <type>Crossing</type>
	//   </wpt>
	// </gpx>
}
<|endoftext|>


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

// Package cas provides remote-apis-sdks client with luci integration.
package cas

import (
	"context"
	"fmt"
	"runtime"
	"strings"

	"github.com/bazelbuild/remote-apis-sdks/go/pkg/client"
	"google.golang.org/grpc/credentials"

	"go.chromium.org/luci/auth"
	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/common/logging"
	"go.chromium.org/luci/hardcoded/chromeinfra"
)

// NewClient returns luci auth configured Client for RBE-CAS.
func NewClient(ctx context.Context, instance string, opts auth.Options, readOnly bool) (*client.Client, error) {
	project := strings.Split(instance, "/")[1]
	var role string
	if readOnly {
		role = "cas-read-only"
	} else {
		role = "cas-read-write"
	}

	// Construct auth.Options.
	opts.ActAsServiceAccount = fmt.Sprintf("%s@%s.iam.gserviceaccount.com", role, project)
	opts.ActViaLUCIRealm = fmt.Sprintf("@internal:%s/%s", project, role)
	opts.Scopes = []string{"https://www.googleapis.com/auth/cloud-platform"}

	if strings.HasSuffix(project, "-dev") || strings.HasSuffix(project, "-staging") {
		// use dev token server for dev/staging projects.
		opts.TokenServerHost = chromeinfra.TokenServerDevHost
	}

	creds, err := auth.NewAuthenticator(ctx, auth.SilentLogin, opts).PerRPCCredentials()
	if err != nil {
		return nil, errors.Annotate(err, "failed to get PerRPCCredentials").Err()
	}
	dialParams := client.DialParams{
		Service:            "remotebuildexecution.googleapis.com:443",
		TransportCredsOnly: true,
	}

	cl, err := client.NewClient(ctx, instance, dialParams, ClientOptions(creds)...)
	if err != nil {
		return nil, errors.Annotate(err, "failed to create client").Err()
	}
	return cl, nil
}

// ClientOptions returns CAS client options.
func ClientOptions(creds credentials.PerRPCCredentials) []client.Opt {
	casConcurrency := runtime.NumCPU() * 2
	if runtime.GOOS == "windows" {
		// This is for better file write performance on Windows (http://b/171672371#comment6).
		casConcurrency = runtime.NumCPU()
	}

	return []client.Opt{
		&client.PerRPCCreds{Creds: creds},
		client.CASConcurrency(casConcurrency),
		client.UtilizeLocality(true),
		&client.TreeSymlinkOpts{Preserved: true, FollowsTarget: false},
		// Set restricted permission for written files.
		client.DirMode(0700),
		client.ExecutableMode(0700),
		client.RegularMode(0600),
		client.CompressedBytestreamThreshold(0),
	}
}

// ContextWithMetadata attaches RBE related metadata with tool name to the
// given context.
func ContextWithMetadata(ctx context.Context, toolName string) (context.Context, error) {
	ctx, err := client.ContextWithMetadata(ctx, &client.ContextMetadata{
		ToolName: toolName,
	})
	if err != nil {
		return nil, errors.Annotate(err, "failed to attach metadata").Err()
	}

	m, err := client.GetContextMetadata(ctx)
	if err != nil {
		return nil, errors.Annotate(err, "failed to extract metadata").Err()
	}

	logging.Infof(ctx, "context metadata: %#+v", *m)

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

// Package cas provides remote-apis-sdks client with luci integration.
package cas

import (
	"context"
	"fmt"
	"runtime"
	"strings"

	"github.com/bazelbuild/remote-apis-sdks/go/pkg/client"
	"google.golang.org/grpc/credentials"

	"go.chromium.org/luci/auth"
	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/common/logging"
	"go.chromium.org/luci/hardcoded/chromeinfra"
)

// NewClient returns luci auth configured Client for RBE-CAS.
func NewClient(ctx context.Context, instance string, opts auth.Options, readOnly bool) (*client.Client, error) {
	project := strings.Split(instance, "/")[1]
	var role string
	if readOnly {
		role = "cas-read-only"
	} else {
		role = "cas-read-write"
	}

	// Construct auth.Options.
	opts.ActAsServiceAccount = fmt.Sprintf("%s@%s.iam.gserviceaccount.com", role, project)
	opts.ActViaLUCIRealm = fmt.Sprintf("@internal:%s/%s", project, role)
	opts.Scopes = []string{"https://www.googleapis.com/auth/cloud-platform"}

	if strings.HasSuffix(project, "-dev") || strings.HasSuffix(project, "-staging") {
		// use dev token server for dev/staging projects.
		opts.TokenServerHost = chromeinfra.TokenServerDevHost
	}

	creds, err := auth.NewAuthenticator(ctx, auth.SilentLogin, opts).PerRPCCredentials()
	if err != nil {
		return nil, errors.Annotate(err, "failed to get PerRPCCredentials").Err()
	}
	dialParams := client.DialParams{
		Service:            "remotebuildexecution.googleapis.com:443",
		TransportCredsOnly: true,
	}

	cl, err := client.NewClient(ctx, instance, dialParams, ClientOptions(creds)...)
	if err != nil {
		return nil, errors.Annotate(err, "failed to create client").Err()
	}
	return cl, nil
}

// ClientOptions returns CAS client options.
func ClientOptions(creds credentials.PerRPCCredentials) []client.Opt {
	casConcurrency := runtime.NumCPU() * 2
	if runtime.GOOS == "windows" {
		// This is for better file write performance on Windows (http://b/171672371#comment6).
		casConcurrency = runtime.NumCPU()
	}

	return []client.Opt{
		&client.PerRPCCreds{Creds: creds},
		client.CASConcurrency(casConcurrency),
		client.UtilizeLocality(true),
		&client.TreeSymlinkOpts{Preserved: true, FollowsTarget: false},
		// Set restricted permission for written files.
		client.DirMode(0700),
		client.ExecutableMode(0700),
		client.RegularMode(0600),
		client.CompressedBytestreamThreshold(0),

		// Do not set per RPC timeout.
		client.RPCTimeouts{},
	}
}

// ContextWithMetadata attaches RBE related metadata with tool name to the
// given context.
func ContextWithMetadata(ctx context.Context, toolName string) (context.Context, error) {
	ctx, err := client.ContextWithMetadata(ctx, &client.ContextMetadata{
		ToolName: toolName,
	})
	if err != nil {
		return nil, errors.Annotate(err, "failed to attach metadata").Err()
	}

	m, err := client.GetContextMetadata(ctx)
	if err != nil {
		return nil, errors.Annotate(err, "failed to extract metadata").Err()
	}

	logging.Infof(ctx, "context metadata: %#+v", *m)

	return ctx, nil
}
<|endoftext|>


<commit_before>package route

import (
	"log"
	"net/http"
	Pathp "path"
	"reflect"
	"strings"
)

func isSpecial(s string) bool {
	switch s {
	case "&", "":
		return true
	default:
		return false
	}
}

//A PathingRouter is part of the filepath, and can take part
//in the descent of the filepath trie.
type PathingRouter interface {
	Child(subpath string) (n Router, remainingSubpath string)
}

var (
	_ PathingRouter = new(Path)
	_ Router        = new(Path)
)

//IgnoreExtensions is a PathRouter that ignores
//the extensions of folders and files when matching
//request URIs.
type IgnoreExtensions Path

func (i IgnoreExtensions) underlying() Path {
	return Path(i)
}

//Function Child is provided by all types implementing the PathingRouter
//interface.
func (i IgnoreExtensions) Child(subpath string) (Router, string) {
	return i.underlying().ChildProcess(
		subpath,
		func(s string) string {
			ext := Pathp.Ext(s)
			return strings.TrimSuffix(s, ext)
		},
	)
}

func (i IgnoreExtensions) RouteHTTP(r *http.Request) Router {
	return PathRouteHTTP(i, r)
}

//Type Path is a Router which routes by URL path. Files or directories
//with empty names are not allowed. The empty name routes to the terminal
//Router, the one used if the path stops here, and the ampersand ("&") path
//swallows the next file segment in the path, regardless of its contents.
//
//It should be noted that when RouteHTTP is called
//the PathRouter is followed to completion from the
//start to end of the URL, thus using two routers separately
//(i.e. separated by a different Router in the routing tree)
//causes two separate and independant operations on the path;
//a Path that routes a/b followed by one that routes a/b/c does
//not result in a route of a/b/a/c, instead resulting in a route of
//just a/b.
type Path map[string]Router

func (p Path) self() Path {
	if p == nil {
		p = make(Path)
	}
	return p
}

//Function AddChild adds a child PathRouter which is used
//as the next name in the path.
//
//A non PathRouter child will cause the Router to
//be returned to the caller of RouteHTTP, causing the
//path to terminate there if it is a Handler.
func (p Path) AddChild(pR Router, name string) Path {
	p[name] = pR
	return p
}

//Sets the Handler that is used when the path terminates here.
//This is the same as AddChild(r, ""); the empty string routes
//to here.
func (p Path) Handler(r Router) Path {
	return p.AddChild(r, "")
}

func isPathRouter(r Router) (b bool) {
	_, b = r.(Path)
	return
}

//Performs RouteHTTP on a trie of PathingRouters.
func PathRouteHTTP(p PathingRouter, rq *http.Request) Router {
	/*
		Now like SubdomainRouter!
	*/
	var (
		currentPathingRouter PathingRouter = p
		path                 string        = strings.Trim(rq.URL.Path, "/")
		currentRouter        Router
	)

	//fix paths
	path = Pathp.Clean(path)

	//We break spec a bit to allow direcories called "."
	if path == "." {
		path = ""
	}
	for {
		currentRouter, path = currentPathingRouter.Child(path)

		var ok bool
		if currentPathingRouter, ok = currentRouter.(PathingRouter); !ok {
			if debug {
				log.Printf("[-] Path routing terminated at %v [%v] and pathRouter %v [%v]", currentRouter, reflect.TypeOf(currentRouter), currentPathingRouter, reflect.TypeOf(currentPathingRouter))
			}
			break
		}

	}

	return currentRouter
}

//RouteHTTP traverses the tree of Paths until the end of the URL path
//is encountered, returning the terminal router or nil.
func (p Path) RouteHTTP(rq *http.Request) Router {
	return PathRouteHTTP(p, rq)
}

//Function Child is provided by all types that implement the PathingRouter interface.
func (p Path) Child(subpath string) (n Router, remainingSubpath string) {
	return p.ChildProcess(subpath, nil)
}

//Function Child returns the next Router associated with the next
//'hop' in the path.
func (p Path) ChildProcess(subpath string, process func(string) string) (n Router, remainingSubpath string) {

	if process == nil {
		process = func(s string) string {
			return s
		}
	}

	if debug {
		log.Printf("[?] Currently in path %+v, with requested path %+q \n", p, subpath)
	}

	//strip leading slashes
	subpath = strings.TrimLeft(subpath, "/")

	//If strings.SplitN(subpath, "/", 3)'s length is two, then this
	//is the only item left in the path, and thus we must terminate here.
	if subpath == "" || subpath == "/" {
		if debug {
			log.Println("[?] Routing into current level (path is empty).")
		}
		return p[""], ""
	}

	processedSubpath := process(subpath)

	//First, check if we have bound a handler for this whole
	//subpath (a/b/c)
	if pR, ok := p[processedSubpath]; ok {
		return pR, ""
	} else if debug {
		log.Printf("[?] %v not wholly in %+v\n", subpath, p)
	}

	//Check if the next node is present
	splt := strings.SplitN(subpath, "/", 2)
	if len(splt) > 1 {
		if pR, ok := p[process(splt[0])]; ok {
			if debug {
				log.Printf("[?] Routed down into %v remaining string %+q.\n", reflect.TypeOf(pR), splt[1])
			}
			return pR, splt[1]
		}
	} else if debug {
		log.Printf("[?] %+v too short.\n", splt)
	}

	//Check if we have a route that begins with the subpath
	/*
		for path, pR := range p {
			if path != "" && strings.HasPrefix(processedSubpath, path) {
				if debug {
					log.Printf(
						"[?] Routed into %v - %+q is a prefix of %+q",
						pR,
						path,
						subpath,
					)
				}
				return pR, strings.TrimLeft(subpath, path)
			} else if debug {
				log.Printf(
					"[?] Did not reoute %+q is not a prefix of %+q",
					path,
					subpath,
				)
			}
		}
		//I actually have no idea what this does
	*/

	if p["&"] != nil {
		return p["&"], subpath[strings.IndexRune(subpath, '/')+1:]
	}

	//Not Found.
	return nil, subpath
}
<commit_msg>Rename IgnoreExtensions -> NoRoutePath, fix bug with "&" notation.<commit_after>package route

import (
	"log"
	"net/http"
	Pathp "path"
	"reflect"
	"strings"
)

func isSpecial(s string) bool {
	switch s {
	case "&", "":
		return true
	default:
		return false
	}
}

//A PathingRouter is part of the filepath, and can take part
//in the descent of the filepath trie.
type PathingRouter interface {
	Child(subpath string) (n Router, remainingSubpath string)
}

var (
	_ PathingRouter = new(Path)
	_ Router        = new(Path)
)

//NoExtPath is a PathRouter that ignores
//the extensions of folders and files when matching
//request URIs.
type NoExtPath Path

func (i NoExtPath) underlying() Path {
	return Path(i)
}

//Function Child is provided by all types implementing the PathingRouter
//interface.
func (i NoExtPath) Child(subpath string) (Router, string) {
	return i.underlying().ChildProcess(
		subpath,
		func(s string) string {
			ext := Pathp.Ext(s)
			return strings.TrimSuffix(s, ext)
		},
	)
}

func (i NoExtPath) RouteHTTP(r *http.Request) Router {
	return PathRouteHTTP(i, r)
}

//Type Path is a Router which routes by URL path. Files or directories
//with empty names are not allowed. The empty name routes to the terminal
//Router, the one used if the path stops here, and the ampersand ("&") path
//swallows the next file segment in the path, regardless of its contents.
//
//It should be noted that when RouteHTTP is called
//the PathRouter is followed to completion from the
//start to end of the URL, thus using two routers separately
//(i.e. separated by a different Router in the routing tree)
//causes two separate and independant operations on the path;
//a Path that routes a/b followed by one that routes a/b/c does
//not result in a route of a/b/a/c, instead resulting in a route of
//just a/b.
type Path map[string]Router

func (p Path) self() Path {
	if p == nil {
		p = make(Path)
	}
	return p
}

//Function AddChild adds a child PathRouter which is used
//as the next name in the path.
//
//A non PathRouter child will cause the Router to
//be returned to the caller of RouteHTTP, causing the
//path to terminate there if it is a Handler.
func (p Path) AddChild(pR Router, name string) Path {
	p[name] = pR
	return p
}

//Sets the Handler that is used when the path terminates here.
//This is the same as AddChild(r, ""); the empty string routes
//to here.
func (p Path) Handler(r Router) Path {
	return p.AddChild(r, "")
}

func isPathRouter(r Router) (b bool) {
	_, b = r.(Path)
	return
}

//Performs RouteHTTP on a trie of PathingRouters.
func PathRouteHTTP(p PathingRouter, rq *http.Request) Router {
	/*
		Now like SubdomainRouter!
	*/
	var (
		currentPathingRouter PathingRouter = p
		path                 string        = strings.Trim(rq.URL.Path, "/")
		currentRouter        Router
	)

	//fix paths
	path = Pathp.Clean(path)

	//We break spec a bit to allow direcories called "."
	if path == "." {
		path = ""
	}
	for {
		currentRouter, path = currentPathingRouter.Child(path)

		var ok bool
		if currentPathingRouter, ok = currentRouter.(PathingRouter); !ok {
			if debug {
				log.Printf("[-] Path routing terminated at %v [%v] and pathRouter %v [%v]", currentRouter, reflect.TypeOf(currentRouter), currentPathingRouter, reflect.TypeOf(currentPathingRouter))
			}
			break
		}

	}

	return currentRouter
}

//RouteHTTP traverses the tree of Paths until the end of the URL path
//is encountered, returning the terminal router or nil.
func (p Path) RouteHTTP(rq *http.Request) Router {
	return PathRouteHTTP(p, rq)
}

//Function Child is provided by all types that implement the PathingRouter interface.
func (p Path) Child(subpath string) (n Router, remainingSubpath string) {
	return p.ChildProcess(subpath, nil)
}

//Function Child returns the next Router associated with the next
//'hop' in the path.
func (p Path) ChildProcess(subpath string, process func(string) string) (n Router, remainingSubpath string) {

	if process == nil {
		process = func(s string) string {
			return s
		}
	}

	if debug {
		log.Printf("[?] Currently in path %+v, with requested path %+q \n", p, subpath)
	}

	//strip leading slashes
	subpath = strings.TrimLeft(subpath, "/")

	//If strings.SplitN(subpath, "/", 3)'s length is two, then this
	//is the only item left in the path, and thus we must terminate here.
	if subpath == "" || subpath == "/" {
		if debug {
			log.Println("[?] Routing into current level (path is empty).")
		}
		return p[""], ""
	}

	processedSubpath := process(subpath)

	//First, check if we have bound a handler for this whole
	//subpath (a/b/c)
	if pR, ok := p[processedSubpath]; ok {
		return pR, ""
	} else if debug {
		log.Printf("[?] %v not wholly in %+v\n", subpath, p)
	}

	//Check if the next node is present
	splt := strings.SplitN(subpath, "/", 2)
	if len(splt) > 1 {
		if pR, ok := p[process(splt[0])]; ok {
			if debug {
				log.Printf("[?] %v -> %v (%v).\n", splt[0], reflect.TypeOf(pR), splt[1])
			}
			return pR, splt[1]
		}
	} else if debug {
		log.Printf("[?] %+v too short.\n", splt)
	}

	//Check if we have a route that begins with the subpath
	/*
		for path, pR := range p {
			if path != "" && strings.HasPrefix(processedSubpath, path) {
				if debug {
					log.Printf(
						"[?] Routed into %v - %+q is a prefix of %+q",
						pR,
						path,
						subpath,
					)
				}
				return pR, strings.TrimLeft(subpath, path)
			} else if debug {
				log.Printf(
					"[?] Did not reoute %+q is not a prefix of %+q",
					path,
					subpath,
				)
			}
		}
		//I actually have no idea what this does
	*/

	if p["&"] != nil {
		log.Printf("Ampersand present, swallowing one.")
		pos := strings.IndexRune(subpath, '/')
		if pos == -1 {
			pos = len(subpath)
		}
		return p["&"], subpath[pos+1:]
	} else if debug {
		log.Printf("[?] No ampersand present in Path, no swallow.")
	}

	//Not Found.
	return nil, subpath
}
<|endoftext|>


<commit_before>package trout

import (
	"encoding/base64"
	"math/rand"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"
)

type testHandler string

func (t testHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte(t))
}

func TestRouting(t *testing.T) {
	type testCase struct {
		url, method, handler string
	}
	cases := []testCase{
		{"/v1", "GET", "get-static"},
		{"/v1/", "GET", "get-static"},
		{"/hello", "GET", "get-dynamic"},
		{"/hello/", "GET", "get-dynamic"},
		{"/v1", "POST", "post-dynamic"},
		{"/v1/", "POST", "post-dynamic"},
		{"/", "GET", "get-root"},
	}
	var router Router
	router.Handle404 = testHandler("404")
	router.Handle405 = testHandler("405")
	router.Endpoint("/{id}").Methods("GET").Handler(testHandler("get-dynamic"))
	router.Endpoint("/v1").Methods("GET").Handler(testHandler("get-static"))
	router.Endpoint("/{id}").Methods("POST").Handler(testHandler("post-dynamic"))
	router.Endpoint("/").Methods("GET").Handler(testHandler("get-root"))
	for _, c := range cases {
		r, err := http.NewRequest(c.method, c.url, nil)
		if err != nil {
			t.Fatalf("Error creating request for %s %s: %+v", c.method, c.url, err)
		}
		h := router.getHandler(r)
		res := string(h.(testHandler))
		if res != c.handler {
			t.Errorf("Expected to route \"%s %s\" to %s, routed to %s", c.method, c.url, c.handler, res)
		}

	}
}

var benchRouter Router
var benchTests []string
var benchMethods = [...]string{"GET", "POST", "PUT", "DELETE"}

func init() {
	for i := 0; i < 100; i++ {
		rand.Seed(time.Now().UnixNano())
		depth := rand.Intn(4) + 1
		var route string
		var req string
		for x := 0; x < depth; x++ {
			rand.Seed(time.Now().UnixNano())
			param := rand.Intn(1) == 1
			pieceLength := rand.Intn(24) + 1
			piece := make([]byte, pieceLength)
			rand.Read(piece)
			pieceStr := base64.URLEncoding.EncodeToString(piece)
			req = req + "/" + pieceStr
			if param {
				pieceStr = "{" + pieceStr + "}"
			}
			route = route + "/" + pieceStr
		}
		benchTests = append(benchTests, req)
		endpoint := benchRouter.Endpoint(route)
		rand.Seed(time.Now().UnixNano())
		get := rand.Intn(1) == 1
		rand.Seed(time.Now().UnixNano())
		post := rand.Intn(1) == 1
		catchAll := !get && !post
		var methods []string
		if get {
			methods = append(methods, "GET")
		}
		if post {
			methods = append(methods, "POST")
		}
		if catchAll {
			methods = append(methods, catchAllMethod)
		}
		endpoint.Methods(methods...).Handler(testHandler("benchmark"))
	}
}

func BenchmarkRouting(b *testing.B) {
	for i := 0; i < b.N; i++ {
		b.StopTimer()
		w := httptest.NewRecorder()
		route := benchTests[i%len(benchTests)]
		method := benchMethods[i%len(benchMethods)]
		req, err := http.NewRequest(method, route, nil)
		if err != nil {
			b.Fatalf(err.Error())
		}
		b.StartTimer()
		benchRouter.ServeHTTP(w, req)
	}
}
<commit_msg>Test our catch-all handlers.<commit_after>package trout

import (
	"encoding/base64"
	"math/rand"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"
)

type testHandler string

func (t testHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte(t))
}

func TestRouting(t *testing.T) {
	type testCase struct {
		url, method, handler string
	}
	cases := []testCase{
		{"/v1", "GET", "get-static"},
		{"/v1/", "GET", "get-static"},
		{"/hello", "GET", "get-dynamic"},
		{"/hello/", "GET", "get-dynamic"},
		{"/v1", "POST", "post-dynamic"},
		{"/v1/", "POST", "post-dynamic"},
		{"/", "GET", "get-root"},
		{"/hello/world", "DELETE", "catch-all"},
	}
	var router Router
	router.Handle404 = testHandler("404")
	router.Handle405 = testHandler("405")
	router.Endpoint("/{id}").Methods("GET").Handler(testHandler("get-dynamic"))
	router.Endpoint("/v1").Methods("GET").Handler(testHandler("get-static"))
	router.Endpoint("/{id}").Methods("POST").Handler(testHandler("post-dynamic"))
	router.Endpoint("/").Methods("GET").Handler(testHandler("get-root"))
	router.Endpoint("/hello/world").Handler(testHandler("catch-all"))
	for _, c := range cases {
		r, err := http.NewRequest(c.method, c.url, nil)
		if err != nil {
			t.Fatalf("Error creating request for %s %s: %+v", c.method, c.url, err)
		}
		h := router.getHandler(r)
		res := string(h.(testHandler))
		if res != c.handler {
			t.Errorf("Expected to route \"%s %s\" to %s, routed to %s", c.method, c.url, c.handler, res)
		}

	}
}

var benchRouter Router
var benchTests []string
var benchMethods = [...]string{"GET", "POST", "PUT", "DELETE"}

func init() {
	for i := 0; i < 100; i++ {
		rand.Seed(time.Now().UnixNano())
		depth := rand.Intn(4) + 1
		var route string
		var req string
		for x := 0; x < depth; x++ {
			rand.Seed(time.Now().UnixNano())
			param := rand.Intn(1) == 1
			pieceLength := rand.Intn(24) + 1
			piece := make([]byte, pieceLength)
			rand.Read(piece)
			pieceStr := base64.URLEncoding.EncodeToString(piece)
			req = req + "/" + pieceStr
			if param {
				pieceStr = "{" + pieceStr + "}"
			}
			route = route + "/" + pieceStr
		}
		benchTests = append(benchTests, req)
		endpoint := benchRouter.Endpoint(route)
		rand.Seed(time.Now().UnixNano())
		get := rand.Intn(1) == 1
		rand.Seed(time.Now().UnixNano())
		post := rand.Intn(1) == 1
		catchAll := !get && !post
		var methods []string
		if get {
			methods = append(methods, "GET")
		}
		if post {
			methods = append(methods, "POST")
		}
		if catchAll {
			methods = append(methods, catchAllMethod)
		}
		endpoint.Methods(methods...).Handler(testHandler("benchmark"))
	}
}

func BenchmarkRouting(b *testing.B) {
	for i := 0; i < b.N; i++ {
		b.StopTimer()
		w := httptest.NewRecorder()
		route := benchTests[i%len(benchTests)]
		method := benchMethods[i%len(benchMethods)]
		req, err := http.NewRequest(method, route, nil)
		if err != nil {
			b.Fatalf(err.Error())
		}
		b.StartTimer()
		benchRouter.ServeHTTP(w, req)
	}
}
<|endoftext|>


<commit_before>package rtnetlink

import (
	"net"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/jsimonetti/rtnetlink/internal/unix"
)

// Tests will only pass on little endian machines

func TestRouteMessageMarshalBinary(t *testing.T) {
	skipBigEndian(t)

	timeout := uint32(255)
	tests := []struct {
		name string
		m    Message
		b    []byte
		err  error
	}{
		{
			name: "empty",
			m:    &RouteMessage{},
			b: []byte{
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x00,
			},
		},
		{
			name: "no attributes",
			m: &RouteMessage{
				Family:    unix.AF_INET,
				DstLength: 8,
				Type:      unix.RTN_UNICAST,
			},
			b: []byte{
				0x02, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
				0x00, 0x00, 0x00, 0x00,
			},
		},
		{
			name: "attributes",
			m: &RouteMessage{
				Attributes: RouteAttributes{
					Dst:      net.ParseIP("10.0.0.0"),
					Gateway:  net.ParseIP("10.10.10.10"),
					OutIface: 4,
					Expires:  &timeout,
					Metrics: &RouteMetrics{
						MTU: 1500,
					},
				},
			},
			b: []byte{
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00,
				0x0a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x05, 0x00,
				0x0a, 0x0a, 0x0a, 0x0a, 0x08, 0x00, 0x04, 0x00,
				0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x17, 0x00,
				0xff, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x80,
				0x08, 0x00, 0x02, 0x00, 0xdc, 0x05, 0x00, 0x00,
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			b, err := tt.m.MarshalBinary()

			if want, got := tt.err, err; want != got {
				t.Fatalf("unexpected error:\n- want: %v\n-  got: %v", want, got)
			}
			if err != nil {
				return
			}

			if diff := cmp.Diff(tt.b, b); diff != "" {
				t.Fatalf("unexpected RouteMessage bytes (-want +got):\n%s", diff)
			}
		})
	}
}

func TestRouteMessageUnmarshalBinary(t *testing.T) {
	skipBigEndian(t)

	timeout := uint32(1000)
	tests := []struct {
		name string
		b    []byte
		m    Message
		err  error
	}{
		{
			name: "empty",
			err:  errInvalidRouteMessage,
		},
		{
			name: "short",
			b:    make([]byte, 3),
			err:  errInvalidRouteMessage,
		},
		{
			name: "invalid attr",
			b: []byte{
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00,
				0x05, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x08, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
			},
			err: errInvalidRouteMessageAttr,
		},
		{
			name: "full",
			b: []byte{
				// RouteMessage struct literal
				//
				// Family
				0x02,
				// DstLength
				0x08,
				// SrcLength
				0x00,
				// Tos
				0x00,
				// Table
				0xfe,
				// Protocol
				0x04,
				// Scope
				0x00,
				// Type
				0x01,
				// Flags
				0x00, 0x00, 0x00, 0x00,
				// RouteAttributes
				// 2 bytes length, 2 bytes type, N bytes value
				//
				// Dst
				0x08, 0x00, 0x01, 0x00,
				0x0a, 0x00, 0x00, 0x00,
				// Src
				0x08, 0x00, 0x07, 0x00,
				0x0a, 0x64, 0x0a, 0x01,
				// Gateway
				0x08, 0x00, 0x05, 0x00,
				0x0a, 0x00, 0x00, 0x01,
				// OutIface
				0x08, 0x00, 0x04, 0x00,
				0x05, 0x00, 0x00, 0x00,
				// Priority
				0x08, 0x00, 0x06, 0x00,
				0x01, 0x00, 0x00, 0x00,
				// Table
				0x08, 0x00, 0x0f, 0x00,
				0x02, 0x00, 0x00, 0x00,
				// Mark
				0x08, 0x00, 0x10, 0x00,
				0x03, 0x00, 0x00, 0x00,
				// Expires
				0x08, 0x00, 0x17, 0x00,
				0xe8, 0x03, 0x00, 0x00,
				// RouteMetrics
				// Length must be manually adjusted as more fields are added.
				0x24, 0x00, 0x08, 0x80,
				// AdvMSS
				0x08, 0x00, 0x08, 0x00,
				0x01, 0x00, 0x00, 0x00,
				// Features
				0x08, 0x00, 0x0c, 0x00,
				0xff, 0xff, 0xff, 0xff,
				// InitCwnd
				0x08, 0x00, 0x0b, 0x00,
				0x02, 0x00, 0x00, 0x00,
				// MTU
				0x08, 0x00, 0x02, 0x00,
				0xdc, 0x05, 0x00, 0x00,
			},
			m: &RouteMessage{
				Family:    2,
				DstLength: 8,
				Table:     unix.RT_TABLE_MAIN,
				Protocol:  unix.RTPROT_STATIC,
				Scope:     unix.RT_SCOPE_UNIVERSE,
				Type:      unix.RTN_UNICAST,
				Attributes: RouteAttributes{
					Dst:      net.IPv4(10, 0, 0, 0),
					Src:      net.IPv4(10, 100, 10, 1),
					Gateway:  net.IPv4(10, 0, 0, 1),
					OutIface: 5,
					Priority: 1,
					Table:    2,
					Mark:     3,
					Expires:  &timeout,
					Metrics: &RouteMetrics{
						AdvMSS:   1,
						Features: 0xffffffff,
						InitCwnd: 2,
						MTU:      1500,
					},
				},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			m := &RouteMessage{}
			err := (m).UnmarshalBinary(tt.b)

			if want, got := tt.err, err; want != got {
				t.Fatalf("unexpected error:\n- want: %v\n-  got: %v", want, got)
			}
			if err != nil {
				return
			}

			if diff := cmp.Diff(tt.m, m); diff != "" {
				t.Fatalf("unexpected RouteMessage (-want +got):\n%s", diff)
			}
		})
	}
}
<commit_msg>rtnetlink: perform marshaling round-trip in RouteMessage tests (#84)<commit_after>package rtnetlink

import (
	"net"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/jsimonetti/rtnetlink/internal/unix"
)

// Tests will only pass on little endian machines

func TestRouteMessageMarshalUnmarshalBinary(t *testing.T) {
	skipBigEndian(t)

	timeout := uint32(255)
	tests := []struct {
		name string
		m    *RouteMessage
		b    []byte
	}{
		{
			name: "empty",
			m:    &RouteMessage{},
			b: []byte{
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x00,
			},
		},
		{
			name: "no attributes",
			m: &RouteMessage{
				Family:    unix.AF_INET,
				DstLength: 8,
				Type:      unix.RTN_UNICAST,
			},
			b: []byte{
				0x02, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
				0x00, 0x00, 0x00, 0x00,
			},
		},
		{
			name: "full",
			m: &RouteMessage{
				Family:    2,
				DstLength: 8,
				Table:     unix.RT_TABLE_MAIN,
				Protocol:  unix.RTPROT_STATIC,
				Scope:     unix.RT_SCOPE_UNIVERSE,
				Type:      unix.RTN_UNICAST,
				Attributes: RouteAttributes{
					Dst:      net.IPv4(10, 0, 0, 0),
					Src:      net.IPv4(10, 100, 10, 1),
					Gateway:  net.IPv4(10, 0, 0, 1),
					OutIface: 5,
					Priority: 1,
					Table:    2,
					Mark:     3,
					Expires:  &timeout,
					Metrics: &RouteMetrics{
						AdvMSS:   1,
						Features: 0xffffffff,
						InitCwnd: 2,
						MTU:      1500,
					},
				},
			},
			b: []byte{
				// RouteMessage struct literal
				//
				// Family
				0x02,
				// DstLength
				0x08,
				// SrcLength
				0x00,
				// Tos
				0x00,
				// Table
				0xfe,
				// Protocol
				0x04,
				// Scope
				0x00,
				// Type
				0x01,
				// Flags
				0x00, 0x00, 0x00, 0x00,
				// RouteAttributes
				// 2 bytes length, 2 bytes type, N bytes value
				//
				// Dst
				0x08, 0x00, 0x01, 0x00,
				0x0a, 0x00, 0x00, 0x00,
				// Src
				0x08, 0x00, 0x07, 0x00,
				0x0a, 0x64, 0x0a, 0x01,
				// Gateway
				0x08, 0x00, 0x05, 0x00,
				0x0a, 0x00, 0x00, 0x01,
				// OutIface
				0x08, 0x00, 0x04, 0x00,
				0x05, 0x00, 0x00, 0x00,
				// Priority
				0x08, 0x00, 0x06, 0x00,
				0x01, 0x00, 0x00, 0x00,
				// Table
				0x08, 0x00, 0x0f, 0x00,
				0x02, 0x00, 0x00, 0x00,
				// Mark
				0x08, 0x00, 0x10, 0x00,
				0x03, 0x00, 0x00, 0x00,
				// Expires
				0x08, 0x00, 0x17, 0x00,
				0xff, 0x00, 0x00, 0x00,
				// RouteMetrics
				// Length must be manually adjusted as more fields are added.
				0x24, 0x00, 0x08, 0x80,
				// AdvMSS
				0x08, 0x00, 0x08, 0x00,
				0x01, 0x00, 0x00, 0x00,
				// Features
				0x08, 0x00, 0x0c, 0x00,
				0xff, 0xff, 0xff, 0xff,
				// InitCwnd
				0x08, 0x00, 0x0b, 0x00,
				0x02, 0x00, 0x00, 0x00,
				// MTU
				0x08, 0x00, 0x02, 0x00,
				0xdc, 0x05, 0x00, 0x00,
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// It's important to be able to parse raw bytes into valid
			// structures so we start with that step first. After, we'll do a
			// marshaling round-trip to ensure that the structure's byte output
			// and parsed form match what is expected, while also comparing
			// against the expected fixtures throughout.
			var m1 RouteMessage
			if err := m1.UnmarshalBinary(tt.b); err != nil {
				t.Fatalf("failed to unmarshal first message from binary: %v", err)
			}

			if diff := cmp.Diff(tt.m, &m1); diff != "" {
				t.Fatalf("unexpected first message (-want +got):\n%s", diff)
			}

			b, err := m1.MarshalBinary()
			if err != nil {
				t.Fatalf("failed to marshal first message binary: %v", err)
			}

			if diff := cmp.Diff(tt.b, b); diff != "" {
				t.Fatalf("unexpected first message bytes (-want +got):\n%s", diff)
			}

			var m2 RouteMessage
			if err := m2.UnmarshalBinary(b); err != nil {
				t.Fatalf("failed to unmarshal second message from binary: %v", err)
			}

			if diff := cmp.Diff(&m1, &m2); diff != "" {
				t.Fatalf("unexpected parsed messages (-want +got):\n%s", diff)
			}
		})
	}
}

func TestRouteMessageUnmarshalBinaryErrors(t *testing.T) {
	skipBigEndian(t)

	tests := []struct {
		name string
		b    []byte
		m    Message
		err  error
	}{
		{
			name: "empty",
			err:  errInvalidRouteMessage,
		},
		{
			name: "short",
			b:    make([]byte, 3),
			err:  errInvalidRouteMessage,
		},
		{
			name: "invalid attr",
			b: []byte{
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00,
				0x05, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x08, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
			},
			err: errInvalidRouteMessageAttr,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var m RouteMessage
			err := m.UnmarshalBinary(tt.b)

			if diff := cmp.Diff(tt.err, err, cmp.Comparer(compareErrors)); diff != "" {
				t.Fatalf("unexpected error (-want +got):\n%s", diff)
			}
		})
	}
}

func compareErrors(x, y error) bool {
	// This is lazy but should be sufficient for the typical stringified errors
	// returned by this package.
	return x.Error() == y.Error()
}
<|endoftext|>


<commit_before>package bourbon

import (
	"github.com/bmizerany/assert"
	"testing"
)

func TestRouteCreateMatcher(t *testing.T) {
	r := &route{parent: new(bourbon), pattern: "/test"}
	assert.Equal(t, true, r.regexp == nil)
	assert.Equal(t, true, r.Regexp().MatchString("/test"))
	assert.Equal(t, true, r.regexp != nil)
}

func TestRouteCreateMatcherWithVariables(t *testing.T) {
	r := &route{parent: new(bourbon), pattern: "/test/{id}"}
	assert.Equal(t, true, r.regexp == nil)
	assert.Equal(t, true, r.Regexp().MatchString("/test/1"))
	assert.Equal(t, true, r.Regexp().MatchString("/test/fe1ca51d-f765-4446-a681-77bd02ad07ca"))
	assert.Equal(t, false, r.Regexp().MatchString("/test/2/failed"))
	assert.Equal(t, true, r.regexp != nil)
}

func TestRouteCreateMatcherWithNestedVariables(t *testing.T) {
	r := &route{parent: new(bourbon), pattern: "/one/{one_id}/two/{two_id}"}
	assert.Equal(t, false, r.Regexp().MatchString("/one/1"))
	assert.Equal(t, false, r.Regexp().MatchString("/one/1/two"))
	assert.Equal(t, false, r.Regexp().MatchString("/one/1/two/2/three"))
	assert.Equal(t, true, r.Regexp().MatchString("/one/1/two/2"))
}
<commit_msg>test route Pattern and Middleware functions<commit_after>package bourbon

import (
	"github.com/bmizerany/assert"
	"testing"
)

func TestRouteCreateMatcher(t *testing.T) {
	r := &route{parent: new(bourbon), pattern: "/test"}
	assert.Equal(t, true, r.regexp == nil)
	assert.Equal(t, true, r.Regexp().MatchString("/test"))
	assert.Equal(t, true, r.regexp != nil)
}

func TestRouteCreateMatcherWithVariables(t *testing.T) {
	r := &route{parent: new(bourbon), pattern: "/test/{id}"}
	assert.Equal(t, true, r.regexp == nil)
	assert.Equal(t, true, r.Regexp().MatchString("/test/1"))
	assert.Equal(t, true, r.Regexp().MatchString("/test/fe1ca51d-f765-4446-a681-77bd02ad07ca"))
	assert.Equal(t, false, r.Regexp().MatchString("/test/2/failed"))
	assert.Equal(t, true, r.regexp != nil)
}

func TestRouteCreateMatcherWithNestedVariables(t *testing.T) {
	r := &route{parent: new(bourbon), pattern: "/one/{one_id}/two/{two_id}"}
	assert.Equal(t, false, r.Regexp().MatchString("/one/1"))
	assert.Equal(t, false, r.Regexp().MatchString("/one/1/two"))
	assert.Equal(t, false, r.Regexp().MatchString("/one/1/two/2/three"))
	assert.Equal(t, true, r.Regexp().MatchString("/one/1/two/2"))
}

func TestRoutePatternWithParents(t *testing.T) {
	parent := new(bourbon)
	parent.SetPrefix("/parent")
	parent.Get("/test", func() {})
	assert.Equal(t, 1, len(parent.Routes()))
	assert.Equal(t, "/parent/test", parent.Routes()[0].Pattern())

	first := new(bourbon)
	first.SetPrefix("/first")
	first.Get("/test", func() {})
	assert.Equal(t, 1, len(first.Routes()))
	assert.Equal(t, "/first/test", first.Routes()[0].Pattern())

	last := new(bourbon)
	last.SetPrefix("/last")
	last.Get("/test", func() {})
	assert.Equal(t, 1, len(last.Routes()))
	assert.Equal(t, "/last/test", last.Routes()[0].Pattern())

	parent.Mount(first)
	first.Mount(last)

	assert.Equal(t, "/parent/test", parent.Routes()[0].Pattern())
	assert.Equal(t, "/parent/first/test", first.Routes()[0].Pattern())
	assert.Equal(t, "/parent/first/last/test", last.Routes()[0].Pattern())
}

func TestRouteMiddlewareWithParents(t *testing.T) {
	parent := new(bourbon)
	parent.Get("/", func(){})
	parent.Use(func() {})

	first := new(bourbon)
	first.Use(func() {})

	last := new(bourbon)
	last.Use(func() {})
	last.Get("/last", func() {})

	parent.Mount(first)
	first.Mount(last)

	r1 := parent.Routes()[0]
	r2 := last.Routes()[0]

	assert.Equal(t, 1, len(r1.Middleware()))
	assert.Equal(t, 3, len(r2.Middleware()))
}
<|endoftext|>