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

package io_test

import (
	"bytes"
	"crypto/sha1"
	"errors"
	"fmt"
	. "io"
	"io/ioutil"
	"runtime"
	"strings"
	"testing"
	"time"
)

func TestMultiReader(t *testing.T) {
	var mr Reader
	var buf []byte
	nread := 0
	withFooBar := func(tests func()) {
		r1 := strings.NewReader("foo ")
		r2 := strings.NewReader("")
		r3 := strings.NewReader("bar")
		mr = MultiReader(r1, r2, r3)
		buf = make([]byte, 20)
		tests()
	}
	expectRead := func(size int, expected string, eerr error) {
		nread++
		n, gerr := mr.Read(buf[0:size])
		if n != len(expected) {
			t.Errorf("#%d, expected %d bytes; got %d",
				nread, len(expected), n)
		}
		got := string(buf[0:n])
		if got != expected {
			t.Errorf("#%d, expected %q; got %q",
				nread, expected, got)
		}
		if gerr != eerr {
			t.Errorf("#%d, expected error %v; got %v",
				nread, eerr, gerr)
		}
		buf = buf[n:]
	}
	withFooBar(func() {
		expectRead(2, "fo", nil)
		expectRead(5, "o ", nil)
		expectRead(5, "bar", nil)
		expectRead(5, "", EOF)
	})
	withFooBar(func() {
		expectRead(4, "foo ", nil)
		expectRead(1, "b", nil)
		expectRead(3, "ar", nil)
		expectRead(1, "", EOF)
	})
	withFooBar(func() {
		expectRead(5, "foo ", nil)
	})
}

func TestMultiWriter(t *testing.T) {
	sink := new(bytes.Buffer)
	// Hide bytes.Buffer's WriteString method:
	testMultiWriter(t, struct {
		Writer
		fmt.Stringer
	}{sink, sink})
}

func TestMultiWriter_String(t *testing.T) {
	testMultiWriter(t, new(bytes.Buffer))
}

// test that a multiWriter.WriteString calls results in at most 1 allocation,
// even if multiple targets don't support WriteString.
func TestMultiWriter_WriteStringSingleAlloc(t *testing.T) {
	var sink1, sink2 bytes.Buffer
	type simpleWriter struct { // hide bytes.Buffer's WriteString
		Writer
	}
	mw := MultiWriter(simpleWriter{&sink1}, simpleWriter{&sink2})
	allocs := int(testing.AllocsPerRun(1000, func() {
		WriteString(mw, "foo")
	}))
	if allocs != 1 {
		t.Errorf("num allocations = %d; want 1", allocs)
	}
}

type writeStringChecker struct{ called bool }

func (c *writeStringChecker) WriteString(s string) (n int, err error) {
	c.called = true
	return len(s), nil
}

func (c *writeStringChecker) Write(p []byte) (n int, err error) {
	return len(p), nil
}

func TestMultiWriter_StringCheckCall(t *testing.T) {
	var c writeStringChecker
	mw := MultiWriter(&c)
	WriteString(mw, "foo")
	if !c.called {
		t.Error("did not see WriteString call to writeStringChecker")
	}
}

func testMultiWriter(t *testing.T, sink interface {
	Writer
	fmt.Stringer
}) {
	sha1 := sha1.New()
	mw := MultiWriter(sha1, sink)

	sourceString := "My input text."
	source := strings.NewReader(sourceString)
	written, err := Copy(mw, source)

	if written != int64(len(sourceString)) {
		t.Errorf("short write of %d, not %d", written, len(sourceString))
	}

	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}

	sha1hex := fmt.Sprintf("%x", sha1.Sum(nil))
	if sha1hex != "01cb303fa8c30a64123067c5aa6284ba7ec2d31b" {
		t.Error("incorrect sha1 value")
	}

	if sink.String() != sourceString {
		t.Errorf("expected %q; got %q", sourceString, sink.String())
	}
}

// Test that MultiReader copies the input slice and is insulated from future modification.
func TestMultiReaderCopy(t *testing.T) {
	slice := []Reader{strings.NewReader("hello world")}
	r := MultiReader(slice...)
	slice[0] = nil
	data, err := ioutil.ReadAll(r)
	if err != nil || string(data) != "hello world" {
		t.Errorf("ReadAll() = %q, %v, want %q, nil", data, err, "hello world")
	}
}

// Test that MultiWriter copies the input slice and is insulated from future modification.
func TestMultiWriterCopy(t *testing.T) {
	var buf bytes.Buffer
	slice := []Writer{&buf}
	w := MultiWriter(slice...)
	slice[0] = nil
	n, err := w.Write([]byte("hello world"))
	if err != nil || n != 11 {
		t.Errorf("Write(`hello world`) = %d, %v, want 11, nil", n, err)
	}
	if buf.String() != "hello world" {
		t.Errorf("buf.String() = %q, want %q", buf.String(), "hello world")
	}
}

// readerFunc is an io.Reader implemented by the underlying func.
type readerFunc func(p []byte) (int, error)

func (f readerFunc) Read(p []byte) (int, error) {
	return f(p)
}

// Test that MultiReader properly flattens chained multiReaders when Read is called
func TestMultiReaderFlatten(t *testing.T) {
	pc := make([]uintptr, 1000) // 1000 should fit the full stack
	var myDepth = runtime.Callers(0, pc)
	var readDepth int // will contain the depth from which fakeReader.Read was called
	var r Reader = MultiReader(readerFunc(func(p []byte) (int, error) {
		readDepth = runtime.Callers(1, pc)
		return 0, errors.New("irrelevant")
	}))

	// chain a bunch of multiReaders
	for i := 0; i < 100; i++ {
		r = MultiReader(r)
	}

	r.Read(nil) // don't care about errors, just want to check the call-depth for Read

	if readDepth != myDepth+2 { // 2 should be multiReader.Read and fakeReader.Read
		t.Errorf("multiReader did not flatten chained multiReaders: expected readDepth %d, got %d",
			myDepth+2, readDepth)
	}
}

// byteAndEOFReader is a Reader which reads one byte (the underlying
// byte) and io.EOF at once in its Read call.
type byteAndEOFReader byte

func (b byteAndEOFReader) Read(p []byte) (n int, err error) {
	if len(p) == 0 {
		// Read(0 bytes) is useless. We expect no such useless
		// calls in this test.
		panic("unexpected call")
	}
	p[0] = byte(b)
	return 1, EOF
}

// This used to yield bytes forever; issue 16795.
func TestMultiReaderSingleByteWithEOF(t *testing.T) {
	got, err := ioutil.ReadAll(LimitReader(MultiReader(byteAndEOFReader('a'), byteAndEOFReader('b')), 10))
	if err != nil {
		t.Fatal(err)
	}
	const want = "ab"
	if string(got) != want {
		t.Errorf("got %q; want %q", got, want)
	}
}

// Test that a reader returning (n, EOF) at the end of an MultiReader
// chain continues to return EOF on its final read, rather than
// yielding a (0, EOF).
func TestMultiReaderFinalEOF(t *testing.T) {
	r := MultiReader(bytes.NewReader(nil), byteAndEOFReader('a'))
	buf := make([]byte, 2)
	n, err := r.Read(buf)
	if n != 1 || err != EOF {
		t.Errorf("got %v, %v; want 1, EOF", n, err)
	}
}

func TestMultiReaderFreesExhaustedReaders(t *testing.T) {
	var mr Reader
	closed := make(chan struct{})
	{
		buf1 := bytes.NewReader([]byte("foo"))
		buf2 := bytes.NewReader([]byte("bar"))
		mr = MultiReader(buf1, buf2)
		runtime.SetFinalizer(buf1, func(*bytes.Reader) {
			close(closed)
		})
	}

	buf := make([]byte, 4)
	if n, err := ReadFull(mr, buf); err != nil || string(buf) != "foob" {
		t.Fatalf(`ReadFull = %d (%q), %v; want 3, "foo", nil`, n, buf[:n], err)
	}

	runtime.GC()
	select {
	case <-closed:
	case <-time.After(5 * time.Second):
		t.Fatal("timeout waiting for collection of buf1")
	}

	if n, err := ReadFull(mr, buf[:2]); err != nil || string(buf[:2]) != "ar" {
		t.Fatalf(`ReadFull = %d (%q), %v; want 2, "ar", nil`, n, buf[:n], err)
	}
}

func TestInterleavedMultiReader(t *testing.T) {
	r1 := strings.NewReader("123")
	r2 := strings.NewReader("45678")

	mr1 := MultiReader(r1, r2)
	mr2 := MultiReader(mr1)

	buf := make([]byte, 4)

	// Have mr2 use mr1's []Readers.
	// Consume r1 (and clear it for GC to handle) and consume part of r2.
	n, err := ReadFull(mr2, buf)
	if got := string(buf[:n]); got != "1234" || err != nil {
		t.Errorf(`ReadFull(mr2) = (%q, %v), want ("1234", nil)`, got, err)
	}

	// Consume the rest of r2 via mr1.
	// This should not panic even though mr2 cleared r1.
	n, err = ReadFull(mr1, buf)
	if got := string(buf[:n]); got != "5678" || err != nil {
		t.Errorf(`ReadFull(mr1) = (%q, %v), want ("5678", nil)`, got, err)
	}
}
<commit_msg>io: fix test when MultiReader is inlined with -l=3<commit_after>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package io_test

import (
	"bytes"
	"crypto/sha1"
	"errors"
	"fmt"
	. "io"
	"io/ioutil"
	"runtime"
	"strings"
	"testing"
	"time"
)

func TestMultiReader(t *testing.T) {
	var mr Reader
	var buf []byte
	nread := 0
	withFooBar := func(tests func()) {
		r1 := strings.NewReader("foo ")
		r2 := strings.NewReader("")
		r3 := strings.NewReader("bar")
		mr = MultiReader(r1, r2, r3)
		buf = make([]byte, 20)
		tests()
	}
	expectRead := func(size int, expected string, eerr error) {
		nread++
		n, gerr := mr.Read(buf[0:size])
		if n != len(expected) {
			t.Errorf("#%d, expected %d bytes; got %d",
				nread, len(expected), n)
		}
		got := string(buf[0:n])
		if got != expected {
			t.Errorf("#%d, expected %q; got %q",
				nread, expected, got)
		}
		if gerr != eerr {
			t.Errorf("#%d, expected error %v; got %v",
				nread, eerr, gerr)
		}
		buf = buf[n:]
	}
	withFooBar(func() {
		expectRead(2, "fo", nil)
		expectRead(5, "o ", nil)
		expectRead(5, "bar", nil)
		expectRead(5, "", EOF)
	})
	withFooBar(func() {
		expectRead(4, "foo ", nil)
		expectRead(1, "b", nil)
		expectRead(3, "ar", nil)
		expectRead(1, "", EOF)
	})
	withFooBar(func() {
		expectRead(5, "foo ", nil)
	})
}

func TestMultiWriter(t *testing.T) {
	sink := new(bytes.Buffer)
	// Hide bytes.Buffer's WriteString method:
	testMultiWriter(t, struct {
		Writer
		fmt.Stringer
	}{sink, sink})
}

func TestMultiWriter_String(t *testing.T) {
	testMultiWriter(t, new(bytes.Buffer))
}

// test that a multiWriter.WriteString calls results in at most 1 allocation,
// even if multiple targets don't support WriteString.
func TestMultiWriter_WriteStringSingleAlloc(t *testing.T) {
	var sink1, sink2 bytes.Buffer
	type simpleWriter struct { // hide bytes.Buffer's WriteString
		Writer
	}
	mw := MultiWriter(simpleWriter{&sink1}, simpleWriter{&sink2})
	allocs := int(testing.AllocsPerRun(1000, func() {
		WriteString(mw, "foo")
	}))
	if allocs != 1 {
		t.Errorf("num allocations = %d; want 1", allocs)
	}
}

type writeStringChecker struct{ called bool }

func (c *writeStringChecker) WriteString(s string) (n int, err error) {
	c.called = true
	return len(s), nil
}

func (c *writeStringChecker) Write(p []byte) (n int, err error) {
	return len(p), nil
}

func TestMultiWriter_StringCheckCall(t *testing.T) {
	var c writeStringChecker
	mw := MultiWriter(&c)
	WriteString(mw, "foo")
	if !c.called {
		t.Error("did not see WriteString call to writeStringChecker")
	}
}

func testMultiWriter(t *testing.T, sink interface {
	Writer
	fmt.Stringer
}) {
	sha1 := sha1.New()
	mw := MultiWriter(sha1, sink)

	sourceString := "My input text."
	source := strings.NewReader(sourceString)
	written, err := Copy(mw, source)

	if written != int64(len(sourceString)) {
		t.Errorf("short write of %d, not %d", written, len(sourceString))
	}

	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}

	sha1hex := fmt.Sprintf("%x", sha1.Sum(nil))
	if sha1hex != "01cb303fa8c30a64123067c5aa6284ba7ec2d31b" {
		t.Error("incorrect sha1 value")
	}

	if sink.String() != sourceString {
		t.Errorf("expected %q; got %q", sourceString, sink.String())
	}
}

// Test that MultiReader copies the input slice and is insulated from future modification.
func TestMultiReaderCopy(t *testing.T) {
	slice := []Reader{strings.NewReader("hello world")}
	r := MultiReader(slice...)
	slice[0] = nil
	data, err := ioutil.ReadAll(r)
	if err != nil || string(data) != "hello world" {
		t.Errorf("ReadAll() = %q, %v, want %q, nil", data, err, "hello world")
	}
}

// Test that MultiWriter copies the input slice and is insulated from future modification.
func TestMultiWriterCopy(t *testing.T) {
	var buf bytes.Buffer
	slice := []Writer{&buf}
	w := MultiWriter(slice...)
	slice[0] = nil
	n, err := w.Write([]byte("hello world"))
	if err != nil || n != 11 {
		t.Errorf("Write(`hello world`) = %d, %v, want 11, nil", n, err)
	}
	if buf.String() != "hello world" {
		t.Errorf("buf.String() = %q, want %q", buf.String(), "hello world")
	}
}

// readerFunc is an io.Reader implemented by the underlying func.
type readerFunc func(p []byte) (int, error)

func (f readerFunc) Read(p []byte) (int, error) {
	return f(p)
}

// Test that MultiReader properly flattens chained multiReaders when Read is called
func TestMultiReaderFlatten(t *testing.T) {
	pc := make([]uintptr, 1000) // 1000 should fit the full stack
	var myDepth = runtime.Callers(0, pc)
	var readDepth int // will contain the depth from which fakeReader.Read was called
	var r Reader = MultiReader(readerFunc(func(p []byte) (int, error) {
		readDepth = runtime.Callers(1, pc)
		return 0, errors.New("irrelevant")
	}))

	// chain a bunch of multiReaders
	for i := 0; i < 100; i++ {
		r = MultiReader(r)
	}

	r.Read(nil) // don't care about errors, just want to check the call-depth for Read

	if readDepth != myDepth+2 { // 2 should be multiReader.Read and fakeReader.Read
		t.Errorf("multiReader did not flatten chained multiReaders: expected readDepth %d, got %d",
			myDepth+2, readDepth)
	}
}

// byteAndEOFReader is a Reader which reads one byte (the underlying
// byte) and io.EOF at once in its Read call.
type byteAndEOFReader byte

func (b byteAndEOFReader) Read(p []byte) (n int, err error) {
	if len(p) == 0 {
		// Read(0 bytes) is useless. We expect no such useless
		// calls in this test.
		panic("unexpected call")
	}
	p[0] = byte(b)
	return 1, EOF
}

// This used to yield bytes forever; issue 16795.
func TestMultiReaderSingleByteWithEOF(t *testing.T) {
	got, err := ioutil.ReadAll(LimitReader(MultiReader(byteAndEOFReader('a'), byteAndEOFReader('b')), 10))
	if err != nil {
		t.Fatal(err)
	}
	const want = "ab"
	if string(got) != want {
		t.Errorf("got %q; want %q", got, want)
	}
}

// Test that a reader returning (n, EOF) at the end of an MultiReader
// chain continues to return EOF on its final read, rather than
// yielding a (0, EOF).
func TestMultiReaderFinalEOF(t *testing.T) {
	r := MultiReader(bytes.NewReader(nil), byteAndEOFReader('a'))
	buf := make([]byte, 2)
	n, err := r.Read(buf)
	if n != 1 || err != EOF {
		t.Errorf("got %v, %v; want 1, EOF", n, err)
	}
}

func TestMultiReaderFreesExhaustedReaders(t *testing.T) {
	var mr Reader
	closed := make(chan struct{})
	// The closure ensures that we don't have a live reference to buf1
	// on our stack after MultiReader is inlined (Issue 18819).  This
	// is a work around for a limitation in liveness analysis.
	func() {
		buf1 := bytes.NewReader([]byte("foo"))
		buf2 := bytes.NewReader([]byte("bar"))
		mr = MultiReader(buf1, buf2)
		runtime.SetFinalizer(buf1, func(*bytes.Reader) {
			close(closed)
		})
	}()

	buf := make([]byte, 4)
	if n, err := ReadFull(mr, buf); err != nil || string(buf) != "foob" {
		t.Fatalf(`ReadFull = %d (%q), %v; want 3, "foo", nil`, n, buf[:n], err)
	}

	runtime.GC()
	select {
	case <-closed:
	case <-time.After(5 * time.Second):
		t.Fatal("timeout waiting for collection of buf1")
	}

	if n, err := ReadFull(mr, buf[:2]); err != nil || string(buf[:2]) != "ar" {
		t.Fatalf(`ReadFull = %d (%q), %v; want 2, "ar", nil`, n, buf[:n], err)
	}
}

func TestInterleavedMultiReader(t *testing.T) {
	r1 := strings.NewReader("123")
	r2 := strings.NewReader("45678")

	mr1 := MultiReader(r1, r2)
	mr2 := MultiReader(mr1)

	buf := make([]byte, 4)

	// Have mr2 use mr1's []Readers.
	// Consume r1 (and clear it for GC to handle) and consume part of r2.
	n, err := ReadFull(mr2, buf)
	if got := string(buf[:n]); got != "1234" || err != nil {
		t.Errorf(`ReadFull(mr2) = (%q, %v), want ("1234", nil)`, got, err)
	}

	// Consume the rest of r2 via mr1.
	// This should not panic even though mr2 cleared r1.
	n, err = ReadFull(mr1, buf)
	if got := string(buf[:n]); got != "5678" || err != nil {
		t.Errorf(`ReadFull(mr1) = (%q, %v), want ("5678", nil)`, got, err)
	}
}
<|endoftext|>


<commit_before>package gossip

import "net"

type MessageHandler interface {
    ProcessMessage(message SipMessage)
}

type SipTransportManager interface {
    Start()
    Send(message *SipMessage)
    AddMessageHandler(handler MessageHandler)
    RemoveMessageHandler(handler MessageHandler)
    Stop()
}

type UdpTransportManager struct {
    address *net.UDPAddr
    conn *net.UDPConn
    messageHandlers []MessageHandler
}
func NewUdpTransportManager(address *net.UDPAddr) (*UdpTransportManager, error) {
    handlers := make([]MessageHandler, 0)
    manager := UdpTransportManager{address, nil, handlers}
    return &manager, nil
}
func (transport *UdpTransportManager) Start() (error) {
    var err error = nil
    transport.conn, err = net.ListenUDP("udp", transport.address)

    if (err == nil) {
        go func() {
            parser := NewMessageParser()
            for {
                pkt := make([]byte, 0)
                _, _, _ = transport.conn.ReadFromUDP(pkt)  // TODO: DO this properly.
                go func() {
                    message, _ := parser.ParseMessage(pkt) // TODO: Handle error
                    for _, handler := range(transport.messageHandlers) {
                        handler.ProcessMessage(message)
                    }
                }()
            }
        }()
    }

    return err
}
<commit_msg>Check in working Transport Manager (UDP only).<commit_after>package gossip

import "fmt"
import "net"
import "sync"

type SipTransportManager interface {
    Start()
    Send(message *SipMessage)
    GetChannel()
    Stop()
}

type listener chan SipMessage

// notify tries to send a message to the listener.
// If the underlying channel has been closed by the receiver, return 'false';
// otherwise, return true.
func (l listener) notify(message SipMessage) (ok bool) {
    defer func() {recover()}()
    l <- message
    return true
}

type UdpTransportManager struct {
    address *net.UDPAddr
    conn *net.UDPConn
    listeners map[listener]bool
    listenerLock sync.Mutex
}

func NewUdpTransportManager(address *net.UDPAddr) (*UdpTransportManager, error) {
    listeners := make(map[listener]bool, 0)
    var listenerLock sync.Mutex
    manager := UdpTransportManager{address, nil, listeners, listenerLock}
    return &manager, nil
}

func (transport *UdpTransportManager) Start() (error) {
    var err error = nil
    transport.conn, err = net.ListenUDP("udp", transport.address)

    if (err == nil) {
        go transport.listen()
    }

    return err
}

func (transport *UdpTransportManager) GetChannel() (c chan SipMessage) {
    c = make (chan SipMessage)

    transport.listenerLock.Lock()
    transport.listeners[c] = true
    transport.listenerLock.Unlock()

    return c
}

func (transport *UdpTransportManager) listen() {
    fmt.Printf("Listening.\n")
    parser := NewMessageParser()
    buffer := make([]byte, 65507)
    for {
        num, _, err := transport.conn.ReadFromUDP(buffer)  // TODO: Do this properly.
        if (err != nil) {
            panic(err)
        }

        pkt := append([]byte(nil), buffer[:num]...)
        go transport.handlePacket(pkt, parser)
    }
}

func (transport *UdpTransportManager) handlePacket(pkt []byte, parser MessageParser) {
    message, err := parser.ParseMessage(pkt)

    // TODO: Test hack
    if (err != nil) {
        fmt.Printf("Error:\n%s\n\n", err.Error())
        return
    }

    // Dispatch the message to all registered listeners.
    // If the listener is a closed channel, remove it from the list.
    deadListeners := make([]chan SipMessage, 0)
    transport.listenerLock.Lock()
    for listener := range(transport.listeners) {
        sent := listener.notify(message)
        if !sent {
            deadListeners = append(deadListeners, listener)
        }
    }
    for _, deadListener := range(deadListeners) {
        delete(transport.listeners, deadListener)
    }
    transport.listenerLock.Unlock()
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io"
	"log"
	"mime"
	"net"
	"net/http"
	"net/textproto"
	"path"
	"strconv"
	"strings"
	"sync"
	"time"

	ftp "github.com/remogatto/ftpget"
	"golang.org/x/net/http2"
)

var dialer = &net.Dialer{
	Timeout:   30 * time.Second,
	KeepAlive: 30 * time.Second,
	DualStack: true,
}

var httpTransport = &http.Transport{
	Proxy:                 http.ProxyFromEnvironment,
	DialContext:           dialer.DialContext,
	TLSHandshakeTimeout:   10 * time.Second,
	ExpectContinueTimeout: 1 * time.Second,
}

func init() {
	httpTransport.RegisterProtocol("ftp", FTPTransport{})
}

var http2Transport = &http2.Transport{}

func dialWithExtraRootCerts(network, addr string) (net.Conn, error) {
	// Dial a TLS connection, and make sure it is valid against either the system default
	// roots or conf.ExtraRootCerts.
	serverName, _, _ := net.SplitHostPort(addr)
	conn, err := tls.DialWithDialer(dialer, network, addr, &tls.Config{
		ServerName:         serverName,
		InsecureSkipVerify: true,
	})
	if err != nil {
		return nil, err
	}
	state := conn.ConnectionState()
	serverCert := state.PeerCertificates[0]

	chains, err := serverCert.Verify(x509.VerifyOptions{
		Intermediates: certPoolWith(state.PeerCertificates[1:]),
		DNSName:       serverName,
	})
	if err == nil {
		state.VerifiedChains = chains
		return conn, nil
	}

	if conf := getConfig(); conf.ExtraRootCerts != nil {
		chains, err = serverCert.Verify(x509.VerifyOptions{
			Intermediates: certPoolWith(state.PeerCertificates[1:]),
			DNSName:       serverName,
			Roots:         conf.ExtraRootCerts,
		})
		if err == nil {
			state.VerifiedChains = chains
			return conn, nil
		}
	}

	conn.Close()
	return nil, err
}

var transportWithExtraRootCerts = &http.Transport{
	DialTLS: dialWithExtraRootCerts,
}

// A simpleTransport fetches a single file over plain HTTP, as simply as
// possible.
type simpleTransport struct{}

func (simpleTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	if req.Method != "GET" {
		return nil, fmt.Errorf("request method not supported by simpleTransport (expected GET, got %s)", req.Method)
	}
	if req.URL.Scheme != "http" {
		return nil, fmt.Errorf("URL scheme not supported by simpleTransport (expected http, got %s)", req.URL.Scheme)
	}

	host := req.URL.Host
	if _, _, err := net.SplitHostPort(host); err != nil {
		host = net.JoinHostPort(host, "80")
	}

	conn, err := dialer.Dial("tcp", host)
	if err != nil {
		return nil, fmt.Errorf("error connecting to %s: %v", host, err)
	}
	if err = req.Write(conn); err != nil {
		return nil, fmt.Errorf("error sending request for %v: %v", req.URL, err)
	}

	br := bufio.NewReader(conn)
	tp := textproto.NewReader(br)
	resp = &http.Response{
		Request: req,
	}

	// Parse the first line of the response.
	line, err := tp.ReadLine()
	if err != nil {
		if err == io.EOF {
			err = io.ErrUnexpectedEOF
		}
		return nil, err
	}
	if i := strings.IndexByte(line, ' '); i == -1 {
		return nil, fmt.Errorf("malformed HTTP response: %q", line)
	} else {
		resp.Proto = line[:i]
		resp.Status = strings.TrimLeft(line[i+1:], " ")
	}
	statusCode := resp.Status
	if i := strings.IndexByte(resp.Status, ' '); i != -1 {
		statusCode = resp.Status[:i]
	}
	if len(statusCode) != 3 {
		return nil, fmt.Errorf("malformed HTTP status code: %q", statusCode)
	}
	resp.StatusCode, err = strconv.Atoi(statusCode)
	if err != nil || resp.StatusCode < 0 {
		return nil, fmt.Errorf("malformed HTTP status code: %q", statusCode)
	}
	var ok bool
	if resp.ProtoMajor, resp.ProtoMinor, ok = http.ParseHTTPVersion(resp.Proto); !ok {
		return nil, fmt.Errorf("malformed HTTP version: %q", resp.Proto)
	}

	// Parse the response headers.
	mimeHeader, err := tp.ReadMIMEHeader()
	if err != nil {
		if err == io.EOF {
			err = io.ErrUnexpectedEOF
		}
		return nil, err
	}
	resp.Header = http.Header(mimeHeader)

	cl := resp.Header.Get("Content-Length")
	if cl != "" {
		if resp.ContentLength, err = strconv.ParseInt(cl, 10, 64); err != nil {
			return nil, fmt.Errorf("invalid Content-Length: %q", cl)
		}
	}
	resp.Header.Del("Content-Length")
	resp.Header.Del("Trailer")

	var body struct {
		io.Reader
		io.Closer
	}
	body.Reader = &io.LimitedReader{br, resp.ContentLength}
	body.Closer = conn
	resp.Body = body

	return resp, nil
}

// A connTransport is an http.RoundTripper that uses a single network
// connection.
type connTransport struct {
	Conn net.Conn

	// Done specifies an optional callback function that is called when the
	// connection is no longer readable. If the read fails with an error other
	// than io.EOF, the error is passed to Done.
	Done func(error)

	once sync.Once
	br   *bufio.Reader
}

func (ct *connTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	ct.once.Do(ct.initialize)

	if err := req.Write(ct.Conn); err != nil {
		return nil, err
	}

	return http.ReadResponse(ct.br, req)
}

func (ct *connTransport) initialize() {
	// Set up the reader goroutine.
	//
	// In order to detact as soon as possible if Conn is closed or otherwise
	// unusable, we constantly read from it in a goroutine, and pass the data
	// through a pipe to the actual reader used in ReadResponse.
	pr, pw := io.Pipe()
	ct.br = bufio.NewReader(pr)

	go func() {
		_, err := io.Copy(pw, ct.Conn)
		if ct.Done != nil {
			ct.Done(err)
		}
		pw.CloseWithError(err)
	}()
}

// An FTPTransport fetches files via FTP.
type FTPTransport struct{}

func (FTPTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	if req.Method != "GET" {
		return &http.Response{
			StatusCode: http.StatusMethodNotAllowed,
			Request:    req,
		}, nil
	}

	fullPath := req.URL.Host + req.URL.Path
	r, w := io.Pipe()
	xfer, err := ftp.GetAsync(fullPath, w)
	if err != nil {
		return nil, err
	}

	go func() {
		for stat := range xfer.Status {
			switch stat {
			case ftp.COMPLETED:
				w.Close()
				return
			case ftp.ERROR:
				err := <-xfer.Error
				log.Printf("FTP: error downloading %v: %v", req.URL, err)
				w.CloseWithError(err)
				return
			}
		}
	}()

	resp = &http.Response{
		StatusCode: 200,
		ProtoMajor: 1,
		ProtoMinor: 1,
		Request:    req,
		Body:       r,
		Header:     make(http.Header),
	}

	ext := path.Ext(req.URL.Path)
	if ext != "" {
		ct := mime.TypeByExtension(ext)
		if ct != "" {
			resp.Header.Set("Content-Type", ct)
		}
	}

	return resp, nil
}
<commit_msg>Stop reading response body when request context is canceled.<commit_after>package main

import (
	"bufio"
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io"
	"log"
	"mime"
	"net"
	"net/http"
	"net/textproto"
	"path"
	"strconv"
	"strings"
	"sync"
	"time"

	ftp "github.com/remogatto/ftpget"
	"golang.org/x/net/http2"
)

var dialer = &net.Dialer{
	Timeout:   30 * time.Second,
	KeepAlive: 30 * time.Second,
	DualStack: true,
}

var httpTransport = &http.Transport{
	Proxy:                 http.ProxyFromEnvironment,
	DialContext:           dialer.DialContext,
	TLSHandshakeTimeout:   10 * time.Second,
	ExpectContinueTimeout: 1 * time.Second,
}

func init() {
	httpTransport.RegisterProtocol("ftp", FTPTransport{})
}

var http2Transport = &http2.Transport{}

func dialWithExtraRootCerts(network, addr string) (net.Conn, error) {
	// Dial a TLS connection, and make sure it is valid against either the system default
	// roots or conf.ExtraRootCerts.
	serverName, _, _ := net.SplitHostPort(addr)
	conn, err := tls.DialWithDialer(dialer, network, addr, &tls.Config{
		ServerName:         serverName,
		InsecureSkipVerify: true,
	})
	if err != nil {
		return nil, err
	}
	state := conn.ConnectionState()
	serverCert := state.PeerCertificates[0]

	chains, err := serverCert.Verify(x509.VerifyOptions{
		Intermediates: certPoolWith(state.PeerCertificates[1:]),
		DNSName:       serverName,
	})
	if err == nil {
		state.VerifiedChains = chains
		return conn, nil
	}

	if conf := getConfig(); conf.ExtraRootCerts != nil {
		chains, err = serverCert.Verify(x509.VerifyOptions{
			Intermediates: certPoolWith(state.PeerCertificates[1:]),
			DNSName:       serverName,
			Roots:         conf.ExtraRootCerts,
		})
		if err == nil {
			state.VerifiedChains = chains
			return conn, nil
		}
	}

	conn.Close()
	return nil, err
}

var transportWithExtraRootCerts = &http.Transport{
	DialTLS: dialWithExtraRootCerts,
}

// A simpleTransport fetches a single file over plain HTTP, as simply as
// possible.
type simpleTransport struct{}

func (simpleTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	if req.Method != "GET" {
		return nil, fmt.Errorf("request method not supported by simpleTransport (expected GET, got %s)", req.Method)
	}
	if req.URL.Scheme != "http" {
		return nil, fmt.Errorf("URL scheme not supported by simpleTransport (expected http, got %s)", req.URL.Scheme)
	}

	host := req.URL.Host
	if _, _, err := net.SplitHostPort(host); err != nil {
		host = net.JoinHostPort(host, "80")
	}

	conn, err := dialer.Dial("tcp", host)
	if err != nil {
		return nil, fmt.Errorf("error connecting to %s: %v", host, err)
	}
	if err = req.Write(conn); err != nil {
		return nil, fmt.Errorf("error sending request for %v: %v", req.URL, err)
	}

	br := bufio.NewReader(conn)
	tp := textproto.NewReader(br)
	resp = &http.Response{
		Request: req,
	}

	// Parse the first line of the response.
	line, err := tp.ReadLine()
	if err != nil {
		if err == io.EOF {
			err = io.ErrUnexpectedEOF
		}
		return nil, err
	}
	if i := strings.IndexByte(line, ' '); i == -1 {
		return nil, fmt.Errorf("malformed HTTP response: %q", line)
	} else {
		resp.Proto = line[:i]
		resp.Status = strings.TrimLeft(line[i+1:], " ")
	}
	statusCode := resp.Status
	if i := strings.IndexByte(resp.Status, ' '); i != -1 {
		statusCode = resp.Status[:i]
	}
	if len(statusCode) != 3 {
		return nil, fmt.Errorf("malformed HTTP status code: %q", statusCode)
	}
	resp.StatusCode, err = strconv.Atoi(statusCode)
	if err != nil || resp.StatusCode < 0 {
		return nil, fmt.Errorf("malformed HTTP status code: %q", statusCode)
	}
	var ok bool
	if resp.ProtoMajor, resp.ProtoMinor, ok = http.ParseHTTPVersion(resp.Proto); !ok {
		return nil, fmt.Errorf("malformed HTTP version: %q", resp.Proto)
	}

	// Parse the response headers.
	mimeHeader, err := tp.ReadMIMEHeader()
	if err != nil {
		if err == io.EOF {
			err = io.ErrUnexpectedEOF
		}
		return nil, err
	}
	resp.Header = http.Header(mimeHeader)

	cl := resp.Header.Get("Content-Length")
	if cl != "" {
		if resp.ContentLength, err = strconv.ParseInt(cl, 10, 64); err != nil {
			return nil, fmt.Errorf("invalid Content-Length: %q", cl)
		}
	}
	resp.Header.Del("Content-Length")
	resp.Header.Del("Trailer")

	var body struct {
		io.Reader
		io.Closer
	}
	body.Reader = &io.LimitedReader{br, resp.ContentLength}
	body.Closer = conn
	resp.Body = body

	return resp, nil
}

// A connTransport is an http.RoundTripper that uses a single network
// connection.
type connTransport struct {
	Conn net.Conn

	// Done specifies an optional callback function that is called when the
	// connection is no longer readable. If the read fails with an error other
	// than io.EOF, the error is passed to Done.
	Done func(error)

	once sync.Once
	br   *bufio.Reader
}

func (ct *connTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	ctx := req.Context()
	select {
	case <-ctx.Done():
		return nil, ctx.Err()
	default:
		// Continue.
	}

	ct.once.Do(ct.initialize)

	if err := req.Write(ct.Conn); err != nil {
		return nil, err
	}

	resp, err = http.ReadResponse(ct.br, req)
	if err == nil {
		resp.Body = &bodyWithContext{
			ReadCloser: resp.Body,
			Ctx:        ctx,
		}
	}
	return resp, err
}

func (ct *connTransport) initialize() {
	// Set up the reader goroutine.
	//
	// In order to detact as soon as possible if Conn is closed or otherwise
	// unusable, we constantly read from it in a goroutine, and pass the data
	// through a pipe to the actual reader used in ReadResponse.
	pr, pw := io.Pipe()
	ct.br = bufio.NewReader(pr)

	go func() {
		_, err := io.Copy(pw, ct.Conn)
		if ct.Done != nil {
			ct.Done(err)
		}
		pw.CloseWithError(err)
	}()
}

// A bodyWithContext wraps a response body, and makes Read return an error if
// the associated context is canceled.
type bodyWithContext struct {
	io.ReadCloser
	Ctx context.Context
}

func (b *bodyWithContext) Read(p []byte) (n int, err error) {
	select {
	case <-b.Ctx.Done():
		return 0, b.Ctx.Err()
	default:
		return b.ReadCloser.Read(p)
	}
}

// An FTPTransport fetches files via FTP.
type FTPTransport struct{}

func (FTPTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	if req.Method != "GET" {
		return &http.Response{
			StatusCode: http.StatusMethodNotAllowed,
			Request:    req,
		}, nil
	}

	fullPath := req.URL.Host + req.URL.Path
	r, w := io.Pipe()
	xfer, err := ftp.GetAsync(fullPath, w)
	if err != nil {
		return nil, err
	}

	go func() {
		for stat := range xfer.Status {
			switch stat {
			case ftp.COMPLETED:
				w.Close()
				return
			case ftp.ERROR:
				err := <-xfer.Error
				log.Printf("FTP: error downloading %v: %v", req.URL, err)
				w.CloseWithError(err)
				return
			}
		}
	}()

	resp = &http.Response{
		StatusCode: 200,
		ProtoMajor: 1,
		ProtoMinor: 1,
		Request:    req,
		Body:       r,
		Header:     make(http.Header),
	}

	ext := path.Ext(req.URL.Path)
	if ext != "" {
		ct := mime.TypeByExtension(ext)
		if ct != "" {
			resp.Header.Set("Content-Type", ct)
		}
	}

	return resp, nil
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"testing"
	"time"
	"bytes"
	"io"
)

func WithFakeEnv(block func()) {
  oldEnv := os.Getenv("TRAQ_DATA_DIR")
  path, _ := os.Getwd()
  os.Setenv("TRAQ_DATA_DIR", path + "/fixtures")

  block()

  os.Setenv("TRAQ_DATA_DIR", oldEnv)
}

func CaptureStdout(block func()) string {
  old := os.Stdout // keep backup of the real stdout
  r, w, _ := os.Pipe()
  os.Stdout = w

  block()

  outC := make(chan string)
  // copy the output in a separate goroutine so printing can't block indefinitely
  go func() {
      var buf bytes.Buffer
      io.Copy(&buf, r)
      outC <- buf.String()
  }()

  // back to normal state
  w.Close()
  os.Stdout = old

  return <-outC
}

func TestPrintDate(t *testing.T) {
  out := CaptureStdout(func() {
    WithFakeEnv(func() {
      PrintDate("example", time.Date(1986, 9, 3, 0, 0, 0, 0, time.UTC))
    })
  })

  expected :=
`Wed Sep 03 20:00:00 +0100 1986;#birth;comment
Wed Sep 03 21:45:33 +0100 1986;#chillout;
Wed Sep 03 23:24:49 +0100 1986;stop;
%%
`
  if out != expected {
    t.Errorf("unexpected PrintDate output. Expected '%v' got '%v'", expected, out)
  }
}

func TestFilePath(t *testing.T) {
	var path string = FilePath("example", time.Date(1986, 9, 3, 0, 0, 0, 0, time.UTC))

	if path != os.Getenv("TRAQ_DATA_DIR")+"/example/1986/1986-09-03" {
		t.Errorf("FilePath = %v, want %v", path, os.Getenv("TRAQ_DATA_DIR")+"/example/1986/1986-09-03")
	}
}

func TestEmptySumFile(t *testing.T) {
	content := []string{""}
	var summed, error = SumFile(content)

	if error == nil {
		var total, ok = summed["#work"]
		if ok {
			t.Errorf("summed['#work'] = %v, should not exist", total)
		}
	} else {
		t.Errorf("parsing error %v", error)
	}
}

func TestSimpleSumFile(t *testing.T) {
	content := []string{
		"Mon Oct 28 21:45:33 +0100 2013;#work;",
		"Mon Oct 28 23:24:49 +0100 2013;stop;",
	}
	var summed, error = SumFile(content)

	if error == nil {
		var total, ok = summed["#work"]
		if total != 5956 || !ok {
			t.Errorf("summed['#work'] = %v, want %v", total, 5956)
		}
	} else {
		t.Errorf("parsing error %v", error)
	}
}

func TestNoStopSumFile(t *testing.T) {
	content := []string{
		"Mon Oct 28 20:00:00 +0100 2013;#play;",
		"Mon Oct 28 21:45:33 +0100 2013;#work;",
		"Mon Oct 28 23:24:49 +0100 2013;stop;",
	}
	var summed, error = SumFile(content)

	if error == nil {
		var total, ok = summed["#play"]
		if total != 6333 || !ok {
			t.Errorf("summed['#play'] = %v, want %v", total, 6333)
		}
		total, ok = summed["#work"]
		if total != 5956 || !ok {
			t.Errorf("summed['#work'] = %v, want %v", total, 5956)
		}
	} else {
		t.Errorf("parsing error %v", error)
	}
}
func TestWithStopSumFile(t *testing.T) {
	content := []string{
		"Mon Oct 28 20:00:00 +0100 2013;#play;",
		"Mon Oct 28 21:45:33 +0100 2013;stop;",
		"Mon Oct 28 21:45:33 +0100 2013;#work;",
		"Mon Oct 28 23:24:49 +0100 2013;stop;",
	}
	var summed, error = SumFile(content)

	if error == nil {
		var total, ok = summed["#play"]
		if total != 6333 || !ok {
			t.Errorf("summed['#play'] = %v, want %v", total, 6333)
		}
		total, ok = summed["#work"]
		if total != 5956 || !ok {
			t.Errorf("summed['#work'] = %v, want %v", total, 5956)
		}
	} else {
		t.Errorf("parsing error %v", error)
	}
}
<commit_msg>improve test coverage<commit_after>package main

import (
	"os"
	"testing"
	"time"
	"bytes"
	"io"
)

// change traq env to use our fixtures
func WithFakeEnv(block func()) {
  oldEnv := os.Getenv("TRAQ_DATA_DIR")
  path, _ := os.Getwd()
  os.Setenv("TRAQ_DATA_DIR", path + "/fixtures")

  block()

  os.Setenv("TRAQ_DATA_DIR", oldEnv)
}

// capture output written to os.Stdout and return it
func CaptureStdout(block func()) string {
  old := os.Stdout // keep backup of the real stdout
  r, w, _ := os.Pipe()
  os.Stdout = w

  block()

  outC := make(chan string)
  // copy the output in a separate goroutine so printing can't block indefinitely
  go func() {
      var buf bytes.Buffer
      io.Copy(&buf, r)
      outC <- buf.String()
  }()

  // back to normal state
  w.Close()
  os.Stdout = old

  return <-outC
}

func TestDatesInMonth(t *testing.T) {
  dates := DatesInMonth(1986, 9)

  if len(dates) != 30 {
    t.Errorf("expected 30 days in Sep 1986, got %v", len(dates))
  }

  if dates[0].Weekday() != time.Monday {
    t.Errorf("Started on a Monday, got %v", dates[0].Weekday())
  }

  if dates[len(dates) - 1].Weekday() != time.Tuesday {
    t.Errorf("Ended on a Tuesday, got %v", dates[len(dates) - 1].Weekday())
  }
}

func TestPrintDate(t *testing.T) {
  out := CaptureStdout(func() {
    WithFakeEnv(func() {
      PrintDate("example", time.Date(1986, 9, 3, 0, 0, 0, 0, time.UTC))
    })
  })

  expected :=
`Wed Sep 03 20:00:00 +0100 1986;#birth;comment
Wed Sep 03 21:45:33 +0100 1986;#chillout;
Wed Sep 03 23:24:49 +0100 1986;stop;
%%
`
  if out != expected {
    t.Errorf("unexpected PrintDate output. Expected '%v' got '%v'", expected, out)
  }
}

func TestFilePath(t *testing.T) {
	var path string = FilePath("example", time.Date(1986, 9, 3, 0, 0, 0, 0, time.UTC))

	if path != os.Getenv("TRAQ_DATA_DIR")+"/example/1986/1986-09-03" {
		t.Errorf("FilePath = %v, want %v", path, os.Getenv("TRAQ_DATA_DIR")+"/example/1986/1986-09-03")
	}
}

func TestEmptySumFile(t *testing.T) {
	content := []string{""}
	var summed, error = SumFile(content)

	if error == nil {
		var total, ok = summed["#work"]
		if ok {
			t.Errorf("summed['#work'] = %v, should not exist", total)
		}
	} else {
		t.Errorf("parsing error %v", error)
	}
}

func TestSimpleSumFile(t *testing.T) {
	content := []string{
		"Mon Oct 28 21:45:33 +0100 2013;#work;",
		"Mon Oct 28 23:24:49 +0100 2013;stop;",
	}
	var summed, error = SumFile(content)

	if error == nil {
		var total, ok = summed["#work"]
		if total != 5956 || !ok {
			t.Errorf("summed['#work'] = %v, want %v", total, 5956)
		}
	} else {
		t.Errorf("parsing error %v", error)
	}
}

func TestNoStopSumFile(t *testing.T) {
	content := []string{
		"Mon Oct 28 20:00:00 +0100 2013;#play;",
		"Mon Oct 28 21:45:33 +0100 2013;#work;",
		"Mon Oct 28 23:24:49 +0100 2013;stop;",
	}
	var summed, error = SumFile(content)

	if error == nil {
		var total, ok = summed["#play"]
		if total != 6333 || !ok {
			t.Errorf("summed['#play'] = %v, want %v", total, 6333)
		}
		total, ok = summed["#work"]
		if total != 5956 || !ok {
			t.Errorf("summed['#work'] = %v, want %v", total, 5956)
		}
	} else {
		t.Errorf("parsing error %v", error)
	}
}
func TestWithStopSumFile(t *testing.T) {
	content := []string{
		"Mon Oct 28 20:00:00 +0100 2013;#play;",
		"Mon Oct 28 21:45:33 +0100 2013;stop;",
		"Mon Oct 28 21:45:33 +0100 2013;#work;",
		"Mon Oct 28 23:24:49 +0100 2013;stop;",
	}
	var summed, error = SumFile(content)

	if error == nil {
		var total, ok = summed["#play"]
		if total != 6333 || !ok {
			t.Errorf("summed['#play'] = %v, want %v", total, 6333)
		}
		total, ok = summed["#work"]
		if total != 5956 || !ok {
			t.Errorf("summed['#work'] = %v, want %v", total, 5956)
		}
	} else {
		t.Errorf("parsing error %v", error)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"github.com/realglobe-Inc/edo/util/strset"
	"github.com/realglobe-Inc/go-lib-rg/erro"
	"net/http"
	"net/url"
	"time"
)

// ログインページにリダイレクトする。
func redirectLoginUi(w http.ResponseWriter, r *http.Request, sys *system, sess *session, hint string) error {

	// TODO 試行回数でエラー。

	v := url.Values{}
	if accNames := sess.accountNames(); len(accNames) > 0 {
		buff, err := json.Marshal(strset.StringSet(accNames))
		if err != nil {
			return redirectError(w, r, sys, sess, sess.request(), erro.Wrap(err))
		}

		v.Set(formAccNames, string(buff))
	}
	if disp := sess.request().display(); disp != "" {
		v.Set(formDisp, disp)
	}
	if loc, locs := sess.locale(), sess.request().uiLocales(); loc != "" || len(locs) > 0 {
		v.Set(formLocs, valuesToForm(append([]string{loc}, locs...)))
	}
	if hint != "" {
		v.Set(formHint, hint)
	}
	var query string
	if len(v) > 0 {
		query = "?" + v.Encode()
	}

	// ログインページに渡すクエリパラメータを生成。

	tic, err := sys.newTicket()
	if err != nil {
		return redirectError(w, r, sys, sess, sess.request(), erro.Wrap(err))
	}
	sess.setLoginTicket(tic)

	// ログイン券を発行。
	log.Debug("Login ticket " + mosaic(tic) + " was generated")

	if sess.id() == "" {
		id, err := sys.sessCont.newId()
		if err != nil {
			return redirectError(w, r, sys, sess, sess.request(), erro.Wrap(err))
		}
		sess.setId(id)
	}
	sess.setExpirationDate(time.Now().Add(sys.sessExpiDur))
	if err := sys.sessCont.put(sess); err != nil {
		return redirectError(w, r, sys, sess, sess.request(), erro.Wrap(err))
	}

	// セッションを保存した。
	log.Debug("Session " + mosaic(sess.id()) + " was saved")

	http.SetCookie(w, &http.Cookie{
		Name:     cookSess,
		Value:    sess.id(),
		Path:     "/",
		Expires:  sess.expirationDate(),
		Secure:   sys.secCook,
		HttpOnly: true})
	w.Header().Add("Cache-Control", "no-store")
	w.Header().Add("Pragma", "no-cache")
	http.Redirect(w, r, sys.uiUri+"/"+loginHtml+query+"#"+tic, http.StatusFound)
	return nil
}

// ログインページからの入力を受け付けて続きをする。
func loginPage(w http.ResponseWriter, r *http.Request, sys *system) error {
	req := newLoginRequest(r)

	sessId := req.session()
	if sessId == "" {
		// セッションが通知されてない。
		return newIdpError(errInvReq, "no session", http.StatusBadRequest, nil)
	}

	// セッションが通知された。
	log.Debug("session " + mosaic(sessId) + " is declared")

	sess, err := sys.sessCont.get(sessId)
	if err != nil {
		return erro.Wrap(err)
	} else if sess == nil {
		// セッションなんて無かった。
		return newIdpError(errInvReq, "no session "+mosaic(sessId), http.StatusBadRequest, nil)
	} else if !sess.valid() {
		// 無効なセッション。
		return newIdpError(errInvReq, "invalid session "+mosaic(sessId), http.StatusBadRequest, nil)
	}

	// セッションが有効だった。
	log.Debug("session " + mosaic(sessId) + " is exist")

	authReq := sess.request()
	if authReq == nil {
		// ユーザー認証・認可処理が始まっていない。
		return newIdpError(errInvReq, "session "+mosaic(sessId)+" is not in authentication process", http.StatusBadRequest, nil)
	}

	// ユーザー認証・認可処理中。
	log.Debug("session " + mosaic(sessId) + " is in authentication process")

	tic := sess.loginTicket()
	if tic == "" {
		// ログイン中でない。
		return redirectError(w, r, sys, sess, authReq, newIdpError(errAccDeny, "not in login process", 0, nil))
	} else if t := req.ticket(); t != tic {
		// 無効なログイン券。
		return redirectError(w, r, sys, sess, authReq, newIdpError(errAccDeny, "invalid login ticket "+mosaic(t), 0, nil))
	}

	// ログイン券が有効だった。
	log.Debug("Login ticket " + mosaic(tic) + " is OK")

	accName, passwd := req.loginInfo()
	if accName == "" || passwd == "" {
		// ログイン情報不備。
		log.Debug("Login info was not found")
		return redirectLoginUi(w, r, sys, sess, "login info was not found")
	}

	// ログイン情報があった。
	log.Debug("Login info was found")

	acc, err := sys.accCont.getByName(accName)
	if err != nil {
		return redirectError(w, r, sys, sess, authReq, erro.Wrap(err))
	} else if acc == nil {
		// アカウントが無い。
		log.Debug("Accout " + accName + " was not found")
		return redirectLoginUi(w, r, sys, sess, "accout "+accName+" was not found")
	}
	if passwd != acc.password() {
		// パスワード間違い。
		log.Debug("Password differs from " + accName + "'s password")
		return redirectLoginUi(w, r, sys, sess, "password differs from "+accName+"'s password")
	}

	// ログインできた。
	log.Debug("Account " + accName + " (" + acc.id() + ") was authenticated")

	sess.loginAccount(acc)

	if loc := req.locale(); loc != "" {
		sess.setLocale(loc)

		// 言語を選択してた。
		log.Debug("Locale " + loc + " was selected")
	}

	return afterLogin(w, r, sys, sess)
}

// ログインが無事終わった後の処理。
func afterLogin(w http.ResponseWriter, r *http.Request, sys *system, sess *session) error {

	prmpts := sess.request().prompts()
	if prmpts[prmptCons] {
		if prmpts[prmptNone] {
			return redirectError(w, r, sys, sess, sess.request(), newIdpError(errConsReq, "cannot consent without UI", 0, nil))
		}

		log.Debug("Consent is forced")
		return redirectConsentUi(w, r, sys, sess, "")
	}

	// TODO essential クレームの有無。
	// TODO value, values 指定クレームの検査。

	// 事前同意を調べる。
	scops, clms, err := sys.consCont.get(sess.currentAccount(), sess.request().ta())
	if err != nil {
		return redirectError(w, r, sys, sess, sess.request(), erro.Wrap(err))
	}
	if satisfiable(scops, clms, sess.request().scopes(), sess.request().claimNames()) {
		// 事前同意で十分。
		log.Debug("Preliminarily consented")
		sess.consent(sess.request().scopes(), sess.request().claimNames(), nil, nil)
		return afterConsent(w, r, sys, sess)
	}

	log.Debug("Consent is required")

	if prmpts[prmptNone] {
		return redirectError(w, r, sys, sess, sess.request(), newIdpError(errConsReq, "cannot consent without UI", 0, nil))
	}

	return redirectConsentUi(w, r, sys, sess, "")
}
<commit_msg>claims パラメータの essential と value に対する処理を実装<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/realglobe-Inc/edo/util/strset"
	"github.com/realglobe-Inc/go-lib-rg/erro"
	"net/http"
	"net/url"
	"reflect"
	"time"
)

// ログインページにリダイレクトする。
func redirectLoginUi(w http.ResponseWriter, r *http.Request, sys *system, sess *session, hint string) error {

	// TODO 試行回数でエラー。

	v := url.Values{}
	if accNames := sess.accountNames(); len(accNames) > 0 {
		buff, err := json.Marshal(strset.StringSet(accNames))
		if err != nil {
			return redirectError(w, r, sys, sess, sess.request(), erro.Wrap(err))
		}

		v.Set(formAccNames, string(buff))
	}
	if disp := sess.request().display(); disp != "" {
		v.Set(formDisp, disp)
	}
	if loc, locs := sess.locale(), sess.request().uiLocales(); loc != "" || len(locs) > 0 {
		v.Set(formLocs, valuesToForm(append([]string{loc}, locs...)))
	}
	if hint != "" {
		v.Set(formHint, hint)
	}
	var query string
	if len(v) > 0 {
		query = "?" + v.Encode()
	}

	// ログインページに渡すクエリパラメータを生成。

	tic, err := sys.newTicket()
	if err != nil {
		return redirectError(w, r, sys, sess, sess.request(), erro.Wrap(err))
	}
	sess.setLoginTicket(tic)

	// ログイン券を発行。
	log.Debug("Login ticket " + mosaic(tic) + " was generated")

	if sess.id() == "" {
		id, err := sys.sessCont.newId()
		if err != nil {
			return redirectError(w, r, sys, sess, sess.request(), erro.Wrap(err))
		}
		sess.setId(id)
	}
	sess.setExpirationDate(time.Now().Add(sys.sessExpiDur))
	if err := sys.sessCont.put(sess); err != nil {
		return redirectError(w, r, sys, sess, sess.request(), erro.Wrap(err))
	}

	// セッションを保存した。
	log.Debug("Session " + mosaic(sess.id()) + " was saved")

	http.SetCookie(w, &http.Cookie{
		Name:     cookSess,
		Value:    sess.id(),
		Path:     "/",
		Expires:  sess.expirationDate(),
		Secure:   sys.secCook,
		HttpOnly: true})
	w.Header().Add("Cache-Control", "no-store")
	w.Header().Add("Pragma", "no-cache")
	http.Redirect(w, r, sys.uiUri+"/"+loginHtml+query+"#"+tic, http.StatusFound)
	return nil
}

// ログインページからの入力を受け付けて続きをする。
func loginPage(w http.ResponseWriter, r *http.Request, sys *system) error {
	req := newLoginRequest(r)

	sessId := req.session()
	if sessId == "" {
		// セッションが通知されてない。
		return newIdpError(errInvReq, "no session", http.StatusBadRequest, nil)
	}

	// セッションが通知された。
	log.Debug("session " + mosaic(sessId) + " is declared")

	sess, err := sys.sessCont.get(sessId)
	if err != nil {
		return erro.Wrap(err)
	} else if sess == nil {
		// セッションなんて無かった。
		return newIdpError(errInvReq, "no session "+mosaic(sessId), http.StatusBadRequest, nil)
	} else if !sess.valid() {
		// 無効なセッション。
		return newIdpError(errInvReq, "invalid session "+mosaic(sessId), http.StatusBadRequest, nil)
	}

	// セッションが有効だった。
	log.Debug("session " + mosaic(sessId) + " is exist")

	authReq := sess.request()
	if authReq == nil {
		// ユーザー認証・認可処理が始まっていない。
		return newIdpError(errInvReq, "session "+mosaic(sessId)+" is not in authentication process", http.StatusBadRequest, nil)
	}

	// ユーザー認証・認可処理中。
	log.Debug("session " + mosaic(sessId) + " is in authentication process")

	tic := sess.loginTicket()
	if tic == "" {
		// ログイン中でない。
		return redirectError(w, r, sys, sess, authReq, newIdpError(errAccDeny, "not in login process", 0, nil))
	} else if t := req.ticket(); t != tic {
		// 無効なログイン券。
		return redirectError(w, r, sys, sess, authReq, newIdpError(errAccDeny, "invalid login ticket "+mosaic(t), 0, nil))
	}

	// ログイン券が有効だった。
	log.Debug("Login ticket " + mosaic(tic) + " is OK")

	accName, passwd := req.loginInfo()
	if accName == "" || passwd == "" {
		// ログイン情報不備。
		log.Debug("Login info was not found")
		return redirectLoginUi(w, r, sys, sess, "login info was not found")
	}

	// ログイン情報があった。
	log.Debug("Login info was found")

	acc, err := sys.accCont.getByName(accName)
	if err != nil {
		return redirectError(w, r, sys, sess, authReq, erro.Wrap(err))
	} else if acc == nil {
		// アカウントが無い。
		log.Debug("Accout " + accName + " was not found")
		return redirectLoginUi(w, r, sys, sess, "accout "+accName+" was not found")
	}
	if passwd != acc.password() {
		// パスワード間違い。
		log.Debug("Password differs from " + accName + "'s password")
		return redirectLoginUi(w, r, sys, sess, "password differs from "+accName+"'s password")
	}

	// ログインできた。
	log.Debug("Account " + accName + " (" + acc.id() + ") was authenticated")

	sess.loginAccount(acc)

	if loc := req.locale(); loc != "" {
		sess.setLocale(loc)

		// 言語を選択してた。
		log.Debug("Locale " + loc + " was selected")
	}

	return afterLogin(w, r, sys, sess)
}

// ログインが無事終わった後の処理。
func afterLogin(w http.ResponseWriter, r *http.Request, sys *system, sess *session) error {

	prmpts := sess.request().prompts()
	if prmpts[prmptCons] {
		if prmpts[prmptNone] {
			return redirectError(w, r, sys, sess, sess.request(), newIdpError(errConsReq, "cannot consent without UI", 0, nil))
		}

		log.Debug("Consent is forced")
		return redirectConsentUi(w, r, sys, sess, "")
	}

	// クレーム指定の検査。
	acc, err := sys.accCont.get(sess.currentAccount())
	if err != nil {
		return redirectError(w, r, sys, sess, sess.request(), erro.Wrap(err))
	} else if acc == nil {
		// アカウントが無い。
		return redirectLoginUi(w, r, sys, sess, "accout "+sess.currentAccount()+" was not found")
	}
	accInfClms, idTokClms := sess.request().claims()
	for _, clms := range []map[string]*claimUnit{accInfClms, idTokClms} {
		for clmName, req := range clms {
			if req == nil {
				continue
			}
			atr := acc.attribute(clmName)
			if req.Ess && (atr == nil || atr == "") {
				return redirectError(w, r, sys, sess, sess.request(), newIdpError(errAccDeny, "essential claim "+clmName+" is not exist", 0, nil))
			} else if (req.Val != nil || req.Val != "") && !reflect.DeepEqual(atr, req.Val) {
				return redirectError(w, r, sys, sess, sess.request(), newIdpError(errAccDeny, fmt.Sprint("claim "+clmName+" is not ", req.Val), 0, nil))
			}
			// TODO values クレーム指定の検査。
		}
	}

	// 事前同意を調べる。
	scops, clms, err := sys.consCont.get(sess.currentAccount(), sess.request().ta())
	if err != nil {
		return redirectError(w, r, sys, sess, sess.request(), erro.Wrap(err))
	}
	if satisfiable(scops, clms, sess.request().scopes(), sess.request().claimNames()) {
		// 事前同意で十分。
		log.Debug("Preliminarily consented")
		sess.consent(sess.request().scopes(), sess.request().claimNames(), nil, nil)
		return afterConsent(w, r, sys, sess)
	}

	log.Debug("Consent is required")

	if prmpts[prmptNone] {
		return redirectError(w, r, sys, sess, sess.request(), newIdpError(errConsReq, "cannot consent without UI", 0, nil))
	}

	return redirectConsentUi(w, r, sys, sess, "")
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"encoding/json"
	"github.com/hyperledger/fabric/core/chaincode/shim"
	"github.com/hyperledger/fabric/core/util"
)

type Chaincode struct { }

type ChaincodeFunctions struct {
	stub shim.ChaincodeStubInterface
}

func main() {
	err := shim.Start(new(Chaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}

func (t Chaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	_ = stub.PutState("deals", []byte("[]"))
	return nil, nil
}

func (t Chaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("invoke is running " + function)
	fns := ChaincodeFunctions{stub}
	if function == "registerDeal" {
		deploymentId := args[0]
		return fns.RegisterDeal(deploymentId)
	}
	return nil, errors.New("Received unknown function invocation: " + function)
}

func (t Chaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("query is running " + function)
	fns := ChaincodeFunctions{stub}
	if function == "ping" {
		return fns.Ping()
	} else if function == "getDeals" {
		return fns.GetDeals()
	}
	return nil, errors.New("Received unknown function query: " + function)
}

type DealConfig struct {
	DeploymentId 	string 		`json:"deploymentId"`
	Issuer 			string 		`json:"issuer"`
	Banks 			[]string 	`json:"banks"`
	BookStatus 		string 		`json:"bookStatus"`
	Price 			float64 	`json:"price"`
}

// Public Functions

func (c ChaincodeFunctions) Ping() ([]byte, error) {
    return []byte("pong"), nil
}

func (c ChaincodeFunctions) RegisterDeal(deploymentId string) ([]byte, error)  {
	c.saveDealToBlockChain(deploymentId)
	c.stub.SetEvent("New Deal Registered", []byte("{\"deploymentId\":\"" + deploymentId + "\"}"))
	return nil, nil
}

func (c ChaincodeFunctions) GetDeals() ([]byte, error) {
	companyBytes, _ := c.stub.ReadCertAttribute("company")
	roleBytes, _ := c.stub.ReadCertAttribute("role")
	company := string(companyBytes)
	role := string(roleBytes)
	deals := c.getDealsFromBlockchain()
	ret := make([]DealConfig, 0)
    for _, deal := range deals {
    	dealConfig := c.getDealConfig(deal)
        if permissionedForDeal(company, role, dealConfig) {
        	ret = append(ret, dealConfig)
        }
    }
    dealsJson, _ := json.Marshal(ret)
    return []byte(dealsJson), nil
}

// Private Functions

func (c ChaincodeFunctions) saveDealToBlockChain(deploymentId string) {
	deals := c.getDealsFromBlockchain()
	deals = append(deals, deploymentId)
	dealRegistryJson, _ := json.Marshal(deals)
	_ = c.stub.PutState("deals", []byte(dealRegistryJson))
}

func (c ChaincodeFunctions) getDealsFromBlockchain() []string {
	dealsJson, _ := c.stub.GetState("deals")
	var deals []string
	_ = json.Unmarshal(dealsJson, &deals)
	return deals
}

func (c ChaincodeFunctions) getDealConfig(address string) DealConfig {
	invokeArgs := util.ToChaincodeArgs("getDealConfig")
	dealConfigJson, _ := c.stub.QueryChaincode(address, invokeArgs)
	var dealConfig DealConfig
	_ = json.Unmarshal(dealConfigJson, &dealConfig)
	dealConfig.DeploymentId = address
	return dealConfig
}

func permissionedForDeal(role string, company string, dealConfig DealConfig) bool {
	if company == dealConfig.Issuer {
		return true
	} else if stringInSlice(company, dealConfig.Banks) {
		return true
	} else if dealConfig.BookStatus != "draft" {
		return true
	} else {
		return false
	}
}

func stringInSlice(a string, list []string) bool {
    for _, b := range list {
        if b == a {
            return true
        }
    }
    return false
}<commit_msg>batch check-in<commit_after>package main

import (
	"errors"
	"fmt"
	"encoding/json"
	"github.com/hyperledger/fabric/core/chaincode/shim"
	"github.com/hyperledger/fabric/core/util"
)

type Chaincode struct { }

type ChaincodeFunctions struct {
	stub shim.ChaincodeStubInterface
}

func main() {
	err := shim.Start(new(Chaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}

func (t Chaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	_ = stub.PutState("deals", []byte("[]"))
	return nil, nil
}

func (t Chaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("invoke is running " + function)
	fns := ChaincodeFunctions{stub}
	if function == "registerDeal" {
		deploymentId := args[0]
		return fns.RegisterDeal(deploymentId)
	}
	return nil, errors.New("Received unknown function invocation: " + function)
}

func (t Chaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("query is running " + function)
	fns := ChaincodeFunctions{stub}
	if function == "ping" {
		return fns.Ping()
	} else if function == "getDeals" {
		return fns.GetDeals()
	}
	return nil, errors.New("Received unknown function query: " + function)
}

type DealConfig struct {
	DeploymentId 	string 		`json:"deploymentId"`
	Issuer 			string 		`json:"issuer"`
	Banks 			[]string 	`json:"banks"`
	BookStatus 		string 		`json:"bookStatus"`
	Price 			float64 	`json:"price"`
}

// Public Functions

func (c ChaincodeFunctions) Ping() ([]byte, error) {
    return []byte("pong"), nil
}

func (c ChaincodeFunctions) RegisterDeal(deploymentId string) ([]byte, error)  {
	c.saveDealToBlockChain(deploymentId)
	c.stub.SetEvent("New Deal Registered", []byte("{\"deploymentId\":\"" + deploymentId + "\"}"))
	return nil, nil
}

func (c ChaincodeFunctions) GetDeals() ([]byte, error) {
	companyBytes, _ := c.stub.ReadCertAttribute("company")
	roleBytes, _ := c.stub.ReadCertAttribute("role")
	company := string(companyBytes)
	role := string(roleBytes)
	deals := c.getDealsFromBlockchain()
	ret := make([]DealConfig, 0)
    for _, deal := range deals {
    	dealConfig := c.getDealConfig(deal)
        if permissionedForDeal(role, company, dealConfig) {
        	ret = append(ret, dealConfig)
        }
    }
    dealsJson, _ := json.Marshal(ret)
    return []byte(dealsJson), nil
}

// Private Functions

func (c ChaincodeFunctions) saveDealToBlockChain(deploymentId string) {
	deals := c.getDealsFromBlockchain()
	deals = append(deals, deploymentId)
	dealRegistryJson, _ := json.Marshal(deals)
	_ = c.stub.PutState("deals", []byte(dealRegistryJson))
}

func (c ChaincodeFunctions) getDealsFromBlockchain() []string {
	dealsJson, _ := c.stub.GetState("deals")
	var deals []string
	_ = json.Unmarshal(dealsJson, &deals)
	return deals
}

func (c ChaincodeFunctions) getDealConfig(address string) DealConfig {
	invokeArgs := util.ToChaincodeArgs("getDealConfig")
	dealConfigJson, _ := c.stub.QueryChaincode(address, invokeArgs)
	var dealConfig DealConfig
	_ = json.Unmarshal(dealConfigJson, &dealConfig)
	dealConfig.DeploymentId = address
	return dealConfig
}

func permissionedForDeal(role string, company string, dealConfig DealConfig) bool {
	if company == dealConfig.Issuer {
		return true
	} else if role == "bank" && stringInSlice(company, dealConfig.Banks) {
		return true
	} else if role == "investor" && dealConfig.BookStatus != "draft" {
		return true
	} else {
		return false
	}
}

func stringInSlice(a string, list []string) bool {
    for _, b := range list {
        if b == a {
            return true
        }
    }
    return false
}<|endoftext|>


<commit_before>package gocb

import (
	"gopkg.in/couchbase/gocbcore.v7"
)

// Get retrieves a document from the bucket
func (b *Bucket) Get(key string, valuePtr interface{}) (Cas, error) {
	return b.get(key, valuePtr)
}

// GetAndTouch retrieves a document and simultaneously updates its expiry time.
func (b *Bucket) GetAndTouch(key string, expiry uint32, valuePtr interface{}) (Cas, error) {
	return b.getAndTouch(key, expiry, valuePtr)
}

// GetAndLock locks a document for a period of time, providing exclusive RW access to it.
func (b *Bucket) GetAndLock(key string, lockTime uint32, valuePtr interface{}) (Cas, error) {
	return b.getAndLock(key, lockTime, valuePtr)
}

// Unlock unlocks a document which was locked with GetAndLock.
func (b *Bucket) Unlock(key string, cas Cas) (Cas, error) {
	cas, _, err := b.unlock(key, cas)
	return cas, err
}

// GetReplica returns the value of a particular document from a replica server.
func (b *Bucket) GetReplica(key string, valuePtr interface{}, replicaIdx int) (Cas, error) {
	return b.getReplica(key, valuePtr, replicaIdx)
}

// Touch touches a document, specifying a new expiry time for it.
func (b *Bucket) Touch(key string, cas Cas, expiry uint32) (Cas, error) {
	cas, _, err := b.touch(key, cas, expiry)
	return cas, err
}

// Remove removes a document from the bucket.
func (b *Bucket) Remove(key string, cas Cas) (Cas, error) {
	cas, _, err := b.remove(key, cas)
	return cas, err
}

// Upsert inserts or replaces a document in the bucket.
func (b *Bucket) Upsert(key string, value interface{}, expiry uint32) (Cas, error) {
	cas, _, err := b.upsert(key, value, expiry)
	return cas, err
}

// Insert inserts a new document to the bucket.
func (b *Bucket) Insert(key string, value interface{}, expiry uint32) (Cas, error) {
	cas, _, err := b.insert(key, value, expiry)
	return cas, err
}

// Replace replaces a document in the bucket.
func (b *Bucket) Replace(key string, value interface{}, cas Cas, expiry uint32) (Cas, error) {
	cas, _, err := b.replace(key, value, cas, expiry)
	return cas, err
}

// Append appends a string value to a document.
func (b *Bucket) Append(key, value string) (Cas, error) {
	cas, _, err := b.append(key, value)
	return cas, err
}

// Prepend prepends a string value to a document.
func (b *Bucket) Prepend(key, value string) (Cas, error) {
	cas, _, err := b.prepend(key, value)
	return cas, err
}

// Counter performs an atomic addition or subtraction for an integer document.  Passing a
// non-negative `initial` value will cause the document to be created if it did  not
// already exist.
func (b *Bucket) Counter(key string, delta, initial int64, expiry uint32) (uint64, Cas, error) {
	val, cas, _, err := b.counter(key, delta, initial, expiry)
	return val, cas, err
}

// ServerStats is a tree of statistics information returned from the server.
//   stats := cb.Stats(...)
//   for server := stats {
//       for statName, stat := server {
//       //...
//       }
//   }
type ServerStats map[string]map[string]string

// Stats returns various server statistics from the cluster.
func (b *Bucket) Stats(key string) (statsOut ServerStats, errOut error) {
	signal := make(chan bool, 1)
	statsOut = make(ServerStats)

	op, errOut := b.client.Stats(key, func(stats map[string]gocbcore.SingleServerStats) {
		for curServer, curStats := range stats {
			if curStats.Error != nil && errOut == nil {
				errOut = curStats.Error
			}
			statsOut[curServer] = curStats.Stats
		}
		signal <- true
	})

	timeoutTmr := gocbcore.AcquireTimer(b.opTimeout)
	select {
	case <-signal:
		gocbcore.ReleaseTimer(timeoutTmr, false)
		return
	case <-timeoutTmr.C:
		gocbcore.ReleaseTimer(timeoutTmr, true)
		if !op.Cancel() {
			<-signal
			return
		}
		return nil, ErrTimeout
	}
}

type ioGetCallback gocbcore.GetCallback
type ioCasCallback gocbcore.StoreCallback
type ioCtrCallback gocbcore.CounterCallback

type hlpGetHandler func(ioGetCallback) (pendingOp, error)

func (b *Bucket) hlpGetExec(valuePtr interface{}, execFn hlpGetHandler) (casOut Cas, errOut error) {
	signal := make(chan bool, 1)
	op, err := execFn(func(bytes []byte, flags uint32, cas gocbcore.Cas, err error) {
		errOut = err
		if errOut == nil {
			errOut = b.transcoder.Decode(bytes, flags, valuePtr)
			if errOut == nil {
				casOut = Cas(cas)
			}
		}
		signal <- true
	})
	if err != nil {
		return 0, err
	}

	timeoutTmr := gocbcore.AcquireTimer(b.opTimeout)
	select {
	case <-signal:
		gocbcore.ReleaseTimer(timeoutTmr, false)
		return
	case <-timeoutTmr.C:
		gocbcore.ReleaseTimer(timeoutTmr, true)
		if !op.Cancel() {
			<-signal
			return
		}
		return 0, ErrTimeout
	}
}

type hlpCasHandler func(ioCasCallback) (pendingOp, error)

func (b *Bucket) hlpCasExec(execFn hlpCasHandler) (casOut Cas, mtOut MutationToken, errOut error) {
	signal := make(chan bool, 1)
	op, err := execFn(func(cas gocbcore.Cas, mt gocbcore.MutationToken, err error) {
		errOut = err
		if errOut == nil {
			casOut = Cas(cas)
			mtOut = MutationToken{mt, b}
		}
		signal <- true
	})
	if err != nil {
		return 0, MutationToken{}, err
	}

	timeoutTmr := gocbcore.AcquireTimer(b.opTimeout)
	select {
	case <-signal:
		gocbcore.ReleaseTimer(timeoutTmr, false)
		return
	case <-timeoutTmr.C:
		gocbcore.ReleaseTimer(timeoutTmr, true)
		if !op.Cancel() {
			<-signal
			return
		}
		return 0, MutationToken{}, ErrTimeout
	}
}

type hlpCtrHandler func(ioCtrCallback) (pendingOp, error)

func (b *Bucket) hlpCtrExec(execFn hlpCtrHandler) (valOut uint64, casOut Cas, mtOut MutationToken, errOut error) {
	signal := make(chan bool, 1)
	op, err := execFn(func(value uint64, cas gocbcore.Cas, mt gocbcore.MutationToken, err error) {
		errOut = err
		if errOut == nil {
			valOut = value
			casOut = Cas(cas)
			mtOut = MutationToken{mt, b}
		}
		signal <- true
	})
	if err != nil {
		return 0, 0, MutationToken{}, err
	}

	timeoutTmr := gocbcore.AcquireTimer(b.opTimeout)
	select {
	case <-signal:
		gocbcore.ReleaseTimer(timeoutTmr, false)
		return
	case <-timeoutTmr.C:
		gocbcore.ReleaseTimer(timeoutTmr, true)
		if !op.Cancel() {
			<-signal
			return
		}
		return 0, 0, MutationToken{}, ErrTimeout
	}
}

func (b *Bucket) get(key string, valuePtr interface{}) (Cas, error) {
	return b.hlpGetExec(valuePtr, func(cb ioGetCallback) (pendingOp, error) {
		op, err := b.client.Get([]byte(key), gocbcore.GetCallback(cb))
		return op, err
	})
}

func (b *Bucket) getAndTouch(key string, expiry uint32, valuePtr interface{}) (Cas, error) {
	return b.hlpGetExec(valuePtr, func(cb ioGetCallback) (pendingOp, error) {
		op, err := b.client.GetAndTouch([]byte(key), expiry, gocbcore.GetCallback(cb))
		return op, err
	})
}

func (b *Bucket) getAndLock(key string, lockTime uint32, valuePtr interface{}) (Cas, error) {
	return b.hlpGetExec(valuePtr, func(cb ioGetCallback) (pendingOp, error) {
		op, err := b.client.GetAndLock([]byte(key), lockTime, gocbcore.GetCallback(cb))
		return op, err
	})
}

func (b *Bucket) unlock(key string, cas Cas) (Cas, MutationToken, error) {
	return b.hlpCasExec(func(cb ioCasCallback) (pendingOp, error) {
		op, err := b.client.Unlock([]byte(key), gocbcore.Cas(cas), gocbcore.UnlockCallback(cb))
		return op, err
	})
}

func (b *Bucket) getReplica(key string, valuePtr interface{}, replicaIdx int) (Cas, error) {
	return b.hlpGetExec(valuePtr, func(cb ioGetCallback) (pendingOp, error) {
		op, err := b.client.GetReplica([]byte(key), replicaIdx, gocbcore.GetCallback(cb))
		return op, err
	})
}

func (b *Bucket) touch(key string, cas Cas, expiry uint32) (Cas, MutationToken, error) {
	return b.hlpCasExec(func(cb ioCasCallback) (pendingOp, error) {
		op, err := b.client.Touch([]byte(key), gocbcore.Cas(cas), expiry, gocbcore.TouchCallback(cb))
		return op, err
	})
}

func (b *Bucket) remove(key string, cas Cas) (Cas, MutationToken, error) {
	return b.hlpCasExec(func(cb ioCasCallback) (pendingOp, error) {
		op, err := b.client.Remove([]byte(key), gocbcore.Cas(cas), gocbcore.RemoveCallback(cb))
		return op, err
	})
}

func (b *Bucket) upsert(key string, value interface{}, expiry uint32) (Cas, MutationToken, error) {
	bytes, flags, err := b.transcoder.Encode(value)
	if err != nil {
		return 0, MutationToken{}, err
	}

	return b.hlpCasExec(func(cb ioCasCallback) (pendingOp, error) {
		op, err := b.client.Set([]byte(key), bytes, flags, expiry, gocbcore.StoreCallback(cb))
		return op, err
	})
}

func (b *Bucket) insert(key string, value interface{}, expiry uint32) (Cas, MutationToken, error) {
	bytes, flags, err := b.transcoder.Encode(value)
	if err != nil {
		return 0, MutationToken{}, err
	}

	return b.hlpCasExec(func(cb ioCasCallback) (pendingOp, error) {
		op, err := b.client.Add([]byte(key), bytes, flags, expiry, gocbcore.StoreCallback(cb))
		return op, err
	})
}

func (b *Bucket) replace(key string, value interface{}, cas Cas, expiry uint32) (Cas, MutationToken, error) {
	bytes, flags, err := b.transcoder.Encode(value)
	if err != nil {
		return 0, MutationToken{}, err
	}

	return b.hlpCasExec(func(cb ioCasCallback) (pendingOp, error) {
		op, err := b.client.Replace([]byte(key), bytes, flags, gocbcore.Cas(cas), expiry, gocbcore.StoreCallback(cb))
		return op, err
	})
}

func (b *Bucket) append(key, value string) (Cas, MutationToken, error) {
	return b.hlpCasExec(func(cb ioCasCallback) (pendingOp, error) {
		op, err := b.client.Append([]byte(key), []byte(value), gocbcore.StoreCallback(cb))
		return op, err
	})
}

func (b *Bucket) prepend(key, value string) (Cas, MutationToken, error) {
	return b.hlpCasExec(func(cb ioCasCallback) (pendingOp, error) {
		op, err := b.client.Prepend([]byte(key), []byte(value), gocbcore.StoreCallback(cb))
		return op, err
	})
}

func (b *Bucket) counter(key string, delta, initial int64, expiry uint32) (uint64, Cas, MutationToken, error) {
	realInitial := uint64(0xFFFFFFFFFFFFFFFF)
	if initial >= 0 {
		realInitial = uint64(initial)
	}

	if delta > 0 {
		return b.hlpCtrExec(func(cb ioCtrCallback) (pendingOp, error) {
			op, err := b.client.Increment([]byte(key), uint64(delta), realInitial, expiry, gocbcore.CounterCallback(cb))
			return op, err
		})
	} else if delta < 0 {
		return b.hlpCtrExec(func(cb ioCtrCallback) (pendingOp, error) {
			op, err := b.client.Decrement([]byte(key), uint64(-delta), realInitial, expiry, gocbcore.CounterCallback(cb))
			return op, err
		})
	} else {
		return 0, 0, MutationToken{}, clientError{"Delta must be a non-zero value."}
	}
}
<commit_msg>Cleanup stats method to reflect other methods.<commit_after>package gocb

import (
	"gopkg.in/couchbase/gocbcore.v7"
)

// Get retrieves a document from the bucket
func (b *Bucket) Get(key string, valuePtr interface{}) (Cas, error) {
	return b.get(key, valuePtr)
}

// GetAndTouch retrieves a document and simultaneously updates its expiry time.
func (b *Bucket) GetAndTouch(key string, expiry uint32, valuePtr interface{}) (Cas, error) {
	return b.getAndTouch(key, expiry, valuePtr)
}

// GetAndLock locks a document for a period of time, providing exclusive RW access to it.
func (b *Bucket) GetAndLock(key string, lockTime uint32, valuePtr interface{}) (Cas, error) {
	return b.getAndLock(key, lockTime, valuePtr)
}

// Unlock unlocks a document which was locked with GetAndLock.
func (b *Bucket) Unlock(key string, cas Cas) (Cas, error) {
	cas, _, err := b.unlock(key, cas)
	return cas, err
}

// GetReplica returns the value of a particular document from a replica server.
func (b *Bucket) GetReplica(key string, valuePtr interface{}, replicaIdx int) (Cas, error) {
	return b.getReplica(key, valuePtr, replicaIdx)
}

// Touch touches a document, specifying a new expiry time for it.
func (b *Bucket) Touch(key string, cas Cas, expiry uint32) (Cas, error) {
	cas, _, err := b.touch(key, cas, expiry)
	return cas, err
}

// Remove removes a document from the bucket.
func (b *Bucket) Remove(key string, cas Cas) (Cas, error) {
	cas, _, err := b.remove(key, cas)
	return cas, err
}

// Upsert inserts or replaces a document in the bucket.
func (b *Bucket) Upsert(key string, value interface{}, expiry uint32) (Cas, error) {
	cas, _, err := b.upsert(key, value, expiry)
	return cas, err
}

// Insert inserts a new document to the bucket.
func (b *Bucket) Insert(key string, value interface{}, expiry uint32) (Cas, error) {
	cas, _, err := b.insert(key, value, expiry)
	return cas, err
}

// Replace replaces a document in the bucket.
func (b *Bucket) Replace(key string, value interface{}, cas Cas, expiry uint32) (Cas, error) {
	cas, _, err := b.replace(key, value, cas, expiry)
	return cas, err
}

// Append appends a string value to a document.
func (b *Bucket) Append(key, value string) (Cas, error) {
	cas, _, err := b.append(key, value)
	return cas, err
}

// Prepend prepends a string value to a document.
func (b *Bucket) Prepend(key, value string) (Cas, error) {
	cas, _, err := b.prepend(key, value)
	return cas, err
}

// Counter performs an atomic addition or subtraction for an integer document.  Passing a
// non-negative `initial` value will cause the document to be created if it did  not
// already exist.
func (b *Bucket) Counter(key string, delta, initial int64, expiry uint32) (uint64, Cas, error) {
	val, cas, _, err := b.counter(key, delta, initial, expiry)
	return val, cas, err
}

// ServerStats is a tree of statistics information returned from the server.
//   stats := cb.Stats(...)
//   for server := stats {
//       for statName, stat := server {
//       //...
//       }
//   }
type ServerStats map[string]map[string]string

// Stats returns various server statistics from the cluster.
func (b *Bucket) Stats(key string) (ServerStats, error) {
	stats, err := b.stats(key)
	return stats, err
}

type opController struct {
	b      *Bucket
	signal chan error
}

func (ctrl *opController) Resolve(err error) {
	ctrl.signal <- err
}

func (ctrl *opController) Wait(op gocbcore.PendingOp, err error) error {
	if err != nil {
		return err
	}

	timeoutTmr := gocbcore.AcquireTimer(ctrl.b.opTimeout)
	select {
	case err = <-ctrl.signal:
		gocbcore.ReleaseTimer(timeoutTmr, false)
		return err
	case <-timeoutTmr.C:
		gocbcore.ReleaseTimer(timeoutTmr, true)
		if !op.Cancel() {
			err = <-ctrl.signal
			return err
		}

		return ErrTimeout
	}
}

func (b *Bucket) newOpController() *opController {
	return &opController{
		b:      b,
		signal: make(chan error, 1),
	}
}

type ioGetCallback gocbcore.GetCallback
type ioCasCallback gocbcore.StoreCallback
type ioCtrCallback gocbcore.CounterCallback

type hlpGetHandler func(ioGetCallback) (pendingOp, error)

func (b *Bucket) hlpGetExec(valuePtr interface{}, execFn hlpGetHandler) (casOut Cas, errOut error) {
	signal := make(chan bool, 1)
	op, err := execFn(func(bytes []byte, flags uint32, cas gocbcore.Cas, err error) {
		errOut = err
		if errOut == nil {
			errOut = b.transcoder.Decode(bytes, flags, valuePtr)
			if errOut == nil {
				casOut = Cas(cas)
			}
		}
		signal <- true
	})
	if err != nil {
		return 0, err
	}

	timeoutTmr := gocbcore.AcquireTimer(b.opTimeout)
	select {
	case <-signal:
		gocbcore.ReleaseTimer(timeoutTmr, false)
		return
	case <-timeoutTmr.C:
		gocbcore.ReleaseTimer(timeoutTmr, true)
		if !op.Cancel() {
			<-signal
			return
		}
		return 0, ErrTimeout
	}
}

type hlpCasHandler func(ioCasCallback) (pendingOp, error)

func (b *Bucket) hlpCasExec(execFn hlpCasHandler) (casOut Cas, mtOut MutationToken, errOut error) {
	signal := make(chan bool, 1)
	op, err := execFn(func(cas gocbcore.Cas, mt gocbcore.MutationToken, err error) {
		errOut = err
		if errOut == nil {
			casOut = Cas(cas)
			mtOut = MutationToken{mt, b}
		}
		signal <- true
	})
	if err != nil {
		return 0, MutationToken{}, err
	}

	timeoutTmr := gocbcore.AcquireTimer(b.opTimeout)
	select {
	case <-signal:
		gocbcore.ReleaseTimer(timeoutTmr, false)
		return
	case <-timeoutTmr.C:
		gocbcore.ReleaseTimer(timeoutTmr, true)
		if !op.Cancel() {
			<-signal
			return
		}
		return 0, MutationToken{}, ErrTimeout
	}
}

type hlpCtrHandler func(ioCtrCallback) (pendingOp, error)

func (b *Bucket) hlpCtrExec(execFn hlpCtrHandler) (valOut uint64, casOut Cas, mtOut MutationToken, errOut error) {
	signal := make(chan bool, 1)
	op, err := execFn(func(value uint64, cas gocbcore.Cas, mt gocbcore.MutationToken, err error) {
		errOut = err
		if errOut == nil {
			valOut = value
			casOut = Cas(cas)
			mtOut = MutationToken{mt, b}
		}
		signal <- true
	})
	if err != nil {
		return 0, 0, MutationToken{}, err
	}

	timeoutTmr := gocbcore.AcquireTimer(b.opTimeout)
	select {
	case <-signal:
		gocbcore.ReleaseTimer(timeoutTmr, false)
		return
	case <-timeoutTmr.C:
		gocbcore.ReleaseTimer(timeoutTmr, true)
		if !op.Cancel() {
			<-signal
			return
		}
		return 0, 0, MutationToken{}, ErrTimeout
	}
}

func (b *Bucket) get(key string, valuePtr interface{}) (Cas, error) {
	return b.hlpGetExec(valuePtr, func(cb ioGetCallback) (pendingOp, error) {
		op, err := b.client.Get([]byte(key), gocbcore.GetCallback(cb))
		return op, err
	})
}

func (b *Bucket) getAndTouch(key string, expiry uint32, valuePtr interface{}) (Cas, error) {
	return b.hlpGetExec(valuePtr, func(cb ioGetCallback) (pendingOp, error) {
		op, err := b.client.GetAndTouch([]byte(key), expiry, gocbcore.GetCallback(cb))
		return op, err
	})
}

func (b *Bucket) getAndLock(key string, lockTime uint32, valuePtr interface{}) (Cas, error) {
	return b.hlpGetExec(valuePtr, func(cb ioGetCallback) (pendingOp, error) {
		op, err := b.client.GetAndLock([]byte(key), lockTime, gocbcore.GetCallback(cb))
		return op, err
	})
}

func (b *Bucket) unlock(key string, cas Cas) (Cas, MutationToken, error) {
	return b.hlpCasExec(func(cb ioCasCallback) (pendingOp, error) {
		op, err := b.client.Unlock([]byte(key), gocbcore.Cas(cas), gocbcore.UnlockCallback(cb))
		return op, err
	})
}

func (b *Bucket) getReplica(key string, valuePtr interface{}, replicaIdx int) (Cas, error) {
	return b.hlpGetExec(valuePtr, func(cb ioGetCallback) (pendingOp, error) {
		op, err := b.client.GetReplica([]byte(key), replicaIdx, gocbcore.GetCallback(cb))
		return op, err
	})
}

func (b *Bucket) touch(key string, cas Cas, expiry uint32) (Cas, MutationToken, error) {
	return b.hlpCasExec(func(cb ioCasCallback) (pendingOp, error) {
		op, err := b.client.Touch([]byte(key), gocbcore.Cas(cas), expiry, gocbcore.TouchCallback(cb))
		return op, err
	})
}

func (b *Bucket) remove(key string, cas Cas) (Cas, MutationToken, error) {
	return b.hlpCasExec(func(cb ioCasCallback) (pendingOp, error) {
		op, err := b.client.Remove([]byte(key), gocbcore.Cas(cas), gocbcore.RemoveCallback(cb))
		return op, err
	})
}

func (b *Bucket) upsert(key string, value interface{}, expiry uint32) (Cas, MutationToken, error) {
	bytes, flags, err := b.transcoder.Encode(value)
	if err != nil {
		return 0, MutationToken{}, err
	}

	return b.hlpCasExec(func(cb ioCasCallback) (pendingOp, error) {
		op, err := b.client.Set([]byte(key), bytes, flags, expiry, gocbcore.StoreCallback(cb))
		return op, err
	})
}

func (b *Bucket) insert(key string, value interface{}, expiry uint32) (Cas, MutationToken, error) {
	bytes, flags, err := b.transcoder.Encode(value)
	if err != nil {
		return 0, MutationToken{}, err
	}

	return b.hlpCasExec(func(cb ioCasCallback) (pendingOp, error) {
		op, err := b.client.Add([]byte(key), bytes, flags, expiry, gocbcore.StoreCallback(cb))
		return op, err
	})
}

func (b *Bucket) replace(key string, value interface{}, cas Cas, expiry uint32) (Cas, MutationToken, error) {
	bytes, flags, err := b.transcoder.Encode(value)
	if err != nil {
		return 0, MutationToken{}, err
	}

	return b.hlpCasExec(func(cb ioCasCallback) (pendingOp, error) {
		op, err := b.client.Replace([]byte(key), bytes, flags, gocbcore.Cas(cas), expiry, gocbcore.StoreCallback(cb))
		return op, err
	})
}

func (b *Bucket) append(key, value string) (Cas, MutationToken, error) {
	return b.hlpCasExec(func(cb ioCasCallback) (pendingOp, error) {
		op, err := b.client.Append([]byte(key), []byte(value), gocbcore.StoreCallback(cb))
		return op, err
	})
}

func (b *Bucket) prepend(key, value string) (Cas, MutationToken, error) {
	return b.hlpCasExec(func(cb ioCasCallback) (pendingOp, error) {
		op, err := b.client.Prepend([]byte(key), []byte(value), gocbcore.StoreCallback(cb))
		return op, err
	})
}

func (b *Bucket) counter(key string, delta, initial int64, expiry uint32) (uint64, Cas, MutationToken, error) {
	realInitial := uint64(0xFFFFFFFFFFFFFFFF)
	if initial >= 0 {
		realInitial = uint64(initial)
	}

	if delta > 0 {
		return b.hlpCtrExec(func(cb ioCtrCallback) (pendingOp, error) {
			op, err := b.client.Increment([]byte(key), uint64(delta), realInitial, expiry, gocbcore.CounterCallback(cb))
			return op, err
		})
	} else if delta < 0 {
		return b.hlpCtrExec(func(cb ioCtrCallback) (pendingOp, error) {
			op, err := b.client.Decrement([]byte(key), uint64(-delta), realInitial, expiry, gocbcore.CounterCallback(cb))
			return op, err
		})
	} else {
		return 0, 0, MutationToken{}, clientError{"Delta must be a non-zero value."}
	}
}

func (b *Bucket) stats(key string) (statsOut ServerStats, errOut error) {
	signal := make(chan bool, 1)
	statsOut = make(ServerStats)

	op, errOut := b.client.Stats(key, func(stats map[string]gocbcore.SingleServerStats) {
		for curServer, curStats := range stats {
			if curStats.Error != nil && errOut == nil {
				errOut = curStats.Error
			}
			statsOut[curServer] = curStats.Stats
		}
		signal <- true
	})

	timeoutTmr := gocbcore.AcquireTimer(b.opTimeout)
	select {
	case <-signal:
		gocbcore.ReleaseTimer(timeoutTmr, false)
		return
	case <-timeoutTmr.C:
		gocbcore.ReleaseTimer(timeoutTmr, true)
		if !op.Cancel() {
			<-signal
			return
		}
		return nil, ErrTimeout
	}
}
<|endoftext|>


<commit_before>package buffstreams

import ()

import (
	"encoding/binary"
	"errors"
	"github.com/Sirupsen/logrus"
	"net"
	"sync"
)

type BuffManager struct {
	dialedConnections map[string]net.Conn
	listeningSockets  map[string]net.Listener
	sync.RWMutex
}

func New() *BuffManager {
	bm := &BuffManager{
		dialedConnections: make(map[string]net.Conn),
		listeningSockets:  make(map[string]net.Listener),
	}
	return bm
}

type ListenCallback func([]byte) error

func formatAddress(address string, port string) string {
	return address + ":" + port
}

func (bm *BuffManager) StartListening(protocol string, port string, cb ListenCallback) error {
	address := formatAddress("", port)
	receiveSocket, err := net.Listen(protocol, address)
	if err != nil {
		return err
	}
	bm.startListening(address, receiveSocket, cb)
	return nil
}

func (bm *BuffManager) startListening(address string, socket net.Listener, cb ListenCallback) {
	bm.Lock()
	bm.listeningSockets[address] = socket
	bm.Unlock()

	go func(address string, listener net.Listener) {
		for {
			conn, err := listener.Accept()
			if err != nil {
				// alert error
				// Hmmm but how - I'm in goroutine land. Need a class level logger? Don't want to
				// tie people to the bleh default logger, also personally would prefer logrus for levels
			} else {
				// Hand this off and immediately listen for more
				go handleListenedConn(address, conn, cb)
			}
		}
	}(address, socket)
}

func handleListenedConn(address string, conn net.Conn, cb ListenCallback) {
	for {
		// Handle getting the data header
		logrus.Info("Reading")
		headerByteSize := MessageSizeToBitLength(4098) // MAKE THIS CONFIGURABLE once you have any idea how you're going to do that
		headerBuffer := make([]byte, headerByteSize)

		// First, read the number of bytes required to determine the message length
		_, err := readFromConnection(conn, headerBuffer)
		if err != nil && err.Error() == "EOF" {
			// Log the error we got from the call to read
			logrus.Error("First")
			logrus.Error(err)
			conn.Close()
			return
		}

		// Now turn that buffer of bytes into an integer - represnts size of message body
		msgLength, bytesParsed := binary.Uvarint(headerBuffer)
		// Not sure what the correct way to handle these errors are. For now, bomb out
		if bytesParsed == 0 {
			// "Buffer too small"
			logrus.Error("Second")
			logrus.Error(err)
			return
		} else if bytesParsed < 0 {
			// "Buffer overflow"
			logrus.Error("Third")
			logrus.Error(err)
			return
		}
		dataBuffer := make([]byte, msgLength)
		bytesLen, err := readFromConnection(conn, dataBuffer)
		if err != nil && err.Error() == "EOF" {
			// log the error from the call to read
			logrus.Error("Fourth")
			logrus.Error(err)
			conn.Close()
			return
		}

		// If we read bytes, there wasn't an error, or if there was it was only EOF
		// And readbytes + EOF is normal, just as readbytes + no err, next read 0 bytes EOF
		// So... we take action on the actual message data
		if bytesLen > 0 && (err == nil || (err != nil && err.Error() == "EOF")) {
			// I ultimately have some design choices here
			// Currently, I am invoking a delegate thats been passed down the stack
			// I could...
			// Keep it as is
			// Tie the protobuffs library deeper into it, and take a reference to
			// the type that the message we're serializing is, and do that work too
			// But you'd still need a callback, unless...
			// I just push it onto a queue (not a slow ass channel, but a queue)
			// which has a reference passed down to it, and the main process
			// spawns a goroutine to reap off the queue and handle those in parallel

			// Callback, atm
			logrus.Info("Callback")
			cb(dataBuffer)
		}
	}
}

func readFromConnection(reader net.Conn, buffer []byte) (int, error) {
	// This fills the buffer
	bytesLen, err := reader.Read(buffer)
	// Output the content of the bytes to the queue
	if bytesLen == 0 {
		if err != nil && err.Error() == "EOF" {
			// "End of individual transmission"
			// We're just done reading from that conn
			return bytesLen, err
		}
	}

	if err != nil {
		//"Underlying network failure?"
		// Not sure what this error would be, but it could exist and i've seen it handled
		// as a general case in other networking code. Following in the footsteps of (greatness|madness)
	}
	// Read some bytes, return the length
	return bytesLen, nil
}

func (bm *BuffManager) DialOut(protocol string, ip string, port string) error {
	address := formatAddress(ip, port)
	if _, ok := bm.dialedConnections[address]; ok == true {
		// Need to clean it out on any error...
		return errors.New("You have a connection to this ip and port open already")
	}
	tcpAddr, err := net.ResolveTCPAddr("tcp", address)
	if err != nil {
		return err
	}
	conn, err := net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		return err
	} else {
		// Store the connection, it's valid
		bm.Lock()
		bm.dialedConnections[address] = conn
		bm.Unlock()
	}
	return nil
}

func (bm *BuffManager) WriteTo(ip string, port string, data []byte) (int, error) {
	address := formatAddress(ip, port)
	// Calculate how big the message is, using a consistent header size. MAKE THIS CONFIGURABLE in some sane way
	toWriteLen := UInt16ToByteArray(uint16(len(data)), MessageSizeToBitLength(4096))
	// Append the size to the message, so now it has a header
	toWrite := append(toWriteLen, data...)
	bm.Lock()
	defer bm.Unlock()
	return bm.dialedConnections[address].Write(toWrite)
}
<commit_msg>Simplifying interface, don't intend to support non TCP just yet.<commit_after>package buffstreams

import ()

import (
	"encoding/binary"
	"errors"
	"github.com/Sirupsen/logrus"
	"net"
	"sync"
)

type BuffManager struct {
	dialedConnections map[string]net.Conn
	listeningSockets  map[string]net.Listener
	sync.RWMutex
}

func New() *BuffManager {
	bm := &BuffManager{
		dialedConnections: make(map[string]net.Conn),
		listeningSockets:  make(map[string]net.Listener),
	}
	return bm
}

type ListenCallback func([]byte) error

func formatAddress(address string, port string) string {
	return address + ":" + port
}

func (bm *BuffManager) StartListening(port string, cb ListenCallback) error {
	address := formatAddress("", port)
	receiveSocket, err := net.Listen("tcp", address)
	if err != nil {
		return err
	}
	bm.startListening(address, receiveSocket, cb)
	return nil
}

func (bm *BuffManager) startListening(address string, socket net.Listener, cb ListenCallback) {
	bm.Lock()
	bm.listeningSockets[address] = socket
	bm.Unlock()

	go func(address string, listener net.Listener) {
		for {
			conn, err := listener.Accept()
			if err != nil {
				// alert error
				// Hmmm but how - I'm in goroutine land. Need a class level logger? Don't want to
				// tie people to the bleh default logger, also personally would prefer logrus for levels
			} else {
				// Hand this off and immediately listen for more
				go handleListenedConn(address, conn, cb)
			}
		}
	}(address, socket)
}

func handleListenedConn(address string, conn net.Conn, cb ListenCallback) {
	for {
		// Handle getting the data header
		logrus.Info("Reading")
		headerByteSize := MessageSizeToBitLength(4098) // MAKE THIS CONFIGURABLE once you have any idea how you're going to do that
		headerBuffer := make([]byte, headerByteSize)

		// First, read the number of bytes required to determine the message length
		_, err := readFromConnection(conn, headerBuffer)
		if err != nil && err.Error() == "EOF" {
			// Log the error we got from the call to read
			logrus.Error("First")
			logrus.Error(err)
			conn.Close()
			return
		}

		// Now turn that buffer of bytes into an integer - represnts size of message body
		msgLength, bytesParsed := binary.Uvarint(headerBuffer)
		// Not sure what the correct way to handle these errors are. For now, bomb out
		if bytesParsed == 0 {
			// "Buffer too small"
			logrus.Error("Second")
			logrus.Error(err)
			return
		} else if bytesParsed < 0 {
			// "Buffer overflow"
			logrus.Error("Third")
			logrus.Error(err)
			return
		}
		dataBuffer := make([]byte, msgLength)
		bytesLen, err := readFromConnection(conn, dataBuffer)
		if err != nil && err.Error() == "EOF" {
			// log the error from the call to read
			logrus.Error("Fourth")
			logrus.Error(err)
			conn.Close()
			return
		}

		// If we read bytes, there wasn't an error, or if there was it was only EOF
		// And readbytes + EOF is normal, just as readbytes + no err, next read 0 bytes EOF
		// So... we take action on the actual message data
		if bytesLen > 0 && (err == nil || (err != nil && err.Error() == "EOF")) {
			// I ultimately have some design choices here
			// Currently, I am invoking a delegate thats been passed down the stack
			// I could...
			// Keep it as is
			// Tie the protobuffs library deeper into it, and take a reference to
			// the type that the message we're serializing is, and do that work too
			// But you'd still need a callback, unless...
			// I just push it onto a queue (not a slow ass channel, but a queue)
			// which has a reference passed down to it, and the main process
			// spawns a goroutine to reap off the queue and handle those in parallel

			// Callback, atm
			logrus.Info("Callback")
			cb(dataBuffer)
		}
	}
}

func readFromConnection(reader net.Conn, buffer []byte) (int, error) {
	// This fills the buffer
	bytesLen, err := reader.Read(buffer)
	// Output the content of the bytes to the queue
	if bytesLen == 0 {
		if err != nil && err.Error() == "EOF" {
			// "End of individual transmission"
			// We're just done reading from that conn
			return bytesLen, err
		}
	}

	if err != nil {
		//"Underlying network failure?"
		// Not sure what this error would be, but it could exist and i've seen it handled
		// as a general case in other networking code. Following in the footsteps of (greatness|madness)
	}
	// Read some bytes, return the length
	return bytesLen, nil
}

func (bm *BuffManager) DialOut(ip string, port string) error {
	address := formatAddress(ip, port)
	if _, ok := bm.dialedConnections[address]; ok == true {
		// Need to clean it out on any error...
		return errors.New("You have a connection to this ip and port open already")
	}
	tcpAddr, err := net.ResolveTCPAddr("tcp", address)
	if err != nil {
		return err
	}
	conn, err := net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		return err
	} else {
		// Store the connection, it's valid
		bm.Lock()
		bm.dialedConnections[address] = conn
		bm.Unlock()
	}
	return nil
}

// Write a version of this that allows for automatic DialOuts, as well as one-shot connections that clean up afterward
func (bm *BuffManager) WriteTo(ip string, port string, data []byte) (int, error) {
	address := formatAddress(ip, port)
	// Calculate how big the message is, using a consistent header size. MAKE THIS CONFIGURABLE in some sane way
	toWriteLen := UInt16ToByteArray(uint16(len(data)), MessageSizeToBitLength(4096))
	// Append the size to the message, so now it has a header
	toWrite := append(toWriteLen, data...)
	bm.Lock()
	defer bm.Unlock()
	return bm.dialedConnections[address].Write(toWrite)
}
<|endoftext|>


<commit_before>// Copyright (C) 2014 The Protocol Authors.

// +build windows

package protocol

// Windows uses backslashes as file separator and disallows a bunch of
// characters in the filename

import (
	"path/filepath"
	"strings"
)

var disallowedCharacters = string([]rune{
	'<', '>', ':', '"', '|', '?', '*',
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
	11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
	21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
	31,
})

type nativeModel struct {
	next Model
}

func (m nativeModel) Index(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) {
	for i, f := range files {
		if strings.ContainsAny(f.Name, disallowedCharacters) {
			if f.IsDeleted() {
				// Don't complain if the file is marked as deleted, since it
				// can't possibly exist here anyway.
				continue
			}
			files[i].Flags |= FlagInvalid
			l.Warnf("File name %q contains invalid characters; marked as invalid.", f.Name)
		}
		files[i].Name = filepath.FromSlash(f.Name)
	}
	m.next.Index(deviceID, folder, files, flags, options)
}

func (m nativeModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) {
	for i, f := range files {
		if strings.ContainsAny(f.Name, disallowedCharacters) {
			if f.IsDeleted() {
				// Don't complain if the file is marked as deleted, since it
				// can't possibly exist here anyway.
				continue
			}
			files[i].Flags |= FlagInvalid
			l.Warnf("File name %q contains invalid characters; marked as invalid.", f.Name)
		}
		files[i].Name = filepath.FromSlash(files[i].Name)
	}
	m.next.IndexUpdate(deviceID, folder, files, flags, options)
}

func (m nativeModel) Request(deviceID DeviceID, folder string, name string, offset int64, size int, hash []byte, flags uint32, options []Option) ([]byte, error) {
	name = filepath.FromSlash(name)
	return m.next.Request(deviceID, folder, name, offset, size, hash, flags, options)
}

func (m nativeModel) ClusterConfig(deviceID DeviceID, config ClusterConfigMessage) {
	m.next.ClusterConfig(deviceID, config)
}

func (m nativeModel) Close(deviceID DeviceID, err error) {
	m.next.Close(deviceID, err)
}
<commit_msg>Remove duplication<commit_after>// Copyright (C) 2014 The Protocol Authors.

// +build windows

package protocol

// Windows uses backslashes as file separator and disallows a bunch of
// characters in the filename

import (
	"path/filepath"
	"strings"
)

var disallowedCharacters = string([]rune{
	'<', '>', ':', '"', '|', '?', '*',
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
	11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
	21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
	31,
})

type nativeModel struct {
	next Model
}

func (m nativeModel) Index(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) {
	fixupFiles(files)
	m.next.Index(deviceID, folder, files, flags, options)
}

func (m nativeModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) {
	fixupFiles(files)
	m.next.IndexUpdate(deviceID, folder, files, flags, options)
}

func (m nativeModel) Request(deviceID DeviceID, folder string, name string, offset int64, size int, hash []byte, flags uint32, options []Option) ([]byte, error) {
	name = filepath.FromSlash(name)
	return m.next.Request(deviceID, folder, name, offset, size, hash, flags, options)
}

func (m nativeModel) ClusterConfig(deviceID DeviceID, config ClusterConfigMessage) {
	m.next.ClusterConfig(deviceID, config)
}

func (m nativeModel) Close(deviceID DeviceID, err error) {
	m.next.Close(deviceID, err)
}

func fixupFiles(files []FileInfo) {
	for i, f := range files {
		if strings.ContainsAny(f.Name, disallowedCharacters) {
			if f.IsDeleted() {
				// Don't complain if the file is marked as deleted, since it
				// can't possibly exist here anyway.
				continue
			}
			files[i].Flags |= FlagInvalid
			l.Warnf("File name %q contains invalid characters; marked as invalid.", f.Name)
		}
		files[i].Name = filepath.FromSlash(files[i].Name)
	}
}
<|endoftext|>


<commit_before>// Package u5 currently provides a single utility to fetch the importers of a GoPackage via godoc.org API.
package u5

import (
	"encoding/json"
	"net/http"

	"github.com/shurcooL/go/gists/gist7480523"
)

type goPackage struct {
	Path     string
	Synopsis string
}

// Importers contains the list of Go packages that import a given Go package.
type Importers struct {
	Results []goPackage
}

// GetGodocOrgImporters fetches the importers of goPackage via godoc.org API.
func GetGodocOrgImporters(goPackage *gist7480523.GoPackage) (*Importers, error) {
	resp, err := http.Get("http://api.godoc.org/importers/" + goPackage.Bpkg.ImportPath)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var importers Importers
	if err := json.NewDecoder(resp.Body).Decode(&importers); err != nil {
		return nil, err
	}

	return &importers, nil
}
<commit_msg>GetGodocOrgImporters: Check for 200 status code, better documented types.<commit_after>// Package u5 currently provides a single utility to fetch the importers of a GoPackage via godoc.org API.
package u5

import (
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/shurcooL/go/gists/gist7480523"
)

// GoPackage represents a Go package.
type GoPackage struct {
	Path     string // Import path of the package.
	Synopsis string // Synopsis of the package.
}

// Importers contains the list of Go packages that import a given Go package.
type Importers struct {
	Results []GoPackage
}

// GetGodocOrgImporters fetches the importers of goPackage via godoc.org API.
func GetGodocOrgImporters(goPackage *gist7480523.GoPackage) (*Importers, error) {
	resp, err := http.Get("http://api.godoc.org/importers/" + goPackage.Bpkg.ImportPath)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("non-200 status code: %v", resp.StatusCode)
	}

	var importers Importers
	if err := json.NewDecoder(resp.Body).Decode(&importers); err != nil {
		return nil, err
	}

	return &importers, nil
}
<|endoftext|>