<commit_before>// Copyright 2016 Google Inc.
//
// 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 view

import (
	"html/template"
)

const (
	css = `
	body {
		font-family: "Go","San Francisco","Helvetica Neue",Helvetica,sans-serif;
		float: none;
		max-width: 800px;
		margin: 20 auto 0;
	}
	form {
		float: none;
		max-width: 800px;
		margin: 0 auto;
	}
	div.formfield {
		margin-top: 12px;
		margin-bottom: 12px;
	}
	label {
		float: left;
		text-align: right;
		margin-right: 15px;
		width: 50%;
	}
	input {
		font-family: "Go Mono","Fira Code",sans-serif;
		font-size: 12pt;
	}
	select {
		font-family: "Go Mono","Fira Code",sans-serif;
		font-size: 12pt;
	}
	textarea {
		font-family: "Go Mono","Fira Code",sans-serif;
		font-size: 12pt;
	}
	div svg {
		display: block;
		margin: 0 auto;
	}
	div.hcentre {
		text-align: center;
	}
	table.browse {
		font-family: "Go Mono","Fira Code",sans-serif;
		font-size: 12pt;
	}
	`

	channelEditorTemplateSrc = `
	<title>{{with .Channel}}{{.Name}}{{else}}[New]{{end}}</title>

{{with .Channel}}{{.Name}}{{else}}[New]{{end}}

	<form method="post">

Name<input type="text" name="Name" required pattern="^[_a-zA-Z][_a-zA-Z0-9]*$" title="Must start with a letter or underscore, and only contain letters, digits, or underscores." value="{{with .Channel}}{{.Name}}{{end}}">

Type<input type="text" name="Type" required value="{{with .Channel}}{{.Type}}{{end}}">

Capacity<input type="text" name="Cap" required pattern="^[0-9]+$" title="Must be a whole number, at least 0." value="{{with .Channel}}{{.Cap}}{{end}}">

<input type="submit" value="Save"> <input type="button" value="Return" onclick="window.location.href='?'">

	</form>
`

	nodeEditorTemplateSrc = `
	<title>{{with .Node}}{{.Name}}{{else}}[New]{{end}}</title>

{{with .Node}}{{.Name}}{{else}}[New]{{end}}

	<form method="post">

Name<input name="Name" type="text" required value="{{with .Node}}{{.Name}}{{end}}">

Multiplicity<input name="Multiplicity" type="text" required pattern="^[1-9][0-9]*$" title="Must be a whole number, at least 1." value="{{with .Node}}{{.Multiplicity}}{{end}}">

Wait for this to finish<input name="Wait" type="checkbox" {{with .Node}}{{if .Wait}}checked{{end}}{{end}}>

<textarea name="Code" rows="25" cols="80">{{with .Node}}{{.Impl}}{{end}}</textarea>

<input type="submit" value="Save"> <input type="button" value="Return" onclick="window.location.href='?'">

	</form>
`

	browseTemplateSrc = `
	<title>SHENZHEN GO</title>

SHENZHEN GO

{{$.Base}}

Up | New

{{range $.Entries}}

{{if .IsDir}}<dir>{{end}}	{{.Name}}
{{end}}

`

	graphEditorTemplateSrc = `
	<title>{{$.Graph.Name}}</title>

{{$.Graph.Name}}

Save | Run | New: Goroutine Channel | View as: Go Dot JSON

{{$.Diagram}}

`
)

var (
	browseTemplate        = template.Must(template.New("browse").Parse(browseTemplateSrc))
	graphEditorTemplate   = template.Must(template.New("graphEditor").Parse(graphEditorTemplateSrc))
	nodeEditorTemplate    = template.Must(template.New("nodeEditor").Parse(nodeEditorTemplateSrc))
	channelEditorTemplate = template.Must(template.New("channelEditor").Parse(channelEditorTemplateSrc))
)
<commit_msg>Tweaks<commit_after>// Copyright 2016 Google Inc.
//
// 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 view

import (
	"html/template"
)

const (
	css = `
	body {
		font-family: "Go","San Francisco","Helvetica Neue",Helvetica,sans-serif;
		float: none;
		max-width: 800px;
		margin: 20 auto 0;
	}
	form {
		float: none;
		max-width: 800px;
		margin: 0 auto;
	}
	div.formfield {
		margin-top: 12px;
		margin-bottom: 12px;
	}
	label {
		float: left;
		text-align: right;
		margin-right: 15px;
		width: 50%;
	}
	input {
		font-family: "Go Mono","Fira Code",sans-serif;
		font-size: 12pt;
	}
	select {
		font-family: "Go Mono","Fira Code",sans-serif;
		font-size: 12pt;
	}
	textarea {
		font-family: "Go Mono","Fira Code",sans-serif;
		font-size: 12pt;
	}
	div svg {
		display: block;
		margin: 0 auto;
	}
	div.hcentre {
		text-align: center;
	}
	table.browse {
		font-family: "Go Mono","Fira Code",sans-serif;
		font-size: 12pt;
		margin-top: 16pt;
	}
	`

	channelEditorTemplateSrc = `
	<title>{{with .Channel}}{{.Name}}{{else}}[New]{{end}}</title>

{{with .Channel}}{{.Name}}{{else}}[New]{{end}}

	<form method="post">

Name<input type="text" name="Name" required pattern="^[_a-zA-Z][_a-zA-Z0-9]*$" title="Must start with a letter or underscore, and only contain letters, digits, or underscores." value="{{with .Channel}}{{.Name}}{{end}}">

Type<input type="text" name="Type" required value="{{with .Channel}}{{.Type}}{{end}}">

Capacity<input type="text" name="Cap" required pattern="^[0-9]+$" title="Must be a whole number, at least 0." value="{{with .Channel}}{{.Cap}}{{end}}">

<input type="submit" value="Save"> <input type="button" value="Return" onclick="window.location.href='?'">

	</form>
`

	nodeEditorTemplateSrc = `
	<title>{{with .Node}}{{.Name}}{{else}}[New]{{end}}</title>

{{with .Node}}{{.Name}}{{else}}[New]{{end}}

	<form method="post">

Name<input name="Name" type="text" required value="{{with .Node}}{{.Name}}{{end}}">

Multiplicity<input name="Multiplicity" type="text" required pattern="^[1-9][0-9]*$" title="Must be a whole number, at least 1." value="{{with .Node}}{{.Multiplicity}}{{end}}">

Wait for this to finish<input name="Wait" type="checkbox" {{with .Node}}{{if .Wait}}checked{{end}}{{end}}>

<textarea name="Code" rows="25" cols="80">{{with .Node}}{{.Impl}}{{end}}</textarea>

<input type="submit" value="Save"> <input type="button" value="Return" onclick="window.location.href='?'">

	</form>
`

	browseTemplateSrc = `
	<title>SHENZHEN GO</title>

SHENZHEN GO

{{$.Base}}

Up | New

{{range $.Entries}}

{{if .IsDir}}<dir>{{end}}	{{.Name}}
{{end}}

`

	graphEditorTemplateSrc = `
	<title>{{$.Graph.Name}}</title>

{{$.Graph.Name}}

Save | Run | New: Goroutine Channel | View as: Go Dot JSON

{{$.Diagram}}

`
)

var (
	browseTemplate        = template.Must(template.New("browse").Parse(browseTemplateSrc))
	graphEditorTemplate   = template.Must(template.New("graphEditor").Parse(graphEditorTemplateSrc))
	nodeEditorTemplate    = template.Must(template.New("nodeEditor").Parse(nodeEditorTemplateSrc))
	channelEditorTemplate = template.Must(template.New("channelEditor").Parse(channelEditorTemplateSrc))
)
<|endoftext|>


<commit_before>// file.go
//
// Copyright (c) 2016, Ayke van Laethem
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Package file implements the file tree interface (tree.Entry) for local
// filesystems.
package file

// TODO: use the Linux system calls openat(), readdirat() etc.

import (
	"io"
	"io/ioutil"
	"os"
	"path"
	"time"

	"github.com/aykevl/dtsync/tree"
)

// A prefix and suffix for files that are being copied.
const (
	TEMPPREFIX = ".usync-"
	TEMPSUFFIX = ".tmp"
)

// filesystem encapsulates the root path, so every Entry can know the root path.
type filesystem struct {
	path string
}

// Entry is one file or directory in the filesystem. It additionally contains
// it's name, parent, root, and stat() result.
type Entry struct {
	name   string
	root   *filesystem
	parent *Entry
	st     os.FileInfo
}

// NewRoot wraps a root directory in an Entry.
func NewRoot(rootPath string) (*Entry, error) {
	rootPath = path.Clean(rootPath)
	st, err := os.Stat(rootPath)
	if err != nil {
		return nil, err
	}
	return &Entry{
		root: &filesystem{
			path: rootPath,
		},
		st: st,
	}, nil
}

// NewTestRoot returns a new root in a temporary directory. It should be removed
// after use using root.Remove()
func NewTestRoot() (*Entry, error) {
	rootPath1, err := ioutil.TempDir("", "usync-test-")
	if err != nil {
		return nil, err
	}
	return NewRoot(rootPath1)
}

// String returns a string representation of this file, for debugging.
func (e *Entry) String() string {
	return "file.Entry(" + e.path() + ")"
}

// pathElements returns a list of path elements to be joined by path.Join.
func (e *Entry) pathElements() []string {
	if e.parent == nil {
		parts := make([]string, 1, 2)
		parts[0] = e.root.path
		return parts
	} else {
		return append(e.parent.pathElements(), e.name)
	}
}

// path returns the full path for this entry.
func (e *Entry) path() string {
	return path.Join(e.pathElements()...)
}

// AddRegular implements tree.TestEntry by adding a single file with the given
// name and contents.
func (e *Entry) AddRegular(name string, contents []byte) (tree.FileEntry, error) {
	if !tree.ValidName(name) {
		return nil, tree.ErrInvalidName
	}

	child := &Entry{
		name:   name,
		parent: e,
		root:   e.root,
	}
	file, err := os.Create(child.path())
	if err != nil {
		return nil, err
	}
	defer file.Close()
	_, err = file.Write(contents)
	if err != nil {
		// "Write must return a non-nil error if it returns n < len(p)."
		return nil, err
	}
	err = file.Sync()
	if err != nil {
		return nil, err
	}
	child.st, err = file.Stat()
	if err != nil {
		return nil, err
	}
	return child, nil
}

// Type returns the file type (regular, directory, or unknown). More types may
// be added in the future.
func (e *Entry) Type() tree.Type {
	switch e.st.Mode() & os.ModeType {
	case 0:
		return tree.TYPE_REGULAR
	case os.ModeDir:
		return tree.TYPE_DIRECTORY
	default:
		return tree.TYPE_UNKNOWN
	}
}

// CreateDir adds a single child directory to this directory.
func (e *Entry) CreateDir(name string) (tree.Entry, error) {
	if !tree.ValidName(name) {
		return nil, tree.ErrInvalidName
	}
	child := &Entry{
		name:   name,
		parent: e,
		root:   e.root,
	}
	err := os.Mkdir(child.path(), 0777)
	if err != nil {
		return nil, err
	}
	child.st, err = os.Stat(child.path())
	if err != nil {
		return nil, err
	}
	return child, nil
}

// GetContents returns an io.ReadCloser (that must be closed) with the contents
// of this entry.
func (e *Entry) GetContents() (io.ReadCloser, error) {
	return os.Open(e.path())
}

// GetFile returns an io.ReadCloser with the named file. The file must be closed
// after use.
func (e *Entry) GetFile(name string) (io.ReadCloser, error) {
	fp, err := os.Open(path.Join(e.path(), name))
	if err != nil {
		if os.IsNotExist(err) {
			return nil, tree.ErrNotFound
		} else {
			return nil, err
		}
	}
	return fp, nil
}

// List returns a directory listing, sorted by name.
func (e *Entry) List() ([]tree.Entry, error) {
	list, err := ioutil.ReadDir(e.path())
	if err != nil {
		return nil, err
	}
	listEntries := make([]tree.Entry, len(list))
	for i, st := range list {
		listEntries[i] = &Entry{
			st:     st,
			name:   st.Name(),
			parent: e,
			root:   e.root,
		}
	}
	return listEntries, nil
}

// ModTime returns the modification time from the (cached) stat() call.
func (e *Entry) ModTime() time.Time {
	return e.st.ModTime()
}

// Fingerprint returns a fingerprint calculated from the file's metadata.
func (e *Entry) Fingerprint() string {
	return tree.Fingerprint(e)
}

// Hash returns the blake2b hash of this file.
func (e *Entry) Hash() ([]byte, error) {
	hash := tree.NewHash()
	file, err := e.GetContents()
	if err != nil {
		return nil, err
	}
	_, err = io.Copy(hash, file)
	if err != nil {
		return nil, err
	}
	return hash.Sum(nil), nil
}

// Name returns the filename.
func (e *Entry) Name() string {
	return e.name
}

// Remove removes this entry, recursively.
func (e *Entry) Remove() error {
	if e.Type() == tree.TYPE_DIRECTORY && e.parent != nil {
		// move to temporary location to provide atomicity in removing a
		// directory tree
		oldPath := e.path()
		tmpName := TEMPPREFIX + e.name + TEMPSUFFIX
		tmpPath := path.Join(e.parent.path(), tmpName)
		err := os.Rename(oldPath, tmpPath)
		if err != nil {
			return err
		}
		e.name = tmpName
	}
	return e.removeSelf()
}

func (e *Entry) removeSelf() error {
	if e.Type() == tree.TYPE_DIRECTORY {
		// remove children first
		list, err := e.List()
		if err != nil {
			return err
		}
		for _, child := range list {
			err = child.(*Entry).removeSelf()
			if err != nil {
				return err
			}
		}
	}

	// Actually remove the file or (empty) directory
	err := os.Remove(e.path())
	if err != nil {
		return err
	}

	// Update parent stat result
	if e.parent != nil {
		st, err := os.Stat(e.parent.path())
		if err != nil {
			return err
		}
		e.parent.st = st
	}
	return nil
}

func (e *Entry) SetFile(name string) (io.WriteCloser, error) {
	_, out, err := e.CreateFile(name, time.Time{})
	return out, err
}

// Size returns the filesize for regular files. For other file types, the result
// is undefined.
func (e *Entry) Size() int64 {
	return e.st.Size()
}

// CreateFile creates the child, implementing tree.FileEntry. This function is useful for CopyTo.
func (e *Entry) CreateFile(name string, modTime time.Time) (tree.Entry, io.WriteCloser, error) {
	child := &Entry{
		name:   name,
		parent: e,
		root:   e.root,
	}

	writer, err := child.replaceFile(modTime)
	return child, writer, err
}

// UpdateFile replaces itself, to implement tree.FileEntry. This function is
// useful for UpdateOver.
func (e *Entry) UpdateFile(modTime time.Time) (io.WriteCloser, error) {
	if e.Type() != tree.TYPE_REGULAR {
		return nil, tree.ErrNoRegular
	}
	return e.replaceFile(modTime)
}

// replaceFile replaces the current file without checking for a type. Used by
// CreateFile and UpdateFile.
func (e *Entry) replaceFile(modTime time.Time) (io.WriteCloser, error) {
	tempPath := path.Join(e.parent.path(), TEMPPREFIX+e.name+TEMPSUFFIX)
	fp, err := os.Create(tempPath)
	if err != nil {
		return nil, err
	}
	return &fileWriter{
		fp: fp,
		closeCall: func() error {
			if !modTime.IsZero() {
				err = os.Chtimes(tempPath, modTime, modTime)
				if err != nil {
					return err
				}
			}
			err = os.Rename(tempPath, e.path())
			if err != nil {
				return err
			}
			e.st, err = os.Stat(e.path())

			// Update parent stat result
			st, err := os.Stat(e.parent.path())
			if err != nil {
				return err
			}
			e.parent.st = st

			return err
		},
	}, nil
}

// UpdateOver replaces this file with the contents and modtime of the other
// file.
func (e *Entry) UpdateOver(other tree.Entry) ([]byte, error) {
	file, ok := other.(tree.FileEntry)
	if !ok {
		return nil, tree.ErrNotImplemented
	}

	switch e.Type() {
	case tree.TYPE_REGULAR:
		out, err := file.UpdateFile(e.ModTime())
		if err != nil {
			return nil, err
		}

		in, err := os.Open(e.path())

		hasher := tree.NewHash()
		hashReader := io.TeeReader(in, hasher)

		_, err = io.Copy(out, hashReader)
		if err != nil {
			return nil, err
		}

		return hasher.Sum(nil), out.Close()

	default:
		return nil, tree.ErrNotImplemented
	}
}

// SetContents writes contents to this file, for testing.
func (e *Entry) SetContents(contents []byte) error {
	fp, err := os.Create(e.path())
	if err != nil {
		return err
	}
	defer fp.Close()
	_, err = fp.Write(contents)
	if err != nil {
		return err
	}
	now := time.Now()
	err = os.Chtimes(e.path(), now, now)
	if err != nil {
		// could not update
		return err
	}
	e.st, err = fp.Stat()
	if err != nil {
		return err
	}
	return fp.Sync()
}

// CopyTo copies this file into the otherParent. The latter must be a directory.
// Only implemented for regular files, not directories.
func (e *Entry) CopyTo(otherParent tree.Entry) (tree.Entry, []byte, error) {
	file, ok := otherParent.(tree.FileEntry)
	if !ok {
		return nil, nil, tree.ErrNotImplemented
	}

	switch e.Type() {
	case tree.TYPE_REGULAR:
		other, out, err := file.CreateFile(e.name, e.ModTime())
		if err != nil {
			return nil, nil, err
		}
		in, err := os.Open(e.path())
		if err != nil {
			return nil, nil, err
		}
		hasher := tree.NewHash()
		hashReader := io.TeeReader(in, hasher)
		_, err = io.Copy(out, hashReader)
		if err != nil {
			return nil, nil, err
		}
		// out.Close() does an fsync and rename
		err = out.Close()
		if err != nil {
			return nil, nil, err
		}
		return other, hasher.Sum(nil), nil

	default:
		return nil, nil, tree.ErrNotImplemented
	}
}
<commit_msg>tree/file: Use filepath instead of path (don't depend on Unix)<commit_after>// file.go
//
// Copyright (c) 2016, Ayke van Laethem
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Package file implements the file tree interface (tree.Entry) for local
// filesystems.
package file

// TODO: use the Linux system calls openat(), readdirat() etc.

import (
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"time"

	"github.com/aykevl/dtsync/tree"
)

// A prefix and suffix for files that are being copied.
const (
	TEMPPREFIX = ".usync-"
	TEMPSUFFIX = ".tmp"
)

// filesystem encapsulates the root path, so every Entry can know the root path.
type filesystem struct {
	path string
}

// Entry is one file or directory in the filesystem. It additionally contains
// it's name, parent, root, and stat() result.
type Entry struct {
	name   string
	root   *filesystem
	parent *Entry
	st     os.FileInfo
}

// NewRoot wraps a root directory in an Entry.
func NewRoot(rootPath string) (*Entry, error) {
	rootPath = filepath.Clean(rootPath)
	st, err := os.Stat(rootPath)
	if err != nil {
		return nil, err
	}
	return &Entry{
		root: &filesystem{
			path: rootPath,
		},
		st: st,
	}, nil
}

// NewTestRoot returns a new root in a temporary directory. It should be removed
// after use using root.Remove()
func NewTestRoot() (*Entry, error) {
	rootPath1, err := ioutil.TempDir("", "usync-test-")
	if err != nil {
		return nil, err
	}
	return NewRoot(rootPath1)
}

// String returns a string representation of this file, for debugging.
func (e *Entry) String() string {
	return "file.Entry(" + e.path() + ")"
}

// pathElements returns a list of path elements to be joined by filepath.Join.
func (e *Entry) pathElements() []string {
	if e.parent == nil {
		parts := make([]string, 1, 2)
		parts[0] = e.root.path
		return parts
	} else {
		return append(e.parent.pathElements(), e.name)
	}
}

// path returns the full path for this entry.
func (e *Entry) path() string {
	return filepath.Join(e.pathElements()...)
}

// AddRegular implements tree.TestEntry by adding a single file with the given
// name and contents.
func (e *Entry) AddRegular(name string, contents []byte) (tree.FileEntry, error) {
	if !tree.ValidName(name) {
		return nil, tree.ErrInvalidName
	}

	child := &Entry{
		name:   name,
		parent: e,
		root:   e.root,
	}
	file, err := os.Create(child.path())
	if err != nil {
		return nil, err
	}
	defer file.Close()
	_, err = file.Write(contents)
	if err != nil {
		// "Write must return a non-nil error if it returns n < len(p)."
		return nil, err
	}
	err = file.Sync()
	if err != nil {
		return nil, err
	}
	child.st, err = file.Stat()
	if err != nil {
		return nil, err
	}
	return child, nil
}

// Type returns the file type (regular, directory, or unknown). More types may
// be added in the future.
func (e *Entry) Type() tree.Type {
	switch e.st.Mode() & os.ModeType {
	case 0:
		return tree.TYPE_REGULAR
	case os.ModeDir:
		return tree.TYPE_DIRECTORY
	default:
		return tree.TYPE_UNKNOWN
	}
}

// CreateDir adds a single child directory to this directory.
func (e *Entry) CreateDir(name string) (tree.Entry, error) {
	if !tree.ValidName(name) {
		return nil, tree.ErrInvalidName
	}
	child := &Entry{
		name:   name,
		parent: e,
		root:   e.root,
	}
	err := os.Mkdir(child.path(), 0777)
	if err != nil {
		return nil, err
	}
	child.st, err = os.Stat(child.path())
	if err != nil {
		return nil, err
	}
	return child, nil
}

// GetContents returns an io.ReadCloser (that must be closed) with the contents
// of this entry.
func (e *Entry) GetContents() (io.ReadCloser, error) {
	return os.Open(e.path())
}

// GetFile returns an io.ReadCloser with the named file. The file must be closed
// after use.
func (e *Entry) GetFile(name string) (io.ReadCloser, error) {
	fp, err := os.Open(filepath.Join(e.path(), name))
	if err != nil {
		if os.IsNotExist(err) {
			return nil, tree.ErrNotFound
		} else {
			return nil, err
		}
	}
	return fp, nil
}

// List returns a directory listing, sorted by name.
func (e *Entry) List() ([]tree.Entry, error) {
	list, err := ioutil.ReadDir(e.path())
	if err != nil {
		return nil, err
	}
	listEntries := make([]tree.Entry, len(list))
	for i, st := range list {
		listEntries[i] = &Entry{
			st:     st,
			name:   st.Name(),
			parent: e,
			root:   e.root,
		}
	}
	return listEntries, nil
}

// ModTime returns the modification time from the (cached) stat() call.
func (e *Entry) ModTime() time.Time {
	return e.st.ModTime()
}

// Fingerprint returns a fingerprint calculated from the file's metadata.
func (e *Entry) Fingerprint() string {
	return tree.Fingerprint(e)
}

// Hash returns the blake2b hash of this file.
func (e *Entry) Hash() ([]byte, error) {
	hash := tree.NewHash()
	file, err := e.GetContents()
	if err != nil {
		return nil, err
	}
	_, err = io.Copy(hash, file)
	if err != nil {
		return nil, err
	}
	return hash.Sum(nil), nil
}

// Name returns the filename.
func (e *Entry) Name() string {
	return e.name
}

// Remove removes this entry, recursively.
func (e *Entry) Remove() error {
	if e.Type() == tree.TYPE_DIRECTORY && e.parent != nil {
		// move to temporary location to provide atomicity in removing a
		// directory tree
		oldPath := e.path()
		tmpName := TEMPPREFIX + e.name + TEMPSUFFIX
		tmpPath := filepath.Join(e.parent.path(), tmpName)
		err := os.Rename(oldPath, tmpPath)
		if err != nil {
			return err
		}
		e.name = tmpName
	}
	return e.removeSelf()
}

func (e *Entry) removeSelf() error {
	if e.Type() == tree.TYPE_DIRECTORY {
		// remove children first
		list, err := e.List()
		if err != nil {
			return err
		}
		for _, child := range list {
			err = child.(*Entry).removeSelf()
			if err != nil {
				return err
			}
		}
	}

	// Actually remove the file or (empty) directory
	err := os.Remove(e.path())
	if err != nil {
		return err
	}

	// Update parent stat result
	if e.parent != nil {
		st, err := os.Stat(e.parent.path())
		if err != nil {
			return err
		}
		e.parent.st = st
	}
	return nil
}

func (e *Entry) SetFile(name string) (io.WriteCloser, error) {
	_, out, err := e.CreateFile(name, time.Time{})
	return out, err
}

// Size returns the filesize for regular files. For other file types, the result
// is undefined.
func (e *Entry) Size() int64 {
	return e.st.Size()
}

// CreateFile creates the child, implementing tree.FileEntry. This function is useful for CopyTo.
func (e *Entry) CreateFile(name string, modTime time.Time) (tree.Entry, io.WriteCloser, error) {
	child := &Entry{
		name:   name,
		parent: e,
		root:   e.root,
	}

	writer, err := child.replaceFile(modTime)
	return child, writer, err
}

// UpdateFile replaces itself, to implement tree.FileEntry. This function is
// useful for UpdateOver.
func (e *Entry) UpdateFile(modTime time.Time) (io.WriteCloser, error) {
	if e.Type() != tree.TYPE_REGULAR {
		return nil, tree.ErrNoRegular
	}
	return e.replaceFile(modTime)
}

// replaceFile replaces the current file without checking for a type. Used by
// CreateFile and UpdateFile.
func (e *Entry) replaceFile(modTime time.Time) (io.WriteCloser, error) {
	tempPath := filepath.Join(e.parent.path(), TEMPPREFIX+e.name+TEMPSUFFIX)
	fp, err := os.Create(tempPath)
	if err != nil {
		return nil, err
	}
	return &fileWriter{
		fp: fp,
		closeCall: func() error {
			if !modTime.IsZero() {
				err = os.Chtimes(tempPath, modTime, modTime)
				if err != nil {
					return err
				}
			}
			err = os.Rename(tempPath, e.path())
			if err != nil {
				return err
			}
			e.st, err = os.Stat(e.path())

			// Update parent stat result
			st, err := os.Stat(e.parent.path())
			if err != nil {
				return err
			}
			e.parent.st = st

			return err
		},
	}, nil
}

// UpdateOver replaces this file with the contents and modtime of the other
// file.
func (e *Entry) UpdateOver(other tree.Entry) ([]byte, error) {
	file, ok := other.(tree.FileEntry)
	if !ok {
		return nil, tree.ErrNotImplemented
	}

	switch e.Type() {
	case tree.TYPE_REGULAR:
		out, err := file.UpdateFile(e.ModTime())
		if err != nil {
			return nil, err
		}

		in, err := os.Open(e.path())

		hasher := tree.NewHash()
		hashReader := io.TeeReader(in, hasher)

		_, err = io.Copy(out, hashReader)
		if err != nil {
			return nil, err
		}

		return hasher.Sum(nil), out.Close()

	default:
		return nil, tree.ErrNotImplemented
	}
}

// SetContents writes contents to this file, for testing.
func (e *Entry) SetContents(contents []byte) error {
	fp, err := os.Create(e.path())
	if err != nil {
		return err
	}
	defer fp.Close()
	_, err = fp.Write(contents)
	if err != nil {
		return err
	}
	now := time.Now()
	err = os.Chtimes(e.path(), now, now)
	if err != nil {
		// could not update
		return err
	}
	e.st, err = fp.Stat()
	if err != nil {
		return err
	}
	return fp.Sync()
}

// CopyTo copies this file into the otherParent. The latter must be a directory.
// Only implemented for regular files, not directories.
func (e *Entry) CopyTo(otherParent tree.Entry) (tree.Entry, []byte, error) {
	file, ok := otherParent.(tree.FileEntry)
	if !ok {
		return nil, nil, tree.ErrNotImplemented
	}

	switch e.Type() {
	case tree.TYPE_REGULAR:
		other, out, err := file.CreateFile(e.name, e.ModTime())
		if err != nil {
			return nil, nil, err
		}
		in, err := os.Open(e.path())
		if err != nil {
			return nil, nil, err
		}
		hasher := tree.NewHash()
		hashReader := io.TeeReader(in, hasher)
		_, err = io.Copy(out, hashReader)
		if err != nil {
			return nil, nil, err
		}
		// out.Close() does an fsync and rename
		err = out.Close()
		if err != nil {
			return nil, nil, err
		}
		return other, hasher.Sum(nil), nil

	default:
		return nil, nil, tree.ErrNotImplemented
	}
}
<|endoftext|>


<commit_before>// +build js

package parse

import (
	"container/list"
	"fmt"
)

type lexer struct {
	name       string
	input      string
	leftDelim  string
	rightDelim string
	state      stateFn
	pos        Pos
	start      Pos
	width      Pos
	lastPos    Pos
	items      chan item
	parenDepth int
	itemsList  *list.List
}

func (l *lexer) emit(t itemType) {
	l.itemsList.PushBack(item{t, l.start, l.input[l.start:l.pos]})
	l.start = l.pos
}

func (l *lexer) errorf(format string, args ...interface{}) stateFn {
	l.itemsList.PushBack(item{itemError, l.start, fmt.Sprintf(format, args...)})
	return nil
}

func (l *lexer) nextItem() item {
	element := l.itemsList.Front()
	for element == nil {
		l.state = l.state(l)
		element = l.itemsList.Front()
	}
	l.itemsList.Remove(element)
	item := element.Value.(item)
	l.lastPos = item.pos
	return item
}

func lex(name, input, left, right string) *lexer {
	if left == "" {
		left = leftDelim
	}
	if right == "" {
		right = rightDelim
	}
	l := &lexer{
		name:       name,
		input:      input,
		leftDelim:  left,
		rightDelim: right,
		itemsList:  list.New(),
	}
	l.state = lexText
	return l
}
<commit_msg>removed broken non-blocking implementation of text/template/parse<commit_after><|endoftext|>


<commit_before>// Copyright 2015 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package local

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/google/syzkaller/fileutil"
	"github.com/google/syzkaller/vm"
)

func init() {
	vm.Register("local", ctor)
}

type instance struct {
	cfg    *vm.Config
	closed chan bool
}

func ctor(cfg *vm.Config) (vm.Instance, error) {
	// Disable annoying segfault dmesg messages, fuzzer is going to crash a lot.
	etrace, err := os.Open("/proc/sys/debug/exception-trace")
	if err == nil {
		etrace.Write([]byte{'0'})
		etrace.Close()
	}

	// Don't write executor core files.
	syscall.Setrlimit(syscall.RLIMIT_CORE, &syscall.Rlimit{0, 0})

	inst := &instance{
		cfg:    cfg,
		closed: make(chan bool),
	}
	return inst, nil
}

func (inst *instance) Close() {
	close(inst.closed)
	os.RemoveAll(inst.cfg.Workdir)
}

func (inst *instance) Forward(port int) (string, error) {
	return fmt.Sprintf("127.0.0.1:%v", port), nil
}

func (inst *instance) Copy(hostSrc string) (string, error) {
	vmDst := filepath.Join(inst.cfg.Workdir, filepath.Base(hostSrc))
	if err := fileutil.CopyFile(hostSrc, vmDst, false); err != nil {
		return "", err
	}
	if err := os.Chmod(vmDst, 0777); err != nil {
		return "", err
	}
	return vmDst, nil
}

func (inst *instance) Run(timeout time.Duration, command string) (<-chan []byte, <-chan error, error) {
	for strings.Index(command, "  ") != -1 {
		command = strings.Replace(command, "  ", " ", -1)
	}
	args := strings.Split(command, " ")
	cmd := exec.Command(args[0], args[1:]...)
	if err := cmd.Start(); err != nil {
		return nil, nil, err
	}
	outputC := make(chan []byte, 10)
	errorC := make(chan error, 2)
	done := make(chan bool)
	go func() {
		errorC <- cmd.Wait()
		close(done)
	}()
	go func() {
		ticker := time.NewTicker(time.Second)
		timeout := time.NewTicker(timeout)
		for {
			select {
			case <-ticker.C:
				select {
				case outputC <- []byte{'.'}:
				default:
				}
			case <-timeout.C:
				errorC <- vm.TimeoutErr
				cmd.Process.Kill()
				ticker.Stop()
				return
			case <-done:
				ticker.Stop()
				timeout.Stop()
				return
			case <-inst.closed:
				errorC <- fmt.Errorf("closed")
				cmd.Process.Kill()
				ticker.Stop()
				timeout.Stop()
				return
			}
		}
	}()
	return outputC, errorC, nil
}
<commit_msg>vm/local: implement debug mode<commit_after>// Copyright 2015 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package local

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/google/syzkaller/fileutil"
	"github.com/google/syzkaller/vm"
)

func init() {
	vm.Register("local", ctor)
}

type instance struct {
	cfg    *vm.Config
	closed chan bool
}

func ctor(cfg *vm.Config) (vm.Instance, error) {
	// Disable annoying segfault dmesg messages, fuzzer is going to crash a lot.
	etrace, err := os.Open("/proc/sys/debug/exception-trace")
	if err == nil {
		etrace.Write([]byte{'0'})
		etrace.Close()
	}

	// Don't write executor core files.
	syscall.Setrlimit(syscall.RLIMIT_CORE, &syscall.Rlimit{0, 0})

	inst := &instance{
		cfg:    cfg,
		closed: make(chan bool),
	}
	return inst, nil
}

func (inst *instance) Close() {
	close(inst.closed)
	os.RemoveAll(inst.cfg.Workdir)
}

func (inst *instance) Forward(port int) (string, error) {
	return fmt.Sprintf("127.0.0.1:%v", port), nil
}

func (inst *instance) Copy(hostSrc string) (string, error) {
	vmDst := filepath.Join(inst.cfg.Workdir, filepath.Base(hostSrc))
	if err := fileutil.CopyFile(hostSrc, vmDst, false); err != nil {
		return "", err
	}
	if err := os.Chmod(vmDst, 0777); err != nil {
		return "", err
	}
	return vmDst, nil
}

func (inst *instance) Run(timeout time.Duration, command string) (<-chan []byte, <-chan error, error) {
	for strings.Index(command, "  ") != -1 {
		command = strings.Replace(command, "  ", " ", -1)
	}
	args := strings.Split(command, " ")
	cmd := exec.Command(args[0], args[1:]...)
	if inst.cfg.Debug {
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stdout
	}
	if err := cmd.Start(); err != nil {
		return nil, nil, err
	}
	outputC := make(chan []byte, 10)
	errorC := make(chan error, 2)
	done := make(chan bool)
	go func() {
		errorC <- cmd.Wait()
		close(done)
	}()
	go func() {
		ticker := time.NewTicker(time.Second)
		timeout := time.NewTicker(timeout)
		for {
			select {
			case <-ticker.C:
				select {
				case outputC <- []byte{'.'}:
				default:
				}
			case <-timeout.C:
				errorC <- vm.TimeoutErr
				cmd.Process.Kill()
				ticker.Stop()
				return
			case <-done:
				ticker.Stop()
				timeout.Stop()
				return
			case <-inst.closed:
				errorC <- fmt.Errorf("closed")
				cmd.Process.Kill()
				ticker.Stop()
				timeout.Stop()
				return
			}
		}
	}()
	return outputC, errorC, nil
}
<|endoftext|>


<commit_before>package types

import (
	"github.com/tendermint/abci/types"
)

// TM2PB is used for converting Tendermint types to protobuf types.
// UNSTABLE
var TM2PB = tm2pb{}

type tm2pb struct{}

func (tm2pb) Header(header *Header) types.Header {
	return types.Header{
		ChainID:        header.ChainID,
		Height:         header.Height,
		Time:           header.Time.Unix(),
		NumTxs:         int32(header.NumTxs), // XXX: overflow
		LastBlockID:    TM2PB.BlockID(header.LastBlockID),
		LastCommitHash: header.LastCommitHash,
		DataHash:       header.DataHash,
		AppHash:        header.AppHash,
	}
}

func (tm2pb) BlockID(blockID BlockID) types.BlockID {
	return types.BlockID{
		Hash:  blockID.Hash,
		Parts: TM2PB.PartSetHeader(blockID.PartsHeader),
	}
}

func (tm2pb) PartSetHeader(partSetHeader PartSetHeader) types.PartSetHeader {
	return types.PartSetHeader{
		Total: int32(partSetHeader.Total), // XXX: overflow
		Hash:  partSetHeader.Hash,
	}
}

func (tm2pb) Validator(val *Validator) types.Validator {
	return types.Validator{
		PubKey: val.PubKey.Bytes(),
		Power:  val.VotingPower,
	}
}

func (tm2pb) Validators(vals *ValidatorSet) []types.Validator {
	validators := make([]types.Validator, len(vals.Validators))
	for i, val := range vals.Validators {
		validators[i] = TM2PB.Validator(val)
	}
	return validators
}

func (tm2pb) ConsensusParams(params *ConsensusParams) *types.ConsensusParams {
	return &types.ConsensusParams{
		BlockSize: &types.BlockSize{

			MaxBytes: int32(params.BlockSize.MaxBytes),
			MaxTxs:   int32(params.BlockSize.MaxTxs),
			MaxGas:   params.BlockSize.MaxGas,
		},
		TxSize: &types.TxSize{
			MaxBytes: int32(params.TxSize.MaxBytes),
			MaxGas:   params.TxSize.MaxGas,
		},
		BlockGossip: &types.BlockGossip{
			BlockPartSizeBytes: int32(params.BlockGossip.BlockPartSizeBytes),
		},
	}
}
<commit_msg>update types<commit_after>package types

import (
	"fmt"
	"reflect"

	"github.com/tendermint/abci/types"
	crypto "github.com/tendermint/go-crypto"
)

// TM2PB is used for converting Tendermint types to protobuf types.
// UNSTABLE
var TM2PB = tm2pb{}

type tm2pb struct{}

func (tm2pb) Header(header *Header) types.Header {
	return types.Header{
		ChainId:       header.ChainID,
		Height:        header.Height,
		Time:          header.Time.Unix(),
		NumTxs:        int32(header.NumTxs), // XXX: overflow
		LastBlockHash: header.LastBlockID.Hash,
		AppHash:       header.AppHash,
	}
}

func (tm2pb) Validator(val *Validator) types.Validator {
	return types.Validator{
		PubKey: TM2PB.PubKey(val.PubKey),
		Power:  val.VotingPower,
	}
}

func (tm2pb) PubKey(pubKey crypto.PubKey) *types.PubKey {
	switch pk := pubKey.(type) {
	case crypto.PubKeyEd25519:
		return &types.PubKey{
			Type: "ed25519",
			Data: pk[:],
		}
	case crypto.PubKeySecp256k1:
		return &types.PubKey{
			Type: "secp256k1",
			Data: pk[:],
		}
	default:
		panic(fmt.Sprintf("unknown pubkey type: %v %v", pubKey, reflect.TypeOf(pubKey)))
	}
}

func (tm2pb) Validators(vals *ValidatorSet) []types.Validator {
	validators := make([]types.Validator, len(vals.Validators))
	for i, val := range vals.Validators {
		validators[i] = TM2PB.Validator(val)
	}
	return validators
}

func (tm2pb) ConsensusParams(params *ConsensusParams) *types.ConsensusParams {
	return &types.ConsensusParams{
		BlockSize: &types.BlockSize{

			MaxBytes: int32(params.BlockSize.MaxBytes),
			MaxTxs:   int32(params.BlockSize.MaxTxs),
			MaxGas:   params.BlockSize.MaxGas,
		},
		TxSize: &types.TxSize{
			MaxBytes: int32(params.TxSize.MaxBytes),
			MaxGas:   params.TxSize.MaxGas,
		},
		BlockGossip: &types.BlockGossip{
			BlockPartSizeBytes: int32(params.BlockGossip.BlockPartSizeBytes),
		},
	}
}
<|endoftext|>


<commit_before>// Copyright 2019 The Prometheus 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 web

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"fmt"
	"net"
	"net/http"
	"os"
	"path/filepath"

	"github.com/go-kit/log"
	"github.com/go-kit/log/level"
	config_util "github.com/prometheus/common/config"
	"gopkg.in/yaml.v2"
)

var (
	errNoTLSConfig = errors.New("TLS config is not present")
)

type Config struct {
	TLSConfig  TLSStruct                     `yaml:"tls_server_config"`
	HTTPConfig HTTPStruct                    `yaml:"http_server_config"`
	Users      map[string]config_util.Secret `yaml:"basic_auth_users"`
}

type TLSStruct struct {
	TLSCertPath              string     `yaml:"cert_file"`
	TLSKeyPath               string     `yaml:"key_file"`
	ClientAuth               string     `yaml:"client_auth_type"`
	ClientCAs                string     `yaml:"client_ca_file"`
	CipherSuites             []Cipher   `yaml:"cipher_suites"`
	CurvePreferences         []Curve    `yaml:"curve_preferences"`
	MinVersion               TLSVersion `yaml:"min_version"`
	MaxVersion               TLSVersion `yaml:"max_version"`
	PreferServerCipherSuites bool       `yaml:"prefer_server_cipher_suites"`
}

// SetDirectory joins any relative file paths with dir.
func (t *TLSStruct) SetDirectory(dir string) {
	t.TLSCertPath = config_util.JoinDir(dir, t.TLSCertPath)
	t.TLSKeyPath = config_util.JoinDir(dir, t.TLSKeyPath)
	t.ClientCAs = config_util.JoinDir(dir, t.ClientCAs)
}

type HTTPStruct struct {
	HTTP2  bool              `yaml:"http2"`
	Header map[string]string `yaml:"headers,omitempty"`
}

func getConfig(configPath string) (*Config, error) {
	content, err := os.ReadFile(configPath)
	if err != nil {
		return nil, err
	}
	c := &Config{
		TLSConfig: TLSStruct{
			MinVersion:               tls.VersionTLS12,
			MaxVersion:               tls.VersionTLS13,
			PreferServerCipherSuites: true,
		},
		HTTPConfig: HTTPStruct{HTTP2: true},
	}
	err = yaml.UnmarshalStrict(content, c)
	if err == nil {
		err = validateHeaderConfig(c.HTTPConfig.Header)
	}
	c.TLSConfig.SetDirectory(filepath.Dir(configPath))
	return c, err
}

func getTLSConfig(configPath string) (*tls.Config, error) {
	c, err := getConfig(configPath)
	if err != nil {
		return nil, err
	}
	return ConfigToTLSConfig(&c.TLSConfig)
}

// ConfigToTLSConfig generates the golang tls.Config from the TLSStruct config.
func ConfigToTLSConfig(c *TLSStruct) (*tls.Config, error) {
	if c.TLSCertPath == "" && c.TLSKeyPath == "" && c.ClientAuth == "" && c.ClientCAs == "" {
		return nil, errNoTLSConfig
	}

	if c.TLSCertPath == "" {
		return nil, errors.New("missing cert_file")
	}

	if c.TLSKeyPath == "" {
		return nil, errors.New("missing key_file")
	}

	loadCert := func() (*tls.Certificate, error) {
		cert, err := tls.LoadX509KeyPair(c.TLSCertPath, c.TLSKeyPath)
		if err != nil {
			return nil, fmt.Errorf("failed to load X509KeyPair: %w", err)
		}
		return &cert, nil
	}

	// Confirm that certificate and key paths are valid.
	if _, err := loadCert(); err != nil {
		return nil, err
	}

	cfg := &tls.Config{
		MinVersion:               (uint16)(c.MinVersion),
		MaxVersion:               (uint16)(c.MaxVersion),
		PreferServerCipherSuites: c.PreferServerCipherSuites,
	}

	cfg.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
		return loadCert()
	}

	var cf []uint16
	for _, c := range c.CipherSuites {
		cf = append(cf, (uint16)(c))
	}
	if len(cf) > 0 {
		cfg.CipherSuites = cf
	}

	var cp []tls.CurveID
	for _, c := range c.CurvePreferences {
		cp = append(cp, (tls.CurveID)(c))
	}
	if len(cp) > 0 {
		cfg.CurvePreferences = cp
	}

	if c.ClientCAs != "" {
		clientCAPool := x509.NewCertPool()
		clientCAFile, err := os.ReadFile(c.ClientCAs)
		if err != nil {
			return nil, err
		}
		clientCAPool.AppendCertsFromPEM(clientCAFile)
		cfg.ClientCAs = clientCAPool
	}

	switch c.ClientAuth {
	case "RequestClientCert":
		cfg.ClientAuth = tls.RequestClientCert
	case "RequireAnyClientCert", "RequireClientCert": // Preserved for backwards compatibility.
		cfg.ClientAuth = tls.RequireAnyClientCert
	case "VerifyClientCertIfGiven":
		cfg.ClientAuth = tls.VerifyClientCertIfGiven
	case "RequireAndVerifyClientCert":
		cfg.ClientAuth = tls.RequireAndVerifyClientCert
	case "", "NoClientCert":
		cfg.ClientAuth = tls.NoClientCert
	default:
		return nil, errors.New("Invalid ClientAuth: " + c.ClientAuth)
	}

	if c.ClientCAs != "" && cfg.ClientAuth == tls.NoClientCert {
		return nil, errors.New("Client CA's have been configured without a Client Auth Policy")
	}

	return cfg, nil
}

// ListenAndServe starts the server on the given address. Based on the file
// tlsConfigPath, TLS or basic auth could be enabled.
func ListenAndServe(server *http.Server, tlsConfigPath string, logger log.Logger) error {
	listener, err := net.Listen("tcp", server.Addr)
	if err != nil {
		return err
	}
	defer listener.Close()
	return Serve(listener, server, tlsConfigPath, logger)
}

// Server starts the server on the given listener. Based on the file
// tlsConfigPath, TLS or basic auth could be enabled.
func Serve(l net.Listener, server *http.Server, tlsConfigPath string, logger log.Logger) error {
	if tlsConfigPath == "" {
		level.Info(logger).Log("msg", "TLS is disabled.", "http2", false)
		return server.Serve(l)
	}

	if err := validateUsers(tlsConfigPath); err != nil {
		return err
	}

	// Setup basic authentication.
	var handler http.Handler = http.DefaultServeMux
	if server.Handler != nil {
		handler = server.Handler
	}

	c, err := getConfig(tlsConfigPath)
	if err != nil {
		return err
	}

	server.Handler = &webHandler{
		tlsConfigPath: tlsConfigPath,
		logger:        logger,
		handler:       handler,
		cache:         newCache(),
	}

	config, err := ConfigToTLSConfig(&c.TLSConfig)
	switch err {
	case nil:
		if !c.HTTPConfig.HTTP2 {
			server.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler))
		}
		// Valid TLS config.
		level.Info(logger).Log("msg", "TLS is enabled.", "http2", c.HTTPConfig.HTTP2)
	case errNoTLSConfig:
		// No TLS config, back to plain HTTP.
		level.Info(logger).Log("msg", "TLS is disabled.", "http2", false)
		return server.Serve(l)
	default:
		// Invalid TLS config.
		return err
	}

	server.TLSConfig = config

	// Set the GetConfigForClient method of the HTTPS server so that the config
	// and certs are reloaded on new connections.
	server.TLSConfig.GetConfigForClient = func(*tls.ClientHelloInfo) (*tls.Config, error) {
		config, err := getTLSConfig(tlsConfigPath)
		if err != nil {
			return nil, err
		}
		config.NextProtos = server.TLSConfig.NextProtos
		return config, nil
	}
	return server.ServeTLS(l, "", "")
}

// Validate configuration file by reading the configuration and the certificates.
func Validate(tlsConfigPath string) error {
	if tlsConfigPath == "" {
		return nil
	}
	if err := validateUsers(tlsConfigPath); err != nil {
		return err
	}
	c, err := getConfig(tlsConfigPath)
	if err != nil {
		return err
	}
	_, err = ConfigToTLSConfig(&c.TLSConfig)
	if err == errNoTLSConfig {
		return nil
	}
	return err
}

type Cipher uint16

func (c *Cipher) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var s string
	err := unmarshal((*string)(&s))
	if err != nil {
		return err
	}
	for _, cs := range tls.CipherSuites() {
		if cs.Name == s {
			*c = (Cipher)(cs.ID)
			return nil
		}
	}
	return errors.New("unknown cipher: " + s)
}

func (c Cipher) MarshalYAML() (interface{}, error) {
	return tls.CipherSuiteName((uint16)(c)), nil
}

type Curve tls.CurveID

var curves = map[string]Curve{
	"CurveP256": (Curve)(tls.CurveP256),
	"CurveP384": (Curve)(tls.CurveP384),
	"CurveP521": (Curve)(tls.CurveP521),
	"X25519":    (Curve)(tls.X25519),
}

func (c *Curve) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var s string
	err := unmarshal((*string)(&s))
	if err != nil {
		return err
	}
	if curveid, ok := curves[s]; ok {
		*c = curveid
		return nil
	}
	return errors.New("unknown curve: " + s)
}

func (c *Curve) MarshalYAML() (interface{}, error) {
	for s, curveid := range curves {
		if *c == curveid {
			return s, nil
		}
	}
	return fmt.Sprintf("%v", c), nil
}

type TLSVersion uint16

var tlsVersions = map[string]TLSVersion{
	"TLS13": (TLSVersion)(tls.VersionTLS13),
	"TLS12": (TLSVersion)(tls.VersionTLS12),
	"TLS11": (TLSVersion)(tls.VersionTLS11),
	"TLS10": (TLSVersion)(tls.VersionTLS10),
}

func (tv *TLSVersion) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var s string
	err := unmarshal((*string)(&s))
	if err != nil {
		return err
	}
	if v, ok := tlsVersions[s]; ok {
		*tv = v
		return nil
	}
	return errors.New("unknown TLS version: " + s)
}

func (tv *TLSVersion) MarshalYAML() (interface{}, error) {
	for s, v := range tlsVersions {
		if *tv == v {
			return s, nil
		}
	}
	return fmt.Sprintf("%v", tv), nil
}

// Listen starts the server on the given address. Based on the file
// tlsConfigPath, TLS or basic auth could be enabled.
//
// Deprecated: Use ListenAndServe instead.
func Listen(server *http.Server, tlsConfigPath string, logger log.Logger) error {
	return ListenAndServe(server, tlsConfigPath, logger)
}
<commit_msg>Rename department of redundancy (#114)<commit_after>// Copyright 2019 The Prometheus 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 web

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"fmt"
	"net"
	"net/http"
	"os"
	"path/filepath"

	"github.com/go-kit/log"
	"github.com/go-kit/log/level"
	config_util "github.com/prometheus/common/config"
	"gopkg.in/yaml.v2"
)

var (
	errNoTLSConfig = errors.New("TLS config is not present")
)

type Config struct {
	TLSConfig  TLSConfig                     `yaml:"tls_server_config"`
	HTTPConfig HTTPConfig                    `yaml:"http_server_config"`
	Users      map[string]config_util.Secret `yaml:"basic_auth_users"`
}

type TLSConfig struct {
	TLSCertPath              string     `yaml:"cert_file"`
	TLSKeyPath               string     `yaml:"key_file"`
	ClientAuth               string     `yaml:"client_auth_type"`
	ClientCAs                string     `yaml:"client_ca_file"`
	CipherSuites             []Cipher   `yaml:"cipher_suites"`
	CurvePreferences         []Curve    `yaml:"curve_preferences"`
	MinVersion               TLSVersion `yaml:"min_version"`
	MaxVersion               TLSVersion `yaml:"max_version"`
	PreferServerCipherSuites bool       `yaml:"prefer_server_cipher_suites"`
}

// SetDirectory joins any relative file paths with dir.
func (t *TLSConfig) SetDirectory(dir string) {
	t.TLSCertPath = config_util.JoinDir(dir, t.TLSCertPath)
	t.TLSKeyPath = config_util.JoinDir(dir, t.TLSKeyPath)
	t.ClientCAs = config_util.JoinDir(dir, t.ClientCAs)
}

type HTTPConfig struct {
	HTTP2  bool              `yaml:"http2"`
	Header map[string]string `yaml:"headers,omitempty"`
}

func getConfig(configPath string) (*Config, error) {
	content, err := os.ReadFile(configPath)
	if err != nil {
		return nil, err
	}
	c := &Config{
		TLSConfig: TLSConfig{
			MinVersion:               tls.VersionTLS12,
			MaxVersion:               tls.VersionTLS13,
			PreferServerCipherSuites: true,
		},
		HTTPConfig: HTTPConfig{HTTP2: true},
	}
	err = yaml.UnmarshalStrict(content, c)
	if err == nil {
		err = validateHeaderConfig(c.HTTPConfig.Header)
	}
	c.TLSConfig.SetDirectory(filepath.Dir(configPath))
	return c, err
}

func getTLSConfig(configPath string) (*tls.Config, error) {
	c, err := getConfig(configPath)
	if err != nil {
		return nil, err
	}
	return ConfigToTLSConfig(&c.TLSConfig)
}

// ConfigToTLSConfig generates the golang tls.Config from the TLSConfig struct.
func ConfigToTLSConfig(c *TLSConfig) (*tls.Config, error) {
	if c.TLSCertPath == "" && c.TLSKeyPath == "" && c.ClientAuth == "" && c.ClientCAs == "" {
		return nil, errNoTLSConfig
	}

	if c.TLSCertPath == "" {
		return nil, errors.New("missing cert_file")
	}

	if c.TLSKeyPath == "" {
		return nil, errors.New("missing key_file")
	}

	loadCert := func() (*tls.Certificate, error) {
		cert, err := tls.LoadX509KeyPair(c.TLSCertPath, c.TLSKeyPath)
		if err != nil {
			return nil, fmt.Errorf("failed to load X509KeyPair: %w", err)
		}
		return &cert, nil
	}

	// Confirm that certificate and key paths are valid.
	if _, err := loadCert(); err != nil {
		return nil, err
	}

	cfg := &tls.Config{
		MinVersion:               (uint16)(c.MinVersion),
		MaxVersion:               (uint16)(c.MaxVersion),
		PreferServerCipherSuites: c.PreferServerCipherSuites,
	}

	cfg.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
		return loadCert()
	}

	var cf []uint16
	for _, c := range c.CipherSuites {
		cf = append(cf, (uint16)(c))
	}
	if len(cf) > 0 {
		cfg.CipherSuites = cf
	}

	var cp []tls.CurveID
	for _, c := range c.CurvePreferences {
		cp = append(cp, (tls.CurveID)(c))
	}
	if len(cp) > 0 {
		cfg.CurvePreferences = cp
	}

	if c.ClientCAs != "" {
		clientCAPool := x509.NewCertPool()
		clientCAFile, err := os.ReadFile(c.ClientCAs)
		if err != nil {
			return nil, err
		}
		clientCAPool.AppendCertsFromPEM(clientCAFile)
		cfg.ClientCAs = clientCAPool
	}

	switch c.ClientAuth {
	case "RequestClientCert":
		cfg.ClientAuth = tls.RequestClientCert
	case "RequireAnyClientCert", "RequireClientCert": // Preserved for backwards compatibility.
		cfg.ClientAuth = tls.RequireAnyClientCert
	case "VerifyClientCertIfGiven":
		cfg.ClientAuth = tls.VerifyClientCertIfGiven
	case "RequireAndVerifyClientCert":
		cfg.ClientAuth = tls.RequireAndVerifyClientCert
	case "", "NoClientCert":
		cfg.ClientAuth = tls.NoClientCert
	default:
		return nil, errors.New("Invalid ClientAuth: " + c.ClientAuth)
	}

	if c.ClientCAs != "" && cfg.ClientAuth == tls.NoClientCert {
		return nil, errors.New("Client CA's have been configured without a Client Auth Policy")
	}

	return cfg, nil
}

// ListenAndServe starts the server on the given address. Based on the file
// tlsConfigPath, TLS or basic auth could be enabled.
func ListenAndServe(server *http.Server, tlsConfigPath string, logger log.Logger) error {
	listener, err := net.Listen("tcp", server.Addr)
	if err != nil {
		return err
	}
	defer listener.Close()
	return Serve(listener, server, tlsConfigPath, logger)
}

// Server starts the server on the given listener. Based on the file
// tlsConfigPath, TLS or basic auth could be enabled.
func Serve(l net.Listener, server *http.Server, tlsConfigPath string, logger log.Logger) error {
	if tlsConfigPath == "" {
		level.Info(logger).Log("msg", "TLS is disabled.", "http2", false)
		return server.Serve(l)
	}

	if err := validateUsers(tlsConfigPath); err != nil {
		return err
	}

	// Setup basic authentication.
	var handler http.Handler = http.DefaultServeMux
	if server.Handler != nil {
		handler = server.Handler
	}

	c, err := getConfig(tlsConfigPath)
	if err != nil {
		return err
	}

	server.Handler = &webHandler{
		tlsConfigPath: tlsConfigPath,
		logger:        logger,
		handler:       handler,
		cache:         newCache(),
	}

	config, err := ConfigToTLSConfig(&c.TLSConfig)
	switch err {
	case nil:
		if !c.HTTPConfig.HTTP2 {
			server.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler))
		}
		// Valid TLS config.
		level.Info(logger).Log("msg", "TLS is enabled.", "http2", c.HTTPConfig.HTTP2)
	case errNoTLSConfig:
		// No TLS config, back to plain HTTP.
		level.Info(logger).Log("msg", "TLS is disabled.", "http2", false)
		return server.Serve(l)
	default:
		// Invalid TLS config.
		return err
	}

	server.TLSConfig = config

	// Set the GetConfigForClient method of the HTTPS server so that the config
	// and certs are reloaded on new connections.
	server.TLSConfig.GetConfigForClient = func(*tls.ClientHelloInfo) (*tls.Config, error) {
		config, err := getTLSConfig(tlsConfigPath)
		if err != nil {
			return nil, err
		}
		config.NextProtos = server.TLSConfig.NextProtos
		return config, nil
	}
	return server.ServeTLS(l, "", "")
}

// Validate configuration file by reading the configuration and the certificates.
func Validate(tlsConfigPath string) error {
	if tlsConfigPath == "" {
		return nil
	}
	if err := validateUsers(tlsConfigPath); err != nil {
		return err
	}
	c, err := getConfig(tlsConfigPath)
	if err != nil {
		return err
	}
	_, err = ConfigToTLSConfig(&c.TLSConfig)
	if err == errNoTLSConfig {
		return nil
	}
	return err
}

type Cipher uint16

func (c *Cipher) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var s string
	err := unmarshal((*string)(&s))
	if err != nil {
		return err
	}
	for _, cs := range tls.CipherSuites() {
		if cs.Name == s {
			*c = (Cipher)(cs.ID)
			return nil
		}
	}
	return errors.New("unknown cipher: " + s)
}

func (c Cipher) MarshalYAML() (interface{}, error) {
	return tls.CipherSuiteName((uint16)(c)), nil
}

type Curve tls.CurveID

var curves = map[string]Curve{
	"CurveP256": (Curve)(tls.CurveP256),
	"CurveP384": (Curve)(tls.CurveP384),
	"CurveP521": (Curve)(tls.CurveP521),
	"X25519":    (Curve)(tls.X25519),
}

func (c *Curve) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var s string
	err := unmarshal((*string)(&s))
	if err != nil {
		return err
	}
	if curveid, ok := curves[s]; ok {
		*c = curveid
		return nil
	}
	return errors.New("unknown curve: " + s)
}

func (c *Curve) MarshalYAML() (interface{}, error) {
	for s, curveid := range curves {
		if *c == curveid {
			return s, nil
		}
	}
	return fmt.Sprintf("%v", c), nil
}

type TLSVersion uint16

var tlsVersions = map[string]TLSVersion{
	"TLS13": (TLSVersion)(tls.VersionTLS13),
	"TLS12": (TLSVersion)(tls.VersionTLS12),
	"TLS11": (TLSVersion)(tls.VersionTLS11),
	"TLS10": (TLSVersion)(tls.VersionTLS10),
}

func (tv *TLSVersion) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var s string
	err := unmarshal((*string)(&s))
	if err != nil {
		return err
	}
	if v, ok := tlsVersions[s]; ok {
		*tv = v
		return nil
	}
	return errors.New("unknown TLS version: " + s)
}

func (tv *TLSVersion) MarshalYAML() (interface{}, error) {
	for s, v := range tlsVersions {
		if *tv == v {
			return s, nil
		}
	}
	return fmt.Sprintf("%v", tv), nil
}

// Listen starts the server on the given address. Based on the file
// tlsConfigPath, TLS or basic auth could be enabled.
//
// Deprecated: Use ListenAndServe instead.
func Listen(server *http.Server, tlsConfigPath string, logger log.Logger) error {
	return ListenAndServe(server, tlsConfigPath, logger)
}
<|endoftext|>


<commit_before>//  Copyright (c) 2013 Couchbase, Inc.
//  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 main

import (
	"fmt"
	"os"
	"os/signal"
	"os/user"
	"syscall"

	"github.com/couchbaselabs/clog"
	"github.com/sbinet/liner"
)

func HandleInteractiveMode(tiServer string) {

	currentUser, err := user.Current()
	if err != nil {
		fmt.Println("Unable to determine home directory, history file disabled")
	}

	var liner = liner.NewLiner()
	defer liner.Close()

	LoadHistory(liner, currentUser)

	go signalCatcher(liner)

	for {
		line, err := liner.Prompt("tuq> ")
		if err != nil {
			break
		}

		if line == "" {
			continue
		}

		UpdateHistory(liner, currentUser, line)
		err = execute_internal(tiServer, line, os.Stdout)
		if err != nil {
			clog.Error(err)
		}
	}

}

/**
 *  Attempt to clean up after ctrl-C otherwise
 *  terminal is left in bad shape
 */
func signalCatcher(liner *liner.State) {
	ch := make(chan os.Signal)
	signal.Notify(ch, syscall.SIGINT)
	<-ch
	liner.Close()
	os.Exit(0)
}
<commit_msg>added more logging to tuq_client to debug MB-9071<commit_after>//  Copyright (c) 2013 Couchbase, Inc.
//  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 main

import (
	"fmt"
	"os"
	"os/signal"
	"os/user"
	"syscall"

	"github.com/couchbaselabs/clog"
	"github.com/sbinet/liner"
)

func HandleInteractiveMode(tiServer string) {

	currentUser, err := user.Current()
	if err != nil {
		fmt.Println("Unable to determine current user, history file disabled: %v", err)
	}

	var liner = liner.NewLiner()
	defer liner.Close()

	LoadHistory(liner, currentUser)

	go signalCatcher(liner)

	for {
		line, err := liner.Prompt("tuq> ")
		if err != nil {
			break
		}

		if line == "" {
			continue
		}

		UpdateHistory(liner, currentUser, line)
		err = execute_internal(tiServer, line, os.Stdout)
		if err != nil {
			clog.Error(err)
		}
	}

}

/**
 *  Attempt to clean up after ctrl-C otherwise
 *  terminal is left in bad shape
 */
func signalCatcher(liner *liner.State) {
	ch := make(chan os.Signal)
	signal.Notify(ch, syscall.SIGINT)
	<-ch
	liner.Close()
	os.Exit(0)
}
<|endoftext|>


<commit_before>package types

import (
	"testing"

	"github.com/NebulousLabs/Sia/encoding"
)

// BenchmarkEncodeEmptyBlock benchmarks encoding an empty block.
//
// i5-4670K, 9a90f86: 48 MB/s
// i5-4670K, f8f2df2: 211 MB/s
func BenchmarkEncodeEmptyBlock(b *testing.B) {
	var block Block
	b.SetBytes(int64(len(encoding.Marshal(block))))
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		encoding.Marshal(block)
	}
}

// BenchmarkDecodeEmptyBlock benchmarks decoding an empty block.
//
// i7-4770,  b0b162d: 38 MB/s
// i5-4670K, 9a90f86: 55 MB/s
// i5-4670K, f8f2df2: 166 MB/s
func BenchmarkDecodeEmptyBlock(b *testing.B) {
	var block Block
	encodedBlock := encoding.Marshal(block)
	b.SetBytes(int64(len(encodedBlock)))
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		err := encoding.Unmarshal(encodedBlock, &block)
		if err != nil {
			b.Fatal(err)
		}
	}
}

// BenchmarkEncodeHeavyBlock benchmarks encoding a "heavy" block.
//
// i5-4670K, f8f2df2: 250 MB/s
func BenchmarkEncodeHeavyBlock(b *testing.B) {
	b.SetBytes(int64(len(encoding.Marshal(heavyBlock))))
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		encoding.Marshal(heavyBlock)
	}
}

// BenchmarkDecodeHeavyBlock benchmarks decoding a "heavy" block.
//
// i5-4670K, f8f2df2: 326 MB/s
func BenchmarkDecodeHeavyBlock(b *testing.B) {
	var block Block
	encodedBlock := encoding.Marshal(heavyBlock)
	b.SetBytes(int64(len(encodedBlock)))
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		err := encoding.Unmarshal(encodedBlock, &block)
		if err != nil {
			b.Fatal(err)
		}
	}
}
<commit_msg>write to ioutil.Discard in encoding benchmarks<commit_after>package types

import (
	"io/ioutil"
	"testing"

	"github.com/NebulousLabs/Sia/encoding"
)

// BenchmarkEncodeEmptyBlock benchmarks encoding an empty block.
//
// i5-4670K, 9a90f86: 48 MB/s
// i5-4670K, f8f2df2: 211 MB/s
func BenchmarkEncodeEmptyBlock(b *testing.B) {
	var block Block
	b.SetBytes(int64(len(encoding.Marshal(block))))
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		block.MarshalSia(ioutil.Discard)
	}
}

// BenchmarkDecodeEmptyBlock benchmarks decoding an empty block.
//
// i7-4770,  b0b162d: 38 MB/s
// i5-4670K, 9a90f86: 55 MB/s
// i5-4670K, f8f2df2: 166 MB/s
func BenchmarkDecodeEmptyBlock(b *testing.B) {
	var block Block
	encodedBlock := encoding.Marshal(block)
	b.SetBytes(int64(len(encodedBlock)))
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		err := encoding.Unmarshal(encodedBlock, &block)
		if err != nil {
			b.Fatal(err)
		}
	}
}

// BenchmarkEncodeHeavyBlock benchmarks encoding a "heavy" block.
//
// i5-4670K, f8f2df2: 250 MB/s
func BenchmarkEncodeHeavyBlock(b *testing.B) {
	b.SetBytes(int64(len(encoding.Marshal(heavyBlock))))
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		heavyBlock.MarshalSia(ioutil.Discard)
	}
}

// BenchmarkDecodeHeavyBlock benchmarks decoding a "heavy" block.
//
// i5-4670K, f8f2df2: 326 MB/s
func BenchmarkDecodeHeavyBlock(b *testing.B) {
	var block Block
	encodedBlock := encoding.Marshal(heavyBlock)
	b.SetBytes(int64(len(encodedBlock)))
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		err := encoding.Unmarshal(encodedBlock, &block)
		if err != nil {
			b.Fatal(err)
		}
	}
}
<|endoftext|>


<commit_before>package container

import (
	"github.com/docker/engine-api/types/strslice"
	"github.com/docker/go-connections/nat"
	"time"
)

// HealthConfig holds configuration settings for the HEALTHCHECK feature.
type HealthConfig struct {
	// Test is the test to perform to check that the container is healthy.
	// An empty slice means to inherit the default.
	// The options are:
	// {} : inherit healthcheck
	// {"NONE"} : disable healthcheck
	// {"CMD", args...} : exec arguments directly
	// {"CMD-SHELL", command} : run command with system's default shell
	Test []string `json:",omitempty"`

	// Zero means to inherit. Durations are expressed as integer nanoseconds.
	Interval time.Duration `json:",omitempty"` // Interval is the time to wait between checks.
	Timeout  time.Duration `json:",omitempty"` // Timeout is the time to wait before considering the check to have hung.

	// Retries is the number of consecutive failures needed to consider a container as unhealthy.
	// Zero means inherit.
	Retries int `json:",omitempty"`
}

// Config contains the configuration data about a container.
// It should hold only portable information about the container.
// Here, "portable" means "independent from the host we are running on".
// Non-portable information *should* appear in HostConfig.
// All fields added to this struct must be marked `omitempty` to keep getting
// predictable hashes from the old `v1Compatibility` configuration.
type Config struct {
	Hostname        string                // Hostname
	Domainname      string                // Domainname
	User            string                // User that will run the command(s) inside the container
	AttachStdin     bool                  // Attach the standard input, makes possible user interaction
	AttachStdout    bool                  // Attach the standard output
	AttachStderr    bool                  // Attach the standard error
	ExposedPorts    map[nat.Port]struct{} `json:",omitempty"` // List of exposed ports
	Tty             bool                  // Attach standard streams to a tty, including stdin if it is not closed.
	OpenStdin       bool                  // Open stdin
	StdinOnce       bool                  // If true, close stdin after the 1 attached client disconnects.
	Env             []string              // List of environment variable to set in the container
	Cmd             strslice.StrSlice     // Command to run when starting the container
	Healthcheck     *HealthConfig         `json:",omitempty"` // Healthcheck describes how to check the container is healthy
	ArgsEscaped     bool                  `json:",omitempty"` // True if command is already escaped (Windows specific)
	Image           string                // Name of the image as it was passed by the operator (eg. could be symbolic)
	Volumes         map[string]struct{}   // List of volumes (mounts) used for the container
	WorkingDir      string                // Current directory (PWD) in the command will be launched
	Entrypoint      strslice.StrSlice     // Entrypoint to run when starting the container
	NetworkDisabled bool                  `json:",omitempty"` // Is network disabled
	MacAddress      string                `json:",omitempty"` // Mac Address of the container
	OnBuild         []string              // ONBUILD metadata that were defined on the image Dockerfile
	Labels          map[string]string     // List of labels set to this container
	StopSignal      string                `json:",omitempty"` // Signal to stop a container
}
<commit_msg>Add shell to support SHELL in dockerfile<commit_after>package container

import (
	"time"

	"github.com/docker/engine-api/types/strslice"
	"github.com/docker/go-connections/nat"
)

// HealthConfig holds configuration settings for the HEALTHCHECK feature.
type HealthConfig struct {
	// Test is the test to perform to check that the container is healthy.
	// An empty slice means to inherit the default.
	// The options are:
	// {} : inherit healthcheck
	// {"NONE"} : disable healthcheck
	// {"CMD", args...} : exec arguments directly
	// {"CMD-SHELL", command} : run command with system's default shell
	Test []string `json:",omitempty"`

	// Zero means to inherit. Durations are expressed as integer nanoseconds.
	Interval time.Duration `json:",omitempty"` // Interval is the time to wait between checks.
	Timeout  time.Duration `json:",omitempty"` // Timeout is the time to wait before considering the check to have hung.

	// Retries is the number of consecutive failures needed to consider a container as unhealthy.
	// Zero means inherit.
	Retries int `json:",omitempty"`
}

// Config contains the configuration data about a container.
// It should hold only portable information about the container.
// Here, "portable" means "independent from the host we are running on".
// Non-portable information *should* appear in HostConfig.
// All fields added to this struct must be marked `omitempty` to keep getting
// predictable hashes from the old `v1Compatibility` configuration.
type Config struct {
	Hostname        string                // Hostname
	Domainname      string                // Domainname
	User            string                // User that will run the command(s) inside the container
	AttachStdin     bool                  // Attach the standard input, makes possible user interaction
	AttachStdout    bool                  // Attach the standard output
	AttachStderr    bool                  // Attach the standard error
	ExposedPorts    map[nat.Port]struct{} `json:",omitempty"` // List of exposed ports
	Tty             bool                  // Attach standard streams to a tty, including stdin if it is not closed.
	OpenStdin       bool                  // Open stdin
	StdinOnce       bool                  // If true, close stdin after the 1 attached client disconnects.
	Env             []string              // List of environment variable to set in the container
	Cmd             strslice.StrSlice     // Command to run when starting the container
	Healthcheck     *HealthConfig         `json:",omitempty"` // Healthcheck describes how to check the container is healthy
	ArgsEscaped     bool                  `json:",omitempty"` // True if command is already escaped (Windows specific)
	Image           string                // Name of the image as it was passed by the operator (eg. could be symbolic)
	Volumes         map[string]struct{}   // List of volumes (mounts) used for the container
	WorkingDir      string                // Current directory (PWD) in the command will be launched
	Entrypoint      strslice.StrSlice     // Entrypoint to run when starting the container
	NetworkDisabled bool                  `json:",omitempty"` // Is network disabled
	MacAddress      string                `json:",omitempty"` // Mac Address of the container
	OnBuild         []string              // ONBUILD metadata that were defined on the image Dockerfile
	Labels          map[string]string     // List of labels set to this container
	StopSignal      string                `json:",omitempty"` // Signal to stop a container
	Shell           strslice.StrSlice     `json:",omitempty"` // Shell for shell-form of RUN, CMD, ENTRYPOINT
}
<|endoftext|>


<commit_before>/*
Copyright 2011 Google Inc.

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 main

import (
	"bytes"
	"exec"
	"flag"
	"fmt"
	"http"
	"http/cgi"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"old/template"
	"time"
	"url"
)

const defaultAddr = ":31798" // default webserver address

var h1TitlePattern = regexp.MustCompile(`<h1>([^<]+)</h1>`)

var (
	httpAddr            = flag.String("http", defaultAddr, "HTTP service address (e.g., '"+defaultAddr+"')")
	httpsAddr           = flag.String("https", "", "HTTPS service address")
	root                = flag.String("root", "", "Website root (parent of 'static', 'content', and 'tmpl")
	gitwebScript        = flag.String("gitwebscript", "/usr/lib/cgi-bin/gitweb.cgi", "Path to gitweb.cgi, or blank to disable.")
	gitwebFiles         = flag.String("gitwebfiles", "/usr/share/gitweb/static", "Path to gitweb's static files.")
	logDir              = flag.String("logdir", "", "Directory to write log files to (one per hour), or empty to not log.")
	logStdout           = flag.Bool("logstdout", true, "Write to stdout?")
	tlsCertFile         = flag.String("tlscert", "", "TLS cert file")
	tlsKeyFile          = flag.String("tlskey", "", "TLS private key file")
	gerritUser          = flag.String("gerrituser", "ubuntu", "Gerrit host's username")
	gerritHost          = flag.String("gerrithost", "", "Gerrit host, or empty.")
	pageHtml, errorHtml *template.Template
)

var fmap = template.FormatterMap{
	"":         textFmt,
	"html":     htmlFmt,
	"html-esc": htmlEscFmt,
}

// Template formatter for "" (default) format.
func textFmt(w io.Writer, format string, x ...interface{}) {
	writeAny(w, false, x[0])
}

// Template formatter for "html" format.
func htmlFmt(w io.Writer, format string, x ...interface{}) {
	writeAny(w, true, x[0])
}

// Template formatter for "html-esc" format.
func htmlEscFmt(w io.Writer, format string, x ...interface{}) {
	var buf bytes.Buffer
	writeAny(&buf, false, x[0])
	template.HTMLEscape(w, buf.Bytes())
}

// Write anything to w; optionally html-escaped.
func writeAny(w io.Writer, html bool, x interface{}) {
	switch v := x.(type) {
	case []byte:
		writeText(w, v, html)
	case string:
		writeText(w, []byte(v), html)
	default:
		if html {
			var buf bytes.Buffer
			fmt.Fprint(&buf, x)
			writeText(w, buf.Bytes(), true)
		} else {
			fmt.Fprint(w, x)
		}
	}
}

// Write text to w; optionally html-escaped.
func writeText(w io.Writer, text []byte, html bool) {
	if html {
		template.HTMLEscape(w, text)
		return
	}
	w.Write(text)
}

func applyTemplate(t *template.Template, name string, data interface{}) []byte {
	var buf bytes.Buffer
	if err := t.Execute(&buf, data); err != nil {
		log.Printf("%s.Execute: %s", name, err)
	}
	return buf.Bytes()
}

func servePage(w http.ResponseWriter, title, subtitle string, content []byte) {
	d := struct {
		Title    string
		Subtitle string
		Content  []byte
	}{
		title,
		subtitle,
		content,
	}

	if err := pageHtml.Execute(w, &d); err != nil {
		log.Printf("godocHTML.Execute: %s", err)
	}
}

func readTemplate(name string) *template.Template {
	fileName := filepath.Join(*root, "tmpl", name)
	data, err := ioutil.ReadFile(fileName)
	if err != nil {
		log.Fatalf("ReadFile %s: %v", fileName, err)
	}
	t, err := template.Parse(string(data), fmap)
	if err != nil {
		log.Fatalf("%s: %v", fileName, err)
	}
	return t
}

func readTemplates() {
	pageHtml = readTemplate("page.html")
	errorHtml = readTemplate("error.html")
}

func serveError(w http.ResponseWriter, r *http.Request, relpath string, err os.Error) {
	contents := applyTemplate(errorHtml, "errorHtml", err) // err may contain an absolute path!
	w.WriteHeader(http.StatusNotFound)
	servePage(w, "File "+relpath, "", contents)
}

func mainHandler(rw http.ResponseWriter, req *http.Request) {
	relPath := req.URL.Path[1:] // serveFile URL paths start with '/'
	if strings.Contains(relPath, "..") {
		return
	}

	if strings.HasPrefix(relPath, "gw/") {
		path := relPath[3:]
		http.Redirect(rw, req, "/code/?p=camlistore.git;f="+path+";hb=master", http.StatusFound)
		return
	}

	absPath := filepath.Join(*root, "content", relPath)
	fi, err := os.Lstat(absPath)
	if err != nil {
		log.Print(err)
		serveError(rw, req, relPath, err)
		return
	}
	if fi.IsDirectory() {
		relPath += "/index.html"
		absPath = filepath.Join(*root, "content", relPath)
		fi, err = os.Lstat(absPath)
		if err != nil {
			log.Print(err)
			serveError(rw, req, relPath, err)
			return
		}
	}

	switch {
	case fi.IsRegular():
		serveFile(rw, req, relPath, absPath)
	}
}

func serveFile(rw http.ResponseWriter, req *http.Request, relPath, absPath string) {
	data, err := ioutil.ReadFile(absPath)
	if err != nil {
		serveError(rw, req, absPath, err)
		return
	}

	title := ""
	if m := h1TitlePattern.FindSubmatch(data); len(m) > 1 {
		title = string(m[1])
	}

	servePage(rw, title, "", data)
}

type gitwebHandler struct {
	Cgi    http.Handler
	Static http.Handler
}

func (h *gitwebHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	if r.URL.RawPath == "/code/" ||
		strings.HasPrefix(r.URL.RawPath, "/code/?") {
		h.Cgi.ServeHTTP(rw, r)
	} else {
		h.Static.ServeHTTP(rw, r)
	}
}

type noWwwHandler struct {
	Handler http.Handler
}

func (h *noWwwHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	host := strings.ToLower(r.Host)
	if host == "www.camlistore.org" {
		http.Redirect(rw, r, "http://camlistore.org"+r.URL.RawPath, http.StatusFound)
		return
	}
	h.Handler.ServeHTTP(rw, r)
}

func fixupGitwebFiles() {
	fi, err := os.Stat(*gitwebFiles)
	if err != nil || !fi.IsDirectory() {
		if *gitwebFiles == "/usr/share/gitweb/static" {
			// Old Debian/Ubuntu location
			*gitwebFiles = "/usr/share/gitweb"
		}
	}
}

func main() {
	flag.Parse()
	readTemplates()

	if *root == "" {
		var err os.Error
		*root, err = os.Getwd()
		if err != nil {
			log.Fatalf("Failed to getwd: %v", err)
		}
	}

	fixupGitwebFiles()

	latestGits := filepath.Join(*root, "latestgits")
	os.Mkdir(latestGits, 0700)
	if *gerritHost != "" {
		go rsyncFromGerrit(latestGits)
	}

	mux := http.DefaultServeMux
	mux.Handle("/favicon.ico", http.FileServer(http.Dir(filepath.Join(*root, "static"))))
	mux.Handle("/robots.txt", http.FileServer(http.Dir(filepath.Join(*root, "static"))))
	mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(filepath.Join(*root, "static")))))
	mux.Handle("/talks/", http.StripPrefix("/talks/", http.FileServer(http.Dir(filepath.Join(*root, "talks")))))

	gerritUrl, _ := url.Parse("http://gerrit-proxy:8000/")
	var gerritHandler http.Handler = http.NewSingleHostReverseProxy(gerritUrl)
	if *httpsAddr != "" {
		proxyHandler := gerritHandler
		gerritHandler = http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
			if req.TLS != nil {
				proxyHandler.ServeHTTP(rw, req)
				return
			}
			http.Redirect(rw, req, "https://camlistore.org"+req.URL.RawPath, http.StatusFound)
		})
	}
	mux.Handle("/r/", gerritHandler)

	testCgi := &cgi.Handler{Path: filepath.Join(*root, "test.cgi"),
		Root: "/test.cgi",
	}
	mux.Handle("/test.cgi", testCgi)
	mux.Handle("/test.cgi/foo", testCgi)

	mux.Handle("/code", http.RedirectHandler("/code/", http.StatusFound))
	if *gitwebScript != "" {
		env := os.Environ()
		env = append(env, "GITWEB_CONFIG="+filepath.Join(*root, "gitweb-camli.conf"))
		env = append(env, "CAMWEB_ROOT="+filepath.Join(*root))
		env = append(env, "CAMWEB_GITDIR="+latestGits)
		mux.Handle("/code/", &fixUpGitwebUrls{&gitwebHandler{
			Cgi: &cgi.Handler{
				Path: *gitwebScript,
				Root: "/code/",
				Env:  env,
			},
			Static: http.StripPrefix("/code/", http.FileServer(http.Dir(*gitwebFiles))),
		}})
	}
	mux.HandleFunc("/", mainHandler)

	var handler http.Handler = &noWwwHandler{Handler: mux}
	if *logDir != "" || *logStdout {
		handler = NewLoggingHandler(handler, *logDir, *logStdout)
	}

	errch := make(chan os.Error)

	httpServer := &http.Server{
		Addr:         *httpAddr,
		Handler:      handler,
		ReadTimeout:  connTimeoutNanos,
		WriteTimeout: connTimeoutNanos,
	}
	go func() {
		errch <- httpServer.ListenAndServe()
	}()

	if *httpsAddr != "" {
		log.Printf("Starting TLS server on %s", *httpsAddr)
		httpsServer := new(http.Server)
		*httpsServer = *httpServer
		httpsServer.Addr = *httpsAddr
		go func() {
			errch <- httpsServer.ListenAndServeTLS(*tlsCertFile, *tlsKeyFile)
		}()
	}

	log.Fatalf("Serve error: %v", <-errch)
}

const connTimeoutNanos = 15e9

type fixUpGitwebUrls struct {
	handler http.Handler
}

// Not sure what's making these broken URLs like:
//
//   http://localhost:8080/code/?p=camlistore.git%3Bf=doc/json-signing/json-signing.txt%3Bhb=master
//
// ... but something is.  Maybe Buzz?  For now just re-write them
// . Doesn't seem to be a bug in the CGI implementation, though, which
// is what I'd originally suspected.
func (fu *fixUpGitwebUrls) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	oldUrl := req.RawURL
	newUrl := strings.Replace(oldUrl, "%3B", ";", -1)
	if newUrl == oldUrl {
		fu.handler.ServeHTTP(rw, req)
		return
	}
	http.Redirect(rw, req, newUrl, http.StatusFound)
}

func rsyncFromGerrit(dest string) {
	for {
		err := exec.Command("rsync", "-avPW", *gerritUser+"@"+*gerritHost+":gerrit/git/", dest+"/").Run()
		if err != nil {
			log.Printf("rsync from gerrit = %v", err)
		}
		time.Sleep(10e9)
	}
}
<commit_msg>add handler to return IP address.  for ec2 wiring.<commit_after>/*
Copyright 2011 Google Inc.

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 main

import (
	"bytes"
	"exec"
	"flag"
	"fmt"
	"http"
	"http/cgi"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"old/template"
	"time"
	"url"
)

const defaultAddr = ":31798" // default webserver address

var h1TitlePattern = regexp.MustCompile(`<h1>([^<]+)</h1>`)

var (
	httpAddr            = flag.String("http", defaultAddr, "HTTP service address (e.g., '"+defaultAddr+"')")
	httpsAddr           = flag.String("https", "", "HTTPS service address")
	root                = flag.String("root", "", "Website root (parent of 'static', 'content', and 'tmpl")
	gitwebScript        = flag.String("gitwebscript", "/usr/lib/cgi-bin/gitweb.cgi", "Path to gitweb.cgi, or blank to disable.")
	gitwebFiles         = flag.String("gitwebfiles", "/usr/share/gitweb/static", "Path to gitweb's static files.")
	logDir              = flag.String("logdir", "", "Directory to write log files to (one per hour), or empty to not log.")
	logStdout           = flag.Bool("logstdout", true, "Write to stdout?")
	tlsCertFile         = flag.String("tlscert", "", "TLS cert file")
	tlsKeyFile          = flag.String("tlskey", "", "TLS private key file")
	gerritUser          = flag.String("gerrituser", "ubuntu", "Gerrit host's username")
	gerritHost          = flag.String("gerrithost", "", "Gerrit host, or empty.")
	pageHtml, errorHtml *template.Template
)

var fmap = template.FormatterMap{
	"":         textFmt,
	"html":     htmlFmt,
	"html-esc": htmlEscFmt,
}

// Template formatter for "" (default) format.
func textFmt(w io.Writer, format string, x ...interface{}) {
	writeAny(w, false, x[0])
}

// Template formatter for "html" format.
func htmlFmt(w io.Writer, format string, x ...interface{}) {
	writeAny(w, true, x[0])
}

// Template formatter for "html-esc" format.
func htmlEscFmt(w io.Writer, format string, x ...interface{}) {
	var buf bytes.Buffer
	writeAny(&buf, false, x[0])
	template.HTMLEscape(w, buf.Bytes())
}

// Write anything to w; optionally html-escaped.
func writeAny(w io.Writer, html bool, x interface{}) {
	switch v := x.(type) {
	case []byte:
		writeText(w, v, html)
	case string:
		writeText(w, []byte(v), html)
	default:
		if html {
			var buf bytes.Buffer
			fmt.Fprint(&buf, x)
			writeText(w, buf.Bytes(), true)
		} else {
			fmt.Fprint(w, x)
		}
	}
}

// Write text to w; optionally html-escaped.
func writeText(w io.Writer, text []byte, html bool) {
	if html {
		template.HTMLEscape(w, text)
		return
	}
	w.Write(text)
}

func applyTemplate(t *template.Template, name string, data interface{}) []byte {
	var buf bytes.Buffer
	if err := t.Execute(&buf, data); err != nil {
		log.Printf("%s.Execute: %s", name, err)
	}
	return buf.Bytes()
}

func servePage(w http.ResponseWriter, title, subtitle string, content []byte) {
	d := struct {
		Title    string
		Subtitle string
		Content  []byte
	}{
		title,
		subtitle,
		content,
	}

	if err := pageHtml.Execute(w, &d); err != nil {
		log.Printf("godocHTML.Execute: %s", err)
	}
}

func readTemplate(name string) *template.Template {
	fileName := filepath.Join(*root, "tmpl", name)
	data, err := ioutil.ReadFile(fileName)
	if err != nil {
		log.Fatalf("ReadFile %s: %v", fileName, err)
	}
	t, err := template.Parse(string(data), fmap)
	if err != nil {
		log.Fatalf("%s: %v", fileName, err)
	}
	return t
}

func readTemplates() {
	pageHtml = readTemplate("page.html")
	errorHtml = readTemplate("error.html")
}

func serveError(w http.ResponseWriter, r *http.Request, relpath string, err os.Error) {
	contents := applyTemplate(errorHtml, "errorHtml", err) // err may contain an absolute path!
	w.WriteHeader(http.StatusNotFound)
	servePage(w, "File "+relpath, "", contents)
}

func mainHandler(rw http.ResponseWriter, req *http.Request) {
	relPath := req.URL.Path[1:] // serveFile URL paths start with '/'
	if strings.Contains(relPath, "..") {
		return
	}

	if strings.HasPrefix(relPath, "gw/") {
		path := relPath[3:]
		http.Redirect(rw, req, "/code/?p=camlistore.git;f="+path+";hb=master", http.StatusFound)
		return
	}

	absPath := filepath.Join(*root, "content", relPath)
	fi, err := os.Lstat(absPath)
	if err != nil {
		log.Print(err)
		serveError(rw, req, relPath, err)
		return
	}
	if fi.IsDirectory() {
		relPath += "/index.html"
		absPath = filepath.Join(*root, "content", relPath)
		fi, err = os.Lstat(absPath)
		if err != nil {
			log.Print(err)
			serveError(rw, req, relPath, err)
			return
		}
	}

	switch {
	case fi.IsRegular():
		serveFile(rw, req, relPath, absPath)
	}
}

func serveFile(rw http.ResponseWriter, req *http.Request, relPath, absPath string) {
	data, err := ioutil.ReadFile(absPath)
	if err != nil {
		serveError(rw, req, absPath, err)
		return
	}

	title := ""
	if m := h1TitlePattern.FindSubmatch(data); len(m) > 1 {
		title = string(m[1])
	}

	servePage(rw, title, "", data)
}

type gitwebHandler struct {
	Cgi    http.Handler
	Static http.Handler
}

func (h *gitwebHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	if r.URL.RawPath == "/code/" ||
		strings.HasPrefix(r.URL.RawPath, "/code/?") {
		h.Cgi.ServeHTTP(rw, r)
	} else {
		h.Static.ServeHTTP(rw, r)
	}
}

type noWwwHandler struct {
	Handler http.Handler
}

func (h *noWwwHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	host := strings.ToLower(r.Host)
	if host == "www.camlistore.org" {
		http.Redirect(rw, r, "http://camlistore.org"+r.URL.RawPath, http.StatusFound)
		return
	}
	h.Handler.ServeHTTP(rw, r)
}

func fixupGitwebFiles() {
	fi, err := os.Stat(*gitwebFiles)
	if err != nil || !fi.IsDirectory() {
		if *gitwebFiles == "/usr/share/gitweb/static" {
			// Old Debian/Ubuntu location
			*gitwebFiles = "/usr/share/gitweb"
		}
	}
}

func main() {
	flag.Parse()
	readTemplates()

	if *root == "" {
		var err os.Error
		*root, err = os.Getwd()
		if err != nil {
			log.Fatalf("Failed to getwd: %v", err)
		}
	}

	fixupGitwebFiles()

	latestGits := filepath.Join(*root, "latestgits")
	os.Mkdir(latestGits, 0700)
	if *gerritHost != "" {
		go rsyncFromGerrit(latestGits)
	}

	mux := http.DefaultServeMux
	mux.Handle("/favicon.ico", http.FileServer(http.Dir(filepath.Join(*root, "static"))))
	mux.Handle("/robots.txt", http.FileServer(http.Dir(filepath.Join(*root, "static"))))
	mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(filepath.Join(*root, "static")))))
	mux.Handle("/talks/", http.StripPrefix("/talks/", http.FileServer(http.Dir(filepath.Join(*root, "talks")))))

	gerritUrl, _ := url.Parse("http://gerrit-proxy:8000/")
	var gerritHandler http.Handler = http.NewSingleHostReverseProxy(gerritUrl)
	if *httpsAddr != "" {
		proxyHandler := gerritHandler
		gerritHandler = http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
			if req.TLS != nil {
				proxyHandler.ServeHTTP(rw, req)
				return
			}
			http.Redirect(rw, req, "https://camlistore.org"+req.URL.RawPath, http.StatusFound)
		})
	}
	mux.Handle("/r/", gerritHandler)
	mux.HandleFunc("/debugz/ip", ipHandler)

	testCgi := &cgi.Handler{Path: filepath.Join(*root, "test.cgi"),
		Root: "/test.cgi",
	}
	mux.Handle("/test.cgi", testCgi)
	mux.Handle("/test.cgi/foo", testCgi)
	mux.Handle("/code", http.RedirectHandler("/code/", http.StatusFound))
	if *gitwebScript != "" {
		env := os.Environ()
		env = append(env, "GITWEB_CONFIG="+filepath.Join(*root, "gitweb-camli.conf"))
		env = append(env, "CAMWEB_ROOT="+filepath.Join(*root))
		env = append(env, "CAMWEB_GITDIR="+latestGits)
		mux.Handle("/code/", &fixUpGitwebUrls{&gitwebHandler{
			Cgi: &cgi.Handler{
				Path: *gitwebScript,
				Root: "/code/",
				Env:  env,
			},
			Static: http.StripPrefix("/code/", http.FileServer(http.Dir(*gitwebFiles))),
		}})
	}
	mux.HandleFunc("/", mainHandler)

	var handler http.Handler = &noWwwHandler{Handler: mux}
	if *logDir != "" || *logStdout {
		handler = NewLoggingHandler(handler, *logDir, *logStdout)
	}

	errch := make(chan os.Error)

	httpServer := &http.Server{
		Addr:         *httpAddr,
		Handler:      handler,
		ReadTimeout:  connTimeoutNanos,
		WriteTimeout: connTimeoutNanos,
	}
	go func() {
		errch <- httpServer.ListenAndServe()
	}()

	if *httpsAddr != "" {
		log.Printf("Starting TLS server on %s", *httpsAddr)
		httpsServer := new(http.Server)
		*httpsServer = *httpServer
		httpsServer.Addr = *httpsAddr
		go func() {
			errch <- httpsServer.ListenAndServeTLS(*tlsCertFile, *tlsKeyFile)
		}()
	}

	log.Fatalf("Serve error: %v", <-errch)
}

const connTimeoutNanos = 15e9

type fixUpGitwebUrls struct {
	handler http.Handler
}

// Not sure what's making these broken URLs like:
//
//   http://localhost:8080/code/?p=camlistore.git%3Bf=doc/json-signing/json-signing.txt%3Bhb=master
//
// ... but something is.  Maybe Buzz?  For now just re-write them
// . Doesn't seem to be a bug in the CGI implementation, though, which
// is what I'd originally suspected.
func (fu *fixUpGitwebUrls) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	oldUrl := req.RawURL
	newUrl := strings.Replace(oldUrl, "%3B", ";", -1)
	if newUrl == oldUrl {
		fu.handler.ServeHTTP(rw, req)
		return
	}
	http.Redirect(rw, req, newUrl, http.StatusFound)
}

func rsyncFromGerrit(dest string) {
	for {
		err := exec.Command("rsync", "-avPW", *gerritUser+"@"+*gerritHost+":gerrit/git/", dest+"/").Run()
		if err != nil {
			log.Printf("rsync from gerrit = %v", err)
		}
		time.Sleep(10e9)
	}
}

func ipHandler(w http.ResponseWriter, r *http.Request) {
	out, _ := exec.Command("ip", "-f", "inet", "addr", "show", "dev", "eth0").Output()
	str := string(out)
	pos := strings.Index(str, "inet ")
	if pos == -1 {
		return
	}
	str = str[pos + 5:]
	pos = strings.Index(str, "/")
	if pos == -1 {
		return
	}
	str = str[:pos]
	w.Write([]byte(str))
}
<|endoftext|>